diff --git a/.semgrepignore b/.semgrepignore index 854bd41f2..0afde493f 100644 --- a/.semgrepignore +++ b/.semgrepignore @@ -38,3 +38,5 @@ packages/core/src/api.js # in the file-load helper anyway. No user input flows here. packages/core/test/unit/maestro-hierarchy.test.js packages/core/test/unit/maestro-hierarchy.parity.test.js + +packages/cli-command/test/noRequireBinding.test.js diff --git a/packages/cli-command/package.json b/packages/cli-command/package.json index ba0216129..08d8edd57 100644 --- a/packages/cli-command/package.json +++ b/packages/cli-command/package.json @@ -27,6 +27,7 @@ ".": "./dist/index.js", "./flags": "./dist/flags.js", "./utils": "./dist/utils.js", + "./intelliStory": "./dist/intelliStory.js", "./test/helpers": "./test/helpers.js" }, "scripts": { @@ -38,6 +39,10 @@ "dependencies": { "@percy/config": "1.32.4", "@percy/core": "1.32.4", - "@percy/logger": "1.32.4" + "@percy/logger": "1.32.4", + "glob-to-regexp": "^0.4.1" + }, + "optionalDependencies": { + "snyk-nodejs-lockfile-parser": "2.7.1" } } diff --git a/packages/cli-command/src/graphTrace.js b/packages/cli-command/src/graphTrace.js new file mode 100644 index 000000000..6561d9703 --- /dev/null +++ b/packages/cli-command/src/graphTrace.js @@ -0,0 +1,117 @@ +import fs from 'fs'; +import path from 'path'; +import url from 'url'; + +const TEMPLATE_PATH = path.resolve(url.fileURLToPath(import.meta.url), '../graphTraceTemplate.html'); + +function templateKindOf(v) { + if (v.changed) return 'is_relevant'; + switch (v.kind) { + case 'dependency': return 'package'; + case 'component': return 'component'; + case 'story': return 'story'; + default: return 'component'; + } +} + +const KIND_RANK = { package: 0, component: 1, is_relevant: 1, story: 2 }; + +function computeLayout(rawVertices, edges, transitiveClosure) { + const n = rawVertices.length; + const vertices = rawVertices.map((v, i) => ({ + index: i, + name: v.file_path, + kind: v.kind, + changed: !!v.changed, + row: 0, + col: 0 + })); + + const incomingMax = new Array(n).fill(0); + for (const triple of transitiveClosure) { + const [u, v, val] = triple; + if (u === v || val <= 0) continue; + if (v < 0 || v >= n) continue; + if (val > incomingMax[v]) incomingMax[v] = val; + } + for (let i = 0; i < n; i++) { + vertices[i].col = vertices[i].kind === 'dependency' ? 0 : incomingMax[i] + 1; + } + + const iterations = n + 2; + for (let iter = 0; iter < iterations; iter++) { + let changed = false; + for (const [s, t] of edges) { + if (s < 0 || s >= n || t < 0 || t >= n) continue; + if (vertices[s].col < vertices[t].col) continue; + vertices[t].col = vertices[s].col + 1; + changed = true; + } + if (!changed) break; + } + + let furthestNonStory = 0; + for (const v of vertices) { + if (v.kind === 'story') continue; + if (v.col > furthestNonStory) furthestNonStory = v.col; + } + for (const v of vertices) { + if (v.kind !== 'story') continue; + if (v.col < furthestNonStory + 1) v.col = furthestNonStory + 1; + } + + const groups = new Map(); + for (const v of vertices) { + let list = groups.get(v.col); + if (!list) groups.set(v.col, list = []); + list.push(v); + } + const rankOf = v => { + const r = KIND_RANK[templateKindOf(v)]; + /* istanbul ignore next */ + return r === undefined ? 99 : r; + }; + for (const list of groups.values()) { + list.sort((a, b) => { + const ra = rankOf(a); + const rb = rankOf(b); + if (ra !== rb) return ra - rb; + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); + list.forEach((v, row) => { v.row = row; }); + } + + return vertices.map(v => ({ + index: v.index, + name: v.name, + row: v.row, + col: v.col, + kind: templateKindOf(v) + })); +} + +const LS = String.fromCharCode(0x2028); +const PS = String.fromCharCode(0x2029); +function safeJson(obj) { + return JSON.stringify(obj) + .replace(/<\//g, '<\\/') + .replace(/`; + + function hostileLine() { + return embeddedJson(renderGraphTraceHtml({ + vertices: [{ kind: 'component', file_path: hostile }], + edges: [], + transitiveClosureMatrixSparse: [] + }), 'vertices'); + } + + it('escapes " { + let line = hostileLine(); + expect(line).not.toContain(''); + expect(line).toContain('<\\/script>'); + }); + + it('escapes HTML comment open and close markers', () => { + let line = hostileLine(); + expect(line).toContain('<\\!--'); + expect(line).toContain('--\\>'); + }); + + it('escapes U+2028 and U+2029 line/paragraph separators', () => { + let line = hostileLine(); + expect(line).not.toContain(LS); + expect(line).not.toContain(PS); + expect(line).toContain('\\u2028'); + expect(line).toContain('\\u2029'); + }); + + it('escapes only the dangerous sequences, leaving the payload intact', () => { + let restored = hostileLine() + .split('<\\!--').join(''); + expect(JSON.parse(restored)[0].name).toEqual(hostile); + }); + }); +}); diff --git a/packages/cli-command/test/index.test.js b/packages/cli-command/test/index.test.js new file mode 100644 index 000000000..c92773f27 --- /dev/null +++ b/packages/cli-command/test/index.test.js @@ -0,0 +1,15 @@ +import * as cliCommand from '../src/index.js'; + +describe('index (public exports)', () => { + it('re-exports the command, legacy, intelliStory and common-package surface', () => { + expect(typeof cliCommand.default).toBe('function'); + expect(typeof cliCommand.command).toBe('function'); + expect(cliCommand._resetShutdownForTest).toBeDefined(); + expect(cliCommand.legacyCommand).toBeDefined(); + expect(cliCommand.flags).toBeDefined(); + expect(typeof cliCommand.applyIntelliStory).toBe('function'); + expect(cliCommand.IntelliStoryBailError.prototype).toBeInstanceOf(Error); + expect(cliCommand.PercyConfig).toBeDefined(); + expect(cliCommand.logger).toBeDefined(); + }); +}); diff --git a/packages/cli-command/test/intelliStory.test.js b/packages/cli-command/test/intelliStory.test.js new file mode 100644 index 000000000..48c800156 --- /dev/null +++ b/packages/cli-command/test/intelliStory.test.js @@ -0,0 +1,691 @@ +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import cp from 'child_process'; +import { mockfs } from './helpers.js'; +import { + IntelliStoryBailError, + validateAndReadStats, + getBaselineAndAffectedNodes, + assertNoDotStorybookChange, + assertNoBailOnChanges, + enforceUntraced, + getAffectedPackages, + getAffectedFileLocations, + extractStorybookPaths, + runGraphGeneration, + maybeWriteTrace, + applyIntelliStory +} from '../src/intelliStory.js'; + +const NODE_MAJOR = parseInt(process.versions.node.split('.')[0], 10); + +const itPosix = path.sep === '/' ? it : xit; + +function git(args, cwd) { + let r = cp.spawnSync('git', args, { cwd, encoding: 'utf8' }); + if (r.status !== 0) throw new Error(`git ${args.join(' ')} failed: ${r.stderr || r.stdout}`); + return r.stdout; +} + +function makeRepo(seed, changed) { + let dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'intelliStory-'))); + git(['init', '-q'], dir); + git(['config', 'user.email', 'test@example.com'], dir); + git(['config', 'user.name', 'Test'], dir); + let writeAll = files => { + for (let [rel, content] of Object.entries(files)) { + let abs = path.join(dir, rel); + fs.mkdirSync(path.dirname(abs), { recursive: true }); + fs.writeFileSync(abs, content); + } + }; + writeAll(seed); + git(['add', '-A'], dir); + git(['commit', '-qm', 'base'], dir); + let baseSha = git(['rev-parse', 'HEAD'], dir).trim(); + if (changed) { + writeAll(changed); + git(['add', '-A'], dir); + git(['commit', '-qm', 'change'], dir); + } + return { dir, baseSha }; +} + +function mockLog() { + return { + debug: jasmine.createSpy('debug'), + info: jasmine.createSpy('info'), + warn: jasmine.createSpy('warn') + }; +} + +async function expectBail(fn, substr) { + let err; + try { + await fn(); + } catch (e) { + err = e; + } + expect(err).toBeInstanceOf(IntelliStoryBailError); + if (substr) expect(err.message).toContain(substr); + return err; +} + +const identity = p => p; + +describe('intelliStory', () => { + describe('validateAndReadStats()', () => { + const log = mockLog(); + + it('bails when the statsFile is not a .json filename', async () => { + await expectBail( + () => validateAndReadStats('/build', 'stats.txt', '/root', log), + 'invalid statsFile'); + }); + + it('bails when the stats file is missing from the build dir', async () => { + await mockfs({ '/build': null }); + await expectBail( + () => validateAndReadStats('/build', undefined, '/root', log), + 'not found in build directory'); + }); + + it('bails when the resolved stats path is a directory', async () => { + await mockfs({ '/build/enriched-stats.json': null }); + await expectBail( + () => validateAndReadStats('/build', undefined, '/root', log), + 'is not a regular file'); + }); + + it('reads files and modules from a valid stats file (buildId no longer required)', async () => { + await mockfs({ '/build/enriched-stats.json': JSON.stringify({ modules: [] }) }); + let res = await validateAndReadStats('/build', undefined, '/root', log); + expect(res).toEqual({ files: [], modules: [] }); + }); + + it('anchors a traversal-prefixed statsFile inside the build dir via basename', async () => { + await mockfs({ '/build/foo.json': JSON.stringify({ modules: [] }) }); + let res = await validateAndReadStats('/build', '../../etc/foo.json', '/root', log); + expect(res).toEqual({ files: [], modules: [] }); + }); + + it('streams modules: indexes src refs, leaves module refs, drops node_modules/string-id and id-less entries', async () => { + await mockfs({ + '/build/enriched-stats.json': JSON.stringify({ + buildId: 'b', + modules: [ + { + id: '/root/src/A.js', + imports: [ + { type: 'src', source: '/root/src/B.js', loc: [{ start: 38, end: 38 }, { start: 40, end: 42 }] }, + { type: 'src', source: '/root/src/B.js' }, + { type: 'src', source: 'lib/rel.js' }, + { type: 'module', source: 'react' } + ], + passThroughExports: [{ type: 'src', source: '/root/src/C.js', loc: [{ start: 5, end: 5 }] }], + nonPassThroughExports: [{ type: 'module', source: 'lodash' }] + }, + { id: '/root/node_modules/dep/index.js' }, + {} + ] + }) + }); + + let res = await validateAndReadStats('/build', undefined, '/root', log); + + expect(res.files).toEqual([path.join('src', 'A.js'), path.join('src', 'B.js'), path.join('src', 'C.js')]); + expect(res.modules.length).toEqual(2); + expect(res.modules[0].id).toEqual(0); + expect(res.modules[0].imports[0].source).toEqual(1); + expect(res.modules[0].imports[1].source).toEqual(1); + expect(res.modules[0].imports[2].source).toEqual('lib/rel.js'); + expect(res.modules[0].imports[3].source).toEqual('react'); + expect(res.modules[0].imports[0].loc).toEqual([[38, 38], [40, 42]]); + expect(res.modules[0].passThroughExports[0].source).toEqual(2); + expect(res.modules[0].passThroughExports[0].loc).toEqual([[5, 5]]); + expect(res.modules[0].nonPassThroughExports).toEqual([{ type: 'module', source: 'lodash' }]); + expect(res.modules[1]).toEqual({}); + }); + }); + + describe('getBaselineAndAffectedNodes()', () => { + const log = mockLog(); + + it('uses an explicit baseline and skips the API base lookup', async () => { + let lookup = jasmine.createSpy('getIntelliStorySnapshotNameToCommit'); + let percy = { client: { getIntelliStorySnapshotNameToCommit: lookup } }; + + let res = await getBaselineAndAffectedNodes(percy, 'HEAD', log); + + expect(res.baseRef).toEqual('HEAD'); + expect(res.baselineSnapshots).toBeNull(); + expect(res.affectedNodes).toEqual([]); + expect(lookup).not.toHaveBeenCalled(); + }); + + it('falls back to the predicted base build commit when no baseline is set', async () => { + let percy = { + client: { + getIntelliStorySnapshotNameToCommit: async () => ({ + base_build_commit_sha: 'HEAD', + snapshots: { 'Button: primary': 'approved' } + }) + } + }; + + let res = await getBaselineAndAffectedNodes(percy, undefined, log); + + expect(res.baseRef).toEqual('HEAD'); + expect(res.affectedNodes).toEqual([]); + expect(res.baselineSnapshots).toEqual({ 'Button: primary': 'approved' }); + }); + + it('defaults baselineSnapshots to {} when the API omits the snapshot map', async () => { + let percy = { client: { getIntelliStorySnapshotNameToCommit: async () => ({ base_build_commit_sha: 'HEAD' }) } }; + let res = await getBaselineAndAffectedNodes(percy, undefined, log); + expect(res.baseRef).toEqual('HEAD'); + expect(res.baselineSnapshots).toEqual({}); + }); + + it('bails when the API predicts no base commit and no baseline is set', async () => { + let percy = { client: { getIntelliStorySnapshotNameToCommit: async () => ({}) } }; + await expectBail( + () => getBaselineAndAffectedNodes(percy, undefined, log), + 'could not predict a base build commit'); + }); + + it('bails on an unsafe baseline ref before shelling out to git', async () => { + let percy = { client: {} }; + await expectBail( + () => getBaselineAndAffectedNodes(percy, '--upload-pack=evil', log), + 'unsafe baseline ref'); + }); + }); + + describe('assertNoDotStorybookChange()', () => { + it('throws when a changed path lives under .storybook', () => { + expect(() => assertNoDotStorybookChange(['src/a.js', '.storybook/preview.js'])) + .toThrowMatching(e => e instanceof IntelliStoryBailError && e.message.includes('.storybook')); + }); + + it('matches a .storybook segment regardless of separator', () => { + expect(() => assertNoDotStorybookChange(['a\\.storybook\\main.js'])).toThrow(); + }); + + it('does not throw when nothing touches .storybook', () => { + expect(() => assertNoDotStorybookChange(['src/a.js', 'src/b.css'])).not.toThrow(); + }); + }); + + describe('assertNoBailOnChanges()', () => { + it('is a no-op when no patterns are configured', () => { + expect(() => assertNoBailOnChanges(['yarn.lock'], undefined)).not.toThrow(); + expect(() => assertNoBailOnChanges(['yarn.lock'], [])).not.toThrow(); + }); + + it('bails when a changed file matches a glob pattern', () => { + expect(() => assertNoBailOnChanges(['yarn.lock'], ['*.lock'])) + .toThrowMatching(e => e instanceof IntelliStoryBailError && e.message.includes('yarn.lock')); + }); + + it('bails on an exact (non-glob) pattern match', () => { + expect(() => assertNoBailOnChanges(['config/settings.js'], ['config/settings.js'])).toThrow(); + }); + + it('does not bail when nothing matches', () => { + expect(() => assertNoBailOnChanges(['src/index.js'], ['*.css'])).not.toThrow(); + }); + + it('treats an over-long glob as non-matching instead of throwing', () => { + expect(() => assertNoBailOnChanges(['yarn.lock'], ['*'.repeat(600)])).not.toThrow(); + }); + }); + + describe('enforceUntraced()', () => { + it('returns the list unchanged when no patterns are configured', () => { + let nodes = ['src/a.js', 'docs/readme.md']; + expect(enforceUntraced(nodes, undefined)).toEqual(nodes); + expect(enforceUntraced(nodes, [])).toEqual(nodes); + }); + + it('drops paths matching an untraced glob', () => { + let nodes = ['src/a.js', 'docs/readme.md', 'CHANGELOG.md']; + expect(enforceUntraced(nodes, ['**/*.md'])).toEqual(['src/a.js']); + }); + + it('keeps paths that do not match', () => { + let nodes = ['src/a.snap', 'src/a.js']; + expect(enforceUntraced(nodes, ['*.snap'])).toEqual(['src/a.snap', 'src/a.js']); + }); + }); + + describe('getAffectedPackages()', () => { + const log = mockLog(); + + it('returns [] when no manifest files changed', async () => { + expect(await getAffectedPackages(['src/a.js', 'src/b.css'], 'HEAD', '/root', log)).toEqual([]); + }); + + it('bails when manifest changes span multiple directories', async () => { + await expectBail( + () => getAffectedPackages(['package.json', 'sub/package.json'], 'HEAD', '/root', log), + 'span multiple directories'); + }); + + it('bails when the manifest dir has no lockfile', async () => { + await mockfs({ '/root/pkg': null }); + await expectBail( + () => getAffectedPackages(['pkg/package.json'], 'HEAD', '/root', log), + 'no lockfile present there'); + }); + + it('bails when the manifest dir has multiple lockfiles', async () => { + await mockfs({ + '/root/pkg/yarn.lock': 'yarn', + '/root/pkg/package-lock.json': '{}' + }); + await expectBail( + () => getAffectedPackages(['pkg/package.json'], 'HEAD', '/root', log), + 'multiple lockfiles'); + }); + }); + + describe('extractStorybookPaths()', () => { + it('maps, dedupes and drops snapshots without an importPath', () => { + let log = mockLog(); + let snapshots = [ + { importPath: 'src/A.stories.js' }, + { importPath: 'src/A.stories.js' }, + { importPath: 'src/B.stories.js' }, + { name: 'no-path' } + ]; + expect(extractStorybookPaths(snapshots, identity, log)) + .toEqual(['src/A.stories.js', 'src/B.stories.js']); + }); + + it('warns when no snapshot carries an importPath', () => { + let log = mockLog(); + expect(extractStorybookPaths([{ name: 'x' }], identity, log)).toEqual([]); + expect(log.warn).toHaveBeenCalledTimes(1); + }); + + it('warns with an empty sample when given no snapshots at all', () => { + let log = mockLog(); + expect(extractStorybookPaths([], identity, log)).toEqual([]); + expect(log.warn).toHaveBeenCalledTimes(1); + }); + }); + + describe('runGraphGeneration()', () => { + it('starts the job and returns the graph payload on done', async () => { + let log = mockLog(); + let generate = jasmine.createSpy('generateIntelliStoryGraph'); + let data = { affected_stories: ['src/A.stories.js'] }; + let percy = { + client: { + generateIntelliStoryGraph: generate, + getStatus: async () => ({ status: 'done', data }) + } + }; + + let payload = { + files: ['f'], + modules: [{ id: 0 }], + storybookPaths: ['p'], + affectedNodes: ['a'], + affectedFileLocations: { 0: [[3, 3], [6, 7]] } + }; + + let res = await runGraphGeneration(percy, 'bld-1', payload, log); + + expect(res).toBe(data); + expect(generate).toHaveBeenCalledWith('bld-1', payload); + }); + + it('bails when the job does not reach done', async () => { + let log = mockLog(); + let percy = { + client: { + generateIntelliStoryGraph: async () => {}, + getStatus: async () => ({ status: 'failed' }) + } + }; + await expectBail( + () => runGraphGeneration(percy, 'bld-1', { files: [], modules: [], storybookPaths: [], affectedNodes: [] }, log), + 'did not complete'); + }); + }); + + describe('maybeWriteTrace()', () => { + const fullData = { + affected_stories: [], + vertices: [{ kind: 'component', file_path: 'A.jsx' }], + edges: [], + transitive_closure_matrix_sparse: [] + }; + + it('renders and writes trace.html when enabled with a complete payload', () => { + let log = mockLog(); + let write = spyOn(fs, 'writeFileSync'); + + maybeWriteTrace(true, fullData, log); + + expect(write).toHaveBeenCalledTimes(1); + let [tracePath, html] = write.calls.mostRecent().args; + expect(tracePath).toEqual(path.resolve(process.cwd(), 'trace.html')); + expect(html).toContain('const vertices'); + expect(log.info).toHaveBeenCalled(); + }); + + it('does nothing when trace is disabled', () => { + let log = mockLog(); + let write = spyOn(fs, 'writeFileSync'); + maybeWriteTrace(false, fullData, log); + expect(write).not.toHaveBeenCalled(); + }); + + it('does nothing when the graph payload is incomplete', () => { + let log = mockLog(); + let write = spyOn(fs, 'writeFileSync'); + maybeWriteTrace(true, { affected_stories: [], vertices: [], edges: [] }, log); + expect(write).not.toHaveBeenCalled(); + }); + + it('warns (without throwing) when the write fails', () => { + let log = mockLog(); + spyOn(fs, 'writeFileSync').and.throwError('disk full'); + expect(() => maybeWriteTrace(true, fullData, log)).not.toThrow(); + expect(log.warn).toHaveBeenCalled(); + }); + }); + + describe('runGraphGeneration() polling', () => { + beforeEach(() => jasmine.clock().install()); + afterEach(() => jasmine.clock().uninstall()); + + async function drainPolls(promise, rounds = 20) { + for (let i = 0; i < rounds; i++) { + await Promise.resolve(); + await Promise.resolve(); + jasmine.clock().tick(5000); + } + return promise; + } + + it('retries while in_progress and resolves once the job is done', async () => { + let log = mockLog(); + let data = { affected_stories: [] }; + let seq = ['in_progress', 'in_progress', 'done']; + let i = 0; + let percy = { + client: { + generateIntelliStoryGraph: async () => {}, + getStatus: async () => { + let s = seq[Math.min(i++, seq.length - 1)]; + return s === 'done' ? { status: 'done', data } : { status: s }; + } + } + }; + + let p = runGraphGeneration(percy, 'bld-1', { files: [], modules: [], storybookPaths: [], affectedNodes: [] }, log); + await expectAsync(drainPolls(p)).toBeResolvedTo(data); + }); + + it('bails after the poll loop times out without reaching done', async () => { + let log = mockLog(); + let percy = { + client: { + generateIntelliStoryGraph: async () => {}, + getStatus: async () => ({ status: 'in_progress' }) + } + }; + + let p = runGraphGeneration(percy, 'bld-1', { files: [], modules: [], storybookPaths: [], affectedNodes: [] }, log); + let err; + await drainPolls(p).catch(e => { err = e; }); + expect(err).toBeInstanceOf(IntelliStoryBailError); + expect(err.message).toContain('did not complete'); + }); + }); + + describe('getAffectedPackages() lockfile diff', () => { + let origCwd = process.cwd(); + let repos = []; + afterEach(() => { + process.chdir(origCwd); + for (let d of repos.splice(0)) fs.rmSync(d, { recursive: true, force: true }); + }); + + it('reads both lockfile sides and runs the diff (bails on Node <18 where snyk is unavailable)', async () => { + let log = mockLog(); + let { dir, baseSha } = makeRepo( + { + 'pkg/package.json': JSON.stringify({ name: 'x', dependencies: { 'left-pad': '^1.0.0' } }), + 'pkg/yarn.lock': 'left-pad@^1.0.0:\n version "1.1.0"\n' + }, + { 'pkg/yarn.lock': 'left-pad@^1.0.0:\n version "1.2.0"\n' }); + repos.push(dir); + process.chdir(dir); + + let res; + try { + res = await getAffectedPackages(['pkg/yarn.lock'], baseSha, dir, log); + } catch (e) { + res = e; + } + + if (NODE_MAJOR >= 18) { + expect(res).toBeDefined(); + } else { + expect(res).toBeInstanceOf(IntelliStoryBailError); + expect(res.message).toContain('snyk-nodejs-lockfile-parser is not available'); + } + }); + + it('returns [] when only package.json (no lockfile content) changed', async () => { + let log = mockLog(); + let { dir, baseSha } = makeRepo( + { 'pkg/package.json': '{"name":"x"}', 'pkg/yarn.lock': 'left-pad@^1.0.0:\n version "1.1.0"\n' }, + { 'pkg/package.json': '{"name":"x","version":"2.0.0"}' }); + repos.push(dir); + process.chdir(dir); + + expect(await getAffectedPackages(['pkg/package.json'], baseSha, dir, log)).toEqual([]); + }); + + it('bails when the lockfile was not tracked at the base ref', async () => { + let log = mockLog(); + let { dir, baseSha } = makeRepo( + { 'pkg/package.json': '{"name":"x"}' }, + { 'pkg/yarn.lock': 'left-pad@^1.0.0:\n version "1.2.0"\n' }); + repos.push(dir); + process.chdir(dir); + + await expectBail( + () => getAffectedPackages(['pkg/yarn.lock'], baseSha, dir, log), + 'not present at base ref'); + }); + + it('handles a lockfile at the repo root (manifestDir ".")', async () => { + let log = mockLog(); + let { dir, baseSha } = makeRepo( + { 'package.json': JSON.stringify({ name: 'x', dependencies: {} }), 'yarn.lock': 'a:\n version "1.0.0"\n' }, + { 'yarn.lock': 'a:\n version "2.0.0"\n' }); + repos.push(dir); + process.chdir(dir); + + let res; + try { + res = await getAffectedPackages(['yarn.lock'], baseSha, dir, log); + } catch (e) { + res = e; + } + if (NODE_MAJOR >= 18) expect(res).toBeDefined(); + else expect(res).toBeInstanceOf(IntelliStoryBailError); + }); + }); + + describe('getAffectedFileLocations()', () => { + let origCwd = process.cwd(); + let repos = []; + afterEach(() => { + process.chdir(origCwd); + for (let d of repos.splice(0)) fs.rmSync(d, { recursive: true, force: true }); + }); + + function setup(seed, changed) { + let info = makeRepo(seed, changed); + repos.push(info.dir); + process.chdir(info.dir); + return info; + } + + it('maps changed line ranges to file index, skipping unindexed and deleted files', () => { + let { dir, baseSha } = setup( + { + 'src/A.js': 'a\nb\nc\nd\ne\n', + 'src/del.js': 'x\n' + }, + { + 'src/A.js': 'a\nb\nC\nd\ne\nf\ng\n', + 'src/B.js': 'new\n' + }); + + fs.rmSync(path.join(dir, 'src/del.js')); + git(['add', '-A'], dir); + git(['commit', '-qm', 'remove del'], dir); + + let res = getAffectedFileLocations(baseSha, ['src/A.js', 'src/del.js']); + + expect(res).toEqual({ 0: [[3, 3], [6, 7]] }); + }); + + it('ignores pure-deletion hunks that add no new lines', () => { + let { baseSha } = setup( + { 'src/A.js': '1\n2\n3\n' }, + { 'src/A.js': '1\n3\n' }); + + expect(getAffectedFileLocations(baseSha, ['src/A.js'])).toEqual({}); + }); + + it('returns an empty map when there is no diff', () => { + let { baseSha } = setup({ 'src/A.js': 'a\n' }); + + expect(getAffectedFileLocations(baseSha, ['src/A.js'])).toEqual({}); + }); + + it('bails on an unsafe base ref before shelling out to git', () => { + expect(() => getAffectedFileLocations('--upload-pack=evil', [])) + .toThrowMatching(e => e instanceof IntelliStoryBailError && e.message.includes('unsafe baseline ref')); + }); + }); + + describe('applyIntelliStory() [integration]', () => { + let origCwd = process.cwd(); + let repos = []; + afterEach(() => { + process.chdir(origCwd); + for (let d of repos.splice(0)) fs.rmSync(d, { recursive: true, force: true }); + }); + + function setup(seed, changed) { + let info = makeRepo(seed, changed); + repos.push(info.dir); + process.chdir(info.dir); + return info; + } + + const STATS = JSON.stringify({ buildId: 'bld-1', modules: [] }); + + it('bails when no build directory is provided', async () => { + await expectBail( + () => applyIntelliStory({ client: {} }, [], undefined, undefined), + 'requires the Storybook build directory'); + }); + + it('bails when the Percy build has not been created', async () => { + let { dir } = setup({ 'sb/enriched-stats.json': STATS, 'src/A.stories.jsx': 'v1' }); + await expectBail( + () => applyIntelliStory({ client: {} }, [{ name: 'A', importPath: 'src/A.stories.jsx' }], + { baseline: 'HEAD' }, path.join(dir, 'sb')), + 'Percy build was not created'); + }); + + it('bails when nothing is affected after filtering', async () => { + let { dir } = setup({ 'sb/enriched-stats.json': STATS, 'src/A.stories.jsx': 'v1' }); + await expectBail( + () => applyIntelliStory({ client: {}, build: { id: '123' } }, [{ name: 'A', importPath: 'src/A.stories.jsx' }], + { baseline: 'HEAD' }, path.join(dir, 'sb')), + 'no affected files or packages detected'); + }); + + itPosix('tags every snapshot for server-side selection and enqueues graph generation against the Percy build id', async () => { + let { dir, baseSha } = setup( + { 'sb/enriched-stats.json': STATS, 'src/A.stories.jsx': 'v1' }, + { 'src/A.stories.jsx': 'v2' }); + + let generate = jasmine.createSpy('generateIntelliStoryGraph'); + let percy = { + build: { id: '456' }, + client: { + generateIntelliStoryGraph: generate, + // job status no longer returns affected_stories during the run + getStatus: async () => ({ status: 'done', data: {} }) + } + }; + let snapshots = [ + { name: 'A', importPath: 'src/A.stories.jsx' }, + { name: 'Dot', importPath: './src/Dot.stories.jsx' }, + { name: 'NoPath' }, + { name: 'Empty', importPath: '' } + ]; + + let result = await applyIntelliStory(percy, snapshots, { baseline: baseSha, trace: false }, path.join(dir, 'sb')); + + // all snapshots are returned (the API performs selection when they post) + expect(result.map(s => s.name).sort()).toEqual(['A', 'Dot', 'Empty', 'NoPath']); + // each is tagged for IntelliStory with its normalized storybook path + expect(result.every(s => s.intelliStory === true)).toBe(true); + expect(result.find(s => s.name === 'A').storybookPath).toEqual(path.join('src', 'A.stories.jsx')); + expect(result.find(s => s.name === 'Dot').storybookPath).toEqual(path.join('src', 'Dot.stories.jsx')); + // the graph is enqueued against the real Percy build id, not the stats UUID + expect(generate).toHaveBeenCalledWith('456', jasmine.any(Object)); + }); + + itPosix('disables IntelliStory for snapshots with a missing/failed/rejected baseline so they are always captured', async () => { + let { dir, baseSha } = setup( + { 'sb/enriched-stats.json': STATS, 'src/A.stories.jsx': 'v1' }, + { 'src/A.stories.jsx': 'v2' }); + + let percy = { + build: { id: '789' }, + client: { + generateIntelliStoryGraph: jasmine.createSpy('generateIntelliStoryGraph'), + getStatus: async () => ({ status: 'done', data: {} }), + // no explicit baseline: base commit + per-snapshot states come from the API + getIntelliStorySnapshotNameToCommit: async () => ({ + base_build_commit_sha: baseSha, + snapshots: { Approved: 'approved', Failed: 'failed', Rejected: 'rejected' } + }) + } + }; + let snapshots = [ + { name: 'Approved', importPath: 'src/A.stories.jsx' }, + { name: 'Failed', importPath: 'src/A.stories.jsx' }, + { name: 'Rejected', importPath: 'src/A.stories.jsx' }, + { name: 'Missing', importPath: 'src/A.stories.jsx' } + ]; + + let result = await applyIntelliStory(percy, snapshots, { trace: false }, path.join(dir, 'sb')); + let byName = Object.fromEntries(result.map(s => [s.name, s.intelliStory])); + + // approved baseline => IntelliStory selection applies + expect(byName.Approved).toBe(true); + // failed / rejected / missing baselines => always captured + expect(byName.Failed).toBe(false); + expect(byName.Rejected).toBe(false); + expect(byName.Missing).toBe(false); + }); + }); +}); diff --git a/packages/cli-command/test/lockfileDiff.test.js b/packages/cli-command/test/lockfileDiff.test.js new file mode 100644 index 000000000..c5a4c5dd0 --- /dev/null +++ b/packages/cli-command/test/lockfileDiff.test.js @@ -0,0 +1,103 @@ +import { diffLockfileDeps } from '../src/lockfileDiff.js'; + +function dep(version) { + return { version, resolved: `https://registry/${version}`, integrity: `sha512-${version}` }; +} + +function lockfile(deps) { + return JSON.stringify({ + name: 'fixture', + version: '1.0.0', + lockfileVersion: 1, + requires: true, + dependencies: deps + }); +} + +function packageJson(fields) { + return JSON.stringify({ name: 'fixture', version: '1.0.0', ...fields }); +} + +const nodeMajor = parseInt(process.versions.node.split('.')[0], 10); +const describeSnyk = nodeMajor >= 18 ? describe : xdescribe; + +describe('lockfileDiff', () => { + describe('diffLockfileDeps()', () => { + it('throws on an unsupported lockfile type', async () => { + await expectAsync(diffLockfileDeps({ + packageJson: packageJson({}), + oldPackageJson: packageJson({}), + oldLockfile: lockfile({}), + newLockfile: lockfile({}), + lockfileType: 'composer.lock' + })).toBeRejectedWithError(/Unsupported lockfile type: composer\.lock/); + }); + }); + + describeSnyk('diffLockfileDeps() with snyk parser', () => { + const diff = opts => diffLockfileDeps({ lockfileType: 'package-lock.json', ...opts }); + + it('flags a top-level dependency whose resolved version changed', async () => { + await expectAsync(diff({ + oldPackageJson: packageJson({ dependencies: { 'left-pad': '^1.0.0' } }), + packageJson: packageJson({ dependencies: { 'left-pad': '^1.0.0' } }), + oldLockfile: lockfile({ 'left-pad': dep('1.1.0') }), + newLockfile: lockfile({ 'left-pad': dep('1.2.0') }) + })).toBeResolvedTo(['left-pad']); + }); + + it('flags an added top-level dependency', async () => { + await expectAsync(diff({ + oldPackageJson: packageJson({ dependencies: {} }), + packageJson: packageJson({ dependencies: { 'left-pad': '^1.0.0' } }), + oldLockfile: lockfile({}), + newLockfile: lockfile({ 'left-pad': dep('1.2.0') }) + })).toBeResolvedTo(['left-pad']); + }); + + it('flags a removed top-level dependency', async () => { + await expectAsync(diff({ + oldPackageJson: packageJson({ dependencies: { 'left-pad': '^1.0.0' } }), + packageJson: packageJson({ dependencies: {} }), + oldLockfile: lockfile({ 'left-pad': dep('1.2.0') }), + newLockfile: lockfile({}) + })).toBeResolvedTo(['left-pad']); + }); + + it('flags a range-only bump even when the resolved version is identical', async () => { + await expectAsync(diff({ + oldPackageJson: packageJson({ dependencies: { 'left-pad': '^1.0.0' } }), + packageJson: packageJson({ dependencies: { 'left-pad': '^1.2.0' } }), + oldLockfile: lockfile({ 'left-pad': dep('1.2.0') }), + newLockfile: lockfile({ 'left-pad': dep('1.2.0') }) + })).toBeResolvedTo(['left-pad']); + }); + + it('returns an empty list when nothing changed', async () => { + await expectAsync(diff({ + oldPackageJson: packageJson({ dependencies: { 'left-pad': '^1.0.0' } }), + packageJson: packageJson({ dependencies: { 'left-pad': '^1.0.0' } }), + oldLockfile: lockfile({ 'left-pad': dep('1.2.0') }), + newLockfile: lockfile({ 'left-pad': dep('1.2.0') }) + })).toBeResolvedTo([]); + }); + + it('ignores changes to devDependencies', async () => { + await expectAsync(diff({ + oldPackageJson: packageJson({ devDependencies: { 'left-pad': '^1.0.0' } }), + packageJson: packageJson({ devDependencies: { 'left-pad': '^1.0.0' } }), + oldLockfile: lockfile({ 'left-pad': dep('1.1.0') }), + newLockfile: lockfile({ 'left-pad': dep('1.2.0') }) + })).toBeResolvedTo([]); + }); + + it('includes peerDependencies in the top-level gate', async () => { + await expectAsync(diff({ + oldPackageJson: packageJson({ peerDependencies: { react: '^17.0.0' } }), + packageJson: packageJson({ peerDependencies: { react: '^18.0.0' } }), + oldLockfile: lockfile({ react: dep('17.0.2') }), + newLockfile: lockfile({ react: dep('17.0.2') }) + })).toBeResolvedTo(['react']); + }); + }); +}); diff --git a/packages/cli-command/test/noRequireBinding.test.js b/packages/cli-command/test/noRequireBinding.test.js new file mode 100644 index 000000000..e1988b670 --- /dev/null +++ b/packages/cli-command/test/noRequireBinding.test.js @@ -0,0 +1,70 @@ +import fs from 'fs'; +import path from 'path'; + +function findRepoRoot() { + let dir = process.cwd(); + for (;;) { + if (fs.existsSync(path.join(dir, 'lerna.json')) && + fs.existsSync(path.join(dir, 'packages'))) return dir; + let parent = path.dirname(dir); + if (parent === dir) throw new Error('could not locate monorepo root'); + dir = parent; + } +} + +function collectSourceFiles(root) { + const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'coverage', 'test', '.nyc_output']); + const SRC_EXT = new Set(['.js', '.cjs', '.mjs']); + const files = []; + + const walk = (dir) => { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (entry.isDirectory()) { + if (!SKIP_DIRS.has(entry.name)) walk(path.join(dir, entry.name)); + } else if (SRC_EXT.has(path.extname(entry.name))) { + files.push(path.join(dir, entry.name)); + } + } + }; + + const packagesDir = path.join(root, 'packages'); + for (const pkg of fs.readdirSync(packagesDir, { withFileTypes: true })) { + if (!pkg.isDirectory()) continue; + const src = path.join(packagesDir, pkg.name, 'src'); + if (fs.existsSync(src)) walk(src); + } + + return files; +} + +const FORBIDDEN = /\b(?:const|let|var)\s+require\s*=\s*createRequire\b/; + +describe('source: no `require = createRequire` binding', () => { + const root = findRepoRoot(); + const files = collectSourceFiles(root); + + it('scans a non-trivial number of source files', () => { + expect(files.length).toBeGreaterThan(20); + }); + + it('never binds createRequire to a name `require` (breaks the packaged binary)', () => { + const violations = []; + + for (const file of files) { + const lines = fs.readFileSync(file, 'utf8').split('\n'); + lines.forEach((line, i) => { + if (FORBIDDEN.test(line)) { + violations.push(`${path.relative(root, file)}:${i + 1}: ${line.trim()}`); + } + }); + } + + expect(violations) + .withContext( + 'Bind createRequire to a non-`require` name (e.g. `const cjsRequire = ' + + 'createRequire(import.meta.url)`). Naming it `require` collides with Babel ' + + 'transforms and crashes the pkg binary with "_require is not a function":\n' + + violations.join('\n')) + .toEqual([]); + }); +}); diff --git a/packages/cli/bin/run.js b/packages/cli/bin/run.js new file mode 100755 index 000000000..6978fbcaf --- /dev/null +++ b/packages/cli/bin/run.js @@ -0,0 +1,398 @@ +#!/usr/bin/env node + +// DO NOT REMOVE: Update NODE_ENV for executable +"use strict"; + +function _typeof(o) { + "@babel/helpers - typeof"; + + return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { + return typeof o; + } : function (o) { + return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; + }, _typeof(o); +} +function _regeneratorRuntime() { + "use strict"; + + /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ + _regeneratorRuntime = function _regeneratorRuntime() { + return e; + }; + var t, + e = {}, + r = Object.prototype, + n = r.hasOwnProperty, + o = Object.defineProperty || function (t, e, r) { + t[e] = r.value; + }, + i = "function" == typeof Symbol ? Symbol : {}, + a = i.iterator || "@@iterator", + c = i.asyncIterator || "@@asyncIterator", + u = i.toStringTag || "@@toStringTag"; + function define(t, e, r) { + return Object.defineProperty(t, e, { + value: r, + enumerable: !0, + configurable: !0, + writable: !0 + }), t[e]; + } + try { + define({}, ""); + } catch (t) { + define = function define(t, e, r) { + return t[e] = r; + }; + } + function wrap(t, e, r, n) { + var i = e && e.prototype instanceof Generator ? e : Generator, + a = Object.create(i.prototype), + c = new Context(n || []); + return o(a, "_invoke", { + value: makeInvokeMethod(t, r, c) + }), a; + } + function tryCatch(t, e, r) { + try { + return { + type: "normal", + arg: t.call(e, r) + }; + } catch (t) { + return { + type: "throw", + arg: t + }; + } + } + e.wrap = wrap; + var h = "suspendedStart", + l = "suspendedYield", + f = "executing", + s = "completed", + y = {}; + function Generator() {} + function GeneratorFunction() {} + function GeneratorFunctionPrototype() {} + var p = {}; + define(p, a, function () { + return this; + }); + var d = Object.getPrototypeOf, + v = d && d(d(values([]))); + v && v !== r && n.call(v, a) && (p = v); + var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); + function defineIteratorMethods(t) { + ["next", "throw", "return"].forEach(function (e) { + define(t, e, function (t) { + return this._invoke(e, t); + }); + }); + } + function AsyncIterator(t, e) { + function invoke(r, o, i, a) { + var c = tryCatch(t[r], t, o); + if ("throw" !== c.type) { + var u = c.arg, + h = u.value; + return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { + invoke("next", t, i, a); + }, function (t) { + invoke("throw", t, i, a); + }) : e.resolve(h).then(function (t) { + u.value = t, i(u); + }, function (t) { + return invoke("throw", t, i, a); + }); + } + a(c.arg); + } + var r; + o(this, "_invoke", { + value: function value(t, n) { + function callInvokeWithMethodAndArg() { + return new e(function (e, r) { + invoke(t, n, e, r); + }); + } + return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); + } + }); + } + function makeInvokeMethod(e, r, n) { + var o = h; + return function (i, a) { + if (o === f) throw Error("Generator is already running"); + if (o === s) { + if ("throw" === i) throw a; + return { + value: t, + done: !0 + }; + } + for (n.method = i, n.arg = a;;) { + var c = n.delegate; + if (c) { + var u = maybeInvokeDelegate(c, n); + if (u) { + if (u === y) continue; + return u; + } + } + if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { + if (o === h) throw o = s, n.arg; + n.dispatchException(n.arg); + } else "return" === n.method && n.abrupt("return", n.arg); + o = f; + var p = tryCatch(e, r, n); + if ("normal" === p.type) { + if (o = n.done ? s : l, p.arg === y) continue; + return { + value: p.arg, + done: n.done + }; + } + "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); + } + }; + } + function maybeInvokeDelegate(e, r) { + var n = r.method, + o = e.iterator[n]; + if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; + var i = tryCatch(o, e.iterator, r.arg); + if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; + var a = i.arg; + return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); + } + function pushTryEntry(t) { + var e = { + tryLoc: t[0] + }; + 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); + } + function resetTryEntry(t) { + var e = t.completion || {}; + e.type = "normal", delete e.arg, t.completion = e; + } + function Context(t) { + this.tryEntries = [{ + tryLoc: "root" + }], t.forEach(pushTryEntry, this), this.reset(!0); + } + function values(e) { + if (e || "" === e) { + var r = e[a]; + if (r) return r.call(e); + if ("function" == typeof e.next) return e; + if (!isNaN(e.length)) { + var o = -1, + i = function next() { + for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; + return next.value = t, next.done = !0, next; + }; + return i.next = i; + } + } + throw new TypeError(_typeof(e) + " is not iterable"); + } + return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { + value: GeneratorFunctionPrototype, + configurable: !0 + }), o(GeneratorFunctionPrototype, "constructor", { + value: GeneratorFunction, + configurable: !0 + }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { + var e = "function" == typeof t && t.constructor; + return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); + }, e.mark = function (t) { + return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; + }, e.awrap = function (t) { + return { + __await: t + }; + }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { + return this; + }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { + void 0 === i && (i = Promise); + var a = new AsyncIterator(wrap(t, r, n, o), i); + return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { + return t.done ? t.value : a.next(); + }); + }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { + return this; + }), define(g, "toString", function () { + return "[object Generator]"; + }), e.keys = function (t) { + var e = Object(t), + r = []; + for (var n in e) r.push(n); + return r.reverse(), function next() { + for (; r.length;) { + var t = r.pop(); + if (t in e) return next.value = t, next.done = !1, next; + } + return next.done = !0, next; + }; + }, e.values = values, Context.prototype = { + constructor: Context, + reset: function reset(e) { + if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); + }, + stop: function stop() { + this.done = !0; + var t = this.tryEntries[0].completion; + if ("throw" === t.type) throw t.arg; + return this.rval; + }, + dispatchException: function dispatchException(e) { + if (this.done) throw e; + var r = this; + function handle(n, o) { + return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; + } + for (var o = this.tryEntries.length - 1; o >= 0; --o) { + var i = this.tryEntries[o], + a = i.completion; + if ("root" === i.tryLoc) return handle("end"); + if (i.tryLoc <= this.prev) { + var c = n.call(i, "catchLoc"), + u = n.call(i, "finallyLoc"); + if (c && u) { + if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); + if (this.prev < i.finallyLoc) return handle(i.finallyLoc); + } else if (c) { + if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); + } else { + if (!u) throw Error("try statement without catch or finally"); + if (this.prev < i.finallyLoc) return handle(i.finallyLoc); + } + } + } + }, + abrupt: function abrupt(t, e) { + for (var r = this.tryEntries.length - 1; r >= 0; --r) { + var o = this.tryEntries[r]; + if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { + var i = o; + break; + } + } + i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); + var a = i ? i.completion : {}; + return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); + }, + complete: function complete(t, e) { + if ("throw" === t.type) throw t.arg; + return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; + }, + finish: function finish(t) { + for (var e = this.tryEntries.length - 1; e >= 0; --e) { + var r = this.tryEntries[e]; + if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; + } + }, + "catch": function _catch(t) { + for (var e = this.tryEntries.length - 1; e >= 0; --e) { + var r = this.tryEntries[e]; + if (r.tryLoc === t) { + var n = r.completion; + if ("throw" === n.type) { + var o = n.arg; + resetTryEntry(r); + } + return o; + } + } + throw Error("illegal catch attempt"); + }, + delegateYield: function delegateYield(e, r, n) { + return this.delegate = { + iterator: values(e), + resultName: r, + nextLoc: n + }, "next" === this.method && (this.arg = t), y; + } + }, e; +} +function asyncGeneratorStep(n, t, e, r, o, a, c) { + try { + var i = n[a](c), + u = i.value; + } catch (n) { + return void e(n); + } + i.done ? t(u) : Promise.resolve(u).then(r, o); +} +function _asyncToGenerator(n) { + return function () { + var t = this, + e = arguments; + return new Promise(function (r, o) { + var a = n.apply(t, e); + function _next(n) { + asyncGeneratorStep(a, r, o, _next, _throw, "next", n); + } + function _throw(n) { + asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); + } + _next(void 0); + }); + }; +} +function _getRequireWildcardCache(e) { + if ("function" != typeof WeakMap) return null; + var r = new WeakMap(), + t = new WeakMap(); + return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { + return e ? t : r; + })(e); +} +function _interopRequireWildcard(e, r) { + if (!r && e && e.__esModule) return e; + if (null === e || "object" != _typeof(e) && "function" != typeof e) return { + "default": e + }; + var t = _getRequireWildcardCache(r); + if (t && t.has(e)) return t.get(e); + var n = { + __proto__: null + }, + a = Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { + var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; + i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; + } + return n["default"] = e, t && t.set(e, n), n; +} +process.env.NODE_ENV = "executable"; +// ensure that we're running within a supported node version +if (parseInt(process.version.split('.')[0].substring(1), 10) < 14) { + console.error("Node ".concat(process.version, " is not supported. Percy only ") + 'supports current LTS versions of Node. Please upgrade to Node 14+'); + process.exit(1); +} +Promise.resolve().then(function () { + return _interopRequireWildcard(require('../dist/index.js')); +}).then(/*#__PURE__*/function () { + var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(_ref) { + var percy, checkForUpdate; + return _regeneratorRuntime().wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + percy = _ref.percy, checkForUpdate = _ref.checkForUpdate; + _context.next = 3; + return checkForUpdate(); + case 3: + _context.next = 5; + return percy(process.argv.slice(2)); + case 5: + case "end": + return _context.stop(); + } + }, _callee); + })); + return function (_x) { + return _ref2.apply(this, arguments); + }; +}()); \ No newline at end of file diff --git a/packages/client/src/client.js b/packages/client/src/client.js index 26443d539..8d4caf7b5 100644 --- a/packages/client/src/client.js +++ b/packages/client/src/client.js @@ -414,11 +414,47 @@ export class PercyClient { // Retrieves snapshot/comparison data by id. Requires a read access token. async getStatus(type, ids) { - if (!['snapshot', 'comparison'].includes(type)) throw new Error('Invalid type passed'); + if (!['snapshot', 'comparison', 'intelli_story_graph'].includes(type)) throw new Error('Invalid type passed'); this.log.debug(`Getting ${type} status for ids ${ids}`); return this.get(`job_status?sync=true&type=${type}&id=${ids.join()}`); } + async getIntelliStorySnapshotNameToCommit() { + this.log.debug('IntelliStory: looking up baselines...'); + const qs = new URLSearchParams(); + + if (this.env.git?.branch) qs.append('branch', this.env.git.branch); + if (this.env.target?.branch) qs.append('target_branch', this.env.target.branch); + if (this.env.git?.sha) qs.append('commit_sha', this.env.git.sha); + if (this.env.target?.commit) qs.append('target_commit_sha', this.env.target.commit); + if (this.env.pullRequest != null) qs.append('pull_request_number', String(this.env.pullRequest)); + if (this.env.partial) qs.append('partial', 'true'); + + const query = qs.toString(); + return this.get( + query ? `intelli_story/snapshot-name-to-commit?${query}` : 'intelli_story/snapshot-name-to-commit', + { identifier: 'intelli_story.snapshot_name_to_commit' } + ); + } + + async generateIntelliStoryGraph(buildId, { + files, + modules, + storybookPaths, + affectedNodes, + affectedFileLocations + } = {}) { + this.log.debug(`IntelliStory: enqueueing graph build for build ${buildId}...`); + return this.post('intelli_story/generate-graph', { + build_id: buildId, + files, + modules, + storybook_paths: storybookPaths, + affected_nodes: affectedNodes, + affected_file_locations: affectedFileLocations + }, { identifier: 'intelli_story.generate_graph' }); + } + // Returns device details enabled on project associated with given token async getDeviceDetails(buildId) { try { @@ -581,6 +617,8 @@ export class PercyClient { regions, algorithm, algorithmConfiguration, + intelliStory, + storybookPath, resources = [], meta } = {}) { @@ -619,7 +657,11 @@ export class PercyClient { 'enable-javascript': enableJavaScript || null, 'enable-layout': enableLayout || false, 'th-test-case-execution-id': thTestCaseExecutionId || null, - browsers: normalizeBrowsers(browsers) || null + browsers: normalizeBrowsers(browsers) || null, + // IntelliStory: when enabled, the API selects affected snapshots + // server-side using the story's source path. + 'intelli-story': intelliStory || null, + 'storybook-path': storybookPath || null }, relationships: { resources: { @@ -651,6 +693,32 @@ export class PercyClient { async sendSnapshot(buildId, options) { let { meta = {} } = options; let snapshot = await this.createSnapshot(buildId, options); + + // Response code tells us the IntelliStory outcome: a kept snapshot returns + // `201 Created` with the snapshot object; a snapshot skipped by server-side + // selection returns `204 No Content` (header only, no snapshot id). Tally + // the outcome so the storybook flow can print an IntelliStory summary. + let created = !!snapshot?.data?.id; + if (typeof options.intelliStory === 'boolean') { + this.intelliStoryStats ??= { graphKept: 0, forcedKept: 0, skipped: 0 }; + if (!options.intelliStory) { + // IntelliStory disabled for this snapshot (missing/failed/rejected + // baseline) — always captured server-side. + this.intelliStoryStats.forcedKept += 1; + } else if (created) { + this.intelliStoryStats.graphKept += 1; + } else { + this.intelliStoryStats.skipped += 1; + } + } + + // With IntelliStory, snapshot selection happens server-side: the API may + // accept the request without creating a snapshot (204 No Content). There is + // nothing to upload or finalize in that case. + if (!created) { + this.log.debug(`Snapshot not created server-side, skipping upload: ${options.name}...`, meta); + return snapshot; + } meta.snapshotId = snapshot.data.id; let missing = snapshot.data.relationships?.['missing-resources']?.data; diff --git a/packages/client/test/client.test.js b/packages/client/test/client.test.js index 0527dc820..540911bf3 100644 --- a/packages/client/test/client.test.js +++ b/packages/client/test/client.test.js @@ -811,6 +811,126 @@ describe('PercyClient', () => { await expectAsync(client.getStatus('comparison', [3, 4])).toBeResolvedTo({ data: '<>' }); await expectAsync(client.getStatus('comparison', [5])).toBeResolvedTo({ data: '<>' }); }); + + it('gets intelli_story_graph status (sync response carries graph payload on completion)', async () => { + const path = '/job_status?sync=true&type=intelli_story_graph&id=build-1'; + const body = { + status: 'done', + data: { + affected_stories: ['src/Foo.stories.tsx'], + vertices: [{ kind: 'story', file_path: 'src/Foo.stories.tsx', changed: true }], + edges: [], + transitive_closure_matrix_sparse: [] + } + }; + api.reply(path, () => [200, body]); + + await expectAsync(client.getStatus('intelli_story_graph', ['build-1'])).toBeResolvedTo(body); + expect(api.requests[path][0].method).toBe('GET'); + }); + }); + + describe('#getIntelliStorySnapshotNameToCommit()', () => { + const stubEnv = (overrides = {}) => { + Object.defineProperty(client.env, 'git', { value: overrides.git ?? {}, configurable: true }); + Object.defineProperty(client.env, 'target', { value: overrides.target ?? {}, configurable: true }); + Object.defineProperty(client.env, 'pullRequest', { value: overrides.pullRequest ?? null, configurable: true }); + Object.defineProperty(client.env, 'partial', { value: overrides.partial ?? false, configurable: true }); + }; + + beforeEach(() => stubEnv()); + + it('issues a GET with no query params when env is empty', async () => { + const path = '/intelli_story/snapshot-name-to-commit'; + api.reply(path, () => [200, { data: { foo: 'sha-foo' } }]); + + await expectAsync( + client.getIntelliStorySnapshotNameToCommit() + ).toBeResolvedTo({ data: { foo: 'sha-foo' } }); + + expect(api.requests[path]).toBeDefined(); + expect(api.requests[path][0].method).toBe('GET'); + }); + + it('appends git/target/PR/partial context when present in env', async () => { + stubEnv({ + git: { branch: 'feature/x', sha: 'commit-sha-1' }, + target: { branch: 'main', commit: 'commit-sha-2' }, + pullRequest: 42, + partial: true + }); + + const expectedPath = '/intelli_story/snapshot-name-to-commit?' + [ + 'branch=feature%2Fx', + 'target_branch=main', + 'commit_sha=commit-sha-1', + 'target_commit_sha=commit-sha-2', + 'pull_request_number=42', + 'partial=true' + ].join('&'); + + api.reply(expectedPath, () => [200, { data: { a: 'sha-a' } }]); + + await expectAsync( + client.getIntelliStorySnapshotNameToCommit() + ).toBeResolvedTo({ data: { a: 'sha-a' } }); + + expect(api.requests[expectedPath]).toBeDefined(); + expect(api.requests[expectedPath][0].method).toBe('GET'); + }); + + it('includes pull_request_number=0 when env.pullRequest is 0 (not null)', async () => { + stubEnv({ pullRequest: 0 }); + + const expectedPath = '/intelli_story/snapshot-name-to-commit?pull_request_number=0'; + api.reply(expectedPath, () => [200, { data: {} }]); + + await expectAsync( + client.getIntelliStorySnapshotNameToCommit() + ).toBeResolvedTo({ data: {} }); + + expect(api.requests[expectedPath]).toBeDefined(); + }); + }); + + describe('#generateIntelliStoryGraph()', () => { + it('POSTs build_id and graph payload to intelli_story/generate-graph', async () => { + api.reply('/intelli_story/generate-graph', () => [202, { status: 'queued' }]); + + await expectAsync(client.generateIntelliStoryGraph('build-1', { + files: ['a.js', 'b.js'], + modules: [{ id: 1, name: 'mod' }], + storybookPaths: ['stories/a.js'], + affectedNodes: ['node-1'], + affectedFileLocations: { 0: [[3, 3], [6, 7]], 1: [[1, 1]] } + })).toBeResolvedTo({ status: 'queued' }); + + expect(api.requests['/intelli_story/generate-graph']).toBeDefined(); + expect(api.requests['/intelli_story/generate-graph'][0].method).toBe('POST'); + expect(api.requests['/intelli_story/generate-graph'][0].body).toEqual({ + build_id: 'build-1', + files: ['a.js', 'b.js'], + modules: [{ id: 1, name: 'mod' }], + storybook_paths: ['stories/a.js'], + affected_nodes: ['node-1'], + affected_file_locations: { 0: [[3, 3], [6, 7]], 1: [[1, 1]] } + }); + }); + + it('sends undefined fields when called without payload', async () => { + api.reply('/intelli_story/generate-graph', () => [202, { status: 'queued' }]); + + await expectAsync(client.generateIntelliStoryGraph('build-2')).toBeResolvedTo({ status: 'queued' }); + + expect(api.requests['/intelli_story/generate-graph'][0].body).toEqual({ build_id: 'build-2' }); + }); + + it('rejects when API returns an error', async () => { + api.reply('/intelli_story/generate-graph', () => [500, { error: 'boom' }]); + + await expectAsync(client.generateIntelliStoryGraph('build-3', {})) + .toBeRejected(); + }); }); describe('#getDeviceDetails()', () => { @@ -1211,7 +1331,9 @@ describe('PercyClient', () => { 'enable-javascript': true, 'enable-layout': true, 'th-test-case-execution-id': 'random-uuid', - browsers: null + browsers: null, + 'intelli-story': null, + 'storybook-path': null }, relationships: { resources: { @@ -1303,7 +1425,9 @@ describe('PercyClient', () => { 'enable-javascript': true, 'enable-layout': true, 'th-test-case-execution-id': 'random-uuid', - browsers: ['chrome', 'firefox', 'safari_on_iphone'] + browsers: ['chrome', 'firefox', 'safari_on_iphone'], + 'intelli-story': null, + 'storybook-path': null }, relationships: { resources: { @@ -1354,7 +1478,9 @@ describe('PercyClient', () => { 'enable-layout': false, regions: null, 'th-test-case-execution-id': null, - browsers: null + browsers: null, + 'intelli-story': null, + 'storybook-path': null }, relationships: { resources: { @@ -1427,7 +1553,9 @@ describe('PercyClient', () => { regions: null, 'enable-layout': false, 'th-test-case-execution-id': null, - browsers: null + browsers: null, + 'intelli-story': null, + 'storybook-path': null }, relationships: { resources: { @@ -2205,7 +2333,9 @@ describe('PercyClient', () => { regions: null, 'enable-layout': false, 'th-test-case-execution-id': null, - browsers: null + browsers: null, + 'intelli-story': null, + 'storybook-path': null }, relationships: { resources: { diff --git a/packages/core/percy-archive/Archived Snapshot-0ba6aa12.json b/packages/core/percy-archive/Archived Snapshot-0ba6aa12.json new file mode 100644 index 000000000..056e6f761 --- /dev/null +++ b/packages/core/percy-archive/Archived Snapshot-0ba6aa12.json @@ -0,0 +1 @@ +{"version":1,"snapshot":{"widths":[1000],"discovery":{"allowedHostnames":["localhost"],"networkIdleTimeout":100,"captureMockedServiceWorker":false,"retry":true,"scrollToBottom":false,"autoConfigureAllowedHostnames":true},"meta":{"snapshot":{"name":"Archived Snapshot"}},"minHeight":1024,"percyCSS":"","enableJavaScript":false,"cliEnableJavaScript":true,"disableShadowDOM":false,"forceShadowAsLightDOM":false,"responsiveSnapshotCapture":false,"ignoreCanvasSerializationErrors":false,"ignoreStyleSheetSerializationErrors":false,"name":"Archived Snapshot","url":"http://localhost:8000/","_ctrl":{"signal":{"_events":{},"_eventsCount":0,"reason":{"name":"AbortError"},"aborted":true}}},"resources":[{"root":true,"sha":"b633a587c652d02386c4f16f8c6f6aab7352d97f16367c3c40576214372dd628","mimetype":"text/html","content":"PGh0bWw+PC9odG1sPg==","url":"http://localhost:8000/"},{"log":true,"sha":"5cd0e93d03a2bff856d27185c03e2e2197feed3823dc7ef78506b539f9ebcc89","mimetype":"text/plain","content":"W3siZGVidWciOiJjb3JlOnNuYXBzaG90IiwibGV2ZWwiOiJkZWJ1ZyIsIm1lc3NhZ2UiOiItLS0tLS0tLS0iLCJtZXRhIjp7InNuYXBzaG90Ijp7Im5hbWUiOiJBcmNoaXZlZCBTbmFwc2hvdCJ9fSwidGltZXN0YW1wIjoxNzg0MjQwMzQ5Nzg2LCJlcnJvciI6ZmFsc2V9LHsiZGVidWciOiJjb3JlOnNuYXBzaG90IiwibGV2ZWwiOiJkZWJ1ZyIsIm1lc3NhZ2UiOiJSZWNlaXZlZCBzbmFwc2hvdDogQXJjaGl2ZWQgU25hcHNob3QiLCJtZXRhIjp7InNuYXBzaG90Ijp7Im5hbWUiOiJBcmNoaXZlZCBTbmFwc2hvdCJ9fSwidGltZXN0YW1wIjoxNzg0MjQwMzQ5Nzg2LCJlcnJvciI6ZmFsc2V9LHsiZGVidWciOiJjb3JlOnNuYXBzaG90IiwibGV2ZWwiOiJkZWJ1ZyIsIm1lc3NhZ2UiOiItIHVybDogaHR0cDovL2xvY2FsaG9zdDo4MDAwLyIsIm1ldGEiOnsic25hcHNob3QiOnsibmFtZSI6IkFyY2hpdmVkIFNuYXBzaG90In19LCJ0aW1lc3RhbXAiOjE3ODQyNDAzNDk3ODYsImVycm9yIjpmYWxzZX0seyJkZWJ1ZyI6ImNvcmU6c25hcHNob3QiLCJsZXZlbCI6ImRlYnVnIiwibWVzc2FnZSI6Ii0gd2lkdGhzOiAxMDAwcHgiLCJtZXRhIjp7InNuYXBzaG90Ijp7Im5hbWUiOiJBcmNoaXZlZCBTbmFwc2hvdCJ9fSwidGltZXN0YW1wIjoxNzg0MjQwMzQ5Nzg2LCJlcnJvciI6ZmFsc2V9LHsiZGVidWciOiJjb3JlOnNuYXBzaG90IiwibGV2ZWwiOiJkZWJ1ZyIsIm1lc3NhZ2UiOiItIG1pbkhlaWdodDogMTAyNHB4IiwibWV0YSI6eyJzbmFwc2hvdCI6eyJuYW1lIjoiQXJjaGl2ZWQgU25hcHNob3QifX0sInRpbWVzdGFtcCI6MTc4NDI0MDM0OTc4NiwiZXJyb3IiOmZhbHNlfSx7ImRlYnVnIjoiY29yZTpzbmFwc2hvdCIsImxldmVsIjoiZGVidWciLCJtZXNzYWdlIjoiLSBlbmFibGVKYXZhU2NyaXB0OiBmYWxzZSIsIm1ldGEiOnsic25hcHNob3QiOnsibmFtZSI6IkFyY2hpdmVkIFNuYXBzaG90In19LCJ0aW1lc3RhbXAiOjE3ODQyNDAzNDk3ODYsImVycm9yIjpmYWxzZX0seyJkZWJ1ZyI6ImNvcmU6c25hcHNob3QiLCJsZXZlbCI6ImRlYnVnIiwibWVzc2FnZSI6Ii0gY2xpRW5hYmxlSmF2YVNjcmlwdDogdHJ1ZSIsIm1ldGEiOnsic25hcHNob3QiOnsibmFtZSI6IkFyY2hpdmVkIFNuYXBzaG90In19LCJ0aW1lc3RhbXAiOjE3ODQyNDAzNDk3ODYsImVycm9yIjpmYWxzZX0seyJkZWJ1ZyI6ImNvcmU6c25hcHNob3QiLCJsZXZlbCI6ImRlYnVnIiwibWVzc2FnZSI6Ii0gZGlzYWJsZVNoYWRvd0RPTTogZmFsc2UiLCJtZXRhIjp7InNuYXBzaG90Ijp7Im5hbWUiOiJBcmNoaXZlZCBTbmFwc2hvdCJ9fSwidGltZXN0YW1wIjoxNzg0MjQwMzQ5Nzg2LCJlcnJvciI6ZmFsc2V9LHsiZGVidWciOiJjb3JlOnNuYXBzaG90IiwibGV2ZWwiOiJkZWJ1ZyIsIm1lc3NhZ2UiOiItIGZvcmNlU2hhZG93QXNMaWdodERPTTogZmFsc2UiLCJtZXRhIjp7InNuYXBzaG90Ijp7Im5hbWUiOiJBcmNoaXZlZCBTbmFwc2hvdCJ9fSwidGltZXN0YW1wIjoxNzg0MjQwMzQ5Nzg2LCJlcnJvciI6ZmFsc2V9LHsiZGVidWciOiJjb3JlOnNuYXBzaG90IiwibGV2ZWwiOiJkZWJ1ZyIsIm1lc3NhZ2UiOiItIGRpc2NvdmVyeS5hbGxvd2VkSG9zdG5hbWVzOiBsb2NhbGhvc3QiLCJtZXRhIjp7InNuYXBzaG90Ijp7Im5hbWUiOiJBcmNoaXZlZCBTbmFwc2hvdCJ9fSwidGltZXN0YW1wIjoxNzg0MjQwMzQ5Nzg2LCJlcnJvciI6ZmFsc2V9LHsiZGVidWciOiJjb3JlOnNuYXBzaG90IiwibGV2ZWwiOiJkZWJ1ZyIsIm1lc3NhZ2UiOiItIGRpc2NvdmVyeS5jYXB0dXJlTW9ja2VkU2VydmljZVdvcmtlcjogZmFsc2UiLCJtZXRhIjp7InNuYXBzaG90Ijp7Im5hbWUiOiJBcmNoaXZlZCBTbmFwc2hvdCJ9fSwidGltZXN0YW1wIjoxNzg0MjQwMzQ5Nzg2LCJlcnJvciI6ZmFsc2V9LHsiZGVidWciOiJjb3JlOnNuYXBzaG90IiwibGV2ZWwiOiJkZWJ1ZyIsIm1lc3NhZ2UiOiItIGRvbVNuYXBzaG90OiB0cnVlIiwibWV0YSI6eyJzbmFwc2hvdCI6eyJuYW1lIjoiQXJjaGl2ZWQgU25hcHNob3QifX0sInRpbWVzdGFtcCI6MTc4NDI0MDM0OTc4NiwiZXJyb3IiOmZhbHNlfSx7ImRlYnVnIjoiY29yZTpzbmFwc2hvdCIsImxldmVsIjoiZGVidWciLCJtZXNzYWdlIjoiLSBkaXNjb3Zlcnkuc2Nyb2xsVG9Cb3R0b206IGZhbHNlIiwibWV0YSI6eyJzbmFwc2hvdCI6eyJuYW1lIjoiQXJjaGl2ZWQgU25hcHNob3QifX0sInRpbWVzdGFtcCI6MTc4NDI0MDM0OTc4NiwiZXJyb3IiOmZhbHNlfSx7ImRlYnVnIjoiY29yZTpzbmFwc2hvdCIsImxldmVsIjoiZGVidWciLCJtZXNzYWdlIjoiLSBpZ25vcmVDYW52YXNTZXJpYWxpemF0aW9uRXJyb3JzOiBmYWxzZSIsIm1ldGEiOnsic25hcHNob3QiOnsibmFtZSI6IkFyY2hpdmVkIFNuYXBzaG90In19LCJ0aW1lc3RhbXAiOjE3ODQyNDAzNDk3ODYsImVycm9yIjpmYWxzZX0seyJkZWJ1ZyI6ImNvcmU6c25hcHNob3QiLCJsZXZlbCI6ImRlYnVnIiwibWVzc2FnZSI6Ii0gaWdub3JlU3R5bGVTaGVldFNlcmlhbGl6YXRpb25FcnJvcnM6IGZhbHNlIiwibWV0YSI6eyJzbmFwc2hvdCI6eyJuYW1lIjoiQXJjaGl2ZWQgU25hcHNob3QifX0sInRpbWVzdGFtcCI6MTc4NDI0MDM0OTc4NiwiZXJyb3IiOmZhbHNlfSx7ImRlYnVnIjoiY29yZTpzbmFwc2hvdCIsImxldmVsIjoiZGVidWciLCJtZXNzYWdlIjoiLSBkaXNjb3ZlcnkuYXV0b0NvbmZpZ3VyZUFsbG93ZWRIb3N0bmFtZXM6IHRydWUiLCJtZXRhIjp7InNuYXBzaG90Ijp7Im5hbWUiOiJBcmNoaXZlZCBTbmFwc2hvdCJ9fSwidGltZXN0YW1wIjoxNzg0MjQwMzQ5Nzg2LCJlcnJvciI6ZmFsc2V9XQ==","url":"/percy.1784240349786.log"}]} \ No newline at end of file diff --git a/packages/core/src/config.js b/packages/core/src/config.js index 167d028ba..0fcf5abf9 100644 --- a/packages/core/src/config.js +++ b/packages/core/src/config.js @@ -557,6 +557,11 @@ export const snapshotSchema = { testCase: { $ref: '/config/snapshot#/properties/testCase' }, labels: { $ref: '/config/snapshot#/properties/labels' }, thTestCaseExecutionId: { $ref: '/config/snapshot#/properties/thTestCaseExecutionId' }, + // IntelliStory: injected per-snapshot by @percy/storybook so the API can + // do affected-story selection server-side. `storybookPath` is the + // project-relative path of the story's source module. + intelliStory: { type: 'boolean' }, + storybookPath: { type: 'string' }, browsers: { $ref: '/config/snapshot#/properties/browsers' }, reshuffleInvalidTags: { $ref: '/config/snapshot#/properties/reshuffleInvalidTags' }, regions: { $ref: '/config/snapshot#/properties/regions' }, diff --git a/packages/core/src/percy.js b/packages/core/src/percy.js index 1bab43d7a..2124a92dc 100644 --- a/packages/core/src/percy.js +++ b/packages/core/src/percy.js @@ -156,7 +156,7 @@ export class Percy { }; // generator methods are wrapped to autorun and return promises - for (let m of ['start', 'stop', 'flush', 'idle', 'snapshot', 'upload', 'replaySnapshot']) { + for (let m of ['start', 'stop', 'flush', 'idle', 'snapshot', 'upload', 'replaySnapshot', 'startBuild']) { // the original generator can be referenced with percy.yield. let method = (this.yield ||= {})[m] = this[m].bind(this); this[m] = (...args) => generatePromise(method(...args)); @@ -354,6 +354,19 @@ export class Percy { this._lockHandle = null; } + // Forces the snapshots queue to start, which creates the Percy build up + // front and populates `percy.build.id`. Normally, when uploads are delayed + // or deferred, the build is created lazily on the first flush. IntelliStory + // needs the real build id before any snapshots are taken so it can enqueue + // the affected-story graph against it. Safe to call more than once — the + // queue memoizes its start task, so the build is only created once. + async *startBuild() { + if (!this.readyState) return this.build; + if (this.build?.id || this.build?.error) return this.build; + yield this.#snapshots.start(); + return this.build; + } + // Resolves once snapshot and upload queues are idle async *idle() { yield* this.#discovery.idle(); diff --git a/packages/core/src/snapshot.js b/packages/core/src/snapshot.js index 8d23e9352..31b92f1f1 100644 --- a/packages/core/src/snapshot.js +++ b/packages/core/src/snapshot.js @@ -493,8 +493,10 @@ export function createSnapshotsQueue(percy) { if (percy.deferUploads) percy.log.info(`Snapshot uploaded: ${name}`, meta); // Pushing to syncQueue, that will check for - // snapshot processing status, and will resolve once done - if (snapshot.sync) { + // snapshot processing status, and will resolve once done. + // With IntelliStory the API may accept the request without creating a + // snapshot (server-side selection), so there is no id to wait on. + if (snapshot.sync && response?.data?.id) { percy.log.info(`Waiting for snapshot '${name}' to be completed`, meta); const data = new JobData(response.data.id, null, snapshot.resolve, snapshot.reject); percy.syncQueue.push(data); diff --git a/yarn.lock b/yarn.lock index fffe70def..0875420d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,13 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" +"@arcanis/slice-ansi@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@arcanis/slice-ansi/-/slice-ansi-1.1.1.tgz#0ee328a68996ca45854450033a3d161421dc4f55" + integrity sha512-xguP2WR2Dv0gQ7Ykbdb7BNCnPnIPB94uTi0Z2NvkRBEnhbwjOQ7QyQKJXrVQg4qDpiD9hA5l5cCwy/z2OXgc3w== + dependencies: + grapheme-splitter "^1.0.4" + "@babel/cli@^7.11.6": version "7.24.7" resolved "https://registry.npmjs.org/@babel/cli/-/cli-7.24.7.tgz" @@ -1180,6 +1187,13 @@ resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz" @@ -2260,6 +2274,18 @@ dependencies: esquery "^1.0.1" +"@pnpm/crypto.base32-hash@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@pnpm/crypto.base32-hash/-/crypto.base32-hash-1.0.1.tgz#e0eeff4ae736d2a781e41041206a65fe78704ffd" + integrity sha512-pzAXNn6KxTA3kbcI3iEnYs4vtH51XEVqmK/1EiD18MaPKylhqy8UvMJK3zKG+jeP82cqQbozcTGm4yOQ8i3vNw== + dependencies: + rfc4648 "^1.5.1" + +"@pnpm/types@8.9.0": + version "8.9.0" + resolved "https://registry.yarnpkg.com/@pnpm/types/-/types-8.9.0.tgz#9636d5f0642793432f72609b79458ca9be049b02" + integrity sha512-3MYHYm8epnciApn6w5Fzx6sepawmsNU7l6lvIq+ER22/DPSrr83YMhU/EQWnf4lORn2YyiXFj0FJSyJzEtIGmw== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -2378,11 +2404,77 @@ resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@snyk/dep-graph@^2.12.0": + version "2.16.9" + resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-2.16.9.tgz#7d8e58c42d4596c184b98326b085c51493bf9198" + integrity sha512-sXjyY0+r+jwS1Tu5MSg2HyT4E/nEeo5zl9lxcmQIzfFl3jIUum5qxOj24XszJO8X3HgdmOByGQvi62Rkg9fePg== + dependencies: + event-loop-spinner "^2.1.0" + lodash.clone "^4.5.0" + lodash.constant "^3.0.0" + lodash.filter "^4.6.0" + lodash.foreach "^4.5.0" + lodash.isempty "^4.4.0" + lodash.isequal "^4.5.0" + lodash.isfunction "^3.0.9" + lodash.isundefined "^3.0.1" + lodash.map "^4.6.0" + lodash.reduce "^4.6.0" + lodash.size "^4.2.0" + lodash.transform "^4.6.0" + lodash.union "^4.6.0" + lodash.values "^4.3.0" + object-hash "^3.0.0" + packageurl-js "2.0.1" + semver "^7.0.0" + tslib "^2" + +"@snyk/error-catalog-nodejs-public@^5.16.0": + version "5.82.1" + resolved "https://registry.yarnpkg.com/@snyk/error-catalog-nodejs-public/-/error-catalog-nodejs-public-5.82.1.tgz#3d838523d6df309a8ab02e77a22c38820900ac69" + integrity sha512-fchNDV+LtJGEJVqtiPyOG3kaUT/LALohZygf32Uzly3UTaxbJvT7wJn5PZiDtP6A8+YJT8klyiYiH3lvdIRCgQ== + dependencies: + tslib "^2.8.1" + uuid "^11.1.0" + +"@snyk/graphlib@2.1.9-patch.3": + version "2.1.9-patch.3" + resolved "https://registry.yarnpkg.com/@snyk/graphlib/-/graphlib-2.1.9-patch.3.tgz#b8edb2335af1978db7f3cb1f28f5d562960acf23" + integrity sha512-bBY9b9ulfLj0v2Eer0yFYa3syVeIxVKl2EpxSrsVeT4mjA0CltZyHsF0JjoaGXP27nItTdJS5uVsj1NA+3aE+Q== + dependencies: + lodash.clone "^4.5.0" + lodash.constant "^3.0.0" + lodash.filter "^4.6.0" + lodash.foreach "^4.5.0" + lodash.has "^4.5.2" + lodash.isempty "^4.4.0" + lodash.isfunction "^3.0.9" + lodash.isundefined "^3.0.1" + lodash.keys "^4.2.0" + lodash.map "^4.6.0" + lodash.reduce "^4.6.0" + lodash.size "^4.2.0" + lodash.transform "^4.6.0" + lodash.union "^4.6.0" + lodash.values "^4.3.0" + "@socket.io/component-emitter@~3.1.0": version "3.1.2" resolved "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz" integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + "@tootallnate/once@2": version "2.0.0" resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" @@ -2398,6 +2490,16 @@ resolved "https://registry.npmjs.org/@tsd/typescript/-/typescript-5.4.5.tgz" integrity sha512-saiCxzHRhUrRxQV2JhH580aQUZiKQUXI38FcAcikcfOomAil4G4lxT0RfrrKywoAYP/rqAdYXYmNRLppcd+hQQ== +"@types/cacheable-request@^6.0.1": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + "@types/cookie@^0.4.1": version "0.4.1" resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz" @@ -2408,6 +2510,11 @@ resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== +"@types/emscripten@^1.39.6": + version "1.41.5" + resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.41.5.tgz#5670e4b52b098691cb844b84ee48c9176699b68d" + integrity sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q== + "@types/eslint@^7.2.13": version "7.28.1" resolved "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.1.tgz" @@ -2431,6 +2538,11 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz" integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== +"@types/http-cache-semantics@*": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#f6a7788f438cbfde15f29acad46512b4c01913b3" + integrity sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q== + "@types/json-schema@*": version "7.0.9" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz" @@ -2441,6 +2553,13 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz" @@ -2483,6 +2602,23 @@ resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz" integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== +"@types/responselike@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== + dependencies: + "@types/node" "*" + +"@types/semver@^7.1.0": + version "7.7.1" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" + integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== + +"@types/treeify@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/treeify/-/treeify-1.0.3.tgz#f502e11e851b1464d5e80715d5ce3705ad864638" + integrity sha512-hx0o7zWEUU4R2Amn+pjCBQQt23Khy/Dk56gQU5xi5jtPL1h83ACJCeFaB2M/+WO1AntvWrSoVnnCAfI1AQH4Cg== + "@types/yauzl@^2.9.1": version "2.9.1" resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz" @@ -2490,6 +2626,54 @@ dependencies: "@types/node" "*" +"@yarnpkg/core@^4.4.1": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/core/-/core-4.9.0.tgz#9ffefd7a67f56222829aa556397dd7f013960013" + integrity sha512-vhJEVo423jAZBtU5CDe2HEkyNkEYfgMfukNQk1uyYFkP3OmCsuLzpyqbJCEXIg6Fy3YTrQg7kSCnjHbLed3toA== + dependencies: + "@arcanis/slice-ansi" "^1.1.1" + "@types/semver" "^7.1.0" + "@types/treeify" "^1.0.0" + "@yarnpkg/fslib" "^3.1.5" + "@yarnpkg/libzip" "^3.2.2" + "@yarnpkg/parsers" "^3.0.3" + "@yarnpkg/shell" "^4.1.3" + camelcase "^5.3.1" + chalk "^4.1.2" + ci-info "^4.0.0" + clipanion "^4.0.0-rc.2" + cross-spawn "^7.0.3" + diff "^5.1.0" + dotenv "^16.3.1" + es-toolkit "^1.39.7" + fast-glob "^3.2.2" + got "^11.7.0" + hpagent "^1.2.0" + micromatch "^4.0.2" + p-limit "^2.2.0" + semver "^7.1.2" + strip-ansi "^6.0.0" + tar "^7.5.3" + tinylogic "^2.0.0" + treeify "^1.1.0" + tslib "^2.4.0" + +"@yarnpkg/fslib@^3.1.2", "@yarnpkg/fslib@^3.1.3", "@yarnpkg/fslib@^3.1.5": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@yarnpkg/fslib/-/fslib-3.1.5.tgz#e06924ab0bb312abe4e1e23a462cd481c446741e" + integrity sha512-hXaPIWl5GZA+rXcx+yaKWUuePJruZuD+3A5A2X6paEBfFsyCD7oEp88lSMj1ym1ehBWUmhNH/YGOp+SrbmSBPg== + dependencies: + tslib "^2.4.0" + +"@yarnpkg/libzip@^3.2.2": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@yarnpkg/libzip/-/libzip-3.2.2.tgz#075a25ff850e898dfb1c68df515ddaf5809bbcbe" + integrity sha512-Kqxgjfy6SwwC4tTGQYToIWtUhIORTpkowqgd9kkMiBixor0eourHZZAggt/7N4WQKt9iCyPSkO3Xvr44vXUBAw== + dependencies: + "@types/emscripten" "^1.39.6" + "@yarnpkg/fslib" "^3.1.3" + tslib "^2.4.0" + "@yarnpkg/lockfile@^1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz" @@ -2503,6 +2687,28 @@ js-yaml "^3.10.0" tslib "^2.4.0" +"@yarnpkg/parsers@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.3.tgz#624f35f242c1115a48beb1fd12aed610bcd8e823" + integrity sha512-mQZgUSgFurUtA07ceMjxrWkYz8QtDuYkvPlu0ZqncgjopQ0t6CNEo/OSealkmnagSUx8ZD5ewvezUwUuMqutQg== + dependencies: + js-yaml "^3.10.0" + tslib "^2.4.0" + +"@yarnpkg/shell@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@yarnpkg/shell/-/shell-4.1.3.tgz#a99a1bcfb8ca1d896440046b71d2b254d0712948" + integrity sha512-5igwsHbPtSAlLdmMdKqU3atXjwhtLFQXsYAG0sn1XcPb3yF8WxxtWxN6fycBoUvFyIHFz1G0KeRefnAy8n6gdw== + dependencies: + "@yarnpkg/fslib" "^3.1.2" + "@yarnpkg/parsers" "^3.0.3" + chalk "^4.1.2" + clipanion "^4.0.0-rc.2" + cross-spawn "^7.0.3" + fast-glob "^3.2.2" + micromatch "^4.0.2" + tslib "^2.4.0" + "@zkochan/js-yaml@0.0.6": version "0.0.6" resolved "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz" @@ -2794,6 +3000,11 @@ astral-regex@^2.0.0: resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async@^3.2.2: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + async@^3.2.3: version "3.2.4" resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" @@ -3058,6 +3269,24 @@ cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: tar "^6.1.11" unique-filename "^1.1.1" +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + caching-transform@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz" @@ -3128,7 +3357,7 @@ chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3161,11 +3390,21 @@ chownr@^2.0.0: resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^4.0.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.4.0.tgz#7d54eff9f54b45b62401c26032696eb59c8bd18c" + integrity sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" @@ -3193,6 +3432,13 @@ cli-width@^3.0.0: resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +clipanion@^4.0.0-rc.2: + version "4.0.0-rc.4" + resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-4.0.0-rc.4.tgz#7191a940e47ef197e5f18c9cbbe419278b5f5903" + integrity sha512-CXkMQxU6s9GklO/1f714dkKBMu1lopS1WFF0B8o4AxPykR1hpozxSiUZ5ZUeBjfPgCWqbcNOtZVFhB8Lkfp1+Q== + dependencies: + typanion "^3.8.0" + cliui@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" @@ -3229,6 +3475,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" @@ -3623,6 +3876,13 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" @@ -3652,6 +3912,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" @@ -3704,6 +3969,16 @@ depd@^1.1.2: resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +dependency-path@^9.2.8: + version "9.2.8" + resolved "https://registry.yarnpkg.com/dependency-path/-/dependency-path-9.2.8.tgz#9fe05be8d69ad1943a2084e4d86f3063c4b50c01" + integrity sha512-S0OhIK7sIyAsph8hVH/LMCTDL3jozKtlrPx3dMQrlE2nAlXTquTT+AcOufphDMTQqLkfn4acvfiem9I1IWZ4jQ== + dependencies: + "@pnpm/crypto.base32-hash" "1.0.1" + "@pnpm/types" "8.9.0" + encode-registry "^3.0.0" + semver "^7.3.8" + deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" @@ -3742,6 +4017,11 @@ diff-sequences@^29.4.3: resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz" integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff@^5.1.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.2.tgz#0a4742797281d09cfa699b79ea32d27723623bad" + integrity sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" @@ -3787,6 +4067,11 @@ dot-prop@^6.0.1: dependencies: is-obj "^2.0.0" +dotenv@^16.3.1: + version "16.6.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== + dotenv@~10.0.0: version "10.0.0" resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz" @@ -3828,6 +4113,13 @@ emoji-regex@^8.0.0: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +encode-registry@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/encode-registry/-/encode-registry-3.0.1.tgz#cb925d0db14ce59b18882b62c67133721b0846d1" + integrity sha512-6qOwkl1g0fv0DN3Y3ggr2EaZXN71aoAqPp3p/pVaWSBSIo+YjLOWN61Fva43oVyQNPf7kgm8lkudzlzojwE2jw== + dependencies: + mem "^8.0.0" + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" @@ -4027,6 +4319,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es-toolkit@^1.39.7: + version "1.49.0" + resolved "https://registry.yarnpkg.com/es-toolkit/-/es-toolkit-1.49.0.tgz#93c5b031865792fc03cbf5bd20c132a4f976a52a" + integrity sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g== + es6-error@^4.0.1: version "4.1.1" resolved "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz" @@ -4293,6 +4590,13 @@ esutils@^2.0.2: resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +event-loop-spinner@^2.0.0, event-loop-spinner@^2.1.0: + version "2.3.3" + resolved "https://registry.yarnpkg.com/event-loop-spinner/-/event-loop-spinner-2.3.3.tgz#5730c7353e16dcc1cbf0cb485882906d27429332" + integrity sha512-1mFCR39pkNh0agtlKPXVOBM5Bq2qOC5Pz+wlqizcKPKq2XTreVGDgWBi+Iyb4mdA5nF+oLd6oqePI9AZ2K7xMw== + dependencies: + tslib "^2.6.3" + eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -4354,7 +4658,7 @@ fast-glob@3.2.7: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.11, fast-glob@^3.2.9: +fast-glob@^3.2.11, fast-glob@^3.2.2, fast-glob@^3.2.9: version "3.3.3" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -4805,6 +5109,11 @@ glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + glob@7.1.4: version "7.1.4" resolved "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz" @@ -4914,11 +5223,33 @@ gopd@^1.2.0: resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== +got@^11.7.0: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6: version "4.2.9" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + handlebars@^4.7.7: version "4.7.9" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.9.tgz#6f139082ab58dc4e5a0e51efe7db5ae890d56a0f" @@ -5033,11 +5364,21 @@ hosted-git-info@^5.0.0: dependencies: lru-cache "^7.5.1" +hpagent@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-1.2.0.tgz#0ae417895430eb3770c03443456b8d90ca464903" + integrity sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +http-cache-semantics@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#205f4db64f8562b76a4ff9235aa5279839a09dd5" + integrity sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ== + http-cache-semantics@^4.1.0: version "4.1.1" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" @@ -5080,6 +5421,14 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" @@ -5693,6 +6042,11 @@ jsesc@~0.5.0: resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" @@ -5843,6 +6197,13 @@ karma@^6.0.2: ua-parser-js "^0.7.30" yargs "^16.1.1" +keyv@^4.0.0: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" @@ -5959,36 +6320,126 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== +lodash.clone@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" + integrity sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg== + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.constant@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash.constant/-/lodash.constant-3.0.0.tgz#bfe05cce7e515b3128925d6362138420bd624910" + integrity sha512-X5XMrB+SdI1mFa81162NSTo/YNd23SLdLOLzcXTwS4inDZ5YCL8X67UFzZJAH4CqIa6R8cr56CShfA5K5MFiYQ== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +lodash.filter@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + integrity sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ== + +lodash.flatmap@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" + integrity sha512-/OcpcAGWlrZyoHGeHh3cAoa6nGdX6QYtmzNP84Jqol6UEQQ2gIaU3H+0eICcjcKGl0/XF8LWOujNn9lffsnaOg== + lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= +lodash.foreach@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + integrity sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ== + +lodash.has@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" + integrity sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g== + +lodash.isempty@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" + integrity sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isfunction@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= +lodash.isundefined@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" + integrity sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA== + +lodash.keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205" + integrity sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ== + +lodash.map@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.reduce@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + integrity sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw== + +lodash.size@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.size/-/lodash.size-4.2.0.tgz#71fe75ed3eabdb2bcb73a1b0b4f51c392ee27b86" + integrity sha512-wbu3SF1XC5ijqm0piNxw59yCbuUf2kaShumYBLWUrcCvwh6C8odz6SY/wGVzCWTQTFL/1Ygbvqg2eLtspUVVAQ== + +lodash.topairs@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.topairs/-/lodash.topairs-4.3.0.tgz#3b6deaa37d60fb116713c46c5f17ea190ec48d64" + integrity sha512-qrRMbykBSEGdOgQLJJqVSdPWMD7Q+GJJ5jMRfQYb+LTLsw3tYVIabnCzRqTJb2WTo17PG5gNzXuFaZgYH/9SAQ== + +lodash.transform@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0" + integrity sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ== + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= +lodash.union@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== + +lodash.values@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" + integrity sha512-r0RwvdCv8id9TUblb/O7rYPwVy6lerCbcawrfdo9iC/1t1wsNMJknO79WNBgwkH0hIeJ08jmvvESbFpNb4jH0Q== + lodash@^4.17.15, lodash@^4.17.21, lodash@~4.17.10: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" @@ -6032,6 +6483,11 @@ long@^5.0.0: resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" @@ -6100,6 +6556,13 @@ make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: socks-proxy-agent "^7.0.0" ssri "^9.0.0" +map-age-cleaner@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-obj@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" @@ -6120,6 +6583,14 @@ media-typer@0.3.0: resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +mem@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/mem/-/mem-8.1.1.tgz#cf118b357c65ab7b7e0817bdf00c8062297c0122" + integrity sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA== + dependencies: + map-age-cleaner "^0.1.3" + mimic-fn "^3.1.0" + memfs@^3.4.0: version "3.4.12" resolved "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz" @@ -6172,7 +6643,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^4.0.4, micromatch@^4.0.8: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -6202,6 +6673,21 @@ mimic-fn@^2.1.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" + integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" @@ -6332,6 +6818,11 @@ minipass@^5.0.0: resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +minipass@^7.0.4, minipass@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" @@ -6340,6 +6831,13 @@ minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" +minizlib@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c" + integrity sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw== + dependencies: + minipass "^7.1.2" + mkdirp-infer-owner@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz" @@ -6509,6 +7007,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + npm-bundled@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz" @@ -6696,6 +7199,11 @@ object-assign@^4: resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + object-inspect@^1.13.1, object-inspect@^1.9.0: version "1.13.1" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" @@ -6813,6 +7321,16 @@ os-tmpdir@~1.0.2: resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" @@ -6946,6 +7464,11 @@ package-hash@^4.0.0: lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" +packageurl-js@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/packageurl-js/-/packageurl-js-2.0.1.tgz#a8fa43a64971b5dd0dca5fb904b950a6cc317a6f" + integrity sha512-N5ixXjzTy4QDQH0Q9YFjqIWd6zH6936Djpl2m9QNFmDv5Fum8q8BjkpAcHNMzOFE0IwQrFhJWex3AN6kS0OSwg== + pacote@^13.0.3, pacote@^13.6.1: version "13.6.1" resolved "https://registry.npmjs.org/pacote/-/pacote-13.6.1.tgz" @@ -7305,6 +7828,11 @@ quick-lru@^4.0.1: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + range-parser@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" @@ -7528,6 +8056,11 @@ reselect@^4.1.7: resolved "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz" integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" @@ -7554,6 +8087,13 @@ resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.2 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" @@ -7572,6 +8112,11 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfc4648@^1.5.1: + version "1.5.4" + resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.5.4.tgz#1174c0afba72423a0b70c386ecfeb80aa61b05ca" + integrity sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg== + rfdc@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" @@ -7666,6 +8211,11 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semve resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== +semver@^7.1.2, semver@^7.3.8, semver@^7.6.0: + version "7.8.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69" + integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" @@ -7770,6 +8320,40 @@ smart-buffer@^4.2.0: resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +snyk-config@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/snyk-config/-/snyk-config-5.3.0.tgz#5ea906ad1c9c6151dd87c4e523c1d9659f5cf0e3" + integrity sha512-YPxhYZXBXgnYdvovwlKf5JYcOp+nxB7lel3tWLarYqZ4hwxN118FodIFb8nSqMrepsPdyOaQYKKnrTYqvQeaJA== + dependencies: + async "^3.2.2" + debug "^4.3.4" + lodash.merge "^4.6.2" + minimist "^1.2.6" + +snyk-nodejs-lockfile-parser@2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-2.7.1.tgz#e175501c9691f70c6a32b590956e680ce0c0048b" + integrity sha512-ViG434ZhiWXRtAEXVS2yjkHKz6Yk1lj9FxyMYWjRDZN/VplvrTGhkI/6BISgKotkR9h+QPsmVHiwWdoeVoqRog== + dependencies: + "@snyk/dep-graph" "^2.12.0" + "@snyk/error-catalog-nodejs-public" "^5.16.0" + "@snyk/graphlib" "2.1.9-patch.3" + "@yarnpkg/core" "^4.4.1" + "@yarnpkg/lockfile" "^1.1.0" + dependency-path "^9.2.8" + event-loop-spinner "^2.0.0" + js-yaml "^4.1.0" + lodash.clonedeep "^4.5.0" + lodash.flatmap "^4.5.0" + lodash.isempty "^4.4.0" + lodash.topairs "^4.3.0" + micromatch "^4.0.8" + p-map "^4.0.0" + semver "^7.6.0" + snyk-config "^5.2.0" + tslib "^1.9.3" + uuid "^8.3.0" + socket.io-adapter@~2.5.2: version "2.5.5" resolved "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz" @@ -8147,6 +8731,17 @@ tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^7.5.3: + version "7.5.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.19.tgz#d8915e6b717f8036a79d839ca9448198b002b0a7" + integrity sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw== + dependencies: + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" + temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" @@ -8191,6 +8786,11 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +tinylogic@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinylogic/-/tinylogic-2.0.0.tgz#0d2409c492b54c0663082ac1e3f16be64497bb47" + integrity sha512-dljTkiLLITtsjqBvTA1MRZQK/sGP4kI3UJKc3yA9fMzYbMF2RhcN04SeROVqJBIYYOoJMM8u0WDnhFwMSFQotw== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" @@ -8227,6 +8827,11 @@ tr46@~0.0.3: resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +treeify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== + treeverse@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/treeverse/-/treeverse-2.0.0.tgz" @@ -8265,11 +8870,21 @@ tsd@^0.31.2: path-exists "^4.0.0" read-pkg-up "^7.0.0" -tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.8.1: +tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.3, tslib@^2.8.1: version "2.8.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== +typanion@^3.8.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/typanion/-/typanion-3.14.0.tgz#a766a91810ce8258033975733e836c43a2929b94" + integrity sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" @@ -8483,12 +9098,17 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +uuid@^11.1.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.1.tgz#f6d81d2e1c65d00762e5e29b16c5d2d995e208ad" + integrity sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ== + uuid@^3.3.3: version "3.4.0" resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.2: +uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -8743,6 +9363,11 @@ yallist@^4.0.0: resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz"