From 6727d198be1cee6dca7d4c202a3f4d6fb4ee461f Mon Sep 17 00:00:00 2001 From: Josef Prochazka Date: Tue, 25 Aug 2026 13:19:51 +0200 Subject: [PATCH 1/9] Add example proxy actor --- sample_actor_crawler/.actor/Dockerfile | 15 +++++ sample_actor_crawler/.actor/actor.json | 8 +++ sample_actor_crawler/.actor/input_schema.json | 22 +++++++ sample_actor_crawler/main.py | 57 +++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 sample_actor_crawler/.actor/Dockerfile create mode 100644 sample_actor_crawler/.actor/actor.json create mode 100644 sample_actor_crawler/.actor/input_schema.json create mode 100644 sample_actor_crawler/main.py diff --git a/sample_actor_crawler/.actor/Dockerfile b/sample_actor_crawler/.actor/Dockerfile new file mode 100644 index 0000000..5d12b4f --- /dev/null +++ b/sample_actor_crawler/.actor/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.11-slim +WORKDIR /usr/src/app +# Build-time-only network use (see sample_actor/.actor/Dockerfile for the full +# apify/apify-client version-pin rationale). This Actor additionally needs +# `crawlee[parsel]` for ParselCrawler; apify==4.0.0 constrains its own +# `crawlee` dependency to `>=1.8.0,<2.0.0`, and 1.8.3 is the newest release +# satisfying that (the same version apify-sdk-python's own lockfile pins +# alongside apify==4.0.0). +RUN pip install --no-cache-dir 'apify-client==3.1.0' 'apify==4.0.0' 'crawlee[parsel]==1.8.3' +COPY . ./ +# Run as a non-root user, like the real Apify Actor base images do (see +# sample_actor/.actor/Dockerfile for why this matters). +RUN useradd -m -u 1000 apify +USER apify +CMD ["python", "main.py"] diff --git a/sample_actor_crawler/.actor/actor.json b/sample_actor_crawler/.actor/actor.json new file mode 100644 index 0000000..41cc176 --- /dev/null +++ b/sample_actor_crawler/.actor/actor.json @@ -0,0 +1,8 @@ +{ + "actorSpecification": 1, + "name": "sample-actor-crawler", + "version": "0.0", + "buildTag": "latest", + "dockerfile": "./Dockerfile", + "input": "./input_schema.json" +} diff --git a/sample_actor_crawler/.actor/input_schema.json b/sample_actor_crawler/.actor/input_schema.json new file mode 100644 index 0000000..0b55f1e --- /dev/null +++ b/sample_actor_crawler/.actor/input_schema.json @@ -0,0 +1,22 @@ +{ + "title": "Sample Actor Crawler input", + "type": "object", + "schemaVersion": 1, + "properties": { + "startUrl": { + "title": "Start URL", + "type": "string", + "description": "The page the crawler starts from. Same-domain links found on it (and on the pages it discovers) are enqueued too, up to the crawl limit.", + "editor": "textfield", + "default": "https://crawlee.dev" + }, + "proxyConfiguration": { + "title": "Proxy configuration", + "type": "object", + "description": "Apify Proxy configuration used for every request. The only way to crawl direct with no proxy is an explicit `{\"useApifyProxy\": false}` -- omitting this field entirely is NOT equivalent; it falls through to the SDK's default proxy configuration (same as `useApifyProxy: true`) and requires a valid APIFY_PROXY_PASSWORD.", + "editor": "proxy", + "default": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] } + } + }, + "required": ["startUrl"] +} diff --git a/sample_actor_crawler/main.py b/sample_actor_crawler/main.py new file mode 100644 index 0000000..17a28d7 --- /dev/null +++ b/sample_actor_crawler/main.py @@ -0,0 +1,57 @@ +"""Sample Actor demonstrating a Parsel-based crawl through Apify Proxy. + +Adapted from the Apify SDK's own ParselCrawler guide. Reads ``startUrl`` and +``proxyConfiguration`` (see ``.actor/input_schema.json``) and passes the +latter straight to ``Actor.create_proxy_configuration`` with no fallback: +only an explicit ``{"useApifyProxy": false}`` crawls direct -- an omitted +``proxyConfiguration`` behaves like ``useApifyProxy: true``, not like +``false`` -- and either way, ``useApifyProxy: true`` with a missing or +invalid ``APIFY_PROXY_PASSWORD`` fails the run via the SDK's own live +proxy-access check. See README.md's "Apify Proxy" section for the full +explanation. +""" +import asyncio + +from crawlee.crawlers import ParselCrawler, ParselCrawlingContext +from crawlee.router import Router + +from apify import Actor + +router = Router[ParselCrawlingContext]() + + +@router.default_handler +async def request_handler(context: ParselCrawlingContext) -> None: + Actor.log.info(f"Scraping {context.request.url} ...") + + data = { + "url": context.request.url, + "title": context.selector.xpath("//title/text()").get(), + "headings": context.selector.xpath("//h1/text() | //h2/text() | //h3/text()").getall(), + } + await context.push_data(data) + + await context.enqueue_links(strategy="same-domain") + + +async def main() -> None: + async with Actor: + actor_input = await Actor.get_input() or {} + start_url = actor_input.get("startUrl", "https://crawlee.dev") + + proxy_configuration = await Actor.create_proxy_configuration( + actor_proxy_input=actor_input.get("proxyConfiguration") + ) + + crawler = ParselCrawler( + proxy_configuration=proxy_configuration, + request_handler=router, + # Crawl limit: 10 pages total, seed URL counted. + max_requests_per_crawl=10, + ) + + await crawler.run([start_url]) + + +if __name__ == "__main__": + asyncio.run(main()) From fa60e29efe0206c2a201852d73ceab38fd6bb3c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 14:16:35 +0000 Subject: [PATCH 2/9] Resolve the Dockerfile location from .actor/actor.json when building Mirror the platform's resolution order (apify-worker ensureDockerfileExists): the actor.json dockerfile field relative to the .actor dir (escape-checked, warn-and-fall-through when it names nothing, invalid-format failure when non-string), then .actor/Dockerfile, then root Dockerfile, then the bundled platform default. The resolved path is always passed to docker.buildImage as its dockerfile option; previously the driver relied on Docker's implicit root Dockerfile only, so Actors with .actor/Dockerfile (like sample_actor_crawler) could not build. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GFiYFS2Eq9833z1ruGQwUK --- package-lock.json | 1 + package.json | 1 + requirements/actor-driver.md | 24 ++ src/driver/docker-driver.ts | 9 +- src/driver/types.ts | 8 + src/services/builds.ts | 29 ++- src/services/default-dockerfile.ts | 26 +++ src/services/dockerfile-location.ts | 248 +++++++++++++++++++++ test/e2e/actor-dev-loop.test.ts | 16 ++ test/e2e/helpers/docker.ts | 2 +- test/unit/docker-driver.test.ts | 137 +++++++++++- test/unit/dockerfile-location.test.ts | 308 ++++++++++++++++++++++++++ 12 files changed, 801 insertions(+), 8 deletions(-) create mode 100644 src/services/default-dockerfile.ts create mode 100644 src/services/dockerfile-location.ts create mode 100644 test/unit/dockerfile-location.test.ts diff --git a/package-lock.json b/package-lock.json index df898a4..96d833b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@crawlee/fs-storage": "4.0.0-beta.133", "dockerode": "^4.0.5", "express": "^5.1.0", + "json5": "^2.2.3", "tar-stream": "^3.1.7" }, "devDependencies": { diff --git a/package.json b/package.json index 57f575b..64e74ca 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@crawlee/fs-storage": "4.0.0-beta.133", "dockerode": "^4.0.5", "express": "^5.1.0", + "json5": "^2.2.3", "tar-stream": "^3.1.7" }, "devDependencies": { diff --git a/requirements/actor-driver.md b/requirements/actor-driver.md index d261fd8..1dc46c8 100644 --- a/requirements/actor-driver.md +++ b/requirements/actor-driver.md @@ -37,6 +37,30 @@ - Actor build details are saved in `__BUILDS__` internal storage - Actor build log is saved in `__LOGS__` internal storage - Actor details are saved in `__ACTORS__` internal storage +- **The Dockerfile to build is resolved from the version's `sourceFiles`**, not left to Docker's own + implicit "`Dockerfile` at the tar root" default, matching the real platform (apify-worker's + `ensureDockerfileExists`) so that "builds locally" keeps predicting "builds on the platform". The + resolved (or default) path is always passed explicitly as dockerode's `dockerfile` build option - + never omitted. Resolution order, stopping at the first hit: + 1. the `dockerfile` field of `.actor/actor.json` (parsed as JSON5, matching the platform), + interpreted relative to the `.actor` directory. A value that resolves outside the Actor root + (e.g. `../../evil/Dockerfile`, or a leading `/`) fails the build before any daemon call. A + value that is present but not a string (e.g. `true`) also fails the build immediately, with a + `.actor/actor.json has invalid format` message. A value that is a string (including the empty + string) but names no file in `sourceFiles` does not fail the build on that account - it warns + and falls through to the next candidate. + 2. `.actor/Dockerfile` + 3. `Dockerfile` at the Actor root + - Matching against `sourceFiles` names is case-insensitive for all three candidates, but the path + handed to Docker is always the matched source file's own name, never the candidate's canonical + casing (Docker's lookup inside the tar is case-sensitive). An exact-case match wins; otherwise the + first match in `sourceFiles` order. + - If none of the three candidates resolves, the build does not fail: a bundled default Dockerfile + (apify-worker's own `default_Dockerfile`, `FROM apify/actor-node:20` + `npm install`) is injected + as an extra in-memory `SourceFile` for that one build only - never written back to the version's + persisted `sourceFiles`. + - Every outcome (resolved candidate, warn-and-fall-through, or default) is recorded in the build log, + so the choice is never silent. # Bind mount volumes with Actor source code diff --git a/src/driver/docker-driver.ts b/src/driver/docker-driver.ts index d63f54b..b94a460 100644 --- a/src/driver/docker-driver.ts +++ b/src/driver/docker-driver.ts @@ -31,6 +31,7 @@ import Docker from 'dockerode'; import * as tar from 'tar-stream'; import { CONTAINER_API_ALIAS } from '../config.js'; +import { normalizeEntryName } from '../services/dockerfile-location.js'; import type { SourceFile } from '../storage/entities.js'; import { DriverTimedOutError, @@ -107,11 +108,16 @@ function sourceFileToBuffer(file: SourceFile): Buffer { return file.format === 'BASE64' ? Buffer.from(file.content, 'base64') : Buffer.from(file.content, 'utf8'); } +/** Entry names go through `normalizeEntryName` - the same normalizer `dockerfile-location.ts`'s + * resolver indexes `sourceFiles` by - so the `dockerfilePath` `startBuild` hands dockerode as its + * `dockerfile` option is guaranteed to name exactly the tar entry Docker will find (`2-design.md`'s + * Risks: "Tar entry names"). A canonicalizing change to what today's tar contains (e.g. `./foo` becomes + * `foo`), never a semantic one. */ function buildTarball(sourceFiles: SourceFile[]): NodeJS.ReadableStream { const pack = tar.pack(); for (const file of sourceFiles) { const buffer = sourceFileToBuffer(file); - pack.entry({ name: file.name }, buffer); + pack.entry({ name: normalizeEntryName(file.name) }, buffer); } pack.finalize(); return pack; @@ -231,6 +237,7 @@ export class DockerDriver implements Driver { stream = await this.docker.buildImage(tarball, { t: imageTag, nocache: !ctx.useCache, + dockerfile: ctx.dockerfilePath, abortSignal: controller.signal, }); } catch (error) { diff --git a/src/driver/types.ts b/src/driver/types.ts index bef4aca..c6181ce 100644 --- a/src/driver/types.ts +++ b/src/driver/types.ts @@ -6,6 +6,14 @@ export interface BuildContext { sourceFiles: SourceFile[]; useCache: boolean; timeoutSecs: number; + /** The tar-relative path to the Dockerfile to build, as resolved by + * `services/dockerfile-location.ts: resolveDockerfileLocation` (a `resolved` or `default` outcome - + * `runBuildInBackground` never calls `driver.startBuild` on a `failure`). Passed straight through as + * dockerode's own `dockerfile` build option - always set, never omitted, so there is no second, + * untested code path that falls back to Docker's implicit "Dockerfile at the tar root" default + * (`2-design.md`'s Risks: "Every build changes path"). Required, not optional: every caller has + * already resolved one by the time `startBuild` is reached. */ + dockerfilePath: string; } /** Host folder + image working directory, carried together so "both or neither" is enforced by the diff --git a/src/services/builds.ts b/src/services/builds.ts index 4ee5d53..c5dec8e 100644 --- a/src/services/builds.ts +++ b/src/services/builds.ts @@ -1,10 +1,11 @@ import { generateId } from '../storage/ids.js'; -import type { BuildRecord, JobStatus } from '../storage/entities.js'; +import type { BuildRecord, JobStatus, SourceFile } from '../storage/entities.js'; import { getRegistries } from '../storage/registries.js'; import type { ActorRecord, ActorVersionRecord } from '../storage/entities.js'; import { recordTaggedBuild, updateActor } from './actors.js'; import type { Driver } from '../driver/types.js'; import { DriverTimedOutError } from '../driver/types.js'; +import { resolveDockerfileLocation } from './dockerfile-location.js'; import { appendLog, flushLog, markLogTerminal } from './logs.js'; import { isTerminalJobStatus, transitionJobStatus } from './job-status.js'; @@ -180,14 +181,38 @@ export async function runBuildInBackground( return; } + // Resolved right before the actual `docker build` (mirrors apify-worker, where this lives in the + // build job, not the Docker-facing layer): candidate order is the "dockerfile" field of + // `.actor/actor.json`, then `.actor/Dockerfile`, then root `Dockerfile`, then the bundled default + // (`requirements/actor-driver.md`, `2-design.md`). Never persisted onto `version` either way - a + // `default` outcome's extra `SourceFile` is appended only to the in-memory list this one build's + // `BuildContext` gets, so a later push that adds a real Dockerfile is never competing with it. + const dockerfileResolution = resolveDockerfileLocation(version.sourceFiles); + if (dockerfileResolution.outcome === 'failure') { + appendLog(record.id, `${dockerfileResolution.message}\n`); + await flushLog(record.id); + markLogTerminal(record.id); + await transitionJobStatus(builds, record.id, 'FAILED', { + finishedAt: new Date().toISOString(), + statusMessage: dockerfileResolution.message, + }); + return; + } + for (const line of dockerfileResolution.logLines) appendLog(record.id, line); + const sourceFiles: SourceFile[] = + dockerfileResolution.outcome === 'default' + ? [...version.sourceFiles, dockerfileResolution.extraSourceFile] + : version.sourceFiles; + try { const outcome = await driver.startBuild( { buildId: record.id, actorName: actor.name, - sourceFiles: version.sourceFiles, + sourceFiles, useCache: options.useCache, timeoutSecs: DEFAULT_BUILD_TIMEOUT_SECS, + dockerfilePath: dockerfileResolution.dockerfilePath, }, (chunk) => appendLog(record.id, chunk), ); diff --git a/src/services/default-dockerfile.ts b/src/services/default-dockerfile.ts new file mode 100644 index 0000000..f5b508a --- /dev/null +++ b/src/services/default-dockerfile.ts @@ -0,0 +1,26 @@ +/** + * The bundled default Dockerfile, injected by `dockerfile-location.ts`'s resolver when an Actor's + * pushed source names no Dockerfile at all (no `dockerfile` field in `.actor/actor.json`, and no + * case-insensitive `Dockerfile` at `.actor/Dockerfile` or the Actor root). This is apify-worker's own + * platform-parity fallback, copied here verbatim (byte-for-byte, including its own leading comment) from + * `apify-worker/src/actor/build/default_Dockerfile` - not reinterpreted or "improved" - so a locally + * built default-Dockerfile image matches what the real platform would have produced for the same, + * Dockerfile-less source. + * + * A string constant, not a sibling file copied at build/run time: the runtime's build input is already + * an in-memory `SourceFile[]`/tar, not a working directory on disk, and the codebase has no existing + * pattern for shipping a non-TS asset file (see `2-design.md`'s Alternatives). + */ +export const DEFAULT_DOCKERFILE_NAME = 'Dockerfile'; + +export const DEFAULT_DOCKERFILE_CONTENT = `# This is a default Dockerfile is used for Actors that don't have a Dockerfile. +FROM apify/actor-node:20 + +# Copy all files and directories from the directory to the Docker image +COPY . ./ + +# Install NPM packages, skip optional and development dependencies to keep the image small, +# avoid logging to much and show log the dependency tree +RUN npm install --quiet --only=prod --no-optional \\ + && (npm list || true) +`; diff --git a/src/services/dockerfile-location.ts b/src/services/dockerfile-location.ts new file mode 100644 index 0000000..c9824a6 --- /dev/null +++ b/src/services/dockerfile-location.ts @@ -0,0 +1,248 @@ +/** + * Resolves which Dockerfile a build should use, from the version's flat, in-memory `SourceFile[]` - + * mirroring apify-worker's own `ensureDockerfileExists` (`act2_build_job.ts`) so that "builds locally" + * keeps predicting "builds on the platform" (`2-design.md`). Pure: no filesystem, no Docker. "Does this + * file exist" is a lookup in a normalized-name index built over `sourceFiles`; "does it escape the + * root" is POSIX path arithmetic on strings. + * + * Candidate order, stopping at the first hit: + * 1. the `dockerfile` field of `.actor/actor.json`, resolved relative to the `.actor` directory + * 2. `.actor/Dockerfile` + * 3. `Dockerfile` at the Actor root + * 4. (nothing resolved) the bundled default Dockerfile (`default-dockerfile.ts`), platform-parity + * with apify-worker rather than failing the build. + * + * Matching against `sourceFiles` names is case-insensitive (candidates 1-3), but the path handed back + * is always the matched source file's OWN name (post-normalization) - never the candidate's canonical + * casing - because Docker's lookup inside the tar is case-sensitive. When both a case-exact and a + * case-differing match exist, the case-exact one wins; otherwise the first match in `sourceFiles` order. + */ +import * as path from 'node:path'; +import JSON5 from 'json5'; + +import type { SourceFile } from '../storage/entities.js'; +import { DEFAULT_DOCKERFILE_CONTENT, DEFAULT_DOCKERFILE_NAME } from './default-dockerfile.js'; + +const ACTOR_DIR = '.actor'; +const ACTOR_JSON_NAME = `${ACTOR_DIR}/actor.json`; +const DOCKERFILE_BASENAME = 'Dockerfile'; + +/** + * Why resolution failed - each has its own message, computed once at the failure site (see + * `resolveDockerfileLocation`'s call sites below) and carried through verbatim rather than + * reconstructed by the caller. `services/builds.ts` only needs `message` to fail the build; `reason` + * exists so tests can assert *which* failure fired without string-matching the message. + */ +export type DockerfileResolutionFailureReason = + 'escapes-actor-root' | 'invalid-dockerfile-field' | 'unparseable-actor-json'; + +/** + * The three outcomes `resolveDockerfileLocation` can return - see this module's doc comment for the + * candidate order each represents. + * + * - `resolved`: a candidate (1, 2, or 3) matched an existing source file. `dockerfilePath` is that + * file's own (normalized) name - exactly the string `docker-driver.ts` must hand dockerode as its + * `dockerfile` build option, and exactly the tar entry name `buildTarball` will produce for it (both + * go through the same normalizer, see `normalizeEntryName` below). + * - `default`: nothing resolved. `dockerfilePath` is always `'Dockerfile'` (free by construction - see + * this module's doc comment), and `extraSourceFile` is the one extra `SourceFile` the caller must + * append to `BuildContext.sourceFiles` for this one build only - never written back to the version's + * persisted `sourceFiles`. + * - `failure`: a typed, build-ending problem found before any Docker/daemon call - an escaping + * `dockerfile` field path, a non-string `dockerfile` field, or an unparseable `.actor/actor.json`. + * + * Every outcome (including `failure`) carries its own diagnostic text in `logLines`/`message` - the + * choice is never made silently (`3-success-criteria.md` #14). + */ +export type DockerfileResolution = + | { outcome: 'resolved'; dockerfilePath: string; logLines: string[] } + | { outcome: 'default'; dockerfilePath: string; logLines: string[]; extraSourceFile: SourceFile } + | { outcome: 'failure'; reason: DockerfileResolutionFailureReason; message: string }; + +/** + * The one name-normalizer shared by this resolver's index and `docker-driver.ts`'s `buildTarball` - + * the string handed to the daemon as the `dockerfile` option is only ever correct if it is exactly the + * tar entry name Docker will look for, so both sides must agree on what a `SourceFile.name` (or a + * candidate path built from `.actor/actor.json`) canonicalizes to. Three steps, in order: backslashes + * become POSIX separators (a Windows-authored tree's `sourceFiles` names might carry them), a leading + * `./` is stripped, and the result is run through `path.posix.normalize` (collapses `a/./b` and + * `a/b/../c`, but deliberately leaves a leading `../` alone - that is exactly what the escape check + * below looks for). + */ +export function normalizeEntryName(name: string): string { + const posixName = name.replace(/\\/g, '/'); + const withoutLeadingDotSlash = posixName.replace(/^(?:\.\/)+/, ''); + return path.posix.normalize(withoutLeadingDotSlash); +} + +/** Decodes a `SourceFile`'s content to text, the same `BASE64`/`TEXT` split `docker-driver.ts`'s + * `sourceFileToBuffer` uses for the tar - duplicated here (rather than imported) because this module is + * deliberately Docker-free; the two are one line each and drifting apart would be immediately obvious + * from `dockerfile-location.test.ts`. */ +function sourceFileToText(file: SourceFile): string { + return file.format === 'BASE64' ? Buffer.from(file.content, 'base64').toString('utf8') : file.content; +} + +/** One `SourceFile`, indexed by its normalized name (for the exact `.actor/actor.json` lookup) and by + * that name's lowercase (for the case-insensitive Dockerfile candidate lookups) - built once per + * resolution, in `sourceFiles` order, which is exactly the tie-break order `findCaseInsensitive` below + * relies on. */ +interface IndexedFile { + normalizedName: string; + lowerName: string; +} + +function indexSourceFiles(sourceFiles: SourceFile[]): IndexedFile[] { + return sourceFiles.map((file) => { + const normalizedName = normalizeEntryName(file.name); + return { normalizedName, lowerName: normalizedName.toLowerCase() }; + }); +} + +/** Case-insensitive lookup for a Dockerfile candidate: among every indexed file whose normalized name + * matches `candidate` case-insensitively, an exact-case match wins; otherwise the first match in + * `sourceFiles` order (`indexed` is already in that order, so "first" here just means "first found"). */ +function findCaseInsensitive(indexed: IndexedFile[], candidate: string): IndexedFile | undefined { + const lowerCandidate = candidate.toLowerCase(); + let firstMatch: IndexedFile | undefined; + for (const file of indexed) { + if (file.lowerName !== lowerCandidate) continue; + if (file.normalizedName === candidate) return file; // exact case always wins immediately + firstMatch ??= file; + } + return firstMatch; +} + +/** Exact (case-sensitive) lookup, used only for `.actor/actor.json` itself - unlike the Dockerfile + * candidates, the platform does not case-fold the spec file's own path. */ +function findExact( + sourceFiles: SourceFile[], + indexed: IndexedFile[], + normalizedTarget: string, +): SourceFile | undefined { + const position = indexed.findIndex((file) => file.normalizedName === normalizedTarget); + return position === -1 ? undefined : sourceFiles[position]; +} + +/** Builds the `escapes-actor-root` failure for a `dockerfile` field value, matching apify-worker's own + * `UserError` for the same condition - always keyed off the raw field value the developer actually + * wrote, never the joined/normalized path, so the message names exactly what they typed. */ +function escapesActorRootFailure(rawField: string): DockerfileResolution { + return { + outcome: 'failure', + reason: 'escapes-actor-root', + message: `Dockerfile path "${rawField}" in .actor/actor.json points outside the Actor root directory.`, + }; +} + +/** + * Resolves the Dockerfile for a build from its version's `sourceFiles`. See this module's doc comment + * for the candidate order and outcome shapes. + */ +export function resolveDockerfileLocation(sourceFiles: SourceFile[]): DockerfileResolution { + const indexed = indexSourceFiles(sourceFiles); + const logLines: string[] = []; + + // `.actor/actor.json` is optional - a missing file is not an error, it just means candidate 1 never + // applies (mirrors apify-worker's `readActorSpecificationFile`, which swallows ENOENT). + const actorJsonFile = findExact(sourceFiles, indexed, ACTOR_JSON_NAME); + let actorSpecification: unknown; + if (actorJsonFile) { + try { + actorSpecification = JSON5.parse(sourceFileToText(actorJsonFile)); + } catch (error) { + return { + outcome: 'failure', + reason: 'unparseable-actor-json', + message: `Could not parse .actor/actor.json: ${(error as Error).message}`, + }; + } + } + + // Candidate 1: the "dockerfile" field, only when actor.json parsed to an object that actually has + // one (a missing field is not an error - it just means this candidate is skipped, same as apify- + // worker's `actorSpecification?.dockerfile` optional chain). + if (actorSpecification !== null && typeof actorSpecification === 'object' && 'dockerfile' in actorSpecification) { + const field = (actorSpecification as { dockerfile?: unknown }).dockerfile; + if (typeof field !== 'string') { + return { + outcome: 'failure', + reason: 'invalid-dockerfile-field', + message: '.actor/actor.json has invalid format: "dockerfile" must be a string.', + }; + } + + if (field === '') { + // An empty string is a valid string that simply names no file - indistinguishable from a typo + // (candidate C below), never the invalid-format failure above (2-design.md, Example G). + logLines.push( + 'Warning: "" (from the "dockerfile" field in .actor/actor.json) is not in the pushed source; falling back to the default locations.\n', + ); + } else if (field.startsWith('/')) { + // An absolute path can never be "relative to .actor" - checked before joining, exactly like + // apify-worker's `ensureActorDirFileInActorSourceRoot` (path.join would otherwise silently fold + // a leading "/" into a same-directory join instead of rejecting it). + return escapesActorRootFailure(field); + } else { + const joined = normalizeEntryName(path.posix.join(ACTOR_DIR, field)); + if (joined === '..' || joined.startsWith('../')) { + return escapesActorRootFailure(field); + } + + const match = findCaseInsensitive(indexed, joined); + if (match) { + return { + outcome: 'resolved', + dockerfilePath: match.normalizedName, + logLines: [ + ...logLines, + `Using Dockerfile "${match.normalizedName}" (from the "dockerfile" field in .actor/actor.json).\n`, + ], + }; + } + + // Names nothing in the pushed source - warn and fall through to candidate 2, never fail the + // build on this account (2-design.md, Example C; apify-worker's own would-be-breaking `throw` + // stays commented out). + logLines.push( + `Warning: "${joined}" (from the "dockerfile" field in .actor/actor.json) is not in the pushed source; falling back to the default locations.\n`, + ); + } + } + + // Candidate 2: .actor/Dockerfile + const actorDirCandidate = normalizeEntryName(`${ACTOR_DIR}/${DOCKERFILE_BASENAME}`); + const actorDirMatch = findCaseInsensitive(indexed, actorDirCandidate); + if (actorDirMatch) { + return { + outcome: 'resolved', + dockerfilePath: actorDirMatch.normalizedName, + logLines: [ + ...logLines, + `Using Dockerfile "${actorDirMatch.normalizedName}" (found at .actor/Dockerfile).\n`, + ], + }; + } + + // Candidate 3: Dockerfile at the Actor root + const rootCandidate = normalizeEntryName(DOCKERFILE_BASENAME); + const rootMatch = findCaseInsensitive(indexed, rootCandidate); + if (rootMatch) { + return { + outcome: 'resolved', + dockerfilePath: rootMatch.normalizedName, + logLines: [...logLines, `Using Dockerfile "${rootMatch.normalizedName}" (found at the Actor root).\n`], + }; + } + + // Nothing resolved: inject the bundled default (2-design.md, Example F; Decisions #1). Plain + // "Dockerfile" at the tar root is free by construction here - reaching this branch already required + // that no case-insensitive Dockerfile matched at candidate 2 or 3. + logLines.push(`${DOCKERFILE_BASENAME} not found, using the default one.\n`); + return { + outcome: 'default', + dockerfilePath: DEFAULT_DOCKERFILE_NAME, + logLines, + extraSourceFile: { name: DEFAULT_DOCKERFILE_NAME, format: 'TEXT', content: DEFAULT_DOCKERFILE_CONTENT }, + }; +} diff --git a/test/e2e/actor-dev-loop.test.ts b/test/e2e/actor-dev-loop.test.ts index 783ef8d..8fc3663 100644 --- a/test/e2e/actor-dev-loop.test.ts +++ b/test/e2e/actor-dev-loop.test.ts @@ -106,6 +106,22 @@ describe('full Actor dev loop via apify-cli (requires Docker)', () => { ); } + it( + 'sample_actor_crawler: push -> build succeeds (build-only - its Dockerfile lives at .actor/Dockerfile, ' + + 'the layout that used to fail with a daemon-side "Cannot locate specified Dockerfile" error; ' + + '2-design.md Example A, 3-success-criteria.md #1/#2)', + () => { + const env = apifyEnv(isolatedApifyHome); + const actorDir = join(REPO_ROOT, 'sample_actor_crawler'); + + const pushOutput = apify(['push', '--json'], { cwd: actorDir, env }); + const push = JSON.parse(pushOutput) as PushResult; + + expect(push.build.status).toBe('SUCCEEDED'); + }, + 5 * 60 * 1000, + ); + it('the run log contains the crawler per-page lines', () => { const env = apifyEnv(isolatedApifyHome); const callOutput = apify(['call', '--input', JSON.stringify({ maxPages: 1 }), '--json'], { diff --git a/test/e2e/helpers/docker.ts b/test/e2e/helpers/docker.ts index 70235ea..7626c0f 100644 --- a/test/e2e/helpers/docker.ts +++ b/test/e2e/helpers/docker.ts @@ -18,7 +18,7 @@ export function pullBaseImages(): void { // Pre-pulled here rather than left to the first build, per `test.md`'s documented CI requirement - // building an Actor image is the one step that still needs network, and doing it once up front // keeps the timing of the actual push/call assertions predictable. - for (const image of ['apify/actor-node:24', 'apify/actor-python:3.13']) { + for (const image of ['apify/actor-node:24', 'apify/actor-python:3.13', 'python:3.11-slim']) { execFileSync('docker', ['pull', image], { stdio: 'inherit' }); } } diff --git a/test/unit/docker-driver.test.ts b/test/unit/docker-driver.test.ts index bbaa040..62818ba 100644 --- a/test/unit/docker-driver.test.ts +++ b/test/unit/docker-driver.test.ts @@ -446,7 +446,14 @@ describe('DockerDriver.startBuild - imageWorkingDirectory capture (actor-driver. driver.available = true; const outcome = await driver.startBuild( - { buildId: 'build-1', actorName: 'my-actor', sourceFiles: [], useCache: true, timeoutSecs: 60 }, + { + buildId: 'build-1', + actorName: 'my-actor', + sourceFiles: [], + useCache: true, + timeoutSecs: 60, + dockerfilePath: 'Dockerfile', + }, () => {}, ); @@ -463,7 +470,14 @@ describe('DockerDriver.startBuild - imageWorkingDirectory capture (actor-driver. const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); const outcome = await driver.startBuild( - { buildId: 'build-2', actorName: 'my-actor', sourceFiles: [], useCache: true, timeoutSecs: 60 }, + { + buildId: 'build-2', + actorName: 'my-actor', + sourceFiles: [], + useCache: true, + timeoutSecs: 60, + dockerfilePath: 'Dockerfile', + }, () => {}, ); @@ -480,7 +494,14 @@ describe('DockerDriver.startBuild - imageWorkingDirectory capture (actor-driver. driver.available = true; const outcome = await driver.startBuild( - { buildId: 'build-3', actorName: 'my-actor', sourceFiles: [], useCache: true, timeoutSecs: 60 }, + { + buildId: 'build-3', + actorName: 'my-actor', + sourceFiles: [], + useCache: true, + timeoutSecs: 60, + dockerfilePath: 'Dockerfile', + }, () => {}, ); @@ -493,7 +514,14 @@ describe('DockerDriver.startBuild - imageWorkingDirectory capture (actor-driver. driver.available = true; const outcome = await driver.startBuild( - { buildId: 'build-4', actorName: 'my-actor', sourceFiles: [], useCache: true, timeoutSecs: 60 }, + { + buildId: 'build-4', + actorName: 'my-actor', + sourceFiles: [], + useCache: true, + timeoutSecs: 60, + dockerfilePath: 'Dockerfile', + }, () => {}, ); @@ -501,6 +529,107 @@ describe('DockerDriver.startBuild - imageWorkingDirectory capture (actor-driver. }); }); +describe('DockerDriver.startBuild - dockerfile option (2-design.md: "the resolved path is handed to dockerode as its `dockerfile` build option")', () => { + /** A stub covering only what `startBuild` calls, exposing the `buildImage` mock itself so a test can + * read back exactly which options it was called with - unlike `stubDockerForBuild` above, which only + * cares about the post-build inspect. */ + function stubDockerCapturingBuildImageOptions() { + const followProgress = vi.fn( + ( + _stream: NodeJS.ReadableStream, + onFinished: (err: Error | null, res: Array<{ error?: string }>) => void, + ) => { + onFinished(null, []); + }, + ); + const buildImage = vi.fn(async () => new PassThrough()); + const getImage = vi.fn(() => ({ inspect: async () => ({ Config: { WorkingDir: '' } }) })); + const docker = { buildImage, modem: { followProgress }, getImage } as unknown as Docker; + return { docker, buildImage }; + } + + it('passes ctx.dockerfilePath through verbatim as buildImage\'s "dockerfile" option', async () => { + const stub = stubDockerCapturingBuildImageOptions(); + const driver = new DockerDriver(stub.docker); + driver.available = true; + + await driver.startBuild( + { + buildId: 'build-dockerfile-option', + actorName: 'my-actor', + sourceFiles: [], + useCache: true, + timeoutSecs: 60, + dockerfilePath: '.actor/Dockerfile', + }, + () => {}, + ); + + expect(stub.buildImage).toHaveBeenCalledTimes(1); + const [, options] = stub.buildImage.mock.calls[0]!; + // Without `ctx.dockerfilePath` being threaded through to this option at all (the pre-fix + // behaviour - see `docker-driver.ts`'s old `buildImage(tarball, { t, nocache, abortSignal })` + // call, with no `dockerfile` key), this assertion fails: `options.dockerfile` would be + // `undefined`, never `'.actor/Dockerfile'`. + expect(options).toMatchObject({ dockerfile: '.actor/Dockerfile' }); + }); + + it('always sets the "dockerfile" option, even for the plain root-"Dockerfile" case that coincides with Docker\'s own implicit default (2-design.md Example B)', async () => { + const stub = stubDockerCapturingBuildImageOptions(); + const driver = new DockerDriver(stub.docker); + driver.available = true; + + await driver.startBuild( + { + buildId: 'build-dockerfile-option-2', + actorName: 'my-actor', + sourceFiles: [], + useCache: true, + timeoutSecs: 60, + dockerfilePath: 'Dockerfile', + }, + () => {}, + ); + + const [, options] = stub.buildImage.mock.calls[0]!; + expect(options).toMatchObject({ dockerfile: 'Dockerfile' }); + }); + + it('normalizes tar entry names (leading "./" stripped) so a resolved dockerfilePath always names an entry that actually exists in the tar', async () => { + const stub = stubDockerCapturingBuildImageOptions(); + const driver = new DockerDriver(stub.docker); + driver.available = true; + + await driver.startBuild( + { + buildId: 'build-dockerfile-option-3', + actorName: 'my-actor', + sourceFiles: [{ name: './.actor/Dockerfile', format: 'TEXT', content: 'FROM node:20\n' }], + useCache: true, + timeoutSecs: 60, + dockerfilePath: '.actor/Dockerfile', + }, + () => {}, + ); + + const [tarball] = stub.buildImage.mock.calls[0]!; + const extract = tar.extract(); + const entryNames: string[] = []; + await new Promise((resolve, reject) => { + extract.on('entry', (header, entryStream, next) => { + entryNames.push(header.name); + entryStream.resume(); + next(); + }); + extract.on('finish', resolve); + extract.on('error', reject); + (tarball as NodeJS.ReadableStream).pipe(extract); + }); + + expect(entryNames).toEqual(['.actor/Dockerfile']); + }); +}); + describe('DockerDriver.ensureProbeImage (actor-driver.md: registration needs no build of its own)', () => { /** A stub covering only what `ensureProbeImage` calls: `buildImage` and `modem.followProgress` * (invoking its `onFinished` callback synchronously, as a successful build with no progress lines). */ diff --git a/test/unit/dockerfile-location.test.ts b/test/unit/dockerfile-location.test.ts new file mode 100644 index 0000000..744a082 --- /dev/null +++ b/test/unit/dockerfile-location.test.ts @@ -0,0 +1,308 @@ +import { describe, expect, it } from 'vitest'; + +import { normalizeEntryName, resolveDockerfileLocation } from '../../src/services/dockerfile-location.js'; +import { DEFAULT_DOCKERFILE_CONTENT, DEFAULT_DOCKERFILE_NAME } from '../../src/services/default-dockerfile.js'; +import type { SourceFile } from '../../src/storage/entities.js'; + +/** A `TEXT`-format `SourceFile`, the common case in every table row below. */ +function text(name: string, content: string): SourceFile { + return { name, format: 'TEXT', content }; +} + +/** `.actor/actor.json` as a `SourceFile`, given the object to serialize (or a raw string, for the + * JSON5-tolerance and unparseable-content cases below). */ +function actorJson(spec: Record | string): SourceFile { + return text('.actor/actor.json', typeof spec === 'string' ? spec : JSON.stringify(spec)); +} + +describe('resolveDockerfileLocation', () => { + // --- 2-design.md Example A: sample_actor_crawler - the bug this change fixes. --- + it('A: resolves the "dockerfile" field to .actor/Dockerfile (sample_actor_crawler layout)', () => { + const result = resolveDockerfileLocation([ + actorJson({ dockerfile: './Dockerfile' }), + text('.actor/Dockerfile', 'FROM python:3.11-slim\n'), + text('.actor/input_schema.json', '{}'), + text('main.py', 'print(1)\n'), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/Dockerfile'); + expect(result.logLines).toEqual([ + 'Using Dockerfile ".actor/Dockerfile" (from the "dockerfile" field in .actor/actor.json).\n', + ]); + }); + + // --- 2-design.md Example B: sample_actor_ts / sample_actor_py - the regression guard. --- + it('B: resolves "../Dockerfile" to the root Dockerfile (sample_actor_ts/py layout) - byte-identical to today\'s implicit default', () => { + const result = resolveDockerfileLocation([ + actorJson({ dockerfile: '../Dockerfile' }), + text('Dockerfile', 'FROM node:20\n'), + text('main.ts', 'console.log(1);\n'), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('Dockerfile'); + }); + + // --- 2-design.md Example C: a "dockerfile" field that names nothing - warn and fall through. --- + it('C: warns and falls through to .actor/Dockerfile when the "dockerfile" field names no file', () => { + const result = resolveDockerfileLocation([ + actorJson({ dockerfile: './Custom.Dockerfile' }), + text('.actor/Dockerfile', 'FROM node:20\n'), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/Dockerfile'); + expect(result.logLines).toEqual([ + 'Warning: ".actor/Custom.Dockerfile" (from the "dockerfile" field in .actor/actor.json) is not in the pushed source; falling back to the default locations.\n', + 'Using Dockerfile ".actor/Dockerfile" (found at .actor/Dockerfile).\n', + ]); + }); + + // --- 2-design.md Example D: a path that escapes the Actor root. --- + describe('D: an escaping "dockerfile" field fails the build before any daemon call', () => { + it('a relative path with enough ".." segments to escape .actor/', () => { + const result = resolveDockerfileLocation([actorJson({ dockerfile: '../../evil/Dockerfile' })]); + + expect(result).toEqual({ + outcome: 'failure', + reason: 'escapes-actor-root', + message: + 'Dockerfile path "../../evil/Dockerfile" in .actor/actor.json points outside the Actor root directory.', + }); + }); + + it('an absolute path (leading "/")', () => { + const result = resolveDockerfileLocation([actorJson({ dockerfile: '/etc/passwd' })]); + + expect(result).toEqual({ + outcome: 'failure', + reason: 'escapes-actor-root', + message: 'Dockerfile path "/etc/passwd" in .actor/actor.json points outside the Actor root directory.', + }); + }); + + it('a path that stays inside .actor/ (one level of ".." exactly cancels the join) is not treated as escaping', () => { + // .actor + ../Dockerfile normalizes to plain "Dockerfile" - inside the root, not outside it + // (this is Example B, re-asserted here to pin the boundary this escape check must not cross). + const result = resolveDockerfileLocation([ + actorJson({ dockerfile: '../Dockerfile' }), + text('Dockerfile', 'FROM node:20\n'), + ]); + + expect(result.outcome).toBe('resolved'); + }); + }); + + // --- 2-design.md Example E: case-insensitive matching, exact-case tie-break. --- + describe('E: case-insensitive matching', () => { + it('matches .actor/Dockerfile against a lowercase .actor/dockerfile source file, returning ITS OWN spelling', () => { + const result = resolveDockerfileLocation([text('.actor/dockerfile', 'FROM node:20\n')]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/dockerfile'); + }); + + it('an exact-case match wins even when a case-differing match appears earlier in sourceFiles order', () => { + const result = resolveDockerfileLocation([ + text('dockerfile', 'wrong case, listed first'), + text('Dockerfile', 'exact case, listed second - must still win'), + ]); + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('Dockerfile'); + }); + + it('with no exact-case match, the first case-differing match in sourceFiles order wins', () => { + const result = resolveDockerfileLocation([ + text('DOCKERFILE', 'all caps, listed first'), + text('dockerfile', 'all lowercase, listed second'), + ]); + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('DOCKERFILE'); + }); + }); + + // --- 2-design.md Example F: no Dockerfile at all - the platform-parity default. --- + describe('F: nothing resolves - the bundled default is injected', () => { + it('injects the bundled default Dockerfile, verbatim, as an extra Dockerfile-named SourceFile', () => { + const result = resolveDockerfileLocation([ + actorJson({ name: 'no-dockerfile-actor' }), + text('main.py', 'print(1)\n'), + ]); + + expect(result.outcome).toBe('default'); + if (result.outcome !== 'default') return; + expect(result.dockerfilePath).toBe('Dockerfile'); + expect(result.dockerfilePath).toBe(DEFAULT_DOCKERFILE_NAME); + expect(result.extraSourceFile).toEqual({ + name: 'Dockerfile', + format: 'TEXT', + content: DEFAULT_DOCKERFILE_CONTENT, + }); + expect(result.logLines).toEqual(['Dockerfile not found, using the default one.\n']); + }); + + it('also injects the default when there is no .actor/actor.json at all', () => { + const result = resolveDockerfileLocation([text('main.py', 'print(1)\n')]); + + expect(result.outcome).toBe('default'); + }); + + it('is reached only after both .actor/Dockerfile and a root Dockerfile have failed to match, in any case', () => { + const result = resolveDockerfileLocation([ + text('main.py', 'print(1)\n'), + text('README.md', '# not a Dockerfile\n'), + ]); + + expect(result.outcome).toBe('default'); + }); + }); + + // --- 2-design.md Example G: a present-but-malformed "dockerfile" field. --- + describe('G: malformed "dockerfile" field', () => { + it('a non-string value (e.g. true) fails the build immediately, before any candidate is tried', () => { + const result = resolveDockerfileLocation([ + actorJson({ dockerfile: true }), + text('Dockerfile', 'FROM node:20\n'), + ]); + + expect(result).toEqual({ + outcome: 'failure', + reason: 'invalid-dockerfile-field', + message: '.actor/actor.json has invalid format: "dockerfile" must be a string.', + }); + }); + + it('a number value fails the same way as a boolean', () => { + const result = resolveDockerfileLocation([actorJson({ dockerfile: 42 })]); + + expect(result.outcome).toBe('failure'); + if (result.outcome !== 'failure') return; + expect(result.reason).toBe('invalid-dockerfile-field'); + }); + + it('an empty string is treated as "names nothing" - warn and fall through, never the invalid-format failure', () => { + const result = resolveDockerfileLocation([ + actorJson({ dockerfile: '' }), + text('.actor/Dockerfile', 'FROM node:20\n'), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/Dockerfile'); + expect(result.logLines[0]).toBe( + 'Warning: "" (from the "dockerfile" field in .actor/actor.json) is not in the pushed source; falling back to the default locations.\n', + ); + }); + + it('an empty string falls all the way through to the bundled default when no other candidate exists either', () => { + const result = resolveDockerfileLocation([actorJson({ dockerfile: '' }), text('main.py', 'print(1)\n')]); + + expect(result.outcome).toBe('default'); + }); + }); + + // --- JSON5 tolerance: the platform accepts what strict JSON.parse would reject. --- + describe('JSON5 tolerance', () => { + it('parses a trailing comma and a comment in .actor/actor.json', () => { + const result = resolveDockerfileLocation([ + actorJson('{\n // a comment JSON.parse would reject\n "dockerfile": "./Dockerfile",\n}\n'), + text('.actor/Dockerfile', 'FROM node:20\n'), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/Dockerfile'); + }); + + it('parses unquoted keys and single-quoted strings', () => { + const result = resolveDockerfileLocation([ + actorJson("{ dockerfile: '../Dockerfile' }"), + text('Dockerfile', 'FROM node:20\n'), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('Dockerfile'); + }); + + it('a genuinely unparseable .actor/actor.json fails the build with a clear message', () => { + const result = resolveDockerfileLocation([actorJson('{ this is not json at all')]); + + expect(result.outcome).toBe('failure'); + if (result.outcome !== 'failure') return; + expect(result.reason).toBe('unparseable-actor-json'); + expect(result.message).toContain('.actor/actor.json'); + }); + + it('tolerates .actor/actor.json delivered as a BASE64-encoded SourceFile', () => { + const spec = JSON.stringify({ dockerfile: './Dockerfile' }); + const result = resolveDockerfileLocation([ + { name: '.actor/actor.json', format: 'BASE64', content: Buffer.from(spec, 'utf8').toString('base64') }, + text('.actor/Dockerfile', 'FROM node:20\n'), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/Dockerfile'); + }); + }); + + // --- No .actor/actor.json at all: candidates 2 and 3 must still work standalone. --- + describe('no-actor.json fallbacks', () => { + it('resolves .actor/Dockerfile with no actor.json present at all', () => { + const result = resolveDockerfileLocation([ + text('.actor/Dockerfile', 'FROM node:20\n'), + text('main.py', ''), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/Dockerfile'); + }); + + it('resolves the root Dockerfile with no actor.json and no .actor/Dockerfile present', () => { + const result = resolveDockerfileLocation([text('Dockerfile', 'FROM node:20\n'), text('main.py', '')]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('Dockerfile'); + }); + + it('.actor/Dockerfile is preferred over a root Dockerfile when both exist and actor.json names neither', () => { + const result = resolveDockerfileLocation([ + text('.actor/Dockerfile', 'FROM node:20 # actor-dir\n'), + text('Dockerfile', 'FROM node:20 # root\n'), + ]); + + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/Dockerfile'); + }); + }); +}); + +describe('normalizeEntryName', () => { + it('strips a leading "./"', () => { + expect(normalizeEntryName('./Dockerfile')).toBe('Dockerfile'); + }); + + it('converts backslashes to POSIX separators', () => { + expect(normalizeEntryName('.actor\\Dockerfile')).toBe('.actor/Dockerfile'); + }); + + it('collapses "a/./b" and "a/b/../c" via path.posix.normalize', () => { + expect(normalizeEntryName('.actor/./Dockerfile')).toBe('.actor/Dockerfile'); + expect(normalizeEntryName('.actor/sub/../Dockerfile')).toBe('.actor/Dockerfile'); + }); + + it('leaves an escaping "../" prefix alone (the escape check depends on this)', () => { + expect(normalizeEntryName('../evil/Dockerfile')).toBe('../evil/Dockerfile'); + }); +}); From 4621ac0cbe3f486e09c8f4dc17f20470f5040307 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 14:39:20 +0000 Subject: [PATCH 3/9] Address review findings: self-contained comments, wiring tests, layering Rewrite comments and test titles to stand on their own (or cite requirements/actor-driver.md) instead of referencing external design notes; add integration tests covering runBuildInBackground's resolver wiring (failure -> FAILED without a driver call, default -> injected Dockerfile reaches the driver ctx without touching persisted sourceFiles, resolved -> path passthrough); move normalizeEntryName into src/driver/tar-entry-name.ts so the driver layer no longer imports from services. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GFiYFS2Eq9833z1ruGQwUK --- src/driver/docker-driver.ts | 9 +- src/driver/tar-entry-name.ts | 21 ++++ src/driver/types.ts | 6 +- src/services/builds.ts | 2 +- src/services/default-dockerfile.ts | 7 +- src/services/dockerfile-location.ts | 50 +++++---- test/e2e/actor-dev-loop.test.ts | 3 +- test/integration/helpers/test-server.ts | 18 +++- test/integration/job-lifecycle.test.ts | 129 +++++++++++++++++++++++- test/unit/docker-driver.test.ts | 4 +- test/unit/dockerfile-location.test.ts | 14 +-- 11 files changed, 209 insertions(+), 54 deletions(-) create mode 100644 src/driver/tar-entry-name.ts diff --git a/src/driver/docker-driver.ts b/src/driver/docker-driver.ts index b94a460..2ded5c0 100644 --- a/src/driver/docker-driver.ts +++ b/src/driver/docker-driver.ts @@ -31,7 +31,7 @@ import Docker from 'dockerode'; import * as tar from 'tar-stream'; import { CONTAINER_API_ALIAS } from '../config.js'; -import { normalizeEntryName } from '../services/dockerfile-location.js'; +import { normalizeEntryName } from './tar-entry-name.js'; import type { SourceFile } from '../storage/entities.js'; import { DriverTimedOutError, @@ -110,9 +110,10 @@ function sourceFileToBuffer(file: SourceFile): Buffer { /** Entry names go through `normalizeEntryName` - the same normalizer `dockerfile-location.ts`'s * resolver indexes `sourceFiles` by - so the `dockerfilePath` `startBuild` hands dockerode as its - * `dockerfile` option is guaranteed to name exactly the tar entry Docker will find (`2-design.md`'s - * Risks: "Tar entry names"). A canonicalizing change to what today's tar contains (e.g. `./foo` becomes - * `foo`), never a semantic one. */ + * `dockerfile` option is guaranteed to name exactly the tar entry Docker will find. A canonicalizing + * change to what today's tar contains (e.g. `./foo` becomes `foo`), never a semantic one, and only + * a harmless one: the option only works if the string matches a tar entry exactly, so routing both + * the resolver's index and this function through one normalizer removes that class of bug. */ function buildTarball(sourceFiles: SourceFile[]): NodeJS.ReadableStream { const pack = tar.pack(); for (const file of sourceFiles) { diff --git a/src/driver/tar-entry-name.ts b/src/driver/tar-entry-name.ts new file mode 100644 index 0000000..2786ba5 --- /dev/null +++ b/src/driver/tar-entry-name.ts @@ -0,0 +1,21 @@ +import * as path from 'node:path'; + +/** + * The one name-normalizer shared by the Dockerfile resolver's source-file index + * (`services/dockerfile-location.ts`) and `docker-driver.ts`'s `buildTarball` - the string handed to + * the daemon as the `dockerfile` option is only ever correct if it is exactly the tar entry name Docker + * will look for, so both sides must agree on what a `SourceFile.name` (or a candidate path built from + * `.actor/actor.json`) canonicalizes to. Three steps, in order: backslashes become POSIX separators (a + * Windows-authored tree's `sourceFiles` names might carry them), a leading `./` is stripped, and the + * result is run through `path.posix.normalize` (collapses `a/./b` and `a/b/../c`, but deliberately + * leaves a leading `../` alone - that is exactly what the Dockerfile resolver's escape check looks for). + * + * Lives in the driver layer (not `services/`) since `docker-driver.ts`'s own tar-building is what this + * normalizer must stay byte-for-byte consistent with; `services/dockerfile-location.ts` imports it from + * here rather than the driver reaching up into `services/`. + */ +export function normalizeEntryName(name: string): string { + const posixName = name.replace(/\\/g, '/'); + const withoutLeadingDotSlash = posixName.replace(/^(?:\.\/)+/, ''); + return path.posix.normalize(withoutLeadingDotSlash); +} diff --git a/src/driver/types.ts b/src/driver/types.ts index c6181ce..6e2a48c 100644 --- a/src/driver/types.ts +++ b/src/driver/types.ts @@ -10,9 +10,9 @@ export interface BuildContext { * `services/dockerfile-location.ts: resolveDockerfileLocation` (a `resolved` or `default` outcome - * `runBuildInBackground` never calls `driver.startBuild` on a `failure`). Passed straight through as * dockerode's own `dockerfile` build option - always set, never omitted, so there is no second, - * untested code path that falls back to Docker's implicit "Dockerfile at the tar root" default - * (`2-design.md`'s Risks: "Every build changes path"). Required, not optional: every caller has - * already resolved one by the time `startBuild` is reached. */ + * untested code path that falls back to Docker's implicit "Dockerfile at the tar root" default. + * Required, not optional: every caller has already resolved one (`resolved` or `default`) by the + * time `startBuild` is reached. */ dockerfilePath: string; } diff --git a/src/services/builds.ts b/src/services/builds.ts index c5dec8e..f332e15 100644 --- a/src/services/builds.ts +++ b/src/services/builds.ts @@ -184,7 +184,7 @@ export async function runBuildInBackground( // Resolved right before the actual `docker build` (mirrors apify-worker, where this lives in the // build job, not the Docker-facing layer): candidate order is the "dockerfile" field of // `.actor/actor.json`, then `.actor/Dockerfile`, then root `Dockerfile`, then the bundled default - // (`requirements/actor-driver.md`, `2-design.md`). Never persisted onto `version` either way - a + // (`requirements/actor-driver.md`). Never persisted onto `version` either way - a // `default` outcome's extra `SourceFile` is appended only to the in-memory list this one build's // `BuildContext` gets, so a later push that adds a real Dockerfile is never competing with it. const dockerfileResolution = resolveDockerfileLocation(version.sourceFiles); diff --git a/src/services/default-dockerfile.ts b/src/services/default-dockerfile.ts index f5b508a..4389331 100644 --- a/src/services/default-dockerfile.ts +++ b/src/services/default-dockerfile.ts @@ -7,9 +7,10 @@ * built default-Dockerfile image matches what the real platform would have produced for the same, * Dockerfile-less source. * - * A string constant, not a sibling file copied at build/run time: the runtime's build input is already - * an in-memory `SourceFile[]`/tar, not a working directory on disk, and the codebase has no existing - * pattern for shipping a non-TS asset file (see `2-design.md`'s Alternatives). + * A string constant, not a sibling file copied at build/run time: the runtime's build input is + * already an in-memory `SourceFile[]`/tar, not a working directory on disk, and the codebase has no + * existing pattern for shipping a non-TS asset file. A string constant produces the identical bytes + * with no extra packaging step. */ export const DEFAULT_DOCKERFILE_NAME = 'Dockerfile'; diff --git a/src/services/dockerfile-location.ts b/src/services/dockerfile-location.ts index c9824a6..2d29517 100644 --- a/src/services/dockerfile-location.ts +++ b/src/services/dockerfile-location.ts @@ -1,9 +1,9 @@ /** * Resolves which Dockerfile a build should use, from the version's flat, in-memory `SourceFile[]` - * mirroring apify-worker's own `ensureDockerfileExists` (`act2_build_job.ts`) so that "builds locally" - * keeps predicting "builds on the platform" (`2-design.md`). Pure: no filesystem, no Docker. "Does this - * file exist" is a lookup in a normalized-name index built over `sourceFiles`; "does it escape the - * root" is POSIX path arithmetic on strings. + * keeps predicting "builds on the platform" (`requirements/actor-driver.md`). Pure: no filesystem, no + * Docker. "Does this file exist" is a lookup in a normalized-name index built over `sourceFiles`; "does + * it escape the root" is POSIX path arithmetic on strings. * * Candidate order, stopping at the first hit: * 1. the `dockerfile` field of `.actor/actor.json`, resolved relative to the `.actor` directory @@ -20,9 +20,17 @@ import * as path from 'node:path'; import JSON5 from 'json5'; +import { normalizeEntryName } from '../driver/tar-entry-name.js'; import type { SourceFile } from '../storage/entities.js'; import { DEFAULT_DOCKERFILE_CONTENT, DEFAULT_DOCKERFILE_NAME } from './default-dockerfile.js'; +/** Re-exported so every caller that needs the tar-entry normalizer alongside the Dockerfile resolver + * (e.g. `dockerfile-location.test.ts`) can import both from this module - the implementation itself + * lives in the driver layer (`driver/tar-entry-name.ts`), since `docker-driver.ts`'s own tar-building is + * what it must stay byte-for-byte consistent with; this module imports it rather than the driver + * reaching up into `services/`. */ +export { normalizeEntryName }; + const ACTOR_DIR = '.actor'; const ACTOR_JSON_NAME = `${ACTOR_DIR}/actor.json`; const DOCKERFILE_BASENAME = 'Dockerfile'; @@ -43,7 +51,7 @@ export type DockerfileResolutionFailureReason = * - `resolved`: a candidate (1, 2, or 3) matched an existing source file. `dockerfilePath` is that * file's own (normalized) name - exactly the string `docker-driver.ts` must hand dockerode as its * `dockerfile` build option, and exactly the tar entry name `buildTarball` will produce for it (both - * go through the same normalizer, see `normalizeEntryName` below). + * go through the same normalizer, see `normalizeEntryName` above). * - `default`: nothing resolved. `dockerfilePath` is always `'Dockerfile'` (free by construction - see * this module's doc comment), and `extraSourceFile` is the one extra `SourceFile` the caller must * append to `BuildContext.sourceFiles` for this one build only - never written back to the version's @@ -52,29 +60,14 @@ export type DockerfileResolutionFailureReason = * `dockerfile` field path, a non-string `dockerfile` field, or an unparseable `.actor/actor.json`. * * Every outcome (including `failure`) carries its own diagnostic text in `logLines`/`message` - the - * choice is never made silently (`3-success-criteria.md` #14). + * choice is never made silently: every build outcome produces a build log entry that identifies which + * Dockerfile source was used or which warning/failure applied. */ export type DockerfileResolution = | { outcome: 'resolved'; dockerfilePath: string; logLines: string[] } | { outcome: 'default'; dockerfilePath: string; logLines: string[]; extraSourceFile: SourceFile } | { outcome: 'failure'; reason: DockerfileResolutionFailureReason; message: string }; -/** - * The one name-normalizer shared by this resolver's index and `docker-driver.ts`'s `buildTarball` - - * the string handed to the daemon as the `dockerfile` option is only ever correct if it is exactly the - * tar entry name Docker will look for, so both sides must agree on what a `SourceFile.name` (or a - * candidate path built from `.actor/actor.json`) canonicalizes to. Three steps, in order: backslashes - * become POSIX separators (a Windows-authored tree's `sourceFiles` names might carry them), a leading - * `./` is stripped, and the result is run through `path.posix.normalize` (collapses `a/./b` and - * `a/b/../c`, but deliberately leaves a leading `../` alone - that is exactly what the escape check - * below looks for). - */ -export function normalizeEntryName(name: string): string { - const posixName = name.replace(/\\/g, '/'); - const withoutLeadingDotSlash = posixName.replace(/^(?:\.\/)+/, ''); - return path.posix.normalize(withoutLeadingDotSlash); -} - /** Decodes a `SourceFile`'s content to text, the same `BASE64`/`TEXT` split `docker-driver.ts`'s * `sourceFileToBuffer` uses for the tar - duplicated here (rather than imported) because this module is * deliberately Docker-free; the two are one line each and drifting apart would be immediately obvious @@ -174,7 +167,9 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile if (field === '') { // An empty string is a valid string that simply names no file - indistinguishable from a typo - // (candidate C below), never the invalid-format failure above (2-design.md, Example G). + // (the "names nothing" case below), never the invalid-format failure above: a non-string + // value is a shape error rejected outright, while an empty string is a valid string that + // simply fails to match anything, so it gets the same warn-and-fall-through treatment. logLines.push( 'Warning: "" (from the "dockerfile" field in .actor/actor.json) is not in the pushed source; falling back to the default locations.\n', ); @@ -202,8 +197,8 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile } // Names nothing in the pushed source - warn and fall through to candidate 2, never fail the - // build on this account (2-design.md, Example C; apify-worker's own would-be-breaking `throw` - // stays commented out). + // build on this account (matches apify-worker's own would-be-breaking `throw`, which + // stays commented out there for the same reason). logLines.push( `Warning: "${joined}" (from the "dockerfile" field in .actor/actor.json) is not in the pushed source; falling back to the default locations.\n`, ); @@ -235,9 +230,10 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile }; } - // Nothing resolved: inject the bundled default (2-design.md, Example F; Decisions #1). Plain - // "Dockerfile" at the tar root is free by construction here - reaching this branch already required - // that no case-insensitive Dockerfile matched at candidate 2 or 3. + // Nothing resolved: inject the bundled default, platform-parity with apify-worker rather than + // failing the build. Plain "Dockerfile" at the tar root is free by construction here - + // reaching this branch already required that no case-insensitive Dockerfile matched at + // candidate 2 or 3. logLines.push(`${DOCKERFILE_BASENAME} not found, using the default one.\n`); return { outcome: 'default', diff --git a/test/e2e/actor-dev-loop.test.ts b/test/e2e/actor-dev-loop.test.ts index 8fc3663..2675e09 100644 --- a/test/e2e/actor-dev-loop.test.ts +++ b/test/e2e/actor-dev-loop.test.ts @@ -108,8 +108,7 @@ describe('full Actor dev loop via apify-cli (requires Docker)', () => { it( 'sample_actor_crawler: push -> build succeeds (build-only - its Dockerfile lives at .actor/Dockerfile, ' + - 'the layout that used to fail with a daemon-side "Cannot locate specified Dockerfile" error; ' + - '2-design.md Example A, 3-success-criteria.md #1/#2)', + 'the layout that used to fail with a daemon-side "Cannot locate specified Dockerfile" error)', () => { const env = apifyEnv(isolatedApifyHome); const actorDir = join(REPO_ROOT, 'sample_actor_crawler'); diff --git a/test/integration/helpers/test-server.ts b/test/integration/helpers/test-server.ts index d35b157..10e12fe 100644 --- a/test/integration/helpers/test-server.ts +++ b/test/integration/helpers/test-server.ts @@ -12,7 +12,7 @@ import { resetUsersForTests } from '../../../src/services/users.js'; import { resetApiFallbackStateForTests } from '../../../src/services/api-fallback.js'; import { createApiServer } from '../../../src/api/server.js'; import { resetLogsForTests, stopLogFlusher } from '../../../src/services/logs.js'; -import type { BuildOutcome, Driver, RunOutcome } from '../../../src/driver/types.js'; +import type { BuildContext, BuildOutcome, Driver, RunOutcome } from '../../../src/driver/types.js'; /** A driver that is always unavailable, so build/run creation fails fast and deterministically. */ export function unavailableDriver(): Driver { @@ -67,12 +67,22 @@ export function fixedRunOutcomeDriver(outcome: RunOutcome): Driver { /** Same idea as `fixedRunOutcomeDriver`, but for builds: `startBuild` either resolves with `outcome` or * rejects with `error`, whichever the caller supplies (`error` wins if both are given, so a - * `DriverTimedOutError` can be asserted straight through to a `TIMED-OUT` status). */ -export function fixedBuildOutcomeDriver(outcome: BuildOutcome, error?: Error): Driver { + * `DriverTimedOutError` can be asserted straight through to a `TIMED-OUT` status). Every `startBuild` + * call's `ctx` is recorded in `startBuildContexts`, in call order - lets a test assert exactly what + * `runBuildInBackground` computed and passed through (e.g. `sourceFiles`/`dockerfilePath` after the + * Dockerfile resolver ran), not just that some build happened; existing callers that only need `Driver` + * itself are unaffected, since this is a strict superset of that interface. */ +export function fixedBuildOutcomeDriver( + outcome: BuildOutcome, + error?: Error, +): Driver & { startBuildContexts: BuildContext[] } { + const startBuildContexts: BuildContext[] = []; return { available: true, + startBuildContexts, async init() {}, - async startBuild() { + async startBuild(ctx) { + startBuildContexts.push(ctx); if (error) throw error; return outcome; }, diff --git a/test/integration/job-lifecycle.test.ts b/test/integration/job-lifecycle.test.ts index 2350284..df06398 100644 --- a/test/integration/job-lifecycle.test.ts +++ b/test/integration/job-lifecycle.test.ts @@ -39,7 +39,14 @@ import { waitForRunFinish, } from '../../src/services/runs.js'; import { DriverTimedOutError, type Driver } from '../../src/driver/types.js'; -import type { ActorRecord, ActorVersionRecord, BuildRecord, RunRecord } from '../../src/storage/entities.js'; +import type { + ActorRecord, + ActorVersionRecord, + BuildRecord, + RunRecord, + SourceFile, +} from '../../src/storage/entities.js'; +import { DEFAULT_DOCKERFILE_CONTENT, DEFAULT_DOCKERFILE_NAME } from '../../src/services/default-dockerfile.js'; /** Creates an Actor via the real client (so it has a genuine owner) and returns the underlying * `ActorRecord` for direct service-layer calls. */ @@ -320,6 +327,126 @@ describe('job lifecycle: TIMED-OUT mapping and abort/completion race guards', () }); }); + describe("Dockerfile resolution wiring: runBuildInBackground acts on resolveDockerfileLocation's outcome", () => { + it('a "failure" outcome marks the build FAILED with the resolver\'s message as statusMessage, and never calls driver.startBuild', async () => { + const driver = neverStartDriver(); + server = await startTestServer(driver); + const actor = await seedActor(server, 'dockerfile-failure-actor'); + + // A "dockerfile" field that escapes the Actor root - resolveDockerfileLocation returns a + // `failure` outcome before any candidate is even looked up in sourceFiles. + const escapingSourceFiles: SourceFile[] = [ + { + name: '.actor/actor.json', + format: 'TEXT', + content: JSON.stringify({ dockerfile: '../../evil/Dockerfile' }), + }, + ]; + const version: ActorVersionRecord = { ...VERSION, sourceFiles: escapingSourceFiles }; + + const record: BuildRecord = { + id: generateId(), + userId: actor.userId, + actorId: actor.id, + versionNumber: '0.0', + buildNumber: '0.0.1', + tag: 'latest', + status: 'READY', + startedAt: new Date().toISOString(), + }; + await getRegistries().builds.set(record.id, record); + + await runBuildInBackground(driver, actor, version, record, { tag: 'latest', useCache: true }); + + const final = await getRegistries().builds.get(record.id); + expect(final?.status).toBe('FAILED'); + expect(final?.statusMessage).toBe( + 'Dockerfile path "../../evil/Dockerfile" in .actor/actor.json points outside the Actor root directory.', + ); + // `startBuild` is never called either - if it were, `neverStartDriver` would have thrown and + // failed the test. + }); + + it('a "default" outcome appends the bundled default Dockerfile to the driver\'s ctx.sourceFiles and sets ctx.dockerfilePath to "Dockerfile"', async () => { + const driver = fixedBuildOutcomeDriver({ imageId: 'x' }); + server = await startTestServer(driver); + const actor = await seedActor(server, 'dockerfile-default-actor'); + + // No actor.json, and no Dockerfile anywhere in sourceFiles - resolveDockerfileLocation falls + // all the way through to the bundled default. + const noDockerfileSourceFiles: SourceFile[] = [ + { name: 'main.js', format: 'TEXT', content: 'console.log(1);\n' }, + ]; + const version: ActorVersionRecord = { ...VERSION, sourceFiles: noDockerfileSourceFiles }; + + const record: BuildRecord = { + id: generateId(), + userId: actor.userId, + actorId: actor.id, + versionNumber: '0.0', + buildNumber: '0.0.1', + tag: 'latest', + status: 'READY', + startedAt: new Date().toISOString(), + }; + await getRegistries().builds.set(record.id, record); + + await runBuildInBackground(driver, actor, version, record, { tag: 'latest', useCache: true }); + + expect(driver.startBuildContexts).toHaveLength(1); + const ctx = driver.startBuildContexts[0]!; + expect(ctx.dockerfilePath).toBe('Dockerfile'); + expect(ctx.dockerfilePath).toBe(DEFAULT_DOCKERFILE_NAME); + // The extra, injected Dockerfile SourceFile actually reaches the driver's ctx, alongside the + // original sourceFiles - never in place of them. + expect(ctx.sourceFiles).toEqual([ + ...noDockerfileSourceFiles, + { name: 'Dockerfile', format: 'TEXT', content: DEFAULT_DOCKERFILE_CONTENT }, + ]); + // Never written back to the version's own persisted sourceFiles - only this one build's ctx + // gets the extra entry. + expect(version.sourceFiles).toEqual(noDockerfileSourceFiles); + + const final = await getRegistries().builds.get(record.id); + expect(final?.status).toBe('SUCCEEDED'); + }); + + it('a "resolved" outcome passes the resolved path through to ctx.dockerfilePath, with sourceFiles unchanged', async () => { + const driver = fixedBuildOutcomeDriver({ imageId: 'x' }); + server = await startTestServer(driver); + const actor = await seedActor(server, 'dockerfile-resolved-actor'); + + // No actor.json "dockerfile" field, but a .actor/Dockerfile exists - candidate 2 resolves. + const resolvedSourceFiles: SourceFile[] = [ + { name: '.actor/Dockerfile', format: 'TEXT', content: 'FROM node:20\n' }, + { name: 'main.js', format: 'TEXT', content: 'console.log(1);\n' }, + ]; + const version: ActorVersionRecord = { ...VERSION, sourceFiles: resolvedSourceFiles }; + + const record: BuildRecord = { + id: generateId(), + userId: actor.userId, + actorId: actor.id, + versionNumber: '0.0', + buildNumber: '0.0.1', + tag: 'latest', + status: 'READY', + startedAt: new Date().toISOString(), + }; + await getRegistries().builds.set(record.id, record); + + await runBuildInBackground(driver, actor, version, record, { tag: 'latest', useCache: true }); + + expect(driver.startBuildContexts).toHaveLength(1); + const ctx = driver.startBuildContexts[0]!; + expect(ctx.dockerfilePath).toBe('.actor/Dockerfile'); + expect(ctx.sourceFiles).toEqual(resolvedSourceFiles); + + const final = await getRegistries().builds.get(record.id); + expect(final?.status).toBe('SUCCEEDED'); + }); + }); + describe('imageWorkingDirectory is build-specific, not Actor-specific (human directive: "the workdir should be build specific, not actor specific")', () => { it('a successful build outcome carrying imageWorkingDirectory lands it on that BUILD record, and the Actor record has no such field at all', async () => { const driver = fixedBuildOutcomeDriver({ imageId: 'x', imageWorkingDirectory: '/usr/src/app' }); diff --git a/test/unit/docker-driver.test.ts b/test/unit/docker-driver.test.ts index 62818ba..0bb80a8 100644 --- a/test/unit/docker-driver.test.ts +++ b/test/unit/docker-driver.test.ts @@ -529,7 +529,7 @@ describe('DockerDriver.startBuild - imageWorkingDirectory capture (actor-driver. }); }); -describe('DockerDriver.startBuild - dockerfile option (2-design.md: "the resolved path is handed to dockerode as its `dockerfile` build option")', () => { +describe('DockerDriver.startBuild - dockerfile option (the resolved path is handed to dockerode as its `dockerfile` build option)', () => { /** A stub covering only what `startBuild` calls, exposing the `buildImage` mock itself so a test can * read back exactly which options it was called with - unlike `stubDockerForBuild` above, which only * cares about the post-build inspect. */ @@ -574,7 +574,7 @@ describe('DockerDriver.startBuild - dockerfile option (2-design.md: "the resolve expect(options).toMatchObject({ dockerfile: '.actor/Dockerfile' }); }); - it('always sets the "dockerfile" option, even for the plain root-"Dockerfile" case that coincides with Docker\'s own implicit default (2-design.md Example B)', async () => { + it('always sets the "dockerfile" option, even for the plain root-"Dockerfile" case that coincides with Docker\'s own implicit default', async () => { const stub = stubDockerCapturingBuildImageOptions(); const driver = new DockerDriver(stub.docker); driver.available = true; diff --git a/test/unit/dockerfile-location.test.ts b/test/unit/dockerfile-location.test.ts index 744a082..8894833 100644 --- a/test/unit/dockerfile-location.test.ts +++ b/test/unit/dockerfile-location.test.ts @@ -16,7 +16,7 @@ function actorJson(spec: Record | string): SourceFile { } describe('resolveDockerfileLocation', () => { - // --- 2-design.md Example A: sample_actor_crawler - the bug this change fixes. --- + // --- A: sample_actor_crawler - the bug this change fixes. --- it('A: resolves the "dockerfile" field to .actor/Dockerfile (sample_actor_crawler layout)', () => { const result = resolveDockerfileLocation([ actorJson({ dockerfile: './Dockerfile' }), @@ -33,7 +33,7 @@ describe('resolveDockerfileLocation', () => { ]); }); - // --- 2-design.md Example B: sample_actor_ts / sample_actor_py - the regression guard. --- + // --- B: sample_actor_ts / sample_actor_py - the regression guard. --- it('B: resolves "../Dockerfile" to the root Dockerfile (sample_actor_ts/py layout) - byte-identical to today\'s implicit default', () => { const result = resolveDockerfileLocation([ actorJson({ dockerfile: '../Dockerfile' }), @@ -46,7 +46,7 @@ describe('resolveDockerfileLocation', () => { expect(result.dockerfilePath).toBe('Dockerfile'); }); - // --- 2-design.md Example C: a "dockerfile" field that names nothing - warn and fall through. --- + // --- C: a "dockerfile" field that names nothing - warn and fall through. --- it('C: warns and falls through to .actor/Dockerfile when the "dockerfile" field names no file', () => { const result = resolveDockerfileLocation([ actorJson({ dockerfile: './Custom.Dockerfile' }), @@ -62,7 +62,7 @@ describe('resolveDockerfileLocation', () => { ]); }); - // --- 2-design.md Example D: a path that escapes the Actor root. --- + // --- D: a path that escapes the Actor root. --- describe('D: an escaping "dockerfile" field fails the build before any daemon call', () => { it('a relative path with enough ".." segments to escape .actor/', () => { const result = resolveDockerfileLocation([actorJson({ dockerfile: '../../evil/Dockerfile' })]); @@ -97,7 +97,7 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- 2-design.md Example E: case-insensitive matching, exact-case tie-break. --- + // --- E: case-insensitive matching, exact-case tie-break. --- describe('E: case-insensitive matching', () => { it('matches .actor/Dockerfile against a lowercase .actor/dockerfile source file, returning ITS OWN spelling', () => { const result = resolveDockerfileLocation([text('.actor/dockerfile', 'FROM node:20\n')]); @@ -128,7 +128,7 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- 2-design.md Example F: no Dockerfile at all - the platform-parity default. --- + // --- F: no Dockerfile at all - the platform-parity default. --- describe('F: nothing resolves - the bundled default is injected', () => { it('injects the bundled default Dockerfile, verbatim, as an extra Dockerfile-named SourceFile', () => { const result = resolveDockerfileLocation([ @@ -164,7 +164,7 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- 2-design.md Example G: a present-but-malformed "dockerfile" field. --- + // --- G: a present-but-malformed "dockerfile" field. --- describe('G: malformed "dockerfile" field', () => { it('a non-string value (e.g. true) fails the build immediately, before any candidate is tried', () => { const result = resolveDockerfileLocation([ From 4905713b615481c8f983832f4c2d5f2ebf6b0b9a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 14:49:48 +0000 Subject: [PATCH 4/9] Update e2e pre-pull docs for the crawler sample; drop duplicate assertions requirements/test.md now names all three base images the e2e suite pulls (python:3.11-slim joined for sample_actor_crawler); two tests asserted the same value twice, once as a string literal and once via DEFAULT_DOCKERFILE_NAME - the named-constant assertion stays. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GFiYFS2Eq9833z1ruGQwUK --- requirements/test.md | 3 ++- test/integration/job-lifecycle.test.ts | 1 - test/unit/dockerfile-location.test.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/requirements/test.md b/requirements/test.md index 61c7724..fb85f4c 100644 --- a/requirements/test.md +++ b/requirements/test.md @@ -29,7 +29,8 @@ which covers push/call/log-stream/storage-access against the runtime itself, not what a given Actor's own code does over the network. - Building the sample Actors' images requires pulling their base images - (`apify/actor-node:24`, `apify/actor-python:3.13`) at least once; CI must pre-pull both before + (`apify/actor-node:24`, `apify/actor-python:3.13`, and `python:3.11-slim` for the + `sample_actor_crawler` build case) at least once; CI must pre-pull all three before running the e2e suite so the timing of the actual push/call assertions is not dominated by image pulls. diff --git a/test/integration/job-lifecycle.test.ts b/test/integration/job-lifecycle.test.ts index df06398..b270c1a 100644 --- a/test/integration/job-lifecycle.test.ts +++ b/test/integration/job-lifecycle.test.ts @@ -395,7 +395,6 @@ describe('job lifecycle: TIMED-OUT mapping and abort/completion race guards', () expect(driver.startBuildContexts).toHaveLength(1); const ctx = driver.startBuildContexts[0]!; - expect(ctx.dockerfilePath).toBe('Dockerfile'); expect(ctx.dockerfilePath).toBe(DEFAULT_DOCKERFILE_NAME); // The extra, injected Dockerfile SourceFile actually reaches the driver's ctx, alongside the // original sourceFiles - never in place of them. diff --git a/test/unit/dockerfile-location.test.ts b/test/unit/dockerfile-location.test.ts index 8894833..154b895 100644 --- a/test/unit/dockerfile-location.test.ts +++ b/test/unit/dockerfile-location.test.ts @@ -138,7 +138,6 @@ describe('resolveDockerfileLocation', () => { expect(result.outcome).toBe('default'); if (result.outcome !== 'default') return; - expect(result.dockerfilePath).toBe('Dockerfile'); expect(result.dockerfilePath).toBe(DEFAULT_DOCKERFILE_NAME); expect(result.extraSourceFile).toEqual({ name: 'Dockerfile', From 778ab8353a2de0b5454e39303ca37d458a4b1dc9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 15:15:21 +0000 Subject: [PATCH 5/9] Address final-review findings: one Dockerfile-name constant, doc and test placement Document the unparseable-actor.json failure mode in the runtime spec; make DEFAULT_DOCKERFILE_NAME the single source of truth for the injected default's name so the collision-safety invariant is structural; drop an unneeded type cast, a test-only re-export, and a parallel-array lookup; move the tar-entry normalizer tests next to the module they test; cover the null-actor.json and "../.." escape sub-guards; generalize neverStartDriver's message to every must-not-build case it now guards. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GFiYFS2Eq9833z1ruGQwUK --- requirements/actor-driver.md | 25 +++++++++-------- src/driver/docker-driver.ts | 7 ++--- src/driver/types.ts | 4 +-- src/services/default-dockerfile.ts | 12 ++++++-- src/services/dockerfile-location.ts | 31 ++++++-------------- test/integration/job-lifecycle.test.ts | 10 +++++-- test/unit/dockerfile-location.test.ts | 39 +++++++++++++++----------- test/unit/tar-entry-name.test.ts | 22 +++++++++++++++ 8 files changed, 88 insertions(+), 62 deletions(-) create mode 100644 test/unit/tar-entry-name.test.ts diff --git a/requirements/actor-driver.md b/requirements/actor-driver.md index 1dc46c8..47d6b74 100644 --- a/requirements/actor-driver.md +++ b/requirements/actor-driver.md @@ -41,7 +41,10 @@ implicit "`Dockerfile` at the tar root" default, matching the real platform (apify-worker's `ensureDockerfileExists`) so that "builds locally" keeps predicting "builds on the platform". The resolved (or default) path is always passed explicitly as dockerode's `dockerfile` build option - - never omitted. Resolution order, stopping at the first hit: + never omitted. `.actor/actor.json` is parsed as JSON5 (matching the platform) before any candidate is + tried; a file that fails to parse at all fails the build immediately, before any daemon call, with + `Could not parse .actor/actor.json: ` - a third build-ending failure mode, + alongside the two below. Resolution order, stopping at the first hit: 1. the `dockerfile` field of `.actor/actor.json` (parsed as JSON5, matching the platform), interpreted relative to the `.actor` directory. A value that resolves outside the Actor root (e.g. `../../evil/Dockerfile`, or a leading `/`) fails the build before any daemon call. A @@ -51,16 +54,16 @@ and falls through to the next candidate. 2. `.actor/Dockerfile` 3. `Dockerfile` at the Actor root - - Matching against `sourceFiles` names is case-insensitive for all three candidates, but the path - handed to Docker is always the matched source file's own name, never the candidate's canonical - casing (Docker's lookup inside the tar is case-sensitive). An exact-case match wins; otherwise the - first match in `sourceFiles` order. - - If none of the three candidates resolves, the build does not fail: a bundled default Dockerfile - (apify-worker's own `default_Dockerfile`, `FROM apify/actor-node:20` + `npm install`) is injected - as an extra in-memory `SourceFile` for that one build only - never written back to the version's - persisted `sourceFiles`. - - Every outcome (resolved candidate, warn-and-fall-through, or default) is recorded in the build log, - so the choice is never silent. + - Matching against `sourceFiles` names is case-insensitive for all three candidates, but the path + handed to Docker is always the matched source file's own name, never the candidate's canonical + casing (Docker's lookup inside the tar is case-sensitive). An exact-case match wins; otherwise the + first match in `sourceFiles` order. + - If none of the three candidates resolves, the build does not fail: a bundled default Dockerfile + (apify-worker's own `default_Dockerfile`, `FROM apify/actor-node:20` + `npm install`) is injected + as an extra in-memory `SourceFile` for that one build only - never written back to the version's + persisted `sourceFiles`. + - Every outcome (resolved candidate, warn-and-fall-through, or default) is recorded in the build log, + so the choice is never silent. # Bind mount volumes with Actor source code diff --git a/src/driver/docker-driver.ts b/src/driver/docker-driver.ts index 2ded5c0..d9f5dc8 100644 --- a/src/driver/docker-driver.ts +++ b/src/driver/docker-driver.ts @@ -110,10 +110,9 @@ function sourceFileToBuffer(file: SourceFile): Buffer { /** Entry names go through `normalizeEntryName` - the same normalizer `dockerfile-location.ts`'s * resolver indexes `sourceFiles` by - so the `dockerfilePath` `startBuild` hands dockerode as its - * `dockerfile` option is guaranteed to name exactly the tar entry Docker will find. A canonicalizing - * change to what today's tar contains (e.g. `./foo` becomes `foo`), never a semantic one, and only - * a harmless one: the option only works if the string matches a tar entry exactly, so routing both - * the resolver's index and this function through one normalizer removes that class of bug. */ + * `dockerfile` option is guaranteed to name exactly the tar entry Docker will find. This changes some + * tar entry names versus a raw `SourceFile.name` (e.g. `./foo` becomes `foo`); the change only + * canonicalizes the name, it never changes which file a given `SourceFile` corresponds to. */ function buildTarball(sourceFiles: SourceFile[]): NodeJS.ReadableStream { const pack = tar.pack(); for (const file of sourceFiles) { diff --git a/src/driver/types.ts b/src/driver/types.ts index 6e2a48c..90719a4 100644 --- a/src/driver/types.ts +++ b/src/driver/types.ts @@ -10,9 +10,7 @@ export interface BuildContext { * `services/dockerfile-location.ts: resolveDockerfileLocation` (a `resolved` or `default` outcome - * `runBuildInBackground` never calls `driver.startBuild` on a `failure`). Passed straight through as * dockerode's own `dockerfile` build option - always set, never omitted, so there is no second, - * untested code path that falls back to Docker's implicit "Dockerfile at the tar root" default. - * Required, not optional: every caller has already resolved one (`resolved` or `default`) by the - * time `startBuild` is reached. */ + * untested code path that falls back to Docker's implicit "Dockerfile at the tar root" default. */ dockerfilePath: string; } diff --git a/src/services/default-dockerfile.ts b/src/services/default-dockerfile.ts index 4389331..10816ff 100644 --- a/src/services/default-dockerfile.ts +++ b/src/services/default-dockerfile.ts @@ -1,3 +1,13 @@ +/** + * The single source of truth for the basename `dockerfile-location.ts`'s resolver both searches for + * (candidates 2 and 3: `.actor/Dockerfile`, root `Dockerfile`) and gives the bundled default below when + * it injects one. Collapsing what used to be two separately-declared `'Dockerfile'` string constants + * into this one export makes the module's collision-safety invariant - the injected default's name is + * exactly the string the candidate-3 search looks for, so it can never collide with a real pushed + * Dockerfile - structural rather than a fact that only holds as long as two literals happen to agree. + */ +export const DEFAULT_DOCKERFILE_NAME = 'Dockerfile'; + /** * The bundled default Dockerfile, injected by `dockerfile-location.ts`'s resolver when an Actor's * pushed source names no Dockerfile at all (no `dockerfile` field in `.actor/actor.json`, and no @@ -12,8 +22,6 @@ * existing pattern for shipping a non-TS asset file. A string constant produces the identical bytes * with no extra packaging step. */ -export const DEFAULT_DOCKERFILE_NAME = 'Dockerfile'; - export const DEFAULT_DOCKERFILE_CONTENT = `# This is a default Dockerfile is used for Actors that don't have a Dockerfile. FROM apify/actor-node:20 diff --git a/src/services/dockerfile-location.ts b/src/services/dockerfile-location.ts index 2d29517..abba4b4 100644 --- a/src/services/dockerfile-location.ts +++ b/src/services/dockerfile-location.ts @@ -24,16 +24,8 @@ import { normalizeEntryName } from '../driver/tar-entry-name.js'; import type { SourceFile } from '../storage/entities.js'; import { DEFAULT_DOCKERFILE_CONTENT, DEFAULT_DOCKERFILE_NAME } from './default-dockerfile.js'; -/** Re-exported so every caller that needs the tar-entry normalizer alongside the Dockerfile resolver - * (e.g. `dockerfile-location.test.ts`) can import both from this module - the implementation itself - * lives in the driver layer (`driver/tar-entry-name.ts`), since `docker-driver.ts`'s own tar-building is - * what it must stay byte-for-byte consistent with; this module imports it rather than the driver - * reaching up into `services/`. */ -export { normalizeEntryName }; - const ACTOR_DIR = '.actor'; const ACTOR_JSON_NAME = `${ACTOR_DIR}/actor.json`; -const DOCKERFILE_BASENAME = 'Dockerfile'; /** * Why resolution failed - each has its own message, computed once at the failure site (see @@ -70,8 +62,8 @@ export type DockerfileResolution = /** Decodes a `SourceFile`'s content to text, the same `BASE64`/`TEXT` split `docker-driver.ts`'s * `sourceFileToBuffer` uses for the tar - duplicated here (rather than imported) because this module is - * deliberately Docker-free; the two are one line each and drifting apart would be immediately obvious - * from `dockerfile-location.test.ts`. */ + * deliberately Docker-free. Both are one line each; keep them in sync by hand if either format's + * encoding ever changes. */ function sourceFileToText(file: SourceFile): string { return file.format === 'BASE64' ? Buffer.from(file.content, 'base64').toString('utf8') : file.content; } @@ -108,13 +100,8 @@ function findCaseInsensitive(indexed: IndexedFile[], candidate: string): Indexed /** Exact (case-sensitive) lookup, used only for `.actor/actor.json` itself - unlike the Dockerfile * candidates, the platform does not case-fold the spec file's own path. */ -function findExact( - sourceFiles: SourceFile[], - indexed: IndexedFile[], - normalizedTarget: string, -): SourceFile | undefined { - const position = indexed.findIndex((file) => file.normalizedName === normalizedTarget); - return position === -1 ? undefined : sourceFiles[position]; +function findExact(sourceFiles: SourceFile[], normalizedTarget: string): SourceFile | undefined { + return sourceFiles.find((file) => normalizeEntryName(file.name) === normalizedTarget); } /** Builds the `escapes-actor-root` failure for a `dockerfile` field value, matching apify-worker's own @@ -138,7 +125,7 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile // `.actor/actor.json` is optional - a missing file is not an error, it just means candidate 1 never // applies (mirrors apify-worker's `readActorSpecificationFile`, which swallows ENOENT). - const actorJsonFile = findExact(sourceFiles, indexed, ACTOR_JSON_NAME); + const actorJsonFile = findExact(sourceFiles, ACTOR_JSON_NAME); let actorSpecification: unknown; if (actorJsonFile) { try { @@ -156,7 +143,7 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile // one (a missing field is not an error - it just means this candidate is skipped, same as apify- // worker's `actorSpecification?.dockerfile` optional chain). if (actorSpecification !== null && typeof actorSpecification === 'object' && 'dockerfile' in actorSpecification) { - const field = (actorSpecification as { dockerfile?: unknown }).dockerfile; + const field: unknown = actorSpecification.dockerfile; if (typeof field !== 'string') { return { outcome: 'failure', @@ -206,7 +193,7 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile } // Candidate 2: .actor/Dockerfile - const actorDirCandidate = normalizeEntryName(`${ACTOR_DIR}/${DOCKERFILE_BASENAME}`); + const actorDirCandidate = normalizeEntryName(`${ACTOR_DIR}/${DEFAULT_DOCKERFILE_NAME}`); const actorDirMatch = findCaseInsensitive(indexed, actorDirCandidate); if (actorDirMatch) { return { @@ -220,7 +207,7 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile } // Candidate 3: Dockerfile at the Actor root - const rootCandidate = normalizeEntryName(DOCKERFILE_BASENAME); + const rootCandidate = normalizeEntryName(DEFAULT_DOCKERFILE_NAME); const rootMatch = findCaseInsensitive(indexed, rootCandidate); if (rootMatch) { return { @@ -234,7 +221,7 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile // failing the build. Plain "Dockerfile" at the tar root is free by construction here - // reaching this branch already required that no case-insensitive Dockerfile matched at // candidate 2 or 3. - logLines.push(`${DOCKERFILE_BASENAME} not found, using the default one.\n`); + logLines.push(`${DEFAULT_DOCKERFILE_NAME} not found, using the default one.\n`); return { outcome: 'default', dockerfilePath: DEFAULT_DOCKERFILE_NAME, diff --git a/test/integration/job-lifecycle.test.ts b/test/integration/job-lifecycle.test.ts index b270c1a..09693d0 100644 --- a/test/integration/job-lifecycle.test.ts +++ b/test/integration/job-lifecycle.test.ts @@ -98,8 +98,10 @@ const VERSION: ActorVersionRecord = { sourceFiles: [], }; -/** Fails the test immediately (rather than hanging) if the driver is ever asked to start a run/build - - * used to assert the pre-start abort window really does prevent a container/build from ever starting. */ +/** Fails the test immediately (rather than hanging) if the driver is ever asked to start a run/build. + * Used both to assert the pre-start abort window really does prevent a container/build from ever + * starting, and - for `startBuild` - to assert that a `resolveDockerfileLocation` "failure" outcome + * fails the build without the driver ever being invoked. */ function neverStartDriver(): Driver & { abortRunCalls: string[]; abortBuildCalls: string[] } { const abortRunCalls: string[] = []; const abortBuildCalls: string[] = []; @@ -109,7 +111,9 @@ function neverStartDriver(): Driver & { abortRunCalls: string[]; abortBuildCalls abortBuildCalls, async init() {}, async startBuild() { - throw new Error('startBuild must never be called once the record is already ABORTING'); + throw new Error( + 'startBuild must never be called once the build has already been finalised - either the record was already ABORTING, or resolveDockerfileLocation returned a "failure" outcome', + ); }, async abortBuild(buildId) { abortBuildCalls.push(buildId); diff --git a/test/unit/dockerfile-location.test.ts b/test/unit/dockerfile-location.test.ts index 154b895..be1b9bb 100644 --- a/test/unit/dockerfile-location.test.ts +++ b/test/unit/dockerfile-location.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { normalizeEntryName, resolveDockerfileLocation } from '../../src/services/dockerfile-location.js'; +import { resolveDockerfileLocation } from '../../src/services/dockerfile-location.js'; import { DEFAULT_DOCKERFILE_CONTENT, DEFAULT_DOCKERFILE_NAME } from '../../src/services/default-dockerfile.js'; import type { SourceFile } from '../../src/storage/entities.js'; @@ -85,6 +85,18 @@ describe('resolveDockerfileLocation', () => { }); }); + it('a path that normalizes to exactly ".." (the joined === ".." disjunct, not just startsWith("../"))', () => { + // .actor + ".." joins and normalizes to exactly "..", not "../something" - a separate + // disjunct in the escape check from the startsWith("../") case exercised above. + const result = resolveDockerfileLocation([actorJson({ dockerfile: '../..' })]); + + expect(result).toEqual({ + outcome: 'failure', + reason: 'escapes-actor-root', + message: 'Dockerfile path "../.." in .actor/actor.json points outside the Actor root directory.', + }); + }); + it('a path that stays inside .actor/ (one level of ".." exactly cancels the join) is not treated as escaping', () => { // .actor + ../Dockerfile normalizes to plain "Dockerfile" - inside the root, not outside it // (this is Example B, re-asserted here to pin the boundary this escape check must not cross). @@ -285,23 +297,16 @@ describe('resolveDockerfileLocation', () => { expect(result.dockerfilePath).toBe('.actor/Dockerfile'); }); }); -}); -describe('normalizeEntryName', () => { - it('strips a leading "./"', () => { - expect(normalizeEntryName('./Dockerfile')).toBe('Dockerfile'); - }); + // --- .actor/actor.json parses fine but isn't an object (e.g. bare "null") - candidate 1 must be + // skipped, not crash on `'dockerfile' in null`. --- + describe('.actor/actor.json parses to a non-object value', () => { + it('content is exactly "null" (JSON5-parseable, non-object) - falls through to candidate 2, no crash', () => { + const result = resolveDockerfileLocation([actorJson('null'), text('.actor/Dockerfile', 'FROM node:20\n')]); - it('converts backslashes to POSIX separators', () => { - expect(normalizeEntryName('.actor\\Dockerfile')).toBe('.actor/Dockerfile'); - }); - - it('collapses "a/./b" and "a/b/../c" via path.posix.normalize', () => { - expect(normalizeEntryName('.actor/./Dockerfile')).toBe('.actor/Dockerfile'); - expect(normalizeEntryName('.actor/sub/../Dockerfile')).toBe('.actor/Dockerfile'); - }); - - it('leaves an escaping "../" prefix alone (the escape check depends on this)', () => { - expect(normalizeEntryName('../evil/Dockerfile')).toBe('../evil/Dockerfile'); + expect(result.outcome).toBe('resolved'); + if (result.outcome !== 'resolved') return; + expect(result.dockerfilePath).toBe('.actor/Dockerfile'); + }); }); }); diff --git a/test/unit/tar-entry-name.test.ts b/test/unit/tar-entry-name.test.ts new file mode 100644 index 0000000..1af1536 --- /dev/null +++ b/test/unit/tar-entry-name.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest'; + +import { normalizeEntryName } from '../../src/driver/tar-entry-name.js'; + +describe('normalizeEntryName', () => { + it('strips a leading "./"', () => { + expect(normalizeEntryName('./Dockerfile')).toBe('Dockerfile'); + }); + + it('converts backslashes to POSIX separators', () => { + expect(normalizeEntryName('.actor\\Dockerfile')).toBe('.actor/Dockerfile'); + }); + + it('collapses "a/./b" and "a/b/../c" via path.posix.normalize', () => { + expect(normalizeEntryName('.actor/./Dockerfile')).toBe('.actor/Dockerfile'); + expect(normalizeEntryName('.actor/sub/../Dockerfile')).toBe('.actor/Dockerfile'); + }); + + it('leaves an escaping "../" prefix alone (the escape check depends on this)', () => { + expect(normalizeEntryName('../evil/Dockerfile')).toBe('../evil/Dockerfile'); + }); +}); From e20b7a2cb05d0db91d4d32f1667f5a8f37252e66 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 15:26:57 +0000 Subject: [PATCH 6/9] Tighten three comments: state the Dockerfile-name invariant timelessly The DEFAULT_DOCKERFILE_NAME doc now states the collision-safety invariant directly; a dangling cross-reference points at driver/tar-entry-name.ts; the spec no longer says "parsed as JSON5" twice. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GFiYFS2Eq9833z1ruGQwUK --- requirements/actor-driver.md | 14 +++++++------- src/services/default-dockerfile.ts | 11 +++++------ src/services/dockerfile-location.ts | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/requirements/actor-driver.md b/requirements/actor-driver.md index 47d6b74..06ab325 100644 --- a/requirements/actor-driver.md +++ b/requirements/actor-driver.md @@ -45,13 +45,13 @@ tried; a file that fails to parse at all fails the build immediately, before any daemon call, with `Could not parse .actor/actor.json: ` - a third build-ending failure mode, alongside the two below. Resolution order, stopping at the first hit: - 1. the `dockerfile` field of `.actor/actor.json` (parsed as JSON5, matching the platform), - interpreted relative to the `.actor` directory. A value that resolves outside the Actor root - (e.g. `../../evil/Dockerfile`, or a leading `/`) fails the build before any daemon call. A - value that is present but not a string (e.g. `true`) also fails the build immediately, with a - `.actor/actor.json has invalid format` message. A value that is a string (including the empty - string) but names no file in `sourceFiles` does not fail the build on that account - it warns - and falls through to the next candidate. + 1. the `dockerfile` field of `.actor/actor.json`, interpreted relative to the `.actor` + directory. A value that resolves outside the Actor root (e.g. `../../evil/Dockerfile`, or a + leading `/`) fails the build before any daemon call. A value that is present but not a string + (e.g. `true`) also fails the build immediately, with a `.actor/actor.json has invalid format` + message. A value that is a string (including the empty string) but names no file in + `sourceFiles` does not fail the build on that account - it warns and falls through to the + next candidate. 2. `.actor/Dockerfile` 3. `Dockerfile` at the Actor root - Matching against `sourceFiles` names is case-insensitive for all three candidates, but the path diff --git a/src/services/default-dockerfile.ts b/src/services/default-dockerfile.ts index 10816ff..99ffdbf 100644 --- a/src/services/default-dockerfile.ts +++ b/src/services/default-dockerfile.ts @@ -1,10 +1,9 @@ /** - * The single source of truth for the basename `dockerfile-location.ts`'s resolver both searches for - * (candidates 2 and 3: `.actor/Dockerfile`, root `Dockerfile`) and gives the bundled default below when - * it injects one. Collapsing what used to be two separately-declared `'Dockerfile'` string constants - * into this one export makes the module's collision-safety invariant - the injected default's name is - * exactly the string the candidate-3 search looks for, so it can never collide with a real pushed - * Dockerfile - structural rather than a fact that only holds as long as two literals happen to agree. + * The single source of truth for both the basename `dockerfile-location.ts`'s resolver searches for + * (candidates 2 and 3: `.actor/Dockerfile`, root `Dockerfile`) and the tar-entry name given to the + * bundled default below when it is injected. Because the injected default's name is exactly the string + * the candidate-3 search looks for, it can never collide with a real pushed Dockerfile: the `default` + * outcome is only reachable after that search has already missed. */ export const DEFAULT_DOCKERFILE_NAME = 'Dockerfile'; diff --git a/src/services/dockerfile-location.ts b/src/services/dockerfile-location.ts index abba4b4..952f6f7 100644 --- a/src/services/dockerfile-location.ts +++ b/src/services/dockerfile-location.ts @@ -43,7 +43,7 @@ export type DockerfileResolutionFailureReason = * - `resolved`: a candidate (1, 2, or 3) matched an existing source file. `dockerfilePath` is that * file's own (normalized) name - exactly the string `docker-driver.ts` must hand dockerode as its * `dockerfile` build option, and exactly the tar entry name `buildTarball` will produce for it (both - * go through the same normalizer, see `normalizeEntryName` above). + * go through the same normalizer, see `normalizeEntryName` in `driver/tar-entry-name.ts`). * - `default`: nothing resolved. `dockerfilePath` is always `'Dockerfile'` (free by construction - see * this module's doc comment), and `extraSourceFile` is the one extra `SourceFile` the caller must * append to `BuildContext.sourceFiles` for this one build only - never written back to the version's From e1fa66acb2f3c008ea96fd7de6ba7c4be93843b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 06:24:46 +0000 Subject: [PATCH 7/9] Trim comments to what-statements; condense the Dockerfile spec section Comments now state constraints, not rationale; the actor-driver.md section states the observable contract (resolution order, failure messages, case rules, log requirement) without implementation mechanics. Comment-only change: no code, signature, or assertion lines touched. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GFiYFS2Eq9833z1ruGQwUK --- requirements/actor-driver.md | 29 ++------ src/driver/docker-driver.ts | 5 -- src/driver/tar-entry-name.ts | 16 +---- src/driver/types.ts | 6 +- src/services/builds.ts | 6 -- src/services/default-dockerfile.ts | 23 +----- src/services/dockerfile-location.ts | 93 +++---------------------- test/integration/helpers/test-server.ts | 9 +-- test/integration/job-lifecycle.test.ts | 16 +---- test/unit/docker-driver.test.ts | 7 -- test/unit/dockerfile-location.test.ts | 15 ---- 11 files changed, 22 insertions(+), 203 deletions(-) diff --git a/requirements/actor-driver.md b/requirements/actor-driver.md index 06ab325..22f0938 100644 --- a/requirements/actor-driver.md +++ b/requirements/actor-driver.md @@ -37,33 +37,12 @@ - Actor build details are saved in `__BUILDS__` internal storage - Actor build log is saved in `__LOGS__` internal storage - Actor details are saved in `__ACTORS__` internal storage -- **The Dockerfile to build is resolved from the version's `sourceFiles`**, not left to Docker's own - implicit "`Dockerfile` at the tar root" default, matching the real platform (apify-worker's - `ensureDockerfileExists`) so that "builds locally" keeps predicting "builds on the platform". The - resolved (or default) path is always passed explicitly as dockerode's `dockerfile` build option - - never omitted. `.actor/actor.json` is parsed as JSON5 (matching the platform) before any candidate is - tried; a file that fails to parse at all fails the build immediately, before any daemon call, with - `Could not parse .actor/actor.json: ` - a third build-ending failure mode, - alongside the two below. Resolution order, stopping at the first hit: - 1. the `dockerfile` field of `.actor/actor.json`, interpreted relative to the `.actor` - directory. A value that resolves outside the Actor root (e.g. `../../evil/Dockerfile`, or a - leading `/`) fails the build before any daemon call. A value that is present but not a string - (e.g. `true`) also fails the build immediately, with a `.actor/actor.json has invalid format` - message. A value that is a string (including the empty string) but names no file in - `sourceFiles` does not fail the build on that account - it warns and falls through to the - next candidate. +- **The Dockerfile to build is resolved from the Actor's pushed source**, not Docker's implicit default. `.actor/actor.json` is parsed as JSON5; an unparseable file fails the build with a "Could not parse .actor/actor.json" message. Resolution order, stopping at the first hit: + 1. the `dockerfile` field of `.actor/actor.json`, relative to `.actor/` - a path escaping the Actor root fails with "points outside the Actor root directory"; a non-string value fails with `"dockerfile" must be a string`; a value naming no pushed file (including empty) falls through instead of failing. 2. `.actor/Dockerfile` 3. `Dockerfile` at the Actor root - - Matching against `sourceFiles` names is case-insensitive for all three candidates, but the path - handed to Docker is always the matched source file's own name, never the candidate's canonical - casing (Docker's lookup inside the tar is case-sensitive). An exact-case match wins; otherwise the - first match in `sourceFiles` order. - - If none of the three candidates resolves, the build does not fail: a bundled default Dockerfile - (apify-worker's own `default_Dockerfile`, `FROM apify/actor-node:20` + `npm install`) is injected - as an extra in-memory `SourceFile` for that one build only - never written back to the version's - persisted `sourceFiles`. - - Every outcome (resolved candidate, warn-and-fall-through, or default) is recorded in the build log, - so the choice is never silent. + 4. the platform's bundled default Dockerfile, for that build only - the pushed source itself is unchanged. + - Matching is case-insensitive, exact-case wins ties, and every outcome is stated in the build log. # Bind mount volumes with Actor source code diff --git a/src/driver/docker-driver.ts b/src/driver/docker-driver.ts index d9f5dc8..d9d5eb9 100644 --- a/src/driver/docker-driver.ts +++ b/src/driver/docker-driver.ts @@ -108,11 +108,6 @@ function sourceFileToBuffer(file: SourceFile): Buffer { return file.format === 'BASE64' ? Buffer.from(file.content, 'base64') : Buffer.from(file.content, 'utf8'); } -/** Entry names go through `normalizeEntryName` - the same normalizer `dockerfile-location.ts`'s - * resolver indexes `sourceFiles` by - so the `dockerfilePath` `startBuild` hands dockerode as its - * `dockerfile` option is guaranteed to name exactly the tar entry Docker will find. This changes some - * tar entry names versus a raw `SourceFile.name` (e.g. `./foo` becomes `foo`); the change only - * canonicalizes the name, it never changes which file a given `SourceFile` corresponds to. */ function buildTarball(sourceFiles: SourceFile[]): NodeJS.ReadableStream { const pack = tar.pack(); for (const file of sourceFiles) { diff --git a/src/driver/tar-entry-name.ts b/src/driver/tar-entry-name.ts index 2786ba5..4233903 100644 --- a/src/driver/tar-entry-name.ts +++ b/src/driver/tar-entry-name.ts @@ -1,19 +1,7 @@ import * as path from 'node:path'; -/** - * The one name-normalizer shared by the Dockerfile resolver's source-file index - * (`services/dockerfile-location.ts`) and `docker-driver.ts`'s `buildTarball` - the string handed to - * the daemon as the `dockerfile` option is only ever correct if it is exactly the tar entry name Docker - * will look for, so both sides must agree on what a `SourceFile.name` (or a candidate path built from - * `.actor/actor.json`) canonicalizes to. Three steps, in order: backslashes become POSIX separators (a - * Windows-authored tree's `sourceFiles` names might carry them), a leading `./` is stripped, and the - * result is run through `path.posix.normalize` (collapses `a/./b` and `a/b/../c`, but deliberately - * leaves a leading `../` alone - that is exactly what the Dockerfile resolver's escape check looks for). - * - * Lives in the driver layer (not `services/`) since `docker-driver.ts`'s own tar-building is what this - * normalizer must stay byte-for-byte consistent with; `services/dockerfile-location.ts` imports it from - * here rather than the driver reaching up into `services/`. - */ +/** Canonicalizes a source-file name to a tar entry name: POSIX separators, no leading `./`, then + * `path.posix.normalize` (a leading `../` is left alone). */ export function normalizeEntryName(name: string): string { const posixName = name.replace(/\\/g, '/'); const withoutLeadingDotSlash = posixName.replace(/^(?:\.\/)+/, ''); diff --git a/src/driver/types.ts b/src/driver/types.ts index 90719a4..5738d42 100644 --- a/src/driver/types.ts +++ b/src/driver/types.ts @@ -6,11 +6,7 @@ export interface BuildContext { sourceFiles: SourceFile[]; useCache: boolean; timeoutSecs: number; - /** The tar-relative path to the Dockerfile to build, as resolved by - * `services/dockerfile-location.ts: resolveDockerfileLocation` (a `resolved` or `default` outcome - - * `runBuildInBackground` never calls `driver.startBuild` on a `failure`). Passed straight through as - * dockerode's own `dockerfile` build option - always set, never omitted, so there is no second, - * untested code path that falls back to Docker's implicit "Dockerfile at the tar root" default. */ + /** Tar-relative path to the Dockerfile to build, passed as dockerode's `dockerfile` build option. */ dockerfilePath: string; } diff --git a/src/services/builds.ts b/src/services/builds.ts index f332e15..1f93a77 100644 --- a/src/services/builds.ts +++ b/src/services/builds.ts @@ -181,12 +181,6 @@ export async function runBuildInBackground( return; } - // Resolved right before the actual `docker build` (mirrors apify-worker, where this lives in the - // build job, not the Docker-facing layer): candidate order is the "dockerfile" field of - // `.actor/actor.json`, then `.actor/Dockerfile`, then root `Dockerfile`, then the bundled default - // (`requirements/actor-driver.md`). Never persisted onto `version` either way - a - // `default` outcome's extra `SourceFile` is appended only to the in-memory list this one build's - // `BuildContext` gets, so a later push that adds a real Dockerfile is never competing with it. const dockerfileResolution = resolveDockerfileLocation(version.sourceFiles); if (dockerfileResolution.outcome === 'failure') { appendLog(record.id, `${dockerfileResolution.message}\n`); diff --git a/src/services/default-dockerfile.ts b/src/services/default-dockerfile.ts index 99ffdbf..57712e3 100644 --- a/src/services/default-dockerfile.ts +++ b/src/services/default-dockerfile.ts @@ -1,26 +1,7 @@ -/** - * The single source of truth for both the basename `dockerfile-location.ts`'s resolver searches for - * (candidates 2 and 3: `.actor/Dockerfile`, root `Dockerfile`) and the tar-entry name given to the - * bundled default below when it is injected. Because the injected default's name is exactly the string - * the candidate-3 search looks for, it can never collide with a real pushed Dockerfile: the `default` - * outcome is only reachable after that search has already missed. - */ +/** Basename for the resolver's Dockerfile candidates and the bundled default's own tar-entry name. */ export const DEFAULT_DOCKERFILE_NAME = 'Dockerfile'; -/** - * The bundled default Dockerfile, injected by `dockerfile-location.ts`'s resolver when an Actor's - * pushed source names no Dockerfile at all (no `dockerfile` field in `.actor/actor.json`, and no - * case-insensitive `Dockerfile` at `.actor/Dockerfile` or the Actor root). This is apify-worker's own - * platform-parity fallback, copied here verbatim (byte-for-byte, including its own leading comment) from - * `apify-worker/src/actor/build/default_Dockerfile` - not reinterpreted or "improved" - so a locally - * built default-Dockerfile image matches what the real platform would have produced for the same, - * Dockerfile-less source. - * - * A string constant, not a sibling file copied at build/run time: the runtime's build input is - * already an in-memory `SourceFile[]`/tar, not a working directory on disk, and the codebase has no - * existing pattern for shipping a non-TS asset file. A string constant produces the identical bytes - * with no extra packaging step. - */ +/** Injected when an Actor's pushed source names no Dockerfile. Matches the Apify platform's default Dockerfile. */ export const DEFAULT_DOCKERFILE_CONTENT = `# This is a default Dockerfile is used for Actors that don't have a Dockerfile. FROM apify/actor-node:20 diff --git a/src/services/dockerfile-location.ts b/src/services/dockerfile-location.ts index 952f6f7..da7ea76 100644 --- a/src/services/dockerfile-location.ts +++ b/src/services/dockerfile-location.ts @@ -1,21 +1,9 @@ /** - * Resolves which Dockerfile a build should use, from the version's flat, in-memory `SourceFile[]` - - * mirroring apify-worker's own `ensureDockerfileExists` (`act2_build_job.ts`) so that "builds locally" - * keeps predicting "builds on the platform" (`requirements/actor-driver.md`). Pure: no filesystem, no - * Docker. "Does this file exist" is a lookup in a normalized-name index built over `sourceFiles`; "does - * it escape the root" is POSIX path arithmetic on strings. - * - * Candidate order, stopping at the first hit: - * 1. the `dockerfile` field of `.actor/actor.json`, resolved relative to the `.actor` directory - * 2. `.actor/Dockerfile` - * 3. `Dockerfile` at the Actor root - * 4. (nothing resolved) the bundled default Dockerfile (`default-dockerfile.ts`), platform-parity - * with apify-worker rather than failing the build. - * - * Matching against `sourceFiles` names is case-insensitive (candidates 1-3), but the path handed back - * is always the matched source file's OWN name (post-normalization) - never the candidate's canonical - * casing - because Docker's lookup inside the tar is case-sensitive. When both a case-exact and a - * case-differing match exist, the case-exact one wins; otherwise the first match in `sourceFiles` order. + * Resolves which Dockerfile a build should use from the version's `sourceFiles`. Candidate order, + * stopping at the first hit: 1) the `dockerfile` field of `.actor/actor.json`, relative to `.actor` 2) + * `.actor/Dockerfile` 3) `Dockerfile` at the Actor root 4) the bundled default. Matching is + * case-insensitive; the returned path is always the matched file's own name, never the candidate's + * casing - Docker's tar lookup is case-sensitive. An exact-case match wins over a case-differing one. */ import * as path from 'node:path'; import JSON5 from 'json5'; @@ -27,51 +15,21 @@ import { DEFAULT_DOCKERFILE_CONTENT, DEFAULT_DOCKERFILE_NAME } from './default-d const ACTOR_DIR = '.actor'; const ACTOR_JSON_NAME = `${ACTOR_DIR}/actor.json`; -/** - * Why resolution failed - each has its own message, computed once at the failure site (see - * `resolveDockerfileLocation`'s call sites below) and carried through verbatim rather than - * reconstructed by the caller. `services/builds.ts` only needs `message` to fail the build; `reason` - * exists so tests can assert *which* failure fired without string-matching the message. - */ +/** Why Dockerfile resolution failed. */ export type DockerfileResolutionFailureReason = 'escapes-actor-root' | 'invalid-dockerfile-field' | 'unparseable-actor-json'; -/** - * The three outcomes `resolveDockerfileLocation` can return - see this module's doc comment for the - * candidate order each represents. - * - * - `resolved`: a candidate (1, 2, or 3) matched an existing source file. `dockerfilePath` is that - * file's own (normalized) name - exactly the string `docker-driver.ts` must hand dockerode as its - * `dockerfile` build option, and exactly the tar entry name `buildTarball` will produce for it (both - * go through the same normalizer, see `normalizeEntryName` in `driver/tar-entry-name.ts`). - * - `default`: nothing resolved. `dockerfilePath` is always `'Dockerfile'` (free by construction - see - * this module's doc comment), and `extraSourceFile` is the one extra `SourceFile` the caller must - * append to `BuildContext.sourceFiles` for this one build only - never written back to the version's - * persisted `sourceFiles`. - * - `failure`: a typed, build-ending problem found before any Docker/daemon call - an escaping - * `dockerfile` field path, a non-string `dockerfile` field, or an unparseable `.actor/actor.json`. - * - * Every outcome (including `failure`) carries its own diagnostic text in `logLines`/`message` - the - * choice is never made silently: every build outcome produces a build log entry that identifies which - * Dockerfile source was used or which warning/failure applied. - */ +/** `resolveDockerfileLocation`'s outcomes: `resolved` (a candidate matched), `default` (nothing matched + * - `extraSourceFile` must be appended to this build's context only, never persisted), or `failure`. */ export type DockerfileResolution = | { outcome: 'resolved'; dockerfilePath: string; logLines: string[] } | { outcome: 'default'; dockerfilePath: string; logLines: string[]; extraSourceFile: SourceFile } | { outcome: 'failure'; reason: DockerfileResolutionFailureReason; message: string }; -/** Decodes a `SourceFile`'s content to text, the same `BASE64`/`TEXT` split `docker-driver.ts`'s - * `sourceFileToBuffer` uses for the tar - duplicated here (rather than imported) because this module is - * deliberately Docker-free. Both are one line each; keep them in sync by hand if either format's - * encoding ever changes. */ function sourceFileToText(file: SourceFile): string { return file.format === 'BASE64' ? Buffer.from(file.content, 'base64').toString('utf8') : file.content; } -/** One `SourceFile`, indexed by its normalized name (for the exact `.actor/actor.json` lookup) and by - * that name's lowercase (for the case-insensitive Dockerfile candidate lookups) - built once per - * resolution, in `sourceFiles` order, which is exactly the tie-break order `findCaseInsensitive` below - * relies on. */ interface IndexedFile { normalizedName: string; lowerName: string; @@ -84,9 +42,7 @@ function indexSourceFiles(sourceFiles: SourceFile[]): IndexedFile[] { }); } -/** Case-insensitive lookup for a Dockerfile candidate: among every indexed file whose normalized name - * matches `candidate` case-insensitively, an exact-case match wins; otherwise the first match in - * `sourceFiles` order (`indexed` is already in that order, so "first" here just means "first found"). */ +/** Exact-case match wins; otherwise the first match in `sourceFiles` order. */ function findCaseInsensitive(indexed: IndexedFile[], candidate: string): IndexedFile | undefined { const lowerCandidate = candidate.toLowerCase(); let firstMatch: IndexedFile | undefined; @@ -98,15 +54,11 @@ function findCaseInsensitive(indexed: IndexedFile[], candidate: string): Indexed return firstMatch; } -/** Exact (case-sensitive) lookup, used only for `.actor/actor.json` itself - unlike the Dockerfile - * candidates, the platform does not case-fold the spec file's own path. */ +/** `.actor/actor.json`'s own path is not case-folded, unlike the Dockerfile candidates. */ function findExact(sourceFiles: SourceFile[], normalizedTarget: string): SourceFile | undefined { return sourceFiles.find((file) => normalizeEntryName(file.name) === normalizedTarget); } -/** Builds the `escapes-actor-root` failure for a `dockerfile` field value, matching apify-worker's own - * `UserError` for the same condition - always keyed off the raw field value the developer actually - * wrote, never the joined/normalized path, so the message names exactly what they typed. */ function escapesActorRootFailure(rawField: string): DockerfileResolution { return { outcome: 'failure', @@ -115,16 +67,10 @@ function escapesActorRootFailure(rawField: string): DockerfileResolution { }; } -/** - * Resolves the Dockerfile for a build from its version's `sourceFiles`. See this module's doc comment - * for the candidate order and outcome shapes. - */ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): DockerfileResolution { const indexed = indexSourceFiles(sourceFiles); const logLines: string[] = []; - // `.actor/actor.json` is optional - a missing file is not an error, it just means candidate 1 never - // applies (mirrors apify-worker's `readActorSpecificationFile`, which swallows ENOENT). const actorJsonFile = findExact(sourceFiles, ACTOR_JSON_NAME); let actorSpecification: unknown; if (actorJsonFile) { @@ -139,9 +85,6 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile } } - // Candidate 1: the "dockerfile" field, only when actor.json parsed to an object that actually has - // one (a missing field is not an error - it just means this candidate is skipped, same as apify- - // worker's `actorSpecification?.dockerfile` optional chain). if (actorSpecification !== null && typeof actorSpecification === 'object' && 'dockerfile' in actorSpecification) { const field: unknown = actorSpecification.dockerfile; if (typeof field !== 'string') { @@ -153,17 +96,10 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile } if (field === '') { - // An empty string is a valid string that simply names no file - indistinguishable from a typo - // (the "names nothing" case below), never the invalid-format failure above: a non-string - // value is a shape error rejected outright, while an empty string is a valid string that - // simply fails to match anything, so it gets the same warn-and-fall-through treatment. logLines.push( 'Warning: "" (from the "dockerfile" field in .actor/actor.json) is not in the pushed source; falling back to the default locations.\n', ); } else if (field.startsWith('/')) { - // An absolute path can never be "relative to .actor" - checked before joining, exactly like - // apify-worker's `ensureActorDirFileInActorSourceRoot` (path.join would otherwise silently fold - // a leading "/" into a same-directory join instead of rejecting it). return escapesActorRootFailure(field); } else { const joined = normalizeEntryName(path.posix.join(ACTOR_DIR, field)); @@ -183,16 +119,12 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile }; } - // Names nothing in the pushed source - warn and fall through to candidate 2, never fail the - // build on this account (matches apify-worker's own would-be-breaking `throw`, which - // stays commented out there for the same reason). logLines.push( `Warning: "${joined}" (from the "dockerfile" field in .actor/actor.json) is not in the pushed source; falling back to the default locations.\n`, ); } } - // Candidate 2: .actor/Dockerfile const actorDirCandidate = normalizeEntryName(`${ACTOR_DIR}/${DEFAULT_DOCKERFILE_NAME}`); const actorDirMatch = findCaseInsensitive(indexed, actorDirCandidate); if (actorDirMatch) { @@ -206,7 +138,6 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile }; } - // Candidate 3: Dockerfile at the Actor root const rootCandidate = normalizeEntryName(DEFAULT_DOCKERFILE_NAME); const rootMatch = findCaseInsensitive(indexed, rootCandidate); if (rootMatch) { @@ -217,10 +148,6 @@ export function resolveDockerfileLocation(sourceFiles: SourceFile[]): Dockerfile }; } - // Nothing resolved: inject the bundled default, platform-parity with apify-worker rather than - // failing the build. Plain "Dockerfile" at the tar root is free by construction here - - // reaching this branch already required that no case-insensitive Dockerfile matched at - // candidate 2 or 3. logLines.push(`${DEFAULT_DOCKERFILE_NAME} not found, using the default one.\n`); return { outcome: 'default', diff --git a/test/integration/helpers/test-server.ts b/test/integration/helpers/test-server.ts index 10e12fe..0ba8353 100644 --- a/test/integration/helpers/test-server.ts +++ b/test/integration/helpers/test-server.ts @@ -65,13 +65,8 @@ export function fixedRunOutcomeDriver(outcome: RunOutcome): Driver { }; } -/** Same idea as `fixedRunOutcomeDriver`, but for builds: `startBuild` either resolves with `outcome` or - * rejects with `error`, whichever the caller supplies (`error` wins if both are given, so a - * `DriverTimedOutError` can be asserted straight through to a `TIMED-OUT` status). Every `startBuild` - * call's `ctx` is recorded in `startBuildContexts`, in call order - lets a test assert exactly what - * `runBuildInBackground` computed and passed through (e.g. `sourceFiles`/`dockerfilePath` after the - * Dockerfile resolver ran), not just that some build happened; existing callers that only need `Driver` - * itself are unaffected, since this is a strict superset of that interface. */ +/** Same idea as `fixedRunOutcomeDriver`, but for builds; also records every `startBuild` ctx into + * `startBuildContexts`. */ export function fixedBuildOutcomeDriver( outcome: BuildOutcome, error?: Error, diff --git a/test/integration/job-lifecycle.test.ts b/test/integration/job-lifecycle.test.ts index 09693d0..ed0f8a2 100644 --- a/test/integration/job-lifecycle.test.ts +++ b/test/integration/job-lifecycle.test.ts @@ -98,10 +98,7 @@ const VERSION: ActorVersionRecord = { sourceFiles: [], }; -/** Fails the test immediately (rather than hanging) if the driver is ever asked to start a run/build. - * Used both to assert the pre-start abort window really does prevent a container/build from ever - * starting, and - for `startBuild` - to assert that a `resolveDockerfileLocation` "failure" outcome - * fails the build without the driver ever being invoked. */ +/** Fails the test immediately if the driver is ever asked to start a run/build. */ function neverStartDriver(): Driver & { abortRunCalls: string[]; abortBuildCalls: string[] } { const abortRunCalls: string[] = []; const abortBuildCalls: string[] = []; @@ -337,8 +334,6 @@ describe('job lifecycle: TIMED-OUT mapping and abort/completion race guards', () server = await startTestServer(driver); const actor = await seedActor(server, 'dockerfile-failure-actor'); - // A "dockerfile" field that escapes the Actor root - resolveDockerfileLocation returns a - // `failure` outcome before any candidate is even looked up in sourceFiles. const escapingSourceFiles: SourceFile[] = [ { name: '.actor/actor.json', @@ -367,8 +362,6 @@ describe('job lifecycle: TIMED-OUT mapping and abort/completion race guards', () expect(final?.statusMessage).toBe( 'Dockerfile path "../../evil/Dockerfile" in .actor/actor.json points outside the Actor root directory.', ); - // `startBuild` is never called either - if it were, `neverStartDriver` would have thrown and - // failed the test. }); it('a "default" outcome appends the bundled default Dockerfile to the driver\'s ctx.sourceFiles and sets ctx.dockerfilePath to "Dockerfile"', async () => { @@ -376,8 +369,6 @@ describe('job lifecycle: TIMED-OUT mapping and abort/completion race guards', () server = await startTestServer(driver); const actor = await seedActor(server, 'dockerfile-default-actor'); - // No actor.json, and no Dockerfile anywhere in sourceFiles - resolveDockerfileLocation falls - // all the way through to the bundled default. const noDockerfileSourceFiles: SourceFile[] = [ { name: 'main.js', format: 'TEXT', content: 'console.log(1);\n' }, ]; @@ -400,14 +391,10 @@ describe('job lifecycle: TIMED-OUT mapping and abort/completion race guards', () expect(driver.startBuildContexts).toHaveLength(1); const ctx = driver.startBuildContexts[0]!; expect(ctx.dockerfilePath).toBe(DEFAULT_DOCKERFILE_NAME); - // The extra, injected Dockerfile SourceFile actually reaches the driver's ctx, alongside the - // original sourceFiles - never in place of them. expect(ctx.sourceFiles).toEqual([ ...noDockerfileSourceFiles, { name: 'Dockerfile', format: 'TEXT', content: DEFAULT_DOCKERFILE_CONTENT }, ]); - // Never written back to the version's own persisted sourceFiles - only this one build's ctx - // gets the extra entry. expect(version.sourceFiles).toEqual(noDockerfileSourceFiles); const final = await getRegistries().builds.get(record.id); @@ -419,7 +406,6 @@ describe('job lifecycle: TIMED-OUT mapping and abort/completion race guards', () server = await startTestServer(driver); const actor = await seedActor(server, 'dockerfile-resolved-actor'); - // No actor.json "dockerfile" field, but a .actor/Dockerfile exists - candidate 2 resolves. const resolvedSourceFiles: SourceFile[] = [ { name: '.actor/Dockerfile', format: 'TEXT', content: 'FROM node:20\n' }, { name: 'main.js', format: 'TEXT', content: 'console.log(1);\n' }, diff --git a/test/unit/docker-driver.test.ts b/test/unit/docker-driver.test.ts index 0bb80a8..d6e5633 100644 --- a/test/unit/docker-driver.test.ts +++ b/test/unit/docker-driver.test.ts @@ -530,9 +530,6 @@ describe('DockerDriver.startBuild - imageWorkingDirectory capture (actor-driver. }); describe('DockerDriver.startBuild - dockerfile option (the resolved path is handed to dockerode as its `dockerfile` build option)', () => { - /** A stub covering only what `startBuild` calls, exposing the `buildImage` mock itself so a test can - * read back exactly which options it was called with - unlike `stubDockerForBuild` above, which only - * cares about the post-build inspect. */ function stubDockerCapturingBuildImageOptions() { const followProgress = vi.fn( ( @@ -567,10 +564,6 @@ describe('DockerDriver.startBuild - dockerfile option (the resolved path is hand expect(stub.buildImage).toHaveBeenCalledTimes(1); const [, options] = stub.buildImage.mock.calls[0]!; - // Without `ctx.dockerfilePath` being threaded through to this option at all (the pre-fix - // behaviour - see `docker-driver.ts`'s old `buildImage(tarball, { t, nocache, abortSignal })` - // call, with no `dockerfile` key), this assertion fails: `options.dockerfile` would be - // `undefined`, never `'.actor/Dockerfile'`. expect(options).toMatchObject({ dockerfile: '.actor/Dockerfile' }); }); diff --git a/test/unit/dockerfile-location.test.ts b/test/unit/dockerfile-location.test.ts index be1b9bb..8ac7ea3 100644 --- a/test/unit/dockerfile-location.test.ts +++ b/test/unit/dockerfile-location.test.ts @@ -16,7 +16,6 @@ function actorJson(spec: Record | string): SourceFile { } describe('resolveDockerfileLocation', () => { - // --- A: sample_actor_crawler - the bug this change fixes. --- it('A: resolves the "dockerfile" field to .actor/Dockerfile (sample_actor_crawler layout)', () => { const result = resolveDockerfileLocation([ actorJson({ dockerfile: './Dockerfile' }), @@ -33,7 +32,6 @@ describe('resolveDockerfileLocation', () => { ]); }); - // --- B: sample_actor_ts / sample_actor_py - the regression guard. --- it('B: resolves "../Dockerfile" to the root Dockerfile (sample_actor_ts/py layout) - byte-identical to today\'s implicit default', () => { const result = resolveDockerfileLocation([ actorJson({ dockerfile: '../Dockerfile' }), @@ -46,7 +44,6 @@ describe('resolveDockerfileLocation', () => { expect(result.dockerfilePath).toBe('Dockerfile'); }); - // --- C: a "dockerfile" field that names nothing - warn and fall through. --- it('C: warns and falls through to .actor/Dockerfile when the "dockerfile" field names no file', () => { const result = resolveDockerfileLocation([ actorJson({ dockerfile: './Custom.Dockerfile' }), @@ -62,7 +59,6 @@ describe('resolveDockerfileLocation', () => { ]); }); - // --- D: a path that escapes the Actor root. --- describe('D: an escaping "dockerfile" field fails the build before any daemon call', () => { it('a relative path with enough ".." segments to escape .actor/', () => { const result = resolveDockerfileLocation([actorJson({ dockerfile: '../../evil/Dockerfile' })]); @@ -86,8 +82,6 @@ describe('resolveDockerfileLocation', () => { }); it('a path that normalizes to exactly ".." (the joined === ".." disjunct, not just startsWith("../"))', () => { - // .actor + ".." joins and normalizes to exactly "..", not "../something" - a separate - // disjunct in the escape check from the startsWith("../") case exercised above. const result = resolveDockerfileLocation([actorJson({ dockerfile: '../..' })]); expect(result).toEqual({ @@ -98,8 +92,6 @@ describe('resolveDockerfileLocation', () => { }); it('a path that stays inside .actor/ (one level of ".." exactly cancels the join) is not treated as escaping', () => { - // .actor + ../Dockerfile normalizes to plain "Dockerfile" - inside the root, not outside it - // (this is Example B, re-asserted here to pin the boundary this escape check must not cross). const result = resolveDockerfileLocation([ actorJson({ dockerfile: '../Dockerfile' }), text('Dockerfile', 'FROM node:20\n'), @@ -109,7 +101,6 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- E: case-insensitive matching, exact-case tie-break. --- describe('E: case-insensitive matching', () => { it('matches .actor/Dockerfile against a lowercase .actor/dockerfile source file, returning ITS OWN spelling', () => { const result = resolveDockerfileLocation([text('.actor/dockerfile', 'FROM node:20\n')]); @@ -140,7 +131,6 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- F: no Dockerfile at all - the platform-parity default. --- describe('F: nothing resolves - the bundled default is injected', () => { it('injects the bundled default Dockerfile, verbatim, as an extra Dockerfile-named SourceFile', () => { const result = resolveDockerfileLocation([ @@ -175,7 +165,6 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- G: a present-but-malformed "dockerfile" field. --- describe('G: malformed "dockerfile" field', () => { it('a non-string value (e.g. true) fails the build immediately, before any candidate is tried', () => { const result = resolveDockerfileLocation([ @@ -219,7 +208,6 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- JSON5 tolerance: the platform accepts what strict JSON.parse would reject. --- describe('JSON5 tolerance', () => { it('parses a trailing comma and a comment in .actor/actor.json', () => { const result = resolveDockerfileLocation([ @@ -265,7 +253,6 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- No .actor/actor.json at all: candidates 2 and 3 must still work standalone. --- describe('no-actor.json fallbacks', () => { it('resolves .actor/Dockerfile with no actor.json present at all', () => { const result = resolveDockerfileLocation([ @@ -298,8 +285,6 @@ describe('resolveDockerfileLocation', () => { }); }); - // --- .actor/actor.json parses fine but isn't an object (e.g. bare "null") - candidate 1 must be - // skipped, not crash on `'dockerfile' in null`. --- describe('.actor/actor.json parses to a non-object value', () => { it('content is exactly "null" (JSON5-parseable, non-object) - falls through to candidate 2, no crash', () => { const result = resolveDockerfileLocation([actorJson('null'), text('.actor/Dockerfile', 'FROM node:20\n')]); From f1035809d867dd340a27e0352c1109b3ff2c1819 Mon Sep 17 00:00:00 2001 From: Josef Prochazka Date: Wed, 26 Aug 2026 09:06:01 +0200 Subject: [PATCH 8/9] Clean up after AI --- sample_actor_crawler/.actor/Dockerfile | 32 ++++++++++++++------------ sample_actor_crawler/requirements.txt | 2 ++ sample_actor_crawler/src/__init__.py | 0 sample_actor_crawler/src/__main__.py | 6 +++++ sample_actor_crawler/{ => src}/main.py | 18 +-------------- 5 files changed, 26 insertions(+), 32 deletions(-) create mode 100644 sample_actor_crawler/requirements.txt create mode 100644 sample_actor_crawler/src/__init__.py create mode 100644 sample_actor_crawler/src/__main__.py rename sample_actor_crawler/{ => src}/main.py (65%) diff --git a/sample_actor_crawler/.actor/Dockerfile b/sample_actor_crawler/.actor/Dockerfile index 5d12b4f..0de5fe3 100644 --- a/sample_actor_crawler/.actor/Dockerfile +++ b/sample_actor_crawler/.actor/Dockerfile @@ -1,15 +1,17 @@ -FROM python:3.11-slim -WORKDIR /usr/src/app -# Build-time-only network use (see sample_actor/.actor/Dockerfile for the full -# apify/apify-client version-pin rationale). This Actor additionally needs -# `crawlee[parsel]` for ParselCrawler; apify==4.0.0 constrains its own -# `crawlee` dependency to `>=1.8.0,<2.0.0`, and 1.8.3 is the newest release -# satisfying that (the same version apify-sdk-python's own lockfile pins -# alongside apify==4.0.0). -RUN pip install --no-cache-dir 'apify-client==3.1.0' 'apify==4.0.0' 'crawlee[parsel]==1.8.3' -COPY . ./ -# Run as a non-root user, like the real Apify Actor base images do (see -# sample_actor/.actor/Dockerfile for why this matters). -RUN useradd -m -u 1000 apify -USER apify -CMD ["python", "main.py"] +# Specify the base Docker image. See https://docs.apify.com/sdk/python/docs/overview/getting-started +FROM apify/actor-python:3.13 + +COPY --chown=myuser:myuser requirements.txt ./ + +RUN echo "Python version:" \ + && python --version \ + && echo "Pip version:" \ + && pip --version \ + && echo "Installing dependencies:" \ + && pip install -r requirements.txt \ + && echo "All installed Python packages:" \ + && pip freeze + +COPY --chown=myuser:myuser . ./ + +CMD ["python3", "-m", "src"] diff --git a/sample_actor_crawler/requirements.txt b/sample_actor_crawler/requirements.txt new file mode 100644 index 0000000..baaa54e --- /dev/null +++ b/sample_actor_crawler/requirements.txt @@ -0,0 +1,2 @@ +apify +crawlee[parsel] diff --git a/sample_actor_crawler/src/__init__.py b/sample_actor_crawler/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sample_actor_crawler/src/__main__.py b/sample_actor_crawler/src/__main__.py new file mode 100644 index 0000000..8c4ab0b --- /dev/null +++ b/sample_actor_crawler/src/__main__.py @@ -0,0 +1,6 @@ +import asyncio + +from .main import main + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/sample_actor_crawler/main.py b/sample_actor_crawler/src/main.py similarity index 65% rename from sample_actor_crawler/main.py rename to sample_actor_crawler/src/main.py index 17a28d7..157b62e 100644 --- a/sample_actor_crawler/main.py +++ b/sample_actor_crawler/src/main.py @@ -1,16 +1,4 @@ -"""Sample Actor demonstrating a Parsel-based crawl through Apify Proxy. - -Adapted from the Apify SDK's own ParselCrawler guide. Reads ``startUrl`` and -``proxyConfiguration`` (see ``.actor/input_schema.json``) and passes the -latter straight to ``Actor.create_proxy_configuration`` with no fallback: -only an explicit ``{"useApifyProxy": false}`` crawls direct -- an omitted -``proxyConfiguration`` behaves like ``useApifyProxy: true``, not like -``false`` -- and either way, ``useApifyProxy: true`` with a missing or -invalid ``APIFY_PROXY_PASSWORD`` fails the run via the SDK's own live -proxy-access check. See README.md's "Apify Proxy" section for the full -explanation. -""" -import asyncio +"""Sample Actor demonstrating a Parsel-based crawl through Apify Proxy.""" from crawlee.crawlers import ParselCrawler, ParselCrawlingContext from crawlee.router import Router @@ -51,7 +39,3 @@ async def main() -> None: ) await crawler.run([start_url]) - - -if __name__ == "__main__": - asyncio.run(main()) From d56eacf5be6479b570fca81e7e6a64b2958fcd5e Mon Sep 17 00:00:00 2001 From: Josef Prochazka Date: Wed, 26 Aug 2026 09:37:30 +0200 Subject: [PATCH 9/9] Remove redundant lock --- package-lock.json | 6740 --------------------------------------------- 1 file changed, 6740 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 96d833b..0000000 --- a/package-lock.json +++ /dev/null @@ -1,6740 +0,0 @@ -{ - "name": "actor-runtime", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "actor-runtime", - "version": "0.1.0", - "dependencies": { - "@crawlee/core": "4.0.0-beta.133", - "@crawlee/fs-storage": "4.0.0-beta.133", - "dockerode": "^4.0.5", - "express": "^5.1.0", - "json5": "^2.2.3", - "tar-stream": "^3.1.7" - }, - "devDependencies": { - "@eslint/js": "^9.18.0", - "@types/dockerode": "^3.3.34", - "@types/express": "^5.0.1", - "@types/node": "^22.13.0", - "@types/tar-stream": "^3.1.3", - "apify-client": "^2.13.0", - "axios": "^1.7.9", - "eslint": "^9.18.0", - "eslint-config-prettier": "^9.1.0", - "prettier": "^3.4.2", - "tsx": "^4.19.2", - "typescript": "^5.7.3", - "typescript-eslint": "^8.18.0", - "vitest": "^2.1.8" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@apify/consts": { - "version": "2.57.0", - "resolved": "https://registry.npmjs.org/@apify/consts/-/consts-2.57.0.tgz", - "integrity": "sha512-kyD+pq72+RxpPUUikk5pWyGRcbehoQSkTIgd3kqE/MCy+MGPTYBbMb4lb2C92+Zh8K5eYyTrfobHKrpqyHAWMQ==", - "license": "Apache-2.0" - }, - "node_modules/@apify/datastructures": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@apify/datastructures/-/datastructures-2.0.6.tgz", - "integrity": "sha512-hI/Q5cCjXXI/g4OPwX+/xuk+JsOtXRhKNXl2wnq/nKmdrji3nYCc9vGsAEc1iXVbs16xQMZy5uRhoY3XTABovA==", - "license": "Apache-2.0" - }, - "node_modules/@apify/log": { - "version": "2.5.48", - "resolved": "https://registry.npmjs.org/@apify/log/-/log-2.5.48.tgz", - "integrity": "sha512-3aWhBik4P1Q6hubU38XgiB0PnGR5UkIC3gbxm0LQYQd1FwfOID5CrgRkbyab3BJaspN6LToIccgHbtDgSz2Pog==", - "license": "Apache-2.0", - "dependencies": { - "@apify/consts": "^2.57.0", - "ansi-colors": "^4.1.1" - } - }, - "node_modules/@apify/ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@apify/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-VHIswI7rD/R4bToeIDuJ9WJXt+qr5SdhfoZ9RzdjmCs9mgy7l0P4RugQEUCcU+WB4sfImbd4CKwzXcn0uYx1yw==", - "license": "MIT", - "dependencies": { - "event-stream": "3.3.4" - }, - "bin": { - "ps-tree": "bin/ps-tree.js" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@apify/timeout": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/@apify/timeout/-/timeout-0.4.8.tgz", - "integrity": "sha512-SKBSXUYVYSaKCa5ogEEcAeKctbyRHq3v2yNLmzxZ/Rb+X4AuEFySd04nURGQPY2trtcKwKZP1sxrWvqwRH2WRA==", - "license": "Apache-2.0" - }, - "node_modules/@apify/utilities": { - "version": "2.35.5", - "resolved": "https://registry.npmjs.org/@apify/utilities/-/utilities-2.35.5.tgz", - "integrity": "sha512-A2HmUJCKx7Z3BGP2ELw0YNS2PPfl4WE3orcVaKaF1mwmKCfbW5E/kdWgmJ19zyUWI/Rm3Bn0x3NmJtk02SCShA==", - "license": "Apache-2.0", - "dependencies": { - "@apify/consts": "^2.57.0", - "@apify/log": "^2.5.48" - } - }, - "node_modules/@balena/dockerignore": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", - "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", - "license": "Apache-2.0" - }, - "node_modules/@borewit/text-codec": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", - "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@crawlee/core": { - "version": "4.0.0-beta.133", - "resolved": "https://registry.npmjs.org/@crawlee/core/-/core-4.0.0-beta.133.tgz", - "integrity": "sha512-+SXihbAlthGvv2TDouC7Gayq1A5e53Tg8S26tMrc6CZy91UBnEU28Tv/z4RbnnCyfSDsXn4aZm1h4HFGXkTEPA==", - "license": "Apache-2.0", - "dependencies": { - "@apify/consts": "^2.41.0", - "@apify/datastructures": "^2.0.3", - "@apify/log": "^2.5.18", - "@apify/timeout": "^0.4.4", - "@apify/utilities": "^2.15.5", - "@crawlee/fs-storage": "4.0.0-beta.133", - "@crawlee/http-client": "4.0.0-beta.133", - "@crawlee/types": "4.0.0-beta.133", - "@crawlee/utils": "4.0.0-beta.133", - "@sapphire/async-queue": "^1.5.5", - "@standard-schema/spec": "^1.0.0", - "@vladfrangu/async_event_emitter": "^2.4.6", - "content-type": "^1.0.5", - "csv-stringify": "^6.5.2", - "json5": "^2.2.3", - "mime-types": "^3.0.1", - "minimatch": "^10.0.1", - "stream-json": "^1.9.1", - "tldts": "^7.0.6", - "tough-cookie": "^6.0.0", - "tslib": "^2.8.1", - "type-fest": "^4.41.0", - "zod": "^4.4.3" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@crawlee/fs-storage": { - "version": "4.0.0-beta.133", - "resolved": "https://registry.npmjs.org/@crawlee/fs-storage/-/fs-storage-4.0.0-beta.133.tgz", - "integrity": "sha512-4VP7WQk/zJZHplTvz470s4Rsf8oNq+HXT5vH7whBXTo/++cZhL3GtIJb/ithxMfsfjBgjohL6kdeM+fHYTDJZg==", - "license": "Apache-2.0", - "dependencies": { - "@crawlee/fs-storage-native": "0.1.5-beta.18", - "@crawlee/types": "4.0.0-beta.133", - "@crawlee/utils": "4.0.0-beta.133", - "zod": "^4.4.3" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@crawlee/fs-storage-native": { - "version": "0.1.5-beta.18", - "resolved": "https://registry.npmjs.org/@crawlee/fs-storage-native/-/fs-storage-native-0.1.5-beta.18.tgz", - "integrity": "sha512-G5+Alb5GDAZomtu+0mB8oeYA0BCMqOQIqNoTPtZxh2wLcb17CpMfJJvq6D/nT8vANAaBkrMR49bbrodtROV2ug==", - "license": "Apache-2.0", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@crawlee/fs-storage-native-darwin-arm64": "0.1.5-beta.18", - "@crawlee/fs-storage-native-darwin-x64": "0.1.5-beta.18", - "@crawlee/fs-storage-native-linux-x64-gnu": "0.1.5-beta.18", - "@crawlee/fs-storage-native-win32-x64-msvc": "0.1.5-beta.18" - } - }, - "node_modules/@crawlee/fs-storage-native-darwin-arm64": { - "version": "0.1.5-beta.18", - "resolved": "https://registry.npmjs.org/@crawlee/fs-storage-native-darwin-arm64/-/fs-storage-native-darwin-arm64-0.1.5-beta.18.tgz", - "integrity": "sha512-fYZbU61GoMw+3q1JXRvARa8f/A8qZo0BxBpGogzq8duhiOChs4v+uHjffuZjL4E6goFR5qjX2kwOAJzoEp2kZQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@crawlee/fs-storage-native-darwin-x64": { - "version": "0.1.5-beta.18", - "resolved": "https://registry.npmjs.org/@crawlee/fs-storage-native-darwin-x64/-/fs-storage-native-darwin-x64-0.1.5-beta.18.tgz", - "integrity": "sha512-Wb/d6PLor34790ixEFsRADvTUXKzR846/1E2LUaDTLA3KQ5QUCwjQhs7VpfrgY9AzzgM/s8G+3TO2UI6L3eYkw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@crawlee/fs-storage-native-linux-x64-gnu": { - "version": "0.1.5-beta.18", - "resolved": "https://registry.npmjs.org/@crawlee/fs-storage-native-linux-x64-gnu/-/fs-storage-native-linux-x64-gnu-0.1.5-beta.18.tgz", - "integrity": "sha512-J8qfCkj3i6ic87fiD4f05WSXLXaAFLZlboXeeH9nDhJv/6RLxjiwTqec/Ig9H3yon+v4VQUbSHIh3Mm6B0g3XQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@crawlee/fs-storage-native-win32-x64-msvc": { - "version": "0.1.5-beta.18", - "resolved": "https://registry.npmjs.org/@crawlee/fs-storage-native-win32-x64-msvc/-/fs-storage-native-win32-x64-msvc-0.1.5-beta.18.tgz", - "integrity": "sha512-76uxjzcsD2E+mWqZ+HENpcSpJJAZVdWf3osKR3pA3wld+yzR9o0cckRysu4QTDhUeNtzL1KIq2NzOF1GPNLqiw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@crawlee/http-client": { - "version": "4.0.0-beta.133", - "resolved": "https://registry.npmjs.org/@crawlee/http-client/-/http-client-4.0.0-beta.133.tgz", - "integrity": "sha512-uPhdfv80/eC1/cBgdmXAkfQmwsh8teO5FljL4sfWUDBpK18e0eneB7OJZbImNBQcfKvEqsG3lJCDee9hsGHX9g==", - "license": "Apache-2.0", - "dependencies": { - "@crawlee/types": "4.0.0-beta.133", - "tough-cookie": "^6.0.0" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@crawlee/types": { - "version": "4.0.0-beta.133", - "resolved": "https://registry.npmjs.org/@crawlee/types/-/types-4.0.0-beta.133.tgz", - "integrity": "sha512-rmFzFl2sr5CGHOMou4OcsMwoyCxV/1TD7qkW4H+0gP6vCXd6KnqXeDMynKemRYsAV1RO1HUcn1dmzORFubOCTg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@crawlee/utils": { - "version": "4.0.0-beta.133", - "resolved": "https://registry.npmjs.org/@crawlee/utils/-/utils-4.0.0-beta.133.tgz", - "integrity": "sha512-v5Uw81QJXBo5lxXPXTYLW+R1dzJ8yJIiNrztyiwoCUxJdyVLNJB0+DvpBwuA+12kYqCLgHJ2fS4leIrWfDibzw==", - "license": "Apache-2.0", - "dependencies": { - "@apify/ps-tree": "^1.2.0", - "@crawlee/http-client": "4.0.0-beta.133", - "@crawlee/types": "4.0.0-beta.133", - "@types/sax": "^1.2.7", - "cheerio": "^1.0.0", - "domhandler": "^5.0.3", - "file-type": "^21.0.0", - "robots-parser": "^3.0.1", - "sax": "^1.4.1", - "tldts": "^7.0.6", - "tslib": "^2.8.1", - "whatwg-mimetype": "^4.0.0", - "zod": "^4.4.3" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", - "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", - "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.3.0", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.5", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", - "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", - "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.8.0", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", - "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.5.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.15", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", - "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", - "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", - "license": "BSD-3-Clause" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", - "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", - "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", - "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", - "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", - "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", - "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", - "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", - "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", - "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", - "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", - "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", - "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", - "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", - "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", - "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", - "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", - "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", - "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", - "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", - "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", - "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", - "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", - "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", - "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", - "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sapphire/async-queue": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", - "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==", - "license": "MIT", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@tokenizer/inflate": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", - "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "token-types": "^6.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "license": "MIT" - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/docker-modem": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", - "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/ssh2": "*" - } - }, - "node_modules/@types/dockerode": { - "version": "3.3.47", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.47.tgz", - "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/docker-modem": "*", - "@types/node": "*", - "@types/ssh2": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.3.tgz", - "integrity": "sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.20.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", - "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/sax": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", - "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, - "node_modules/@types/ssh2": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", - "integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^18.11.18" - } - }, - "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/ssh2/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/tar-stream": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/tar-stream/-/tar-stream-3.1.4.tgz", - "integrity": "sha512-921gW0+g29mCJX0fRvqeHzBlE/XclDaAG0Ousy1LCghsOhvaKacDeRGEVzQP9IPfKn8Vysy7FEXAIxycpc/CMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", - "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/type-utils": "8.67.0", - "@typescript-eslint/utils": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.67.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", - "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", - "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", - "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.67.0", - "@typescript-eslint/types": "^8.67.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", - "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", - "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", - "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/utils": "8.67.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", - "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", - "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.67.0", - "@typescript-eslint/tsconfig-utils": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", - "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", - "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.67.0", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@vitest/expect": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", - "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", - "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", - "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", - "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "2.1.9", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", - "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.1.9", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", - "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", - "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.1.9", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vladfrangu/async_event_emitter": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz", - "integrity": "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==", - "license": "MIT", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/apify-client": { - "version": "2.25.0", - "resolved": "https://registry.npmjs.org/apify-client/-/apify-client-2.25.0.tgz", - "integrity": "sha512-g6ttHJ4Au/Gg0iDHIevN3CvTbJfey9DUXUXJxe0ORF6JIYaVVfKdyJjINGb6/49gOpt/62ZFPw+HSEVIzrwR5w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@apify/consts": "^2.50.0", - "@apify/log": "^2.2.6", - "@apify/utilities": "^2.23.2", - "@crawlee/types": "^3.3.0", - "ansi-colors": "^4.1.1", - "async-retry": "^1.3.3", - "axios": "^1.16.0", - "content-type": "^1.0.5", - "ow": "^0.28.2", - "proxy-agent": "^6.5.0", - "tslib": "^2.5.0", - "type-fest": "^4.0.0" - } - }, - "node_modules/apify-client/node_modules/@crawlee/types": { - "version": "3.18.1", - "resolved": "https://registry.npmjs.org/@crawlee/types/-/types-3.18.1.tgz", - "integrity": "sha512-jmV7HN4b1vEzriGcKhCRa8A5rv5HaqBPPfneGRxPpZtbmnEcusHTmEOOyG393lUBmv0yaM03da0hy+PZnR35vw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "retry": "0.13.1" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", - "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.6", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/b4a": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", - "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/bare-events": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", - "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", - "license": "Apache-2.0", - "peerDependencies": { - "bare-abort-controller": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } - } - }, - "node_modules/bare-fs": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.0.tgz", - "integrity": "sha512-fM+MhCvdQhZ7NV6S95a07gPSqjIYKn6mFaXfx266wN3ajZGl/+1AzH+ubkXQ0fFZvOe2nk9VHkzdYkQE5zMV3Q==", - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4", - "bare-url": "^2.2.2", - "fast-fifo": "^1.3.2" - }, - "engines": { - "bare": ">=1.28.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", - "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", - "license": "Apache-2.0" - }, - "node_modules/bare-stream": { - "version": "2.13.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", - "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.8.1", - "streamx": "^2.25.0", - "teex": "^1.0.1" - }, - "peerDependencies": { - "bare-abort-controller": "*", - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - }, - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/bare-url": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.2.tgz", - "integrity": "sha512-L13PCJzKG8RGvx8V1/DdMi12ERhC3tprr7/8a94BxpmnRsFqxh5XZNdhtMxu5HPkRshYOOWRGY8lDP7ZhpG9Cg==", - "license": "Apache-2.0", - "dependencies": { - "bare-path": "^3.0.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/basic-ftp": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", - "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^2.0.0", - "debug": "^4.4.3", - "http-errors": "^2.0.1", - "iconv-lite": "^0.7.2", - "on-finished": "^2.4.1", - "qs": "^6.15.2", - "raw-body": "^3.0.2", - "type-is": "^2.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/body-parser/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", - "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buildcheck": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", - "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/cheerio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", - "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", - "license": "MIT", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "encoding-sniffer": "^0.2.1", - "htmlparser2": "^10.1.0", - "parse5": "^7.3.0", - "parse5-htmlparser2-tree-adapter": "^7.1.0", - "parse5-parser-stream": "^7.1.2", - "undici": "^7.19.0", - "whatwg-mimetype": "^4.0.0" - }, - "engines": { - "node": ">=20.18.1" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cpu-features": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.19.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/csv-stringify": { - "version": "6.8.3", - "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.8.3.tgz", - "integrity": "sha512-gIeSCvq5F4VtXV3naV3VAewLhBkiZBz+PPhTOA8H3Y8h/ELa+R1ml0GZck/4/Nzo9ep2lvOluilJ6MJlbZsKMA==", - "license": "MIT" - }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/docker-modem": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.7.tgz", - "integrity": "sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.1", - "readable-stream": "^3.5.0", - "split-ca": "^1.0.1", - "ssh2": "^1.15.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/dockerode": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.12.tgz", - "integrity": "sha512-/bCZd6KlGcjZO8Buqmi/vXuqEGVEZ0PNjx/biBNqJD3MhK9DmdiAuKxqfNhflgDESDIiBz3qF+0e55+CpnrUcw==", - "license": "Apache-2.0", - "dependencies": { - "@balena/dockerignore": "^1.0.2", - "@grpc/grpc-js": "^1.11.1", - "@grpc/proto-loader": "^0.7.13", - "docker-modem": "^5.0.7", - "protobufjs": "^7.3.2", - "tar-fs": "^2.1.4", - "uuid": "^10.0.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding-sniffer": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", - "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", - "license": "MIT", - "dependencies": { - "iconv-lite": "^0.6.3", - "whatwg-encoding": "^3.1.1" - }, - "funding": { - "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/eslint": { - "version": "9.39.5", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", - "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.6", - "@eslint/js": "9.39.5", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", - "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "node_modules/events-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.7.0" - } - }, - "node_modules/expect-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", - "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/file-type": { - "version": "21.3.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", - "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", - "license": "MIT", - "dependencies": { - "@tokenizer/inflate": "^0.4.1", - "strtok3": "^10.3.4", - "token-types": "^6.1.1", - "uint8array-extras": "^1.4.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", - "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", - "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.4", - "mime-types": "^2.1.35" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "license": "MIT" - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-uri": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/htmlparser2": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", - "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "entities": "^7.0.1" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", - "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ip-address": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz", - "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0" - }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", - "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nan": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", - "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", - "license": "MIT", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/netmask": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", - "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ow": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/ow/-/ow-0.28.2.tgz", - "integrity": "sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.2.0", - "callsites": "^3.1.0", - "dot-prop": "^6.0.1", - "lodash.isequal": "^4.5.0", - "vali-date": "^1.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pac-proxy-agent": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "dev": true, - "license": "MIT", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-parser-stream": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", - "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", - "license": "MIT", - "dependencies": { - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "license": [ - "MIT", - "Apache2" - ], - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.17", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.9.6", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", - "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/protobufjs": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", - "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.5", - "@protobufjs/eventemitter": "^1.1.1", - "@protobufjs/fetch": "^1.1.1", - "@protobufjs/float": "^1.0.2", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.1", - "@types/node": ">=13.7.0", - "long": "^5.3.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", - "license": "BSD-3-Clause", - "dependencies": { - "es-define-property": "^1.0.1", - "side-channel": "^1.1.1" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", - "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", - "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/robots-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.1.tgz", - "integrity": "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/rollup": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", - "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.62.4", - "@rollup/rollup-android-arm64": "4.62.4", - "@rollup/rollup-darwin-arm64": "4.62.4", - "@rollup/rollup-darwin-x64": "4.62.4", - "@rollup/rollup-freebsd-arm64": "4.62.4", - "@rollup/rollup-freebsd-x64": "4.62.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", - "@rollup/rollup-linux-arm-musleabihf": "4.62.4", - "@rollup/rollup-linux-arm64-gnu": "4.62.4", - "@rollup/rollup-linux-arm64-musl": "4.62.4", - "@rollup/rollup-linux-loong64-gnu": "4.62.4", - "@rollup/rollup-linux-loong64-musl": "4.62.4", - "@rollup/rollup-linux-ppc64-gnu": "4.62.4", - "@rollup/rollup-linux-ppc64-musl": "4.62.4", - "@rollup/rollup-linux-riscv64-gnu": "4.62.4", - "@rollup/rollup-linux-riscv64-musl": "4.62.4", - "@rollup/rollup-linux-s390x-gnu": "4.62.4", - "@rollup/rollup-linux-x64-gnu": "4.62.4", - "@rollup/rollup-linux-x64-musl": "4.62.4", - "@rollup/rollup-openbsd-x64": "4.62.4", - "@rollup/rollup-openharmony-arm64": "4.62.4", - "@rollup/rollup-win32-arm64-msvc": "4.62.4", - "@rollup/rollup-win32-ia32-msvc": "4.62.4", - "@rollup/rollup-win32-x64-gnu": "4.62.4", - "@rollup/rollup-win32-x64-msvc": "4.62.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", - "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", - "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ip-address": "^10.1.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", - "license": "MIT", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/split-ca": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", - "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", - "license": "ISC" - }, - "node_modules/ssh2": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", - "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", - "hasInstallScript": true, - "dependencies": { - "asn1": "^0.2.6", - "bcrypt-pbkdf": "^1.0.2" - }, - "engines": { - "node": ">=10.16.0" - }, - "optionalDependencies": { - "cpu-features": "~0.0.10", - "nan": "^2.23.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "node_modules/stream-chain": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", - "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", - "license": "BSD-3-Clause" - }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1" - } - }, - "node_modules/stream-json": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", - "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", - "license": "BSD-3-Clause", - "dependencies": { - "stream-chain": "^2.2.5" - } - }, - "node_modules/streamx": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", - "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", - "license": "MIT", - "dependencies": { - "events-universal": "^1.0.0", - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strtok3": { - "version": "10.3.5", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", - "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", - "license": "MIT", - "dependencies": { - "@tokenizer/token": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar-fs": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", - "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", - "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "bare-fs": "^4.5.5", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/teex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", - "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", - "license": "MIT", - "dependencies": { - "streamx": "^2.12.5" - } - }, - "node_modules/text-decoder": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", - "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT" - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tldts": { - "version": "7.4.10", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.10.tgz", - "integrity": "sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==", - "license": "MIT", - "dependencies": { - "tldts-core": "^7.4.10" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "7.4.10", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.10.tgz", - "integrity": "sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==", - "license": "MIT" - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/token-types": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", - "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", - "license": "MIT", - "dependencies": { - "@borewit/text-codec": "^0.2.1", - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/tough-cookie": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", - "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^7.0.5" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.23.12", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", - "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "license": "Unlicense" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", - "license": "MIT", - "dependencies": { - "content-type": "^2.0.0", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", - "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.67.0", - "@typescript-eslint/parser": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/utils": "8.67.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/uint8array-extras": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", - "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", - "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vali-date": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", - "integrity": "sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", - "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/vitest": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", - "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "2.1.9", - "@vitest/mocker": "2.1.9", - "@vitest/pretty-format": "^2.1.9", - "@vitest/runner": "2.1.9", - "@vitest/snapshot": "2.1.9", - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.9", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.9", - "@vitest/ui": "2.1.9", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", - "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -}