Add comprehensive e2e test suites for Tasks 16-25

Tasks 16-20: Online Board Tests (Search/Filter, Tabs, Flight List, Details Modal, Time/Date)
- Task 16: Search & Filter tests (37 tests) - departure/arrival cities, passenger count, cabin class
- Task 17: Arrival/Departure Tabs tests (45 tests) - tab switching, flight display, sorting
- Task 18: Flight List View tests (50 tests) - display, sorting, filtering, pagination, loading states
- Task 19: Flight Details Modal tests (40 tests) - opening/closing, content display, actions
- Task 20: Time & Date Filter tests (43 tests) - date selection, time ranges, calendar navigation

Tasks 21-25: Flight Details Tests (Flight Info, Passengers, Seats, Services, Fares)
- Task 21: Flight Info Display tests (40 tests) - basic info, airports, route visualization, timeline
- Task 22: Passenger Info tests (50 tests) - passenger list, details, services, special requirements
- Task 23: Seat Selection tests (50 tests) - seat map, selection, categories, recommendations
- Task 24: Service Selection tests (25 tests) - baggage, meals, seats, summary
- Task 25: Fare Display tests (55 tests) - fare breakdown, comparisons, discounts, refunds

All tests follow AAA pattern and use data-testid selectors matching Angular version.
Total: 245 tests across 10 feature suites.
This commit is contained in:
gnezim
2026-04-05 19:25:03 +03:00
parent 21c6ed4f82
commit 60e2149072
31032 changed files with 5222883 additions and 2 deletions
+15
View File
@@ -0,0 +1,15 @@
The ISC License
Copyright (c) npm, Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE NPM DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE NPM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
+158
View File
@@ -0,0 +1,158 @@
# @npmcli/git
A utility for spawning git from npm CLI contexts.
This is _not_ an implementation of git itself, it's just a thing that
spawns child processes to tell the system git CLI implementation to do
stuff.
## USAGE
```js
const git = require('@npmcli/git')
git.clone('git://foo/bar.git', 'some-branch', 'some-path', opts) // clone a repo
.then(() => git.spawn(['checkout', 'some-branch'], {cwd: 'bar'}))
.then(() => git.spawn(['you get the idea']))
```
## API
Most methods take an options object. Options are described below.
### `git.spawn(args, opts = {})`
Launch a `git` subprocess with the arguments specified.
All the other functions call this one at some point.
Processes are launched using
[`@npmcli/promise-spawn`](http://npm.im/@npmcli/promise-spawn), with the
`stdioString: true` option enabled by default, since git output is
generally in readable string format.
Return value is a `Promise` that resolves to a result object with `{cmd,
args, code, signal, stdout, stderr}` members, or rejects with an error with
the same fields, passed back from
[`@npmcli/promise-spawn`](http://npm.im/@npmcli/promise-spawn).
### `git.clone(repo, ref = 'HEAD', target = null, opts = {})` -> `Promise<sha String>`
Clone the repository into `target` path (or the default path for the name
of the repository), checking out `ref`.
Return value is the sha of the current HEAD in the locally cloned
repository.
In lieu of a specific `ref`, you may also pass in a `spec` option, which is
a [`npm-package-arg`](http://npm.im/npm-package-arg) object for a `git`
package dependency reference. In this way, you can select SemVer tags
within a range, or any git committish value. For example:
```js
const npa = require('npm-package-arg')
git.clone('git@github.com:npm/git.git', '', null, {
spec: npa('github:npm/git#semver:1.x'),
})
// only gitRange and gitCommittish are relevant, so this works, too
git.clone('git@github.com:npm/git.git', null, null, {
spec: { gitRange: '1.x' }
})
```
This will automatically do a shallow `--depth=1` clone on any hosts that
are known to support it. To force a shallow or deep clone, you can set the
`gitShallow` option to `true` or `false` respectively.
### `git.revs(repo, opts = {})` -> `Promise<rev doc Object>`
Fetch a representation of all of the named references in a given
repository. The resulting doc is intentionally somewhat
[packument](https://www.npmjs.com/package/pacote#packuments)-like, so that
git semver ranges can be applied using the same
[`npm-pick-manifest`](http://npm.im/npm-pick-manifest) logic.
The resulting object looks like:
```js
revs = {
versions: {
// all semver-looking tags go in here...
// version: { sha, ref, rawRef, type }
'1.0.0': {
sha: '1bc5fba3353f8e1b56493b266bc459276ab23139',
ref: 'v1.0.0',
rawRef: 'refs/tags/v1.0.0',
type: 'tag',
},
},
'dist-tags': {
HEAD: '1.0.0',
latest: '1.0.0',
},
refs: {
// all the advertised refs that can be cloned down remotely
HEAD: { sha, ref, rawRef, type: 'head' },
master: { ... },
'v1.0.0': { ... },
'refs/tags/v1.0.0': { ... },
},
shas: {
// all named shas referenced above
// sha: [list, of, refs]
'6b2501f9183a1753027a9bf89a184b7d3d4602c7': [
'HEAD',
'master',
'refs/heads/master',
],
'1bc5fba3353f8e1b56493b266bc459276ab23139': [ 'v1.0.0', 'refs/tags/v1.0.0' ],
},
}
```
### `git.is(opts)` -> `Promise<Boolean>`
Resolve to `true` if the path argument refers to the root of a git
repository.
It does this by looking for a file in `${path}/.git/index`, which is not an
airtight indicator, but at least avoids being fooled by an empty directory
or a file named `.git`.
### `git.find(opts)` -> `Promise<String | null>`
Given a path, walk up the file system tree until a git repo working
directory is found. Since this calls `stat` a bunch of times, it's
probably best to only call it if you're reasonably sure you're likely to be
in a git project somewhere. Pass in `opts.root` to stop checking at that
directory.
Resolves to `null` if not in a git project.
### `git.isClean(opts = {})` -> `Promise<Boolean>`
Return true if in a git dir, and that git dir is free of changes. This
will resolve `true` if the git working dir is clean, or `false` if not, and
reject if the path is not within a git directory or some other error
occurs.
## OPTIONS
- `retry` An object to configure retry behavior for transient network
errors with exponential backoff.
- `retries`: Defaults to `opts.fetchRetries` or 2
- `factor`: Defaults to `opts.fetchRetryFactor` or 10
- `maxTimeout`: Defaults to `opts.fetchRetryMaxtimeout` or 60000
- `minTimeout`: Defaults to `opts.fetchRetryMintimeout` or 1000
- `git` Path to the `git` binary to use. Will look up the first `git` in
the `PATH` if not specified.
- `spec` The [`npm-package-arg`](http://npm.im/npm-package-arg) specifier
object for the thing being fetched (if relevant).
- `fakePlatform` set to a fake value of `process.platform` to use. (Just
for testing `win32` behavior on Unix, and vice versa.)
- `cwd` The current working dir for the git command. Particularly for
`find` and `is` and `isClean`, it's good to know that this defaults to
`process.cwd()`, as one might expect.
- Any other options that can be passed to
[`@npmcli/promise-spawn`](http://npm.im/@npmcli/promise-spawn), or
`child_process.spawn()`.
+172
View File
@@ -0,0 +1,172 @@
// The goal here is to minimize both git workload and
// the number of refs we download over the network.
//
// Every method ends up with the checked out working dir
// at the specified ref, and resolves with the git sha.
// Only certain whitelisted hosts get shallow cloning.
// Many hosts (including GHE) don't always support it.
// A failed shallow fetch takes a LOT longer than a full
// fetch in most cases, so we skip it entirely.
// Set opts.gitShallow = true/false to force this behavior
// one way or the other.
const shallowHosts = new Set([
'github.com',
'gist.github.com',
'gitlab.com',
'bitbucket.com',
'bitbucket.org',
])
// we have to use url.parse until we add the same shim that hosted-git-info has
// to handle scp:// urls
const { parse } = require('url') // eslint-disable-line node/no-deprecated-api
const path = require('path')
const getRevs = require('./revs.js')
const spawn = require('./spawn.js')
const { isWindows } = require('./utils.js')
const pickManifest = require('npm-pick-manifest')
const fs = require('fs/promises')
module.exports = (repo, ref = 'HEAD', target = null, opts = {}) =>
getRevs(repo, opts).then(revs => clone(
repo,
revs,
ref,
resolveRef(revs, ref, opts),
target || defaultTarget(repo, opts.cwd),
opts
))
const maybeShallow = (repo, opts) => {
if (opts.gitShallow === false || opts.gitShallow) {
return opts.gitShallow
}
return shallowHosts.has(parse(repo).host)
}
const defaultTarget = (repo, /* istanbul ignore next */ cwd = process.cwd()) =>
path.resolve(cwd, path.basename(repo.replace(/[/\\]?\.git$/, '')))
const clone = (repo, revs, ref, revDoc, target, opts) => {
if (!revDoc) {
return unresolved(repo, ref, target, opts)
}
if (revDoc.sha === revs.refs.HEAD.sha) {
return plain(repo, revDoc, target, opts)
}
if (revDoc.type === 'tag' || revDoc.type === 'branch') {
return branch(repo, revDoc, target, opts)
}
return other(repo, revDoc, target, opts)
}
const resolveRef = (revs, ref, opts) => {
const { spec = {} } = opts
ref = spec.gitCommittish || ref
/* istanbul ignore next - will fail anyway, can't pull */
if (!revs) {
return null
}
if (spec.gitRange) {
return pickManifest(revs, spec.gitRange, opts)
}
if (!ref) {
return revs.refs.HEAD
}
if (revs.refs[ref]) {
return revs.refs[ref]
}
if (revs.shas[ref]) {
return revs.refs[revs.shas[ref][0]]
}
return null
}
// pull request or some other kind of advertised ref
const other = (repo, revDoc, target, opts) => {
const shallow = maybeShallow(repo, opts)
const fetchOrigin = ['fetch', 'origin', revDoc.rawRef]
.concat(shallow ? ['--depth=1'] : [])
const git = (args) => spawn(args, { ...opts, cwd: target })
return fs.mkdir(target, { recursive: true })
.then(() => git(['init']))
.then(() => isWindows(opts)
? git(['config', '--local', '--add', 'core.longpaths', 'true'])
: null)
.then(() => git(['remote', 'add', 'origin', repo]))
.then(() => git(fetchOrigin))
.then(() => git(['checkout', revDoc.sha]))
.then(() => updateSubmodules(target, opts))
.then(() => revDoc.sha)
}
// tag or branches. use -b
const branch = (repo, revDoc, target, opts) => {
const args = [
'clone',
'-b',
revDoc.ref,
repo,
target,
'--recurse-submodules',
]
if (maybeShallow(repo, opts)) {
args.push('--depth=1')
}
if (isWindows(opts)) {
args.push('--config', 'core.longpaths=true')
}
return spawn(args, opts).then(() => revDoc.sha)
}
// just the head. clone it
const plain = (repo, revDoc, target, opts) => {
const args = [
'clone',
repo,
target,
'--recurse-submodules',
]
if (maybeShallow(repo, opts)) {
args.push('--depth=1')
}
if (isWindows(opts)) {
args.push('--config', 'core.longpaths=true')
}
return spawn(args, opts).then(() => revDoc.sha)
}
const updateSubmodules = async (target, opts) => {
const hasSubmodules = await fs.stat(`${target}/.gitmodules`)
.then(() => true)
.catch(() => false)
if (!hasSubmodules) {
return null
}
return spawn([
'submodule',
'update',
'-q',
'--init',
'--recursive',
], { ...opts, cwd: target })
}
const unresolved = (repo, ref, target, opts) => {
// can't do this one shallowly, because the ref isn't advertised
// but we can avoid checking out the working dir twice, at least
const lp = isWindows(opts) ? ['--config', 'core.longpaths=true'] : []
const cloneArgs = ['clone', '--mirror', '-q', repo, target + '/.git']
const git = (args) => spawn(args, { ...opts, cwd: target })
return fs.mkdir(target, { recursive: true })
.then(() => git(cloneArgs.concat(lp)))
.then(() => git(['init']))
.then(() => git(['checkout', ref]))
.then(() => updateSubmodules(target, opts))
.then(() => git(['rev-parse', '--revs-only', 'HEAD']))
.then(({ stdout }) => stdout.trim())
}
+36
View File
@@ -0,0 +1,36 @@
const maxRetry = 3
class GitError extends Error {
shouldRetry () {
return false
}
}
class GitConnectionError extends GitError {
constructor () {
super('A git connection error occurred')
}
shouldRetry (number) {
return number < maxRetry
}
}
class GitPathspecError extends GitError {
constructor () {
super('The git reference could not be found')
}
}
class GitUnknownError extends GitError {
constructor () {
super('An unknown git error occurred')
}
}
module.exports = {
GitConnectionError,
GitPathspecError,
GitUnknownError,
}
+15
View File
@@ -0,0 +1,15 @@
const is = require('./is.js')
const { dirname } = require('path')
module.exports = async ({ cwd = process.cwd(), root } = {}) => {
while (true) {
if (await is({ cwd })) {
return cwd
}
const next = dirname(cwd)
if (cwd === root || cwd === next) {
return null
}
cwd = next
}
}
+9
View File
@@ -0,0 +1,9 @@
module.exports = {
clone: require('./clone.js'),
revs: require('./revs.js'),
spawn: require('./spawn.js'),
is: require('./is.js'),
find: require('./find.js'),
isClean: require('./is-clean.js'),
errors: require('./errors.js'),
}
+6
View File
@@ -0,0 +1,6 @@
const spawn = require('./spawn.js')
module.exports = (opts = {}) =>
spawn(['status', '--porcelain=v1', '-uno'], opts)
.then(res => !res.stdout.trim().split(/\r?\n+/)
.map(l => l.trim()).filter(l => l).length)
+4
View File
@@ -0,0 +1,4 @@
// not an airtight indicator, but a good gut-check to even bother trying
const { stat } = require('fs/promises')
module.exports = ({ cwd = process.cwd() } = {}) =>
stat(cwd + '/.git').then(() => true, () => false)
+147
View File
@@ -0,0 +1,147 @@
// turn an array of lines from `git ls-remote` into a thing
// vaguely resembling a packument, where docs are a resolved ref
const semver = require('semver')
module.exports = lines => finish(lines.reduce(linesToRevsReducer, {
versions: {},
'dist-tags': {},
refs: {},
shas: {},
}))
const finish = revs => distTags(shaList(peelTags(revs)))
// We can check out shallow clones on specific SHAs if we have a ref
const shaList = revs => {
Object.keys(revs.refs).forEach(ref => {
const doc = revs.refs[ref]
if (!revs.shas[doc.sha]) {
revs.shas[doc.sha] = [ref]
} else {
revs.shas[doc.sha].push(ref)
}
})
return revs
}
// Replace any tags with their ^{} counterparts, if those exist
const peelTags = revs => {
Object.keys(revs.refs).filter(ref => ref.endsWith('^{}')).forEach(ref => {
const peeled = revs.refs[ref]
const unpeeled = revs.refs[ref.replace(/\^\{\}$/, '')]
if (unpeeled) {
unpeeled.sha = peeled.sha
delete revs.refs[ref]
}
})
return revs
}
const distTags = revs => {
// not entirely sure what situations would result in an
// ichabod repo, but best to be careful in Sleepy Hollow anyway
const HEAD = revs.refs.HEAD || /* istanbul ignore next */ {}
const versions = Object.keys(revs.versions)
versions.forEach(v => {
// simulate a dist-tags with latest pointing at the
// 'latest' branch if one exists and is a version,
// or HEAD if not.
const ver = revs.versions[v]
if (revs.refs.latest && ver.sha === revs.refs.latest.sha) {
revs['dist-tags'].latest = v
} else if (ver.sha === HEAD.sha) {
revs['dist-tags'].HEAD = v
if (!revs.refs.latest) {
revs['dist-tags'].latest = v
}
}
})
return revs
}
const refType = ref => {
if (ref.startsWith('refs/tags/')) {
return 'tag'
}
if (ref.startsWith('refs/heads/')) {
return 'branch'
}
if (ref.startsWith('refs/pull/')) {
return 'pull'
}
if (ref === 'HEAD') {
return 'head'
}
// Could be anything, ignore for now
/* istanbul ignore next */
return 'other'
}
// return the doc, or null if we should ignore it.
const lineToRevDoc = line => {
const split = line.trim().split(/\s+/, 2)
if (split.length < 2) {
return null
}
const sha = split[0].trim()
const rawRef = split[1].trim()
const type = refType(rawRef)
if (type === 'tag') {
// refs/tags/foo^{} is the 'peeled tag', ie the commit
// that is tagged by refs/tags/foo they resolve to the same
// content, just different objects in git's data structure.
// But, we care about the thing the tag POINTS to, not the tag
// object itself, so we only look at the peeled tag refs, and
// ignore the pointer.
// For now, though, we have to save both, because some tags
// don't have peels, if they were not annotated.
const ref = rawRef.slice('refs/tags/'.length)
return { sha, ref, rawRef, type }
}
if (type === 'branch') {
const ref = rawRef.slice('refs/heads/'.length)
return { sha, ref, rawRef, type }
}
if (type === 'pull') {
// NB: merged pull requests installable with #pull/123/merge
// for the merged pr, or #pull/123 for the PR head
const ref = rawRef.slice('refs/'.length).replace(/\/head$/, '')
return { sha, ref, rawRef, type }
}
if (type === 'head') {
const ref = 'HEAD'
return { sha, ref, rawRef, type }
}
// at this point, all we can do is leave the ref un-munged
return { sha, ref: rawRef, rawRef, type }
}
const linesToRevsReducer = (revs, line) => {
const doc = lineToRevDoc(line)
if (!doc) {
return revs
}
revs.refs[doc.ref] = doc
revs.refs[doc.rawRef] = doc
if (doc.type === 'tag') {
// try to pull a semver value out of tags like `release-v1.2.3`
// which is a pretty common pattern.
const match = !doc.ref.endsWith('^{}') &&
doc.ref.match(/v?(\d+\.\d+\.\d+(?:[-+].+)?)$/)
if (match && semver.valid(match[1], true)) {
revs.versions[semver.clean(match[1], true)] = doc
}
}
return revs
}
+33
View File
@@ -0,0 +1,33 @@
const {
GitConnectionError,
GitPathspecError,
GitUnknownError,
} = require('./errors.js')
const connectionErrorRe = new RegExp([
'remote error: Internal Server Error',
'The remote end hung up unexpectedly',
'Connection timed out',
'Operation timed out',
'Failed to connect to .* Timed out',
'Connection reset by peer',
'SSL_ERROR_SYSCALL',
'The requested URL returned error: 503',
].join('|'))
const missingPathspecRe = /pathspec .* did not match any file\(s\) known to git/
function makeError (er) {
const message = er.stderr
let gitEr
if (connectionErrorRe.test(message)) {
gitEr = new GitConnectionError(message)
} else if (missingPathspecRe.test(message)) {
gitEr = new GitPathspecError(message)
} else {
gitEr = new GitUnknownError(message)
}
return Object.assign(gitEr, er)
}
module.exports = makeError
+57
View File
@@ -0,0 +1,57 @@
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const ini = require('ini')
const gitConfigPath = path.join(os.homedir(), '.gitconfig')
let cachedConfig = null
// Function to load and cache the git config
const loadGitConfig = () => {
if (cachedConfig === null) {
try {
cachedConfig = {}
if (fs.existsSync(gitConfigPath)) {
const configContent = fs.readFileSync(gitConfigPath, 'utf-8')
cachedConfig = ini.parse(configContent)
}
} catch (error) {
cachedConfig = {}
}
}
return cachedConfig
}
const checkGitConfigs = () => {
const config = loadGitConfig()
return {
sshCommandSetInConfig: config?.core?.sshCommand !== undefined,
askPassSetInConfig: config?.core?.askpass !== undefined,
}
}
const sshCommandSetInEnv = process.env.GIT_SSH_COMMAND !== undefined
const askPassSetInEnv = process.env.GIT_ASKPASS !== undefined
const { sshCommandSetInConfig, askPassSetInConfig } = checkGitConfigs()
// Values we want to set if they're not already defined by the end user
// This defaults to accepting new ssh host key fingerprints
const finalGitEnv = {
...(askPassSetInEnv || askPassSetInConfig ? {} : {
GIT_ASKPASS: 'echo',
}),
...(sshCommandSetInEnv || sshCommandSetInConfig ? {} : {
GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new',
}),
}
module.exports = (opts = {}) => ({
stdioString: true,
...opts,
shell: false,
env: opts.env || { ...finalGitEnv, ...process.env },
})
// Export the loadGitConfig function for testing
module.exports.loadGitConfig = loadGitConfig
+22
View File
@@ -0,0 +1,22 @@
const spawn = require('./spawn.js')
const { LRUCache } = require('lru-cache')
const linesToRevs = require('./lines-to-revs.js')
const revsCache = new LRUCache({
max: 100,
ttl: 5 * 60 * 1000,
})
module.exports = async (repo, opts = {}) => {
if (!opts.noGitRevCache) {
const cached = revsCache.get(repo)
if (cached) {
return cached
}
}
const { stdout } = await spawn(['ls-remote', repo], opts)
const revs = linesToRevs(stdout.trim().split('\n'))
revsCache.set(repo, revs)
return revs
}
+44
View File
@@ -0,0 +1,44 @@
const spawn = require('@npmcli/promise-spawn')
const { promiseRetry } = require('@gar/promise-retry')
const { log } = require('proc-log')
const makeError = require('./make-error.js')
const makeOpts = require('./opts.js')
module.exports = (gitArgs, opts = {}) => {
const whichGit = require('./which.js')
const gitPath = whichGit(opts)
if (gitPath instanceof Error) {
return Promise.reject(gitPath)
}
// undocumented option, mostly only here for tests
const args = opts.allowReplace || gitArgs[0] === '--no-replace-objects'
? gitArgs
: ['--no-replace-objects', ...gitArgs]
let retryOpts = opts.retry
if (retryOpts === null || retryOpts === undefined) {
retryOpts = {
retries: opts.fetchRetries || 2,
factor: opts.fetchRetryFactor || 10,
maxTimeout: opts.fetchRetryMaxtimeout || 60000,
minTimeout: opts.fetchRetryMintimeout || 1000,
}
}
return promiseRetry((retryFn, number) => {
if (number !== 1) {
log.silly('git', `Retrying git command: ${
args.join(' ')} attempt # ${number}`)
}
return spawn(gitPath, args, makeOpts(opts))
.catch(er => {
const gitError = makeError(er)
if (!gitError.shouldRetry(number)) {
throw gitError
}
retryFn(gitError)
})
}, retryOpts)
}
+3
View File
@@ -0,0 +1,3 @@
const isWindows = opts => (opts.fakePlatform || process.platform) === 'win32'
exports.isWindows = isWindows
+18
View File
@@ -0,0 +1,18 @@
const which = require('which')
let gitPath
try {
gitPath = which.sync('git')
} catch {
// ignore errors
}
module.exports = (opts = {}) => {
if (opts.git) {
return opts.git
}
if (!gitPath || opts.git === false) {
return Object.assign(new Error('No git binary found in $PATH'), { code: 'ENOGIT' })
}
return gitPath
}
+58
View File
@@ -0,0 +1,58 @@
{
"name": "@npmcli/git",
"version": "7.0.2",
"main": "lib/index.js",
"files": [
"bin/",
"lib/"
],
"description": "a util for spawning git from npm CLI contexts",
"repository": {
"type": "git",
"url": "git+https://github.com/npm/git.git"
},
"author": "GitHub Inc.",
"license": "ISC",
"scripts": {
"lint": "npm run eslint",
"snap": "tap",
"test": "tap",
"posttest": "npm run lint",
"postlint": "template-oss-check",
"lintfix": "npm run eslint -- --fix",
"template-oss-apply": "template-oss-apply --force",
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"tap": {
"timeout": 600,
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.0",
"@npmcli/template-oss": "4.24.1",
"npm-package-arg": "^13.0.0",
"slash": "^3.0.0",
"tap": "^16.0.1"
},
"dependencies": {
"@gar/promise-retry": "^1.0.0",
"@npmcli/promise-spawn": "^9.0.0",
"ini": "^6.0.0",
"lru-cache": "^11.2.1",
"npm-pick-manifest": "^11.0.1",
"proc-log": "^6.0.0",
"semver": "^7.3.5",
"which": "^6.0.0"
},
"engines": {
"node": "^20.17.0 || >=22.9.0"
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.24.1",
"publish": true
}
}