diff --git a/.github/workflows/twitter-release.yml b/.github/workflows/twitter-release.yml index 3e383031f..e20b3ff9c 100644 --- a/.github/workflows/twitter-release.yml +++ b/.github/workflows/twitter-release.yml @@ -17,3 +17,9 @@ jobs: consumer-secret: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} access-token: ${{ secrets.TWITTER_ACCESS_TOKEN }} access-token-secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} + - uses: smapiot/send-bluesky-post-action@v2 + name: Bluesky post new release + with: + status: "New ${{ github.event.repository.name }} release ${{ github.event.release.tag_name }}! ${{ github.event.release.html_url }} #microfrontends #piral #react #release" + bluesky-email: ${{ secrets.BLUESKY_EMAIL }} + bluesky-password: ${{ secrets.BLUESKY_PASSWORD }} diff --git a/.github/workflows/twitter-tip.yml b/.github/workflows/twitter-tip.yml index 994084b72..17f056554 100644 --- a/.github/workflows/twitter-tip.yml +++ b/.github/workflows/twitter-tip.yml @@ -23,3 +23,9 @@ jobs: consumer-secret: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} access-token: ${{ secrets.TWITTER_ACCESS_TOKEN }} access-token-secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} + - uses: smapiot/send-bluesky-post-action@v2 + name: Bluesky post daily tip + with: + status: "${{ steps.tweet.outputs.TIP }} #piral #dailytips #microfrontends" + bluesky-email: ${{ secrets.BLUESKY_EMAIL }} + bluesky-password: ${{ secrets.BLUESKY_PASSWORD }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d2f20b73..88c3efc0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Piral Changelog +## 1.7.3 (tbd) + +- Fixed `pilet upgrade` command with `npm` client not changing *package.json* +- Fixed shared dependency list from website emulator to exclude legacy dependencies +- Fixed issue with relative app path in emulator package generated on Windows +- Added `--allow-self-signed` flag to `piral-cli` commands using HTTP requests +- Added support for `react-router` v7 + ## 1.7.2 (November 8, 2024) - Fixed removal of `MutationEvent` in recent Chrome in `piral-blazor` (#724) by @dheid diff --git a/src/framework/piral-core/src/defaults/DefaultRouteSwitch_v7.tsx b/src/framework/piral-core/src/defaults/DefaultRouteSwitch_v7.tsx new file mode 100644 index 000000000..b6bd35182 --- /dev/null +++ b/src/framework/piral-core/src/defaults/DefaultRouteSwitch_v7.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +//@ts-ignore +import { Routes, Route } from 'react-router'; +import { RouteSwitchProps } from '../types'; + +export const DefaultRouteSwitch: React.FC = ({ paths, NotFound, ...props }) => { + return ( + + {paths.map(({ path, Component }) => ( + //@ts-ignore + } /> + ))} + { + //@ts-ignore + } /> + } + + ); +}; +DefaultRouteSwitch.displayName = 'DefaultRouteSwitch'; diff --git a/src/framework/piral-core/src/defaults/DefaultRouter_v7.tsx b/src/framework/piral-core/src/defaults/DefaultRouter_v7.tsx new file mode 100644 index 000000000..6d091bc4f --- /dev/null +++ b/src/framework/piral-core/src/defaults/DefaultRouter_v7.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; +//@ts-ignore +import { BrowserRouter } from 'react-router'; +import { RouterProps } from '../types'; + +export const DefaultRouter: React.FC = ({ children, publicPath }) => { + return {children}; +}; +DefaultRouter.displayName = 'DefaultRouter'; diff --git a/src/framework/piral-core/src/defaults/navigator_v7.tsx b/src/framework/piral-core/src/defaults/navigator_v7.tsx new file mode 100644 index 000000000..cfbfc5aa7 --- /dev/null +++ b/src/framework/piral-core/src/defaults/navigator_v7.tsx @@ -0,0 +1,97 @@ +import * as React from 'react'; +//@ts-ignore +import { UNSAFE_NavigationContext as RouterContext, Navigate, useLocation } from 'react-router'; +import { NavigationApi } from '../types'; + +let _nav: any; +const _noop = () => {}; + +export function useRouterContext() { + return React.useContext(RouterContext); +} + +export function useCurrentNavigation() { + const ctx: any = useRouterContext(); + const location = useLocation(); + + React.useEffect(() => { + if (_nav) { + window.dispatchEvent( + new CustomEvent('piral-navigate', { + detail: { + location, + }, + }), + ); + } + }, [location]); + + React.useEffect(() => { + _nav = ctx.navigator; + + return () => { + _nav = undefined; + }; + }, []); +} + +export function createRedirect(to: string) { + return () => ; +} + +export function createNavigation(publicPath: string): NavigationApi { + const enhance = (info) => ({ + ...info, + location: { + get href() { + return _nav.createHref(info.location); + }, + ...info.location, + }, + }); + + return { + get path() { + const loc = _nav ? _nav.location : location; + return loc.pathname; + }, + get url() { + const loc = _nav ? _nav.location : location; + return `${loc.pathname}${loc.search}${loc.hash}`; + }, + push(target, state) { + if (_nav) { + _nav.push(target, state); + } + }, + replace(target, state) { + if (_nav) { + _nav.replace(target, state); + } + }, + go(n) { + if (_nav) { + _nav.go(n); + } + }, + block(blocker) { + if (!_nav) { + return _noop; + } + return _nav.block((transition) => blocker(enhance(transition))); + }, + listen(listener) { + const handler = (e: CustomEvent) => listener(enhance(e.detail)); + + window.addEventListener('piral-navigate', handler); + + return () => { + window.removeEventListener('piral-navigate', handler); + }; + }, + get router() { + return _nav; + }, + publicPath, + }; +} diff --git a/src/framework/piral-core/src/tools/codegen.ts b/src/framework/piral-core/src/tools/codegen.ts index 2962f1b25..da4ea0b69 100644 --- a/src/framework/piral-core/src/tools/codegen.ts +++ b/src/framework/piral-core/src/tools/codegen.ts @@ -164,17 +164,26 @@ export function createDefaultState(imports: Array, exports: Array { const targetDir = dirname(entryModule); @@ -80,7 +99,7 @@ export async function addPiralInstancePilet(baseDir = process.cwd(), options: Ad if (piletJsonPath) { const piletJsonDir = dirname(piletJsonPath); const root = await findPiletRoot(piletJsonDir); - const packageName = await installPiralInstance(app, fullBase, root, npmClient, selected); + const packageName = await installPiralInstance(app, fullBase, root, npmClient, agent, selected); const piralInfo = await readPiralPackage(root, packageName); const isEmulator = checkAppShellPackage(piralInfo); diff --git a/src/tooling/piral-cli/src/apps/new-pilet.ts b/src/tooling/piral-cli/src/apps/new-pilet.ts index 8830f33c5..dc5ce615b 100644 --- a/src/tooling/piral-cli/src/apps/new-pilet.ts +++ b/src/tooling/piral-cli/src/apps/new-pilet.ts @@ -29,6 +29,8 @@ import { defaultSchemaVersion, piletJsonSchemaUrl, ensure, + getCertificate, + getAgent, } from '../common'; export interface NewPiletOptions { @@ -47,6 +49,16 @@ export interface NewPiletOptions { */ source?: string; + /** + * Defines a custom certificate for the website emulator. + */ + cert?: string; + + /** + * Allow self-signed certificates. + */ + allowSelfSigned?: boolean; + /** * Determines if files should be overwritten by the scaffolding. */ @@ -110,6 +122,8 @@ export const newPiletDefaults: NewPiletOptions = { bundlerName: 'none', variables: {}, name: undefined, + cert: undefined, + allowSelfSigned: config.allowSelfSigned, }; export async function newPilet(baseDir = process.cwd(), options: NewPiletOptions = {}) { @@ -126,6 +140,8 @@ export async function newPilet(baseDir = process.cwd(), options: NewPiletOptions variables = newPiletDefaults.variables, npmClient: defaultNpmClient = newPiletDefaults.npmClient, name = newPiletDefaults.name, + cert = newPiletDefaults.cert, + allowSelfSigned = newPiletDefaults.allowSelfSigned, } = options; ensure('baseDir', baseDir, 'string'); @@ -142,6 +158,8 @@ export async function newPilet(baseDir = process.cwd(), options: NewPiletOptions if (success) { const npmClient = await determineNpmClient(root, defaultNpmClient); + const ca = await getCertificate(cert); + const agent = getAgent({ ca, allowSelfSigned }); const projectName = name || basename(root); progress(`Scaffolding new pilet in %s ...`, root); @@ -196,7 +214,7 @@ always-auth=true`, ); const sourceName = source || `empty-piral@${cliVersion}`; - const packageName = await installPiralInstance(sourceName, fullBase, root, npmClient, true); + const packageName = await installPiralInstance(sourceName, fullBase, root, npmClient, agent, true); const piralInfo = await readPiralPackage(root, packageName); const isEmulator = checkAppShellPackage(piralInfo); const { preScaffold, postScaffold, files, template: preSelectedTemplate } = getPiletsInfo(piralInfo); diff --git a/src/tooling/piral-cli/src/apps/publish-pilet.ts b/src/tooling/piral-cli/src/apps/publish-pilet.ts index 853c4a870..e1ade8170 100644 --- a/src/tooling/piral-cli/src/apps/publish-pilet.ts +++ b/src/tooling/piral-cli/src/apps/publish-pilet.ts @@ -1,3 +1,4 @@ +import { Agent } from 'https'; import { relative, resolve } from 'path'; import { LogLevels, PiletSchemaVersion, PiletPublishSource, PublishScheme } from '../types'; import { @@ -17,6 +18,7 @@ import { triggerBuildPilet, getCertificate, ensure, + getAgent, } from '../common'; export interface PublishPiletOptions { @@ -52,6 +54,11 @@ export interface PublishPiletOptions { * Defines a custom certificate for the feed service. */ cert?: string; + + /** + * Allow self-signed certificates. + */ + allowSelfSigned?: boolean; /** * Sets the schema version of the pilet. Usually, the default one should be picked. @@ -110,7 +117,6 @@ export const publishPiletDefaults: PublishPiletOptions = { url: undefined, apiKey: undefined, fresh: false, - cert: undefined, logLevel: LogLevels.info, schemaVersion: undefined, mode: 'basic', @@ -118,6 +124,8 @@ export const publishPiletDefaults: PublishPiletOptions = { fields: {}, headers: {}, interactive: false, + cert: undefined, + allowSelfSigned: config.allowSelfSigned, }; async function getFiles( @@ -129,7 +137,7 @@ async function getFiles( logLevel: LogLevels, bundlerName: string, _?: Record, - ca?: Buffer, + agent?: Agent, hooks?: PublishPiletOptions['hooks'], ): Promise> { if (fresh) { @@ -181,14 +189,14 @@ async function getFiles( } case 'remote': { log('generalDebug_0003', `Download file from "${sources.join('", "')}".`); - const allFiles = await Promise.all(sources.map((s) => downloadFile(s, ca))); + const allFiles = await Promise.all(sources.map((s) => downloadFile(s, agent))); return allFiles.reduce((result, files) => [...result, ...files], []); } case 'npm': { log('generalDebug_0003', `View npm package "${sources.join('", "')}".`); const allUrls = await Promise.all(sources.map((s) => findNpmTarball(s))); log('generalDebug_0003', `Download file from "${allUrls.join('", "')}".`); - const allFiles = await Promise.all(allUrls.map((url) => downloadFile(url, ca))); + const allFiles = await Promise.all(allUrls.map((url) => downloadFile(url, agent))); return allFiles.reduce((result, files) => [...result, ...files], []); } } @@ -204,11 +212,12 @@ export async function publishPilet(baseDir = process.cwd(), options: PublishPile logLevel = publishPiletDefaults.logLevel, from = publishPiletDefaults.from, schemaVersion = publishPiletDefaults.schemaVersion, - cert = publishPiletDefaults.cert, fields = publishPiletDefaults.fields, headers = publishPiletDefaults.headers, mode = publishPiletDefaults.mode, interactive = publishPiletDefaults.interactive, + cert = publishPiletDefaults.cert, + allowSelfSigned = publishPiletDefaults.allowSelfSigned, _ = {}, hooks = {}, bundlerName, @@ -229,10 +238,11 @@ export async function publishPilet(baseDir = process.cwd(), options: PublishPile } const ca = await getCertificate(cert); + const agent = getAgent({ ca, allowSelfSigned }); log('generalDebug_0003', 'Getting the tgz files ...'); const sources = Array.isArray(source) ? source : [source]; - const files = await getFiles(fullBase, sources, from, fresh, schemaVersion, logLevel, bundlerName, _, ca, hooks); + const files = await getFiles(fullBase, sources, from, fresh, schemaVersion, logLevel, bundlerName, _, agent, hooks); const successfulUploads: Array = []; log('generalDebug_0003', 'Received available tgz files.'); @@ -249,7 +259,7 @@ export async function publishPilet(baseDir = process.cwd(), options: PublishPile if (content) { progress(`Publishing "%s" to "%s" ...`, file, url); - const result = await postFile(url, mode, apiKey, content, fields, headers, ca, interactive); + const result = await postFile(url, mode, apiKey, content, fields, headers, agent, interactive); if (result.success) { successfulUploads.push(file); diff --git a/src/tooling/piral-cli/src/apps/publish-piral.ts b/src/tooling/piral-cli/src/apps/publish-piral.ts index 202f07a55..322e02cf6 100644 --- a/src/tooling/piral-cli/src/apps/publish-piral.ts +++ b/src/tooling/piral-cli/src/apps/publish-piral.ts @@ -25,6 +25,7 @@ import { triggerBuildShell, publishPackageEmulator, ensure, + getAgent, } from '../common'; export interface PublishPiralOptions { @@ -58,6 +59,11 @@ export interface PublishPiralOptions { * Defines a custom certificate for the feed service. */ cert?: string; + + /** + * Allow self-signed certificates. + */ + allowSelfSigned?: boolean; /** * Places additional headers that should be posted to the feed service. @@ -107,10 +113,11 @@ export const publishPiralDefaults: PublishPiralOptions = { interactive: false, apiKey: undefined, fresh: false, - cert: undefined, mode: 'basic', headers: {}, type: 'emulator', + cert: undefined, + allowSelfSigned: config.allowSelfSigned, }; export async function publishPiral(baseDir = process.cwd(), options: PublishPiralOptions = {}) { @@ -121,10 +128,11 @@ export async function publishPiral(baseDir = process.cwd(), options: PublishPira fresh = publishPiralDefaults.fresh, url = config.url ?? publishPiralDefaults.url, apiKey = config.apiKeys?.[url] ?? config.apiKey ?? publishPiralDefaults.apiKey, - cert = publishPiralDefaults.cert, headers = publishPiralDefaults.headers, mode = publishPiralDefaults.mode, type = publishPiralDefaults.type, + cert = publishPiralDefaults.cert, + allowSelfSigned = publishPiralDefaults.allowSelfSigned, _ = {}, hooks = {}, bundlerName, @@ -144,6 +152,7 @@ export async function publishPiral(baseDir = process.cwd(), options: PublishPira } const ca = await getCertificate(cert); + const agent = getAgent({ ca, allowSelfSigned }); log('generalDebug_0003', 'Getting the files ...'); const entryFiles = await retrievePiralRoot(fullBase, './'); @@ -224,7 +233,7 @@ export async function publishPiral(baseDir = process.cwd(), options: PublishPira const files = await matchFiles(targetDir, '**/*'); progress(`Publishing release artifacts to "%s" ...`, url); - const result = await publishWebsiteEmulator(version, url, apiKey, mode, targetDir, files, interactive, headers, ca); + const result = await publishWebsiteEmulator(version, url, apiKey, mode, targetDir, files, interactive, headers, agent); if (!result.success) { fail('failedUploading_0064'); @@ -249,7 +258,7 @@ export async function publishPiral(baseDir = process.cwd(), options: PublishPira const files = await matchFiles(targetDir, '**/*'); progress(`Publishing emulator to "%s" ...`, url); - const result = await publishWebsiteEmulator(version, url, apiKey, mode, targetDir, files, interactive, headers, ca); + const result = await publishWebsiteEmulator(version, url, apiKey, mode, targetDir, files, interactive, headers, agent); if (!result.success) { fail('failedUploading_0064'); diff --git a/src/tooling/piral-cli/src/apps/run-emulator-piral.ts b/src/tooling/piral-cli/src/apps/run-emulator-piral.ts index e377c4d32..1f28c06e0 100644 --- a/src/tooling/piral-cli/src/apps/run-emulator-piral.ts +++ b/src/tooling/piral-cli/src/apps/run-emulator-piral.ts @@ -18,6 +18,8 @@ import { findPiralInstance, determineNpmClient, ensure, + getCertificate, + getAgent, } from '../common'; export interface RunEmulatorPiralOptions { @@ -61,6 +63,16 @@ export interface RunEmulatorPiralOptions { * The package registry to use for resolving the specified Piral app. */ registry?: string; + + /** + * Defines a custom certificate for the website emulator. + */ + cert?: string; + + /** + * Allow self-signed certificates. + */ + allowSelfSigned?: boolean; } export const runEmulatorPiralDefaults: RunEmulatorPiralOptions = { @@ -70,6 +82,8 @@ export const runEmulatorPiralDefaults: RunEmulatorPiralOptions = { strictPort: config.strictPort, registry: config.registry, npmClient: undefined, + cert: undefined, + allowSelfSigned: config.allowSelfSigned, }; function createTempDir() { @@ -94,13 +108,15 @@ export async function runEmulatorPiral(baseDir = process.cwd(), options: RunEmul logLevel = runEmulatorPiralDefaults.logLevel, npmClient: defaultNpmClient = runEmulatorPiralDefaults.npmClient, registry = runEmulatorPiralDefaults.registry, + cert = runEmulatorPiralDefaults.cert, + allowSelfSigned = runEmulatorPiralDefaults.allowSelfSigned, app, feed, } = options; - + ensure('baseDir', baseDir, 'string'); ensure('app', app, 'string'); - + const publicUrl = '/'; const api = config.piletApi; const fullBase = resolve(process.cwd(), baseDir); @@ -114,6 +130,8 @@ export async function runEmulatorPiral(baseDir = process.cwd(), options: RunEmul process.stdin?.setMaxListeners(16); const appRoot = await createTempDir(); + const ca = await getCertificate(cert); + const agent = getAgent({ ca, allowSelfSigned }); if (registry !== runEmulatorPiralDefaults.registry) { progress(`Setting up npm registry (%s) ...`, registry); @@ -128,10 +146,8 @@ always-auth=true`, } const npmClient = await determineNpmClient(appRoot, defaultNpmClient); - const packageName = await installPiralInstance(app, fullBase, appRoot, npmClient); - const piral = await findPiralInstance(packageName, appRoot, { - port: originalPort, - }); + const packageName = await installPiralInstance(app, fullBase, appRoot, npmClient, agent); + const piral = await findPiralInstance(packageName, appRoot, { port: originalPort }, agent); const port = await getAvailablePort(piral.port, strictPort); const krasBaseConfig = resolve(fullBase, krasrc); diff --git a/src/tooling/piral-cli/src/apps/upgrade-pilet.ts b/src/tooling/piral-cli/src/apps/upgrade-pilet.ts index c87dbf137..f7dddaf44 100644 --- a/src/tooling/piral-cli/src/apps/upgrade-pilet.ts +++ b/src/tooling/piral-cli/src/apps/upgrade-pilet.ts @@ -26,6 +26,9 @@ import { getPiletScaffoldData, retrievePiletData, ensure, + config, + getCertificate, + getAgent, } from '../common'; export interface UpgradePiletOptions { @@ -58,6 +61,16 @@ export interface UpgradePiletOptions { */ install?: boolean; + /** + * Defines a custom certificate for the website emulator. + */ + cert?: string; + + /** + * Allow self-signed certificates. + */ + allowSelfSigned?: boolean; + /** * Defines the used npm client. By default, "npm" is used * if no other client is autodetected. The autodetection @@ -79,6 +92,8 @@ export const upgradePiletDefaults: UpgradePiletOptions = { install: true, npmClient: undefined, variables: {}, + cert: undefined, + allowSelfSigned: config.allowSelfSigned, }; export async function upgradePilet(baseDir = process.cwd(), options: UpgradePiletOptions = {}) { @@ -90,6 +105,8 @@ export async function upgradePilet(baseDir = process.cwd(), options: UpgradePile install = upgradePiletDefaults.install, variables = upgradePiletDefaults.variables, npmClient: defaultNpmClient = upgradePiletDefaults.npmClient, + cert = upgradePiletDefaults.cert, + allowSelfSigned = upgradePiletDefaults.allowSelfSigned, } = options; ensure('baseDir', baseDir, 'string'); @@ -106,10 +123,12 @@ export async function upgradePilet(baseDir = process.cwd(), options: UpgradePile } const npmClient = await determineNpmClient(root, defaultNpmClient); + const ca = await getCertificate(cert); + const agent = getAgent({ ca, allowSelfSigned }); // in case we run from a user's CLI we want to allow updating const interactive = isInteractive(); - const { apps, piletPackage } = await retrievePiletData(root, undefined, interactive); + const { apps, piletPackage } = await retrievePiletData(root, undefined, agent, interactive); const { devDependencies = {}, dependencies = {}, source } = piletPackage; if (apps.length === 0) { @@ -144,7 +163,7 @@ export async function upgradePilet(baseDir = process.cwd(), options: UpgradePile if (!monorepoRef) { // only install the latest if the shell does come from remote progress(`Updating npm package to %s ...`, packageRef); - await installNpmPackage(npmClient, packageRef, root, '--no-save'); + await installNpmPackage(npmClient, packageRef, root); } const piralInfo = await readPiralPackage(root, sourceName); diff --git a/src/tooling/piral-cli/src/commands.ts b/src/tooling/piral-cli/src/commands.ts index ab42a711c..d274a4eac 100644 --- a/src/tooling/piral-cli/src/commands.ts +++ b/src/tooling/piral-cli/src/commands.ts @@ -218,6 +218,9 @@ const allCommands: Array> = [ .string('ca-cert') .describe('ca-cert', 'Sets a custom certificate authority to use, if any.') .default('ca-cert', apps.publishPiralDefaults.cert) + .boolean('allow-self-signed') + .describe('allow-self-signed', 'Indicates that self-signed certificates should be allowed.') + .default('allow-self-signed', apps.publishPiralDefaults.allowSelfSigned) .number('log-level') .describe('log-level', 'Sets the log level to use (1-5).') .default('log-level', apps.publishPiralDefaults.logLevel) @@ -250,6 +253,7 @@ const allCommands: Array> = [ logLevel: args['log-level'] as LogLevels, apiKey: args['api-key'] as string, cert: args['ca-cert'] as string, + allowSelfSigned: args['allow-self-signed'] as boolean, url: args.url as string, interactive: args.interactive as boolean, mode: args.mode as PublishScheme, @@ -687,6 +691,9 @@ const allCommands: Array> = [ .boolean('interactive') .describe('interactive', 'Defines if authorization tokens can be retrieved interactively.') .default('interactive', apps.publishPiletDefaults.interactive) + .boolean('allow-self-signed') + .describe('allow-self-signed', 'Indicates that self-signed certificates should be allowed.') + .default('allow-self-signed', apps.publishPiletDefaults.allowSelfSigned) .string('base') .default('base', process.cwd()) .describe('base', 'Sets the base directory. By default the current directory is used.'); @@ -698,6 +705,7 @@ const allCommands: Array> = [ url: args.url as string, logLevel: args['log-level'] as LogLevels, cert: args['ca-cert'] as string, + allowSelfSigned: args['allow-self-signed'] as boolean, bundlerName: args.bundler as string, fresh: args.fresh as boolean, from: args.from as PiletPublishSource, @@ -787,6 +795,12 @@ const allCommands: Array> = [ .choices('bundler', bundlerKeys) .describe('bundler', 'Sets the default bundler to install.') .default('bundler', apps.newPiletDefaults.bundlerName) + .string('ca-cert') + .describe('ca-cert', 'Sets a custom certificate authority to use, if any.') + .default('ca-cert', apps.newPiletDefaults.cert) + .boolean('allow-self-signed') + .describe('allow-self-signed', 'Indicates that self-signed certificates should be allowed.') + .default('allow-self-signed', apps.newPiletDefaults.allowSelfSigned) .option('vars', undefined) .describe('vars', 'Sets additional variables to be used when scaffolding.') .default('vars', apps.newPiletDefaults.variables) @@ -811,6 +825,8 @@ const allCommands: Array> = [ bundlerName: args.bundler as string, variables: args.vars as Record, name: args['name'] as string, + cert: args['ca-cert'] as string, + allowSelfSigned: args['allow-self-signed'] as boolean, }); }, }, @@ -819,7 +835,7 @@ const allCommands: Array> = [ alias: ['upgrade'], description: 'Upgrades an existing pilet to the latest version of the used Piral instance.', arguments: ['[target-version]'], - flags(argv) { + flags(argv: any) { return argv .positional('target-version', { type: 'string', @@ -842,6 +858,12 @@ const allCommands: Array> = [ .choices('npm-client', clientTypeKeys) .describe('npm-client', 'Sets the npm client to be used when upgrading.') .default('npm-client', apps.upgradePiletDefaults.npmClient) + .string('ca-cert') + .describe('ca-cert', 'Sets a custom certificate authority to use, if any.') + .default('ca-cert', apps.upgradePiletDefaults.cert) + .boolean('allow-self-signed') + .describe('allow-self-signed', 'Indicates that self-signed certificates should be allowed.') + .default('allow-self-signed', apps.upgradePiletDefaults.allowSelfSigned) .option('vars', undefined) .describe('vars', 'Sets additional variables to be used when scaffolding.') .default('vars', apps.upgradePiletDefaults.variables) @@ -858,6 +880,8 @@ const allCommands: Array> = [ install: args.install as boolean, npmClient: args['npm-client'] as NpmClientType, variables: args.vars as Record, + cert: args['ca-cert'] as string, + allowSelfSigned: args['allow-self-signed'] as boolean, }); }, }, @@ -916,6 +940,12 @@ const allCommands: Array> = [ .boolean('selected') .describe('selected', 'Defines if the provided Piral instance should be selected initially.') .default('selected', apps.addPiralInstancePiletDefaults.selected) + .string('ca-cert') + .describe('ca-cert', 'Sets a custom certificate authority to use, if any.') + .default('ca-cert', apps.addPiralInstancePiletDefaults.cert) + .boolean('allow-self-signed') + .describe('allow-self-signed', 'Indicates that self-signed certificates should be allowed.') + .default('allow-self-signed', apps.addPiralInstancePiletDefaults.allowSelfSigned) .string('base') .default('base', process.cwd()) .describe('base', 'Sets the base directory. By default the current directory is used.'); @@ -927,6 +957,8 @@ const allCommands: Array> = [ npmClient: args['npm-client'] as NpmClientType, app: args.app as string, source: args.source as string, + cert: args['ca-cert'] as string, + allowSelfSigned: args['allow-self-signed'] as boolean, }); }, }, @@ -995,6 +1027,12 @@ const allCommands: Array> = [ .choices('npm-client', clientTypeKeys) .describe('npm-client', 'Sets the npm client to be used when installing the emulator.') .default('npm-client', apps.runEmulatorPiralDefaults.npmClient) + .string('ca-cert') + .describe('ca-cert', 'Sets a custom certificate authority to use, if any.') + .default('ca-cert', apps.runEmulatorPiralDefaults.cert) + .boolean('allow-self-signed') + .describe('allow-self-signed', 'Indicates that self-signed certificates should be allowed.') + .default('allow-self-signed', apps.runEmulatorPiralDefaults.allowSelfSigned) .boolean('open') .describe('open', 'Opens the Piral instance directly in the browser.') .default('open', apps.runEmulatorPiralDefaults.open) @@ -1014,6 +1052,8 @@ const allCommands: Array> = [ logLevel: args['log-level'] as LogLevels, open: args.open as boolean, feed: args.feed as string, + cert: args['ca-cert'] as string, + allowSelfSigned: args['allow-self-signed'] as boolean, }); }, }, diff --git a/src/tooling/piral-cli/src/common/config.ts b/src/tooling/piral-cli/src/common/config.ts index 249e19387..9df93a833 100644 --- a/src/tooling/piral-cli/src/common/config.ts +++ b/src/tooling/piral-cli/src/common/config.ts @@ -73,6 +73,10 @@ export interface PiralCliConfig { * Npm registry. */ registry?: string; + /** + * Allow self-signed certificates. + */ + allowSelfSigned?: boolean; } export const config: PiralCliConfig = rc( @@ -89,6 +93,7 @@ export const config: PiralCliConfig = rc( validators: {}, schemaVersion: 'v2' as const, openBrowser: false, + allowSelfSigned: false, port: 1234, strictPort: false, language: 'ts' as const, diff --git a/src/tooling/piral-cli/src/common/constants.ts b/src/tooling/piral-cli/src/common/constants.ts index 4c293b10a..aeaaf7dfe 100644 --- a/src/tooling/piral-cli/src/common/constants.ts +++ b/src/tooling/piral-cli/src/common/constants.ts @@ -29,6 +29,7 @@ export const bundlerNames = [ 'webpack5' as const, 'vite' as const, 'vite5' as const, + 'vite6' as const, 'xbuild' as const, ]; export const declarationEntryExtensions = ['.html', '.pug', ...entryModuleExtensions]; diff --git a/src/tooling/piral-cli/src/common/emulator.ts b/src/tooling/piral-cli/src/common/emulator.ts index 269a0cc6c..84705121f 100644 --- a/src/tooling/piral-cli/src/common/emulator.ts +++ b/src/tooling/piral-cli/src/common/emulator.ts @@ -1,4 +1,4 @@ -import { join, resolve, relative, basename, extname } from 'path'; +import { join, resolve, relative, basename, posix } from 'path'; import { findDependencyVersion, copyScaffoldingFiles, isValidDependency, flattenExternals } from './package'; import { createPiralStubIndexIfNotExists } from './template'; import { filesTar, filesOnceTar, packageJson, piralJson, emulatorJson } from './constants'; @@ -139,9 +139,9 @@ export async function createEmulatorSources( version: cliVersion, generated: true, }, - main: `./${join(appDir, 'index.js')}`, - typings: `./${join(appDir, 'index.d.ts')}`, - app: `./${join(appDir, 'index.html')}`, + main: `./${posix.join(appDir, 'index.js')}`, + typings: `./${posix.join(appDir, 'index.d.ts')}`, + app: `./${posix.join(appDir, 'index.html')}`, peerDependencies: {}, optionalDependencies, devDependencies: { diff --git a/src/tooling/piral-cli/src/common/http.test.ts b/src/tooling/piral-cli/src/common/http.test.ts index 210ad6ad2..0e8cdac30 100644 --- a/src/tooling/piral-cli/src/common/http.test.ts +++ b/src/tooling/piral-cli/src/common/http.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vitest } from 'vitest'; -import { postFile, downloadFile } from './http'; +import { postFile, downloadFile, getAgent } from './http'; const apiUrl = 'http://sample.fooo.com/api/v1/pilet'; @@ -80,7 +80,7 @@ vitest.mock('../external', async () => { }, }, }, - FormData: (await vitest.importActual('form-data') as any).default, + FormData: ((await vitest.importActual('form-data')) as any).default, }; }); @@ -155,10 +155,11 @@ describe('HTTP Module', () => { it('downloadFile calls results in error', async () => { errorOther = true; - let result = await downloadFile('http://sample.com/', Buffer.from('example')); + const agent = getAgent({ ca: Buffer.from('example') }); + let result = await downloadFile('http://sample.com/', agent); expect(result.length).toBe(0); errorOther = false; - result = await downloadFile('http://sample.com/', Buffer.from('example')); + result = await downloadFile('http://sample.com/', agent); expect(result.length).toBe(0); }); }); diff --git a/src/tooling/piral-cli/src/common/http.ts b/src/tooling/piral-cli/src/common/http.ts index aba9131da..1b14e7bc1 100644 --- a/src/tooling/piral-cli/src/common/http.ts +++ b/src/tooling/piral-cli/src/common/http.ts @@ -100,8 +100,28 @@ export function getAuthorizationHeaders(scheme: PublishScheme, key: string) { return {}; } -export function downloadFile(target: string, ca?: Buffer): Promise> { - const httpsAgent = ca ? new Agent({ ca }) : undefined; +export interface AgentOptions { + ca?: Buffer; + allowSelfSigned?: boolean; +} + +export async function getDefaultAgent() { + const ca = await getCertificate(); + const allowSelfSigned = config.allowSelfSigned; + return getAgent({ ca, allowSelfSigned }); +} + +export function getAgent({ allowSelfSigned, ca }: AgentOptions) { + if (ca) { + return new Agent({ ca }); + } else if (allowSelfSigned) { + return new Agent({ rejectUnauthorized: false }); + } else { + return undefined; + } +} + +export function downloadFile(target: string, httpsAgent: Agent): Promise> { return axios.default .get(target, { responseType: 'stream', @@ -206,10 +226,9 @@ export async function postForm( key: string, formData: FormDataObj, customHeaders: Record = {}, - ca?: Buffer, + httpsAgent?: Agent, interactive = false, ): Promise { - const httpsAgent = ca ? new Agent({ ca }) : undefined; const form = createAxiosForm(formData); const headers: Record = { @@ -237,7 +256,7 @@ export async function postForm( error, interactive, httpsAgent, - (mode, token) => postForm(target, mode, token, formData, customHeaders, ca, false), + (mode, token) => postForm(target, mode, token, formData, customHeaders, httpsAgent, false), (status, statusText, response) => { if (status === 500) { log('failedHttpPost_0065', response); @@ -266,9 +285,9 @@ export function postFile( file: Buffer, customFields: Record = {}, customHeaders: Record = {}, - ca?: Buffer, + agent?: Agent, interactive = false, ): Promise { const data: FormDataObj = { ...customFields, file: [file, 'pilet.tgz'] }; - return postForm(target, scheme, key, data, customHeaders, ca, interactive); + return postForm(target, scheme, key, data, customHeaders, agent, interactive); } diff --git a/src/tooling/piral-cli/src/common/npm.test.ts b/src/tooling/piral-cli/src/common/npm.test.ts index b87bf6e1e..02491a456 100644 --- a/src/tooling/piral-cli/src/common/npm.test.ts +++ b/src/tooling/piral-cli/src/common/npm.test.ts @@ -202,39 +202,39 @@ describe('npm Module', () => { it('installs a package using the npm command line tool without a target', async () => { wrongCase = false; - await installNpmPackage('npm', 'foo', 'latest').then((result) => expect(result).toEqual(jsonValueString)); + await installNpmPackage({ direct: 'npm' }, 'foo', 'latest').then((result) => expect(result).toEqual(jsonValueString)); wrongCase = true; - await installNpmPackage('npm', 'foo', 'latest').then((result) => expect(result).not.toEqual(jsonValueString)); + await installNpmPackage({ direct: 'npm' }, 'foo', 'latest').then((result) => expect(result).not.toEqual(jsonValueString)); }); it('installs a package using the npm command line tool without a version', async () => { wrongCase = false; - await installNpmPackage('npm', 'foo').then((result) => expect(result).toEqual(jsonValueString)); + await installNpmPackage({ direct: 'npm' }, 'foo').then((result) => expect(result).toEqual(jsonValueString)); wrongCase = true; - await installNpmPackage('npm', 'foo').then((result) => expect(result).not.toEqual(jsonValueString)); + await installNpmPackage({ direct: 'npm' }, 'foo').then((result) => expect(result).not.toEqual(jsonValueString)); }); it('installs a package using the Yarn command line tool without a version', async () => { wrongCase = false; - await installNpmPackage('yarn', 'foo').then((result) => expect(result).toEqual(jsonValueString)); + await installNpmPackage({ direct: 'yarn' }, 'foo').then((result) => expect(result).toEqual(jsonValueString)); wrongCase = true; - await installNpmPackage('yarn', 'foo').then((result) => expect(result).not.toEqual(jsonValueString)); + await installNpmPackage({ direct: 'yarn' }, 'foo').then((result) => expect(result).not.toEqual(jsonValueString)); }); it('installs a package using the Pnpm command line tool without a version', async () => { wrongCase = false; - await installNpmPackage('pnpm', 'foo').then((result) => expect(result).toEqual(jsonValueString)); + await installNpmPackage({ direct: 'pnpm' }, 'foo').then((result) => expect(result).toEqual(jsonValueString)); wrongCase = true; - await installNpmPackage('pnpm', 'foo').then((result) => expect(result).not.toEqual(jsonValueString)); + await installNpmPackage({ direct: 'pnpm' }, 'foo').then((result) => expect(result).not.toEqual(jsonValueString)); }); it('installs a package using the npm command line tool with some flag', async () => { wrongCase = false; - await installNpmPackage('npm', 'foo', '1.3', '.', '--a=b').then((result) => + await installNpmPackage({ direct: 'pnpm' }, 'foo', '1.3', '.', '--a=b').then((result) => expect(result).toEqual(jsonValueString), ); wrongCase = true; - await installNpmPackage('npm', 'foo', '1.3', '.', '--a=b').then((result) => + await installNpmPackage({ direct: 'pnpm' }, 'foo', '1.3', '.', '--a=b').then((result) => expect(result).not.toEqual(jsonValueString), ); }); @@ -281,23 +281,23 @@ describe('npm Module', () => { it('install dependencies with npm client', async () => { wrongCase = false; - await installNpmDependencies('npm').then((result) => expect(result).toEqual(jsonValueString)); + await installNpmDependencies({ direct: 'npm' }).then((result) => expect(result).toEqual(jsonValueString)); wrongCase = true; - await installNpmDependencies('npm').then((result) => expect(result).not.toEqual(jsonValueString)); + await installNpmDependencies({ direct: 'npm' }).then((result) => expect(result).not.toEqual(jsonValueString)); }); it('install dependencies with pnpm client', async () => { wrongCase = false; - await installNpmDependencies('pnpm').then((result) => expect(result).toEqual(jsonValueString)); + await installNpmDependencies({ direct: 'pnpm' }).then((result) => expect(result).toEqual(jsonValueString)); wrongCase = true; - await installNpmDependencies('pnpm').then((result) => expect(result).not.toEqual(jsonValueString)); + await installNpmDependencies({ direct: 'pnpm' }).then((result) => expect(result).not.toEqual(jsonValueString)); }); it('install dependencies with yarn client', async () => { wrongCase = false; - await installNpmDependencies('yarn').then((result) => expect(result).toEqual(jsonValueString)); + await installNpmDependencies({ direct: 'yarn' }).then((result) => expect(result).toEqual(jsonValueString)); wrongCase = true; - await installNpmDependencies('yarn').then((result) => expect(result).not.toEqual(jsonValueString)); + await installNpmDependencies({ direct: 'yarn' }).then((result) => expect(result).not.toEqual(jsonValueString)); }); it('create npm package', async () => { diff --git a/src/tooling/piral-cli/src/common/npm.ts b/src/tooling/piral-cli/src/common/npm.ts index 8e6bab679..094a79c4a 100644 --- a/src/tooling/piral-cli/src/common/npm.ts +++ b/src/tooling/piral-cli/src/common/npm.ts @@ -5,10 +5,10 @@ import { config } from './config'; import { legacyCoreExternals, frameworkLibs, defaultRegistry, packageJson } from './constants'; import { inspectPackage } from './inspect'; import { readJson, checkExists } from './io'; -import { clients, detectClients, isWrapperClient } from '../npm-clients'; +import { clients, detectDirectClients, detectWrapperClients, isDirectClient, isWrapperClient } from '../npm-clients'; import { clientTypeKeys } from '../helpers'; import { getModulePath } from '../external'; -import { PackageType, NpmClientType } from '../types'; +import { PackageType, NpmClientType, NpmClient, NpmDirectClientType, NpmWapperClientType } from '../types'; const gitPrefix = 'git+'; const filePrefix = 'file:'; @@ -63,56 +63,88 @@ async function detectMonorepoRoot(root: string): Promise<[] | [string, NpmClient return []; } -/** - * For details about how this works consult issue - * https://github.com/smapiot/piral/issues/203 - * @param root The project's root directory. - */ -export async function determineNpmClient(root: string, selected?: NpmClientType): Promise { - if (!selected || !clientTypeKeys.includes(selected)) { - log('generalDebug_0003', 'No npm client selected. Checking for lock or config files ...'); +async function determineWrapperClient(root: string): Promise { + const searchedClients = await detectWrapperClients(root); + const foundClients = searchedClients.filter((m) => m.result).map((m) => m.client); - const searchedClients = await detectClients(root); - const foundClients = searchedClients.filter((m) => m.result); - - log( - 'generalDebug_0003', - `Results of the lock file check: ${searchedClients.map((m) => `${m.client}=${m.result}`).join(', ')}`, - ); + if (foundClients.length > 0) { + const [client] = foundClients; if (foundClients.length > 1) { - const wrapperClient = foundClients.find((m) => isWrapperClient(m.client)); - - if (wrapperClient) { - const { client } = wrapperClient; - log('generalDebug_0003', `Found valid wrapper client via lock or config file: "${client}".`); - } + log( + 'generalWarning_0001', + `Found multiple clients via their lock or config files: "${foundClients.join('", "')}".`, + ); } - if (foundClients.length > 0) { - const { client } = foundClients[0]; + log('generalDebug_0003', `Found valid direct client via lock or config file: "${client}".`); + return client; + } - if (foundClients.length > 1) { - const clientStr = `"${foundClients.map((m) => m.client).join('", "')}"`; - log('generalWarning_0001', `Found multiple clients via their lock or config files: ${clientStr}.`); - } + const defaultClient = config.npmClient; - log('generalDebug_0003', `Found valid direct client via lock or config file: "${client}".`); - return client; - } + if (isWrapperClient(defaultClient)) { + log('generalDebug_0003', `Using the default client: "${defaultClient}".`); + return defaultClient; + } + + return undefined; +} - const defaultClient = config.npmClient; +async function determineDirectClient(root: string): Promise { + const searchedClients = await detectDirectClients(root); + const foundClients = searchedClients.filter((m) => m.result).map((m) => m.client); - if (clientTypeKeys.includes(defaultClient)) { - log('generalDebug_0003', `Using the default client: "${defaultClient}".`); - return defaultClient; + if (foundClients.length > 0) { + const [client] = foundClients; + + if (foundClients.length > 1) { + log( + 'generalWarning_0001', + `Found multiple clients via their lock or config files: "${foundClients.join('", "')}".`, + ); } - log('generalDebug_0003', 'Using the fallback "npm" client.'); - return 'npm'; + log('generalDebug_0003', `Found valid direct client via lock or config file: "${client}".`); + return client; + } + + const defaultClient = config.npmClient; + + if (isDirectClient(defaultClient)) { + log('generalDebug_0003', `Using the default client: "${defaultClient}".`); + return defaultClient; } - return selected; + log('generalDebug_0003', 'Using the fallback "npm" client.'); + return 'npm'; +} + +/** + * For details about how this works consult issue + * https://github.com/smapiot/piral/issues/203 + * @param root The project's root directory. + */ +export async function determineNpmClient(root: string, selected?: NpmClientType): Promise { + if (!selected || !clientTypeKeys.includes(selected)) { + log('generalDebug_0003', 'No npm client selected. Checking for lock or config files ...'); + const [direct, wrapper] = await Promise.all([determineDirectClient(root), determineWrapperClient(root)]); + return { + direct, + wrapper, + }; + } else if (isDirectClient(selected)) { + return { + proposed: selected, + direct: selected, + }; + } else { + return { + proposed: selected, + direct: await determineDirectClient(root), + wrapper: selected, + }; + } } export async function isMonorepoPackageRef(refName: string, root: string): Promise { @@ -126,35 +158,37 @@ export async function isMonorepoPackageRef(refName: string, root: string): Promi return false; } -export function installNpmDependencies(client: NpmClientType, target = '.'): Promise { - const { installDependencies } = clients[client]; +export function installNpmDependencies(client: NpmClient, target = '.'): Promise { + const { installDependencies } = clients[client.direct]; return installDependencies(target); } export async function installNpmPackageFromOptionalRegistry( packageRef: string, - target = '.', + target: string, registry: string, ): Promise { + const client = await determineNpmClient(target, 'npm'); + try { - await installNpmPackage('npm', packageRef, target, '--registry', registry); + await installNpmPackage(client, packageRef, target, '--registry', registry); } catch (e) { if (registry === defaultRegistry) { throw e; } - await installNpmPackage('npm', packageRef, target, '--registry', defaultRegistry); + await installNpmPackage(client, packageRef, target, '--registry', defaultRegistry); } } export async function uninstallNpmPackage( - client: NpmClientType, + client: NpmClient, packageRef: string, target = '.', ...flags: Array ): Promise { try { - const { uninstallPackage } = clients[client]; + const { uninstallPackage } = clients[client.direct]; return await uninstallPackage(packageRef, target, ...flags); } catch (ex) { log( @@ -166,13 +200,13 @@ export async function uninstallNpmPackage( } export async function installNpmPackage( - client: NpmClientType, + client: NpmClient, packageRef: string, target = '.', ...flags: Array ): Promise { try { - const { installPackage } = clients[client]; + const { installPackage } = clients[client.direct]; return await installPackage(packageRef, target, ...flags); } catch (ex) { log( @@ -183,8 +217,8 @@ export async function installNpmPackage( } } -export function initNpmProject(client: NpmClientType, projectName: string, target: string) { - const { initProject } = clients[client]; +export function initNpmProject(client: NpmClient, projectName: string, target: string) { + const { initProject } = clients[client.wrapper || client.direct]; return initProject(projectName, target); } diff --git a/src/tooling/piral-cli/src/common/package.ts b/src/tooling/piral-cli/src/common/package.ts index b7c2f93af..92b7e78cd 100644 --- a/src/tooling/piral-cli/src/common/package.ts +++ b/src/tooling/piral-cli/src/common/package.ts @@ -1,3 +1,4 @@ +import { Agent } from 'https'; import { resolve, join, extname, basename, dirname, relative } from 'path'; import { log, fail } from './log'; import { cliVersion } from './info'; @@ -161,7 +162,8 @@ async function loadPiralInstance(root: string, details?: PiralInstanceDetails): export async function findPiralInstance( proposedApp: string, rootDir: string, - details?: PiralInstanceDetails, + details: PiralInstanceDetails, + agent: Agent, interactive = false, ) { const path = findPackageRoot(proposedApp, rootDir); @@ -172,13 +174,13 @@ export async function findPiralInstance( if (url) { log('generalDebug_0003', `Updating the emulator from remote "${url}" ...`); - await updateFromEmulatorWebsite(root, url, interactive); + await updateFromEmulatorWebsite(root, url, agent, interactive); } return await loadPiralInstance(root, details); } else if (url) { log('generalDebug_0003', `Piral instance not installed yet - trying from remote "${url}" ...`); - const emulator = await scaffoldFromEmulatorWebsite(rootDir, url); + const emulator = await scaffoldFromEmulatorWebsite(rootDir, url, agent); return await loadPiralInstance(emulator.path, details); } @@ -190,6 +192,7 @@ export async function findPiralInstances( piletPackage: PiletPackageData, piletDefinition: undefined | PiletDefinition, rootDir: string, + agent: Agent, interactive?: boolean, ) { if (proposedApps) { @@ -208,7 +211,7 @@ export async function findPiralInstances( if (proposedApps.length > 0) { return Promise.all( proposedApps.map((proposedApp) => - findPiralInstance(proposedApp, rootDir, piletDefinition?.piralInstances?.[proposedApp], interactive), + findPiralInstance(proposedApp, rootDir, piletDefinition?.piralInstances?.[proposedApp], agent, interactive), ), ); } @@ -280,11 +283,7 @@ export async function getPiralPackage(app: string, data: PiralInstanceData, vers }; } -async function getAvailableFiles( - root: string, - name: string, - dirName: string, -): Promise> { +async function getAvailableFiles(root: string, name: string, dirName: string): Promise> { const source = getPiralPath(root, name); const tgz = `${dirName}.tar`; log('generalDebug_0003', `Checking if "${tgz}" exists in "${source}" ...`); @@ -780,13 +779,13 @@ export async function findPiletRoot(proposedRoot: string) { return dirname(packageJsonPath); } -export async function retrievePiletData(target: string, app?: string, interactive?: boolean) { +export async function retrievePiletData(target: string, app?: string, agent?: Agent, interactive?: boolean) { const piletJsonPath = await findFile(target, piletJson); const proposedRoot = piletJsonPath ? dirname(piletJsonPath) : target; const root = await findPiletRoot(proposedRoot); const piletPackage = await readJson(root, packageJson); const piletDefinition: PiletDefinition = piletJsonPath && (await readJson(proposedRoot, piletJson)); - const appPackages = await findPiralInstances(app && [app], piletPackage, piletDefinition, root, interactive); + const appPackages = await findPiralInstances(app && [app], piletPackage, piletDefinition, root, agent, interactive); const apps: Array = []; for (const appPackage of appPackages) { diff --git a/src/tooling/piral-cli/src/common/release.ts b/src/tooling/piral-cli/src/common/release.ts index 6574f99ff..db1302e44 100644 --- a/src/tooling/piral-cli/src/common/release.ts +++ b/src/tooling/piral-cli/src/common/release.ts @@ -1,3 +1,4 @@ +import { Agent } from 'https'; import { basename, dirname, relative } from 'path'; import { readBinary } from './io'; import { publishNpmPackage } from './npm'; @@ -39,7 +40,7 @@ export async function publishWebsiteEmulator( files: Array, interactive: boolean, headers?: Record, - ca?: Buffer, + agent?: Agent, ) { const data: FormDataObj = { version, @@ -53,5 +54,5 @@ export async function publishWebsiteEmulator( data[relPath] = [content, fileName]; } - return await postForm(url, mode, apiKey, data, headers, ca, interactive); + return await postForm(url, mode, apiKey, data, headers, agent, interactive); } diff --git a/src/tooling/piral-cli/src/common/shell.ts b/src/tooling/piral-cli/src/common/shell.ts index ac4c6af42..1b0b2bba9 100644 --- a/src/tooling/piral-cli/src/common/shell.ts +++ b/src/tooling/piral-cli/src/common/shell.ts @@ -1,10 +1,11 @@ +import { Agent } from 'https'; import { progress } from './log'; import { packageJson, piletJson } from './constants'; import { readJson, updateExistingJson, writeJson } from './io'; import { scaffoldFromEmulatorWebsite } from './website'; import { combinePackageRef, getPackageName, getPackageVersion } from './npm'; import { dissectPackageName, installNpmPackage, isLinkedPackage } from './npm'; -import { NpmClientType, PackageType, PiralInstanceDetails } from '../types'; +import { NpmClient, PackageType, PiralInstanceDetails } from '../types'; async function updatePiletJson(target: string, appName: string, appDetails: PiralInstanceDetails) { const oldContent = await readJson(target, piletJson); @@ -29,7 +30,7 @@ async function setupPiralInstance( hadVersion: boolean, rootDir: string, sourceVersion: string, - npmClient: NpmClientType, + npmClient: NpmClient, ) { if (!isLinkedPackage(sourceName, type, hadVersion, rootDir)) { const packageRef = combinePackageRef(sourceName, sourceVersion, type); @@ -54,13 +55,14 @@ export async function installPiralInstance( usedSource: string, baseDir: string, rootDir: string, - npmClient: NpmClientType, + npmClient: NpmClient, + agent: Agent, selected?: boolean, ): Promise { const [sourceName, sourceVersion, hadVersion, type] = await dissectPackageName(baseDir, usedSource); if (type === 'remote') { - const emulator = await scaffoldFromEmulatorWebsite(rootDir, sourceName); + const emulator = await scaffoldFromEmulatorWebsite(rootDir, sourceName, agent); const packageName = emulator.name; await updatePiletJson(rootDir, packageName, { selected, diff --git a/src/tooling/piral-cli/src/common/website.ts b/src/tooling/piral-cli/src/common/website.ts index 98ef3af0a..3f248893b 100644 --- a/src/tooling/piral-cli/src/common/website.ts +++ b/src/tooling/piral-cli/src/common/website.ts @@ -1,7 +1,7 @@ import { Agent } from 'https'; import { posix, relative, resolve } from 'path'; import { createPiralStubIndexIfNotExists } from './template'; -import { getAuthorizationHeaders, getAxiosOptions, getCertificate, handleAxiosError } from './http'; +import { getAuthorizationHeaders, getAxiosOptions, handleAxiosError } from './http'; import { packageJson } from './constants'; import { updateConfig } from './config'; import { ForceOverwrite } from './enums'; @@ -11,7 +11,7 @@ import { progress, log } from './log'; import { axios, isInteractive } from '../external'; import { EmulatorWebsiteManifestFiles, EmulatorWebsiteManifest } from '../types'; -async function requestManifest(url: string, interactive: boolean, httpsAgent?: Agent) { +async function requestManifest(url: string, httpsAgent: Agent, interactive: boolean) { const opts = getAxiosOptions(url); try { @@ -26,7 +26,7 @@ async function requestManifest(url: string, interactive: boolean, httpsAgent?: A value: headers.authorization, }, }); - return await requestManifest(url, false, httpsAgent); + return await requestManifest(url, httpsAgent, false); }); } } @@ -92,6 +92,7 @@ async function createEmulatorFiles( peerDependencies: {}, optionalDependencies: emulatorJson.dependencies.optional, devDependencies: emulatorJson.dependencies.included, + sharedDependencies: [Object.keys(emulatorJson.importmap.imports)], }, true, ); @@ -105,13 +106,11 @@ async function createEmulatorFiles( await downloadEmulatorFiles(manifestUrl, targetDir, appDir, emulatorJson.files, httpsAgent); } -export async function updateFromEmulatorWebsite(targetDir: string, manifestUrl: string, interactive: boolean) { +export async function updateFromEmulatorWebsite(targetDir: string, manifestUrl: string, httpsAgent: Agent, interactive: boolean) { progress(`Updating emulator from %s ...`, manifestUrl); - const ca = await getCertificate(); - const httpsAgent = ca ? new Agent({ ca }) : undefined; try { - const response = await requestManifest(manifestUrl, interactive, httpsAgent); + const response = await requestManifest(manifestUrl, httpsAgent, interactive); const nextEmulator: EmulatorWebsiteManifest = response.data; const currentEmulator = await readJson(targetDir, packageJson); @@ -130,12 +129,10 @@ export async function updateFromEmulatorWebsite(targetDir: string, manifestUrl: } } -export async function scaffoldFromEmulatorWebsite(rootDir: string, manifestUrl: string) { +export async function scaffoldFromEmulatorWebsite(rootDir: string, manifestUrl: string, httpsAgent: Agent) { progress(`Downloading emulator from %s ...`, manifestUrl); - const ca = await getCertificate(); - const httpsAgent = ca ? new Agent({ ca }) : undefined; const interactive = isInteractive(); - const response = await requestManifest(manifestUrl, interactive, httpsAgent); + const response = await requestManifest(manifestUrl, httpsAgent, interactive); const emulatorJson: EmulatorWebsiteManifest = response.data; const targetDir = resolve(rootDir, 'node_modules', emulatorJson.name); const appDir = resolve(targetDir, 'app'); diff --git a/src/tooling/piral-cli/src/npm-clients/index.ts b/src/tooling/piral-cli/src/npm-clients/index.ts index a90ea1e30..5114660db 100644 --- a/src/tooling/piral-cli/src/npm-clients/index.ts +++ b/src/tooling/piral-cli/src/npm-clients/index.ts @@ -1,5 +1,6 @@ import { dirname, resolve } from 'path'; import { findFile } from '../common/io'; +import type { NpmClientType, NpmDirectClientType, NpmWapperClientType } from '../types'; import * as lerna from './lerna'; import * as npm from './npm'; @@ -19,20 +20,26 @@ export const clients = { bun, }; -type ClientName = keyof typeof clients; +const directClients: Array = ['npm', 'pnp', 'yarn', 'pnpm', 'bun']; +const wrapperClients: Array = ['lerna', 'rush']; -const directClients = ['npm', 'pnp', 'yarn', 'pnpm', 'bun']; +export function isWrapperClient(client: NpmClientType): client is NpmWapperClientType { + return wrapperClients.includes(client as any); +} -export function isWrapperClient(client: ClientName) { - return !directClients.includes(client); +export function isDirectClient(client: NpmClientType): client is NpmDirectClientType { + return directClients.includes(client as any); } -export async function detectClients(root: string) { +async function detectClients( + root: string, + clientNames: Array, +): Promise> { const packageJson = await findFile(resolve(root, '..'), 'package.json'); const stopDir = packageJson ? dirname(packageJson) : undefined; return await Promise.all( - Object.keys(clients).map(async (client: ClientName) => { + clientNames.map(async (client) => { const result = await clients[client].detectClient(root, stopDir); return { client, @@ -41,3 +48,11 @@ export async function detectClients(root: string) { }), ); } + +export function detectDirectClients(root: string) { + return detectClients(root, directClients); +} + +export function detectWrapperClients(root: string) { + return detectClients(root, wrapperClients); +} diff --git a/src/tooling/piral-cli/src/types/internal.ts b/src/tooling/piral-cli/src/types/internal.ts index 2aee89914..83f7f3c68 100644 --- a/src/tooling/piral-cli/src/types/internal.ts +++ b/src/tooling/piral-cli/src/types/internal.ts @@ -1,5 +1,11 @@ import type { LogLevels } from './common'; -import type { ImportmapVersions, PiletSchemaVersion } from './public'; +import type { + ImportmapVersions, + NpmClientType, + NpmDirectClientType, + NpmWapperClientType, + PiletSchemaVersion, +} from './public'; export interface PiralInstanceDetails { selected?: boolean; @@ -34,3 +40,21 @@ export interface FileInfo { * 2. The (short) error message */ export type QuickMessage = [LogLevels, string, string]; + +/** + * Result of identifying an npm client in a project. + */ +export interface NpmClient { + /** + * The proposed client - can be anything. + */ + proposed?: NpmClientType; + /** + * The direct npm client. + */ + direct: NpmDirectClientType; + /** + * The wrapper npm client, if any. + */ + wrapper?: NpmWapperClientType; +} diff --git a/src/tooling/piral-cli/src/types/public.ts b/src/tooling/piral-cli/src/types/public.ts index 93552ae78..6d0c9e67a 100644 --- a/src/tooling/piral-cli/src/types/public.ts +++ b/src/tooling/piral-cli/src/types/public.ts @@ -261,7 +261,11 @@ export type PiletBuildType = 'default' | 'standalone' | 'manifest'; export type PackageType = 'registry' | 'file' | 'git' | 'remote'; -export type NpmClientType = 'npm' | 'yarn' | 'pnp' | 'pnpm' | 'lerna' | 'rush' | 'bun'; +export type NpmDirectClientType = 'npm' | 'yarn' | 'pnp' | 'pnpm' | 'bun'; + +export type NpmWapperClientType = 'lerna' | 'rush'; + +export type NpmClientType = NpmDirectClientType | NpmWapperClientType; export type Framework = 'piral' | 'piral-core' | 'piral-base'; diff --git a/src/tooling/publish-microfrontend/src/http.ts b/src/tooling/publish-microfrontend/src/http.ts index cbb40782d..0be89a7fe 100644 --- a/src/tooling/publish-microfrontend/src/http.ts +++ b/src/tooling/publish-microfrontend/src/http.ts @@ -34,10 +34,22 @@ export interface PostFormResult { export type FormDataObj = Record; +export interface AgentOptions { + ca?: Buffer; + allowSelfSigned?: boolean; +} -export async function downloadFile(url: string, ca?: Buffer): Promise> { - const httpsAgent = ca ? new Agent({ ca }) : undefined; +export function getAgent({ allowSelfSigned, ca }: AgentOptions) { + if (ca) { + return new Agent({ ca }); + } else if (allowSelfSigned) { + return new Agent({ rejectUnauthorized: false }); + } else { + return undefined; + } +} +export async function downloadFile(url: string, httpsAgent?: Agent): Promise> { try { const res = await axios.get(url, { responseType: 'stream', @@ -58,10 +70,9 @@ export function postForm( key: string, formData: FormDataObj, customHeaders: Record = {}, - ca?: Buffer, + httpsAgent?: Agent, interactive = false, ): Promise { - const httpsAgent = ca ? new Agent({ ca }) : undefined; const form = new FormData(); Object.keys(formData).forEach((key) => { @@ -124,7 +135,7 @@ export function postForm( if (typeof interactiveAuth === 'string') { return getTokenInteractively(interactiveAuth, httpsAgent).then(({ mode, token }) => - postForm(target, mode, token, formData, customHeaders, ca, false), + postForm(target, mode, token, formData, customHeaders, httpsAgent, false), ); } } @@ -173,9 +184,9 @@ export function postFile( file: Buffer, customFields: Record = {}, customHeaders: Record = {}, - ca?: Buffer, + agent?: Agent, interactive = false, ): Promise { const data: FormDataObj = { ...customFields, file: [file, 'microfrontend.tgz'] }; - return postForm(target, scheme, key, data, customHeaders, ca, interactive); + return postForm(target, scheme, key, data, customHeaders, agent, interactive); } diff --git a/src/tooling/publish-microfrontend/src/index.ts b/src/tooling/publish-microfrontend/src/index.ts index 9337542a0..21fe965c6 100644 --- a/src/tooling/publish-microfrontend/src/index.ts +++ b/src/tooling/publish-microfrontend/src/index.ts @@ -7,13 +7,14 @@ import { basename } from 'path'; import { readFile } from 'fs/promises'; import { progress, fail, logDone, logFail, logInfo } from './log'; import { getCa, getFiles } from './utils'; -import { postFile } from './http'; +import { getAgent, postFile } from './http'; const current = process.cwd(); const defaultArgs = rc('microfrontend', { url: undefined, apiKey: undefined, cert: undefined, + allowSelfSigned: false, mode: 'basic', from: 'local', fields: {}, @@ -34,6 +35,9 @@ const args = yargs .string('cert') .describe('cert', 'Sets a custom certificate authority to use, if any.') .default('cert', defaultArgs.cert) + .boolean('allow-self-signed') + .describe('allow-self-signed', 'Indicates that self-signed certificates should be allowed.') + .default('allow-self-signed', defaultArgs.allowSelfSigned) .choices('mode', publishModeKeys) .describe('mode', 'Sets the authorization mode to use.') .default('mode', defaultArgs.mode) @@ -52,10 +56,11 @@ const args = yargs .default('interactive', defaultArgs.interactive).argv; async function run() { - const { cert, source, from, url, 'api-key': apiKey, headers, fields, interactive, mode } = args; + const { cert, source, from, url, 'api-key': apiKey, 'allow-self-signed': allowSelfSigned, headers, fields, interactive, mode } = args; const sources = Array.isArray(source) ? source : [source]; const ca = await getCa(cert); - const files = await getFiles(current, sources, from, ca); + const agent = getAgent({ ca, allowSelfSigned }); + const files = await getFiles(current, sources, from, agent); const successfulUploads: Array = []; if (files.length === 0) { @@ -76,7 +81,7 @@ async function run() { content, fields, headers, - ca, + agent, interactive, ); diff --git a/src/tooling/publish-microfrontend/src/utils.ts b/src/tooling/publish-microfrontend/src/utils.ts index abf51b86e..27568f677 100644 --- a/src/tooling/publish-microfrontend/src/utils.ts +++ b/src/tooling/publish-microfrontend/src/utils.ts @@ -1,4 +1,5 @@ import glob from 'glob'; +import { Agent } from 'https'; import { existsSync, statSync } from 'fs'; import { stat, readFile, readdir, copyFile, rm } from 'fs/promises'; import { dirname, basename, resolve } from 'path'; @@ -83,7 +84,7 @@ export async function getFiles( baseDir: string, sources: Array, from: string, - ca: Buffer, + agent: Agent, ): Promise> { switch (from) { case 'local': { @@ -127,12 +128,12 @@ export async function getFiles( return allMatches.filter(isFile); } case 'remote': { - const allFiles = await Promise.all(sources.map((s) => downloadFile(s, ca))); + const allFiles = await Promise.all(sources.map((s) => downloadFile(s, agent))); return allFiles.reduce((result, files) => [...result, ...files], []); } case 'npm': { const allUrls = await Promise.all(sources.map((s) => findTarball(s))); - const allFiles = await Promise.all(allUrls.map((url) => downloadFile(url, ca))); + const allFiles = await Promise.all(allUrls.map((url) => downloadFile(url, agent))); return allFiles.reduce((result, files) => [...result, ...files], []); } } diff --git a/yarn.lock b/yarn.lock index 20dcda4cb..3f4db40ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2047,21 +2047,21 @@ dependencies: "@octokit/openapi-types" "^18.0.0" -"@pidoc/components@0.18.3": - version "0.18.3" - resolved "https://registry.yarnpkg.com/@pidoc/components/-/components-0.18.3.tgz#5b4b76ab4db83ceda097a05194f7dc0efd574688" - integrity sha512-SmyH6YQHGmSepH17Dz7evvA5bIekYU0kSEUaBTNibS64Py77PzPa9JXnaFSxK+5s/Drew+YMAYdN/CdACDeOTg== +"@pidoc/components@0.18.4": + version "0.18.4" + resolved "https://registry.yarnpkg.com/@pidoc/components/-/components-0.18.4.tgz#b39c860925684342a538d38c4ebd393f6521d768" + integrity sha512-wC8v1X/Fv12KrnoGf0U1LZenSqrXT3xdiXf5eHq9HMkI8pWng8wTGD1goMJ0pKYimZxW8dIHgo8cPy2OMzToUw== dependencies: flexsearch "0.6.32" stickyfilljs "^2.1.0" -"@pidoc/core@^0.18.3": - version "0.18.3" - resolved "https://registry.yarnpkg.com/@pidoc/core/-/core-0.18.3.tgz#2a139bc8fb797aaad16e7706d08e3b5ebff796c3" - integrity sha512-sLR5Ws6pdXOgRyYseVrdZKdmgaY1rVWQbWlhJTp3kaKxaz719bP+ZDK1k7jN/L+b4UFENSSL53moNZzqEUbcMw== +"@pidoc/core@^0.18.4": + version "0.18.4" + resolved "https://registry.yarnpkg.com/@pidoc/core/-/core-0.18.4.tgz#1cad7f92a23598d1b2c9478b6cc980d13269879f" + integrity sha512-eA6WUhW5TJvYqek0lWT4J9e4LENiAgAlPhzTIzszQYBBT1Q/D9qhOrnlE0oWYt7Y/Gb2ctJdYqa9VRblgU5U9A== dependencies: "@asciidoctor/core" "^3.0.4" - "@pidoc/components" "0.18.3" + "@pidoc/components" "0.18.4" chokidar "^3.6.0" keyword-extractor "0.0.28" markdown-it "^14.1.0" @@ -2082,9 +2082,9 @@ markdown-it-sup "^2.0.0" markdown-it-task-checkbox "^1.0.6" markdown-it-video "^0.6.3" - pug "^3.0.2" - yaml "^2.4.1" - yargs "^15.4.1" + pug "^3" + yaml "^2" + yargs "^17" "@pkgjs/parseargs@^0.11.0": version "0.11.0" @@ -4810,9 +4810,9 @@ create-react-class@^15.6.0: object-assign "^4.1.1" cross-spawn@^7.0.0, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -9917,7 +9917,7 @@ pug-walk@^2.0.0: resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe" integrity sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ== -pug@^3.0.2: +pug@^3: version "3.0.3" resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.3.tgz#e18324a314cd022883b1e0372b8af3a1a99f7597" integrity sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g== @@ -11084,7 +11084,16 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11129,7 +11138,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11150,6 +11159,13 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12216,7 +12232,7 @@ workerpool@^3.1.1: object-assign "4.1.1" rsvp "^4.8.4" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12234,6 +12250,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -12344,10 +12369,10 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^2.4.1: - version "2.5.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" - integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== +yaml@^2: + version "2.6.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" + integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== yargs-parser@21.1.1, yargs-parser@^21.1.1: version "21.1.1" @@ -12367,7 +12392,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@17.7.2, yargs@^17.2.1, yargs@^17.6.2: +yargs@17.7.2, yargs@^17, yargs@^17.2.1, yargs@^17.6.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==