From 6bbc6495d22e664f627389f577d00036f063b52f Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Fri, 4 Jul 2025 15:02:29 +0200 Subject: [PATCH 01/58] docs: add minimal description to the README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d82d01d..5c3beca 100644 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ # lambda-shell-runtime + +Custom AWS Lambda runtime for shell/bash functions. Write your Lambda functions in bash and deploy them as container images. + From 2ba1eec14ca79f44ca2eac14440c1bd193e44ba5 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Tue, 8 Jul 2025 04:47:20 +0200 Subject: [PATCH 02/58] feat: initial release of Lambda Shell Runtime base images Introduce the first stable version of the lambda-shell-runtime abstraction. This includes: - Minimal shell-based Lambda runtime - Base Docker images (`tiny`, `slim`, `full`) with curl, jq, and http-cli - Runtime bootstrap and handler logic for shell-based functions - GitHub Actions-based semantic release setup - Support scripts for local testing and GHCR publishing This forms the foundation for writing Lambda functions entirely in shell and deploying them via container images. --- .github/workflows/release.yml | 26 + .gitignore | 17 + Dockerfile | 43 + README.md | 70 +- build | 49 + functions/handler.sh | 7 + package-lock.json | 6973 +++++++++++++++++++++++++++++++++ package.json | 33 + runtime/Dockerfile | 18 + runtime/bootstrap | 32 + runtime/handler.sh | 3 + scripts/publish | 14 + slim.Dockerfile | 43 + test/run | 28 + tiny.Dockerfile | 34 + 15 files changed, 7389 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100755 build create mode 100644 functions/handler.sh create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 runtime/Dockerfile create mode 100755 runtime/bootstrap create mode 100644 runtime/handler.sh create mode 100644 scripts/publish create mode 100644 slim.Dockerfile create mode 100644 test/run create mode 100644 tiny.Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..660646d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release + +on: + push: + branches: + - main + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - run: npm ci + - run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GHCR_PAT: ${{ secrets.GHCR_PAT }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee8e4bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Docker artifacts +*.tar +*.tar.gz +*.tar.xz +*.tgz +*.img + +# Build output +.DS_Store +build/ +dist/ +*.log +node_modules/ + +# VSCode settings +.vscode/ +examples/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4d617e7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +# Stage 1: Build stage +FROM public.ecr.aws/lambda/provided:al2023 AS builder + +RUN dnf install -y unzip && \ + dnf clean all +# Install AWS CLI v2 (minimal installation) +RUN curl -sL "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o awscliv2.zip && \ + unzip awscliv2.zip && \ + ./aws/install --bin-dir /aws-cli-bin --install-dir /aws-cli && \ + rm -rf aws awscliv2.zip + +# Download http-cli +RUN --mount=type=secret,id=github_token \ + curl -H "Authorization: token $(cat /run/secrets/github_token)" \ + -L https://github.com/ql4b/http-cli/archive/refs/heads/develop.zip \ + -o http-cli.zip && \ + unzip http-cli.zip && \ + mkdir -p /http-cli-bin && \ + mv http-cli-develop/http-cli /http-cli-bin/ && \ + chmod +x /http-cli-bin/http-cli && \ + rm -rf http-cli.zip http-cli-develop + +# Stage 2: Runtime stage +FROM public.ecr.aws/lambda/provided:al2023 + +# Install only runtime dependencies +RUN dnf install -y jq && \ + dnf clean all && \ + rm -rf /var/cache/dnf + +# Copy AWS CLI binaries only +COPY --from=builder /aws-cli-bin/aws /usr/local/bin/aws +COPY --from=builder /aws-cli /usr/local/aws-cli + +# Copy http-cli +COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli + +COPY runtime/bootstrap /var/runtime/bootstrap +RUN chmod +x /var/runtime/bootstrap + +WORKDIR /var/task + +COPY functions/handler.sh handler.sh \ No newline at end of file diff --git a/README.md b/README.md index 5c3beca..f9a6cb2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,72 @@ # lambda-shell-runtime -Custom AWS Lambda runtime for shell/bash functions. Write your Lambda functions in bash and deploy them as container images. +Custom AWS Lambda runtime environment for executing shell/bash functions with AWS Lambda. +Implement AWS Lambda functions in Bash, packaged as OCI-compliant container images that interface with the Lambda Runtime API and follow the custom runtime execution flow. + +Inspired by: https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html + +## Runtime Variants + +Each runtime variant has its own Dockerfile: + +- `tiny`: [`jq`](https://stedolan.github.io/jq/) + [`http-cli`](https://github.com/ql4b/http-cli) +- `slim`: same as `tiny` + AWS CLI v1 +- `full`: same as `slim` + AWS CLI v2 + +### Build locally + +```bash +# Build tiny +docker build -f tiny.Dockerfile -t lambda-shell-base:tiny . + +# Build slim +docker build -f slim.Dockerfile -t lambda-shell-base:slim . + +# Build full +docker build -f Dockerfile -t lambda-shell-base:full . +``` + +## Usage + +To use this runtime in your own Lambda container image: + +```Dockerfile +FROM ghcr.io/ql4b/lambda-shell-base:tiny + +WORKDIR /var/task + +COPY relay.sh handler.sh . +``` + +Your `handler.sh` file should define bash functions, and the handler name passed to Lambda should match `filename.functionname`, e.g., `handler.hello`. + +## Local testing + +Use [aws-lambda-runtime-interface-emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator) for local testing. + +```bash +docker run -d \ + -v ~/.aws-lambda-rie:/aws-lambda \ + -p 9000:8080 \ + --env HANDLER="handler.hello" \ + --entrypoint /aws-lambda/aws-lambda-rie \ + lambda-shell-base:tiny \ + /var/runtime/bootstrap + +curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' +``` + +## Publishing + +Images are published to GitHub Container Registry (GHCR): + +```bash +./scripts/publish +``` + +## Layout + +- `runtime/` โ€” custom bootstrap and core loop +- `test/` โ€” simple example test function + runner +- `scripts/` โ€” utilities like publishing diff --git a/build b/build new file mode 100755 index 0000000..f379056 --- /dev/null +++ b/build @@ -0,0 +1,49 @@ + +#!/bin/sh + +set -e + +# Default config +PLATFORM="linux/arm64" +MODE="--load" +VARIANTS="tiny slim full" + +# Parse arguments +while [ $# -gt 0 ]; do + case "$1" in + --platform) + PLATFORM="$2" + shift 2 + ;; + --push) + MODE="--push" + shift + ;; + --load) + MODE="--load" + shift + ;; + tiny|slim|full) + VARIANTS="$1" + shift + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +for VARIANT in $VARIANTS; do + DOCKERFILE="./base/${VARIANT}.Dockerfile" + [ "$VARIANT" = "full" ] && DOCKERFILE="./base/Dockerfile" + + echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." + docker buildx build \ + --platform "$PLATFORM" \ + --secret id=github_token,env=GITHUB_TOKEN \ + --tag lambda-shell-base:$VARIANT \ + --file "$DOCKERFILE" \ + $MODE \ + ./base +done \ No newline at end of file diff --git a/functions/handler.sh b/functions/handler.sh new file mode 100644 index 0000000..7f72d48 --- /dev/null +++ b/functions/handler.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +function hello() { + EVENT="$1" + echo "$EVENT" \ + | jq +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2dc38cb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6973 @@ +{ + "name": "lambda-shell-runtime", + "version": "0.0.0-semantic-release", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lambda-shell-runtime", + "version": "0.0.0-semantic-release", + "devDependencies": { + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^13.0.1", + "@semantic-release/exec": "^6.0.3", + "@semantic-release/github": "^9.2.4", + "@semantic-release/release-notes-generator": "^14.0.3", + "semantic-release": "^24.2.6" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz", + "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.1.0.tgz", + "integrity": "sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz", + "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.2.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5.0.0" + } + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@semantic-release/changelog": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", + "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^11.0.0", + "lodash": "^4.17.4" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/commit-analyzer": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz", + "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@semantic-release/exec": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-6.0.3.tgz", + "integrity": "sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "parse-json": "^5.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/github": { + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.2.6.tgz", + "integrity": "sha512-shi+Lrf6exeNZF+sBhK+P011LSbhmIAoUEgEY6SsxF8irJ+J2stwI5jkyDQ+4gzYyDImzV6LCKdYB9FXnQRWKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^5.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-retry": "^6.0.0", + "@octokit/plugin-throttling": "^8.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^6.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/github/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/github/node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/github/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.2.tgz", + "integrity": "sha512-+M9/Lb35IgnlUO6OSJ40Ie+hUsZLuph2fqXC/qrKn0fMvUU/jiCjpoL6zEm69vzcmaZJ8yNKtMBEKHWN49WBbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^9.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.9.3", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/npm/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@semantic-release/npm/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/release-notes-generator": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.3.tgz", + "integrity": "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^2.0.0", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-package-up": "^11.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.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/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "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/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "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/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/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/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", + "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.1.0.tgz", + "integrity": "sha512-dpC440QnORNCO81XYuRRFOLCsjKj4W7tMkUIn3lR6F/FAaJcWLi7iCj6IcEvSQY2zw6VUgwUKd5DEHKEWrpmEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.2.0.tgz", + "integrity": "sha512-uLnoLeIW4XaoFtH37qEcg/SXMJmKF4vi7V0H2rnPueg+VEtFGA/asSCNTcq4M/GQ6QmlzchAEtOoDTtKqWeHag==", + "dev": true, + "license": "MIT", + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/env-ci": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.1.tgz", + "integrity": "sha512-mT3ks8F0kwpo7SYNds6nWj0PaRh+qJxIeBVBXAKTN9hphAzZv7s0QAZQbqnB1fAv/r4pJUGE15BV9UrS31FP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/env-ci/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/env-ci/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/env-ci/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/env-ci/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", + "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-regex": "^4.0.5", + "super-regex": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", + "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "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/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/hook-std": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", + "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "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/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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "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/import-fresh/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/import-from-esm": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz", + "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": ">=18.20" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/into-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", + "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "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-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.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-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "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/issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/marked-terminal": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz", + "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.4.1", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.2.0", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <16" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz", + "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-url": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz", + "integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "10.9.3", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.3.tgz", + "integrity": "sha512-6Eh1u5Q+kIVXeA8e7l2c/HpnFFcwrkt37xDMujD5be1gloWa9p6j3Fsv3mByXXmqJHy+2cElRMML8opNT7xIJQ==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.1", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.2.0", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.2.2", + "@npmcli/run-script": "^9.1.0", + "@sigstore/tuf": "^3.1.1", + "abbrev": "^3.0.1", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.4.1", + "ci-info": "^4.2.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.1.0", + "ini": "^5.0.0", + "init-package-json": "^7.0.2", + "is-cidr": "^5.1.1", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.1", + "libnpmexec": "^9.0.1", + "libnpmfund": "^6.0.1", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.1", + "libnpmpublish": "^10.0.1", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.2.0", + "nopt": "^8.1.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.2", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^19.0.1", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.1.0", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.1", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^20.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "20.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.4.3", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.1", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.7.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "@sigstore/sign": "^3.1.0", + "@sigstore/tuf": "^3.1.0", + "@sigstore/verify": "^2.1.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "make-fetch-happen": "^14.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.21", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "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/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": true, + "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/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.14", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": true, + "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/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-each-series": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", + "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", + "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "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/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/registry-auth-token": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "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": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/semantic-release": { + "version": "24.2.6", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.6.tgz", + "integrity": "sha512-D0cwjlO5RZzHHxAcsoF1HxiRLfC3ehw+ay+zntzFs6PNX6aV0JzKNG15mpxPipBYa/l4fHly88dHvgDyqwb1Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/commit-analyzer": "^13.0.0-beta.1", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^11.0.0", + "@semantic-release/npm": "^12.0.2", + "@semantic-release/release-notes-generator": "^14.0.0-beta.1", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^9.0.0", + "debug": "^4.0.0", + "env-ci": "^11.0.0", + "execa": "^9.0.0", + "figures": "^6.0.0", + "find-versions": "^6.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^8.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "marked": "^15.0.0", + "marked-terminal": "^7.3.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-package-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": ">=20.8.1" + } + }, + "node_modules/semantic-release/node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/semantic-release/node_modules/@octokit/core": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz", + "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.1", + "@octokit/request": "^10.0.2", + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/semantic-release/node_modules/@octokit/endpoint": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", + "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/semantic-release/node_modules/@octokit/graphql": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz", + "integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.2", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/semantic-release/node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/semantic-release/node_modules/@octokit/plugin-paginate-rest": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz", + "integrity": "sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.1.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/semantic-release/node_modules/@octokit/plugin-retry": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.0.1.tgz", + "integrity": "sha512-KUoYR77BjF5O3zcwDQHRRZsUvJwepobeqiSSdCJ8lWt27FZExzb0GgVxrhhfuyF6z2B2zpO0hN5pteni1sqWiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=7" + } + }, + "node_modules/semantic-release/node_modules/@octokit/plugin-throttling": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-11.0.1.tgz", + "integrity": "sha512-S+EVhy52D/272L7up58dr3FNSMXWuNZolkL4zMJBNIfIxyZuUcczsQAU4b5w6dewJXnKYVgSHSV5wxitMSW1kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": "^7.0.0" + } + }, + "node_modules/semantic-release/node_modules/@octokit/request": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.3.tgz", + "integrity": "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.0", + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/semantic-release/node_modules/@octokit/request-error": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz", + "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/semantic-release/node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } + }, + "node_modules/semantic-release/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/semantic-release/node_modules/@semantic-release/github": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.3.tgz", + "integrity": "sha512-T2fKUyFkHHkUNa5XNmcsEcDPuG23hwBKptfUVcFXDVG2cSjXXZYDOfVYwfouqbWo/8UefotLaoGfQeK+k3ep6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.0", + "@octokit/plugin-paginate-rest": "^13.0.0", + "@octokit/plugin-retry": "^8.0.0", + "@octokit/plugin-throttling": "^11.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=24.1.0" + } + }, + "node_modules/semantic-release/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/semantic-release/node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/semantic-release/node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/semantic-release/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/issue-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/semantic-release/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/semantic-release/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/signale/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "license": "ISC", + "dependencies": { + "through2": "~2.0.0" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/super-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", + "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-timeout": "^1.0.1", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.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==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "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/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5ed9f47 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "lambda-shell-runtime", + "version": "0.0.0-semantic-release", + "private": true, + "scripts": { + "release": "semantic-release" + }, + "devDependencies": { + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^13.0.1", + "@semantic-release/exec": "^6.0.3", + "@semantic-release/github": "^9.2.4", + "@semantic-release/release-notes-generator": "^14.0.3", + "semantic-release": "^24.2.6" + }, + "release": { + "branches": [ + "main" + ], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/changelog", + "@semantic-release/github", + [ + "@semantic-release/exec", + { + "publishCmd": "./scripts/publish ${nextRelease.version}" + } + ] + ] + } +} diff --git a/runtime/Dockerfile b/runtime/Dockerfile new file mode 100644 index 0000000..280b7a6 --- /dev/null +++ b/runtime/Dockerfile @@ -0,0 +1,18 @@ +FROM public.ecr.aws/lambda/provided:al2023 + +# Set the working directory +WORKDIR /var/task + +# Copy bootstrap and handler files +COPY bootstrap /var/runtime/bootstrap +COPY handler.sh /var/task/handler.sh + +# Ensure bootstrap is executable +RUN chmod +x /var/runtime/bootstrap + +# Optional: copy additional CLI tools or libraries +# COPY bin/ /var/task/bin/ +# ENV PATH="/var/task/bin:${PATH}" + +# Set Lambda bootstrap as entrypoint +ENTRYPOINT ["/var/runtime/bootstrap"] diff --git a/runtime/bootstrap b/runtime/bootstrap new file mode 100755 index 0000000..b6708fc --- /dev/null +++ b/runtime/bootstrap @@ -0,0 +1,32 @@ +#!/bin/sh + +# https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html + +# set -euo pipefail +set -eo pipefail + +# Initialization - load function handler +source $LAMBDA_TASK_ROOT/"$(echo $HANDLER | cut -d. -f1).sh" + +# Processing +while true +do + HEADERS="$(mktemp)" + # Get an event. The HTTP request will block until one is received + EVENT_DATA=$(curl -sS -LD "$HEADERS" "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") + + # Extract request ID by scraping response headers received above + REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) + + # Run the handler function from the script + # set -x + RESPONSE=$($(echo "$HANDLER" | cut -d. -f2) "$EVENT_DATA") + # set +x + + echo "$RESPONSE" | curl -sS -X POST \ + "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${REQUEST_ID}/response" \ + -H "Content-Type: application/json" \ + -d @- + +# --data-binary "$RESPONSE" +done \ No newline at end of file diff --git a/runtime/handler.sh b/runtime/handler.sh new file mode 100644 index 0000000..ab41f35 --- /dev/null +++ b/runtime/handler.sh @@ -0,0 +1,3 @@ +#!/bin/sh +echo "Default handler โ€” override this in your runtime build context." >&2 +exit 1 \ No newline at end of file diff --git a/scripts/publish b/scripts/publish new file mode 100644 index 0000000..6d0b03d --- /dev/null +++ b/scripts/publish @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +VERSION="$1" + +echo "Publishing Docker images with version $VERSION" + +for VARIANT in tiny slim full; do + IMAGE="ghcr.io/ql4b/lambda-shell-base:$VARIANT" + docker tag lambda-shell-base:$VARIANT "$IMAGE:$VERSION" + docker tag lambda-shell-base:$VARIANT "$IMAGE:latest" + docker push "$IMAGE:$VERSION" + docker push "$IMAGE:latest" +done diff --git a/slim.Dockerfile b/slim.Dockerfile new file mode 100644 index 0000000..8589f61 --- /dev/null +++ b/slim.Dockerfile @@ -0,0 +1,43 @@ +# Ultra-minimal AWS CLI (CLI v1 via pip) +FROM public.ecr.aws/lambda/python:3.11 AS builder + +RUN yum install -y unzip && \ + yum clean all + +RUN pip install awscli --target /aws-cli + +# Download http-cli +RUN --mount=type=secret,id=github_token \ + curl -H "Authorization: token $(cat /run/secrets/github_token)" \ + -L https://github.com/ql4b/http-cli/archive/refs/heads/develop.zip \ + -o http-cli.zip && \ + unzip http-cli.zip && \ + mkdir -p /http-cli-bin && \ + mv http-cli-develop/http-cli /http-cli-bin/ && \ + chmod +x /http-cli-bin/http-cli && \ + rm -rf http-cli.zip http-cli-develop + +# Stage 2: Runtime stage +FROM public.ecr.aws/lambda/provided:al2023 + +# Install only runtime dependencies +RUN dnf install -y jq python3 && \ + dnf clean all && \ + rm -rf /var/cache/dnf + +COPY --from=builder /aws-cli /opt/python +ENV PYTHONPATH=/opt/python + +# Copy http-cli +COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli + +ENV PATH="/var/task/bin:${PATH}" + +COPY runtime/bootstrap /var/runtime/bootstrap +RUN chmod +x /var/runtime/bootstrap + +WORKDIR /var/task + +COPY functions/handler.sh handler.sh + + diff --git a/test/run b/test/run new file mode 100644 index 0000000..a3d99fb --- /dev/null +++ b/test/run @@ -0,0 +1,28 @@ +#!/bin/sh + +# Setup the AWS Lambda Runtime Interface Emulator +mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \ + https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64 \ + && chmod +x ~/.aws-lambda-rie/aws-lambda-rie + +# Start the Lambda shell container +docker run \ + -p 9000:8080 \ + --platform linux/arm64 \ + -v ~/.aws:/root/.aws:ro \ + --env AWS_PROFILE=${AWS_PROFILE} \ + --env AWS_REGION=${AWS_REGION} \ + --volume ~/.aws-lambda-rie:/aws-lambda \ + --entrypoint /aws-lambda/aws-lambda-rie \ + --env HANDLER="handler.hello" \ + lambda-shell-base:tiny \ + /var/runtime/bootstrap + +# Invoke test: basic curl request +curl -XPOST \ + "http://localhost:9000/2015-03-31/functions/function/invocations" \ + -d '{}' + +# Invoke test using http-bin +http-bin -d '{}' \ + "http://localhost:9000/2015-03-31/functions/function/invocations" diff --git a/tiny.Dockerfile b/tiny.Dockerfile new file mode 100644 index 0000000..416bddd --- /dev/null +++ b/tiny.Dockerfile @@ -0,0 +1,34 @@ +FROM public.ecr.aws/lambda/provided:al2023 AS builder + +RUN dnf install -y unzip && \ + dnf clean all + +# Download http-cli +RUN --mount=type=secret,id=github_token \ + curl -H "Authorization: token $(cat /run/secrets/github_token)" \ + -L https://github.com/ql4b/http-cli/archive/refs/heads/develop.zip \ + -o http-cli.zip && \ + unzip http-cli.zip && \ + mkdir -p /http-cli-bin && \ + mv http-cli-develop/http-cli /http-cli-bin/ && \ + chmod +x /http-cli-bin/http-cli && \ + rm -rf http-cli.zip http-cli-develop + +# Stage 2: Runtime stage +FROM public.ecr.aws/lambda/provided:al2023 + +# Install only runtime dependencies +RUN dnf install -y jq && \ + dnf clean all && \ + rm -rf /var/cache/dnf + +# Copy http-cli +COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli +ENV PATH="/var/task/bin:${PATH}" + +COPY runtime/bootstrap /var/runtime/bootstrap +RUN chmod +x /var/runtime/bootstrap + +WORKDIR /var/task + +COPY functions/handler.sh handler.sh From 416e2441e1197d9107bb65551576d95e5b6bc37b Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 03:33:29 +0200 Subject: [PATCH 03/58] chore: run semantic release on develop branch --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 660646d..3555dff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Release on: push: branches: - - main + - develop jobs: release: From 2ee3dc1e668a1ad1c18f80c8a934af7461a2ce76 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 03:42:21 +0200 Subject: [PATCH 04/58] ci: set develop as prelease branch --- .github/workflows/release.yml | 6 ++++++ package.json | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3555dff..483cf59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,12 @@ on: push: branches: - develop + - main + +permissions: + contents: write + issues: write + pull-requests: write jobs: release: diff --git a/package.json b/package.json index 5ed9f47..304c9bd 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,11 @@ }, "release": { "branches": [ - "main" + "main", + { + "name": "develop", + "prerelease": true + } ], "plugins": [ "@semantic-release/commit-analyzer", From 520bcf2ae332f1e3614f5fbea62104f2a377c8d5 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 03:47:51 +0200 Subject: [PATCH 05/58] feat: trigger prerelease for lambda-shell-runtime From 150c58573319b322b3706931f15bb942296dd858 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 03:48:46 +0200 Subject: [PATCH 06/58] chore: make publish script executable --- scripts/publish | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/publish diff --git a/scripts/publish b/scripts/publish old mode 100644 new mode 100755 From 0fe7502b2aa91968741f6be7aca9469bc392d268 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 03:49:40 +0200 Subject: [PATCH 07/58] feat: trigger pre-release v0.1.1 From fd860aeca7a075631593ba6cc419a96a47f6e418 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 03:52:18 +0200 Subject: [PATCH 08/58] fix: correct Docker tag format in publish script --- scripts/publish | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/publish b/scripts/publish index 6d0b03d..53ff76b 100755 --- a/scripts/publish +++ b/scripts/publish @@ -6,9 +6,10 @@ VERSION="$1" echo "Publishing Docker images with version $VERSION" for VARIANT in tiny slim full; do - IMAGE="ghcr.io/ql4b/lambda-shell-base:$VARIANT" - docker tag lambda-shell-base:$VARIANT "$IMAGE:$VERSION" - docker tag lambda-shell-base:$VARIANT "$IMAGE:latest" - docker push "$IMAGE:$VERSION" - docker push "$IMAGE:latest" + BASE_IMAGE="ghcr.io/ql4b/lambda-shell-base" + TAG="$VERSION-$VARIANT" + docker tag lambda-shell-base:$VARIANT "$BASE_IMAGE:$TAG" + docker tag lambda-shell-base:$VARIANT "$BASE_IMAGE:$VARIANT-latest" + docker push "$BASE_IMAGE:$TAG" + docker push "$BASE_IMAGE:$VARIANT-latest" done From 2a17c10a1c7125c64f64b42178cf603b7d6f0b44 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 03:56:00 +0200 Subject: [PATCH 09/58] chore(ci): build Docker images before semantic-release --- .github/workflows/release.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 483cf59..33447cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,14 @@ jobs: restore-keys: | ${{ runner.os }}-node- - run: npm ci + - name: Build Docker images + run: | + echo "${{ secrets.GHCR_PAT }}" > github_token + export DOCKER_BUILDKIT=1 + docker build --progress=plain --secret id=github_token,src=github_token -t lambda-shell-base:tiny -f tiny.Dockerfile . + docker build --progress=plain --secret id=github_token,src=github_token -t lambda-shell-base:slim -f slim.Dockerfile . + docker build --progress=plain --secret id=github_token,src=github_token -t lambda-shell-base:full -f Dockerfile . + shell: bash - run: npx semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 51db3f712f493e00821e489b691616c98b193c42 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 04:21:13 +0200 Subject: [PATCH 10/58] feat: trigger pre-release v0.1.1 From 5c50562ef1246cf5cc2ed2289d28fc9558ad5d1e Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 04:40:53 +0200 Subject: [PATCH 11/58] fix: fix base image names and tags --- .github/workflows/release.yml | 6 +++--- README.md | 10 +++++----- build | 2 +- scripts/publish | 6 +++--- test/run | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 33447cd..beaeed8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,9 +30,9 @@ jobs: run: | echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 - docker build --progress=plain --secret id=github_token,src=github_token -t lambda-shell-base:tiny -f tiny.Dockerfile . - docker build --progress=plain --secret id=github_token,src=github_token -t lambda-shell-base:slim -f slim.Dockerfile . - docker build --progress=plain --secret id=github_token,src=github_token -t lambda-shell-base:full -f Dockerfile . + docker build --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . + docker build --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . + docker build --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . shell: bash - run: npx semantic-release env: diff --git a/README.md b/README.md index f9a6cb2..5400b5f 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ Each runtime variant has its own Dockerfile: ```bash # Build tiny -docker build -f tiny.Dockerfile -t lambda-shell-base:tiny . +docker build -f tiny.Dockerfile -t lambda-shell-runtime:tiny . # Build slim -docker build -f slim.Dockerfile -t lambda-shell-base:slim . +docker build -f slim.Dockerfile -t lambda-shell-runtime:slim . # Build full -docker build -f Dockerfile -t lambda-shell-base:full . +docker build -f Dockerfile -t lambda-shell-runtime:full . ``` ## Usage @@ -32,7 +32,7 @@ docker build -f Dockerfile -t lambda-shell-base:full . To use this runtime in your own Lambda container image: ```Dockerfile -FROM ghcr.io/ql4b/lambda-shell-base:tiny +FROM ghcr.io/ql4b/lambda-shell-runtime:tiny WORKDIR /var/task @@ -51,7 +51,7 @@ docker run -d \ -p 9000:8080 \ --env HANDLER="handler.hello" \ --entrypoint /aws-lambda/aws-lambda-rie \ - lambda-shell-base:tiny \ + lambda-shell-runtime:tiny \ /var/runtime/bootstrap curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' diff --git a/build b/build index f379056..e2689cb 100755 --- a/build +++ b/build @@ -42,7 +42,7 @@ for VARIANT in $VARIANTS; do docker buildx build \ --platform "$PLATFORM" \ --secret id=github_token,env=GITHUB_TOKEN \ - --tag lambda-shell-base:$VARIANT \ + --tag lambda-shell-runtime:$VARIANT \ --file "$DOCKERFILE" \ $MODE \ ./base diff --git a/scripts/publish b/scripts/publish index 53ff76b..74a6959 100755 --- a/scripts/publish +++ b/scripts/publish @@ -6,10 +6,10 @@ VERSION="$1" echo "Publishing Docker images with version $VERSION" for VARIANT in tiny slim full; do - BASE_IMAGE="ghcr.io/ql4b/lambda-shell-base" + BASE_IMAGE="ghcr.io/ql4b/lambda-shell-runtime" TAG="$VERSION-$VARIANT" - docker tag lambda-shell-base:$VARIANT "$BASE_IMAGE:$TAG" - docker tag lambda-shell-base:$VARIANT "$BASE_IMAGE:$VARIANT-latest" + docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$TAG" + docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$VARIANT-latest" docker push "$BASE_IMAGE:$TAG" docker push "$BASE_IMAGE:$VARIANT-latest" done diff --git a/test/run b/test/run index a3d99fb..c991e34 100644 --- a/test/run +++ b/test/run @@ -15,7 +15,7 @@ docker run \ --volume ~/.aws-lambda-rie:/aws-lambda \ --entrypoint /aws-lambda/aws-lambda-rie \ --env HANDLER="handler.hello" \ - lambda-shell-base:tiny \ + lambda-shell-runtime:tiny \ /var/runtime/bootstrap # Invoke test: basic curl request From 8a10828e9711d52280ac6ab9379c2167d0a214c5 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 04:47:57 +0200 Subject: [PATCH 12/58] fix(ci): update workflow to support develop branch --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index beaeed8..4cdbe0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,8 @@ jobs: docker build --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . docker build --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . shell: bash + - name: Log in to GHCR + run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - run: npx semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 53e289edb4d1983077dd204997da9ff75592d84c Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 05:00:00 +0200 Subject: [PATCH 13/58] feat(ci): enable multi-arch Docker builds for amd64 and arm64 --- .github/workflows/release.yml | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4cdbe0c..dbcc9c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,16 +26,31 @@ jobs: restore-keys: | ${{ runner.os }}-node- - run: npm ci - - name: Build Docker images + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push multi-arch Docker images + env: + GHCR_PAT: ${{ secrets.GHCR_PAT }} run: | - echo "${{ secrets.GHCR_PAT }}" > github_token - export DOCKER_BUILDKIT=1 - docker build --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . - docker build --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . - docker build --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . - shell: bash - - name: Log in to GHCR - run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin + echo "$GHCR_PAT" | docker login ghcr.io -u skunxicat --password-stdin + + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -t ghcr.io/ql4b/lambda-shell-runtime:tiny-latest \ + -f tiny.Dockerfile . \ + --push + + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -t ghcr.io/ql4b/lambda-shell-runtime:slim-latest \ + -f slim.Dockerfile . \ + --push + + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -t ghcr.io/ql4b/lambda-shell-runtime:full-latest \ + -f Dockerfile . \ + --push - run: npx semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b297a9d9514c1f8a9bd0eaef656bdce6cdc1aee8 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 05:11:07 +0200 Subject: [PATCH 14/58] fix(docker): use ARM architecture for AWS CLI v2 install --- .github/workflows/release.yml | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dbcc9c7..7b8f43f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,31 +26,16 @@ jobs: restore-keys: | ${{ runner.os }}-node- - run: npm ci - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Build and push multi-arch Docker images - env: - GHCR_PAT: ${{ secrets.GHCR_PAT }} + - name: Build Docker images run: | - echo "$GHCR_PAT" | docker login ghcr.io -u skunxicat --password-stdin - - docker buildx build \ - --platform linux/amd64,linux/arm64 \ - -t ghcr.io/ql4b/lambda-shell-runtime:tiny-latest \ - -f tiny.Dockerfile . \ - --push - - docker buildx build \ - --platform linux/amd64,linux/arm64 \ - -t ghcr.io/ql4b/lambda-shell-runtime:slim-latest \ - -f slim.Dockerfile . \ - --push - - docker buildx build \ - --platform linux/amd64,linux/arm64 \ - -t ghcr.io/ql4b/lambda-shell-runtime:full-latest \ - -f Dockerfile . \ - --push + echo "${{ secrets.GHCR_PAT }}" > github_token + export DOCKER_BUILDKIT=1 + docker build --load --platform linux/arm64 --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . + docker build --load --platform linux/arm64 --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . + docker build --load --platform linux/arm64 --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . + shell: bash + - name: Log in to GHCR + run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - run: npx semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From e05d28510623a0fa3c4c58ae8e9bc105715ee996 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 05:17:27 +0200 Subject: [PATCH 15/58] fix(docker): use ARM architecture for AWS CLI v2 install --- .github/workflows/release.yml | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b8f43f..3eba6f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,13 +26,33 @@ jobs: restore-keys: | ${{ runner.os }}-node- - run: npm ci - - name: Build Docker images + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push multi-arch Docker images run: | echo "${{ secrets.GHCR_PAT }}" > github_token - export DOCKER_BUILDKIT=1 - docker build --load --platform linux/arm64 --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . - docker build --load --platform linux/arm64 --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . - docker build --load --platform linux/arm64 --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . + docker login ghcr.io -u skunxicat --password-stdin < github_token + docker buildx create --use + docker buildx build \ + --platform linux/arm64 \ + --secret id=github_token,src=github_token \ + --tag ghcr.io/ql4b/lambda-shell-runtime:tiny \ + --file tiny.Dockerfile \ + --push . + docker buildx build \ + --platform linux/arm64 \ + --secret id=github_token,src=github_token \ + --tag ghcr.io/ql4b/lambda-shell-runtime:slim \ + --file slim.Dockerfile \ + --push . + docker buildx build \ + --platform linux/arm64 \ + --secret id=github_token,src=github_token \ + --tag ghcr.io/ql4b/lambda-shell-runtime:full \ + --file Dockerfile \ + --push . shell: bash - name: Log in to GHCR run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin From 52f1af6013a59a4213a1d2651acea25afd7a4a1f Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 05:42:19 +0200 Subject: [PATCH 16/58] fix: fix local builds --- .github/workflows/release.yml | 26 +++++--------------------- build | 6 +++--- test/run | 2 +- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3eba6f1..eb59554 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,29 +30,13 @@ jobs: uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push multi-arch Docker images + - name: Build Docker images run: | echo "${{ secrets.GHCR_PAT }}" > github_token - docker login ghcr.io -u skunxicat --password-stdin < github_token - docker buildx create --use - docker buildx build \ - --platform linux/arm64 \ - --secret id=github_token,src=github_token \ - --tag ghcr.io/ql4b/lambda-shell-runtime:tiny \ - --file tiny.Dockerfile \ - --push . - docker buildx build \ - --platform linux/arm64 \ - --secret id=github_token,src=github_token \ - --tag ghcr.io/ql4b/lambda-shell-runtime:slim \ - --file slim.Dockerfile \ - --push . - docker buildx build \ - --platform linux/arm64 \ - --secret id=github_token,src=github_token \ - --tag ghcr.io/ql4b/lambda-shell-runtime:full \ - --file Dockerfile \ - --push . + export DOCKER_BUILDKIT=1 + docker build --platform linux/arm64 --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . + docker build --platform linux/arm64 --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . + docker build --platform linux/arm64 --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . shell: bash - name: Log in to GHCR run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin diff --git a/build b/build index e2689cb..0c33dcd 100755 --- a/build +++ b/build @@ -35,8 +35,8 @@ while [ $# -gt 0 ]; do done for VARIANT in $VARIANTS; do - DOCKERFILE="./base/${VARIANT}.Dockerfile" - [ "$VARIANT" = "full" ] && DOCKERFILE="./base/Dockerfile" + DOCKERFILE="./${VARIANT}.Dockerfile" + [ "$VARIANT" = "full" ] && DOCKERFILE="./Dockerfile" echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." docker buildx build \ @@ -45,5 +45,5 @@ for VARIANT in $VARIANTS; do --tag lambda-shell-runtime:$VARIANT \ --file "$DOCKERFILE" \ $MODE \ - ./base + . done \ No newline at end of file diff --git a/test/run b/test/run index c991e34..beff63d 100644 --- a/test/run +++ b/test/run @@ -15,7 +15,7 @@ docker run \ --volume ~/.aws-lambda-rie:/aws-lambda \ --entrypoint /aws-lambda/aws-lambda-rie \ --env HANDLER="handler.hello" \ - lambda-shell-runtime:tiny \ + lambda-shell-runtime:debug-tiny \ /var/runtime/bootstrap # Invoke test: basic curl request From a3767bbdd2126d63b255208b79813b37a6aafbf3 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 06:04:02 +0200 Subject: [PATCH 17/58] feat: add --tag flag option for build --- build | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build b/build index 0c33dcd..1b76218 100755 --- a/build +++ b/build @@ -6,8 +6,10 @@ set -e # Default config PLATFORM="linux/arm64" MODE="--load" +TAG="lambda-shell-runtime" VARIANTS="tiny slim full" + # Parse arguments while [ $# -gt 0 ]; do case "$1" in @@ -15,6 +17,10 @@ while [ $# -gt 0 ]; do PLATFORM="$2" shift 2 ;; + --tag) + TAG="$2" + shift 2 + ;; --push) MODE="--push" shift @@ -42,7 +48,7 @@ for VARIANT in $VARIANTS; do docker buildx build \ --platform "$PLATFORM" \ --secret id=github_token,env=GITHUB_TOKEN \ - --tag lambda-shell-runtime:$VARIANT \ + --tag $TAG:$VARIANT \ --file "$DOCKERFILE" \ $MODE \ . From 4335d83fca2fb2e92a448db2e738f10df3e1bf8c Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 06:59:23 +0200 Subject: [PATCH 18/58] fix: add /var/task/bin to PATH in full variant Dockerfile --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 4d617e7..42882ee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,8 @@ COPY --from=builder /aws-cli /usr/local/aws-cli # Copy http-cli COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli +ENV PATH="/var/task/bin:${PATH}" + COPY runtime/bootstrap /var/runtime/bootstrap RUN chmod +x /var/runtime/bootstrap From 767e9631f0bddcde6fd740eacaef65f5bb85216f Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 08:29:39 +0200 Subject: [PATCH 19/58] fix(ci): build and load Docker images for arm64 using buildx --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb59554..f6d5656 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,9 +34,9 @@ jobs: run: | echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 - docker build --platform linux/arm64 --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . - docker build --platform linux/arm64 --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . - docker build --platform linux/arm64 --load --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . + docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . + docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . + docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . shell: bash - name: Log in to GHCR run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin From 08db91ceca01befc3490b68b1b98451ca2c08c9a Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 12:30:35 +0200 Subject: [PATCH 20/58] feat: micro runtime image uses awscurl instead of aws-cli --- .github/workflows/release.yml | 5 ++-- Dockerfile | 6 +++- build | 3 +- micro.Dockerfile | 53 +++++++++++++++++++++++++++++++++++ test/run | 25 +++++++++++++++++ 5 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 micro.Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6d5656..c644061 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,8 +35,9 @@ jobs: echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . - docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . - docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . + docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:micro -f micro.Dockerfile . + # docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . + # docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . shell: bash - name: Log in to GHCR run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin diff --git a/Dockerfile b/Dockerfile index 42882ee..39bd1f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ RUN --mount=type=secret,id=github_token \ FROM public.ecr.aws/lambda/provided:al2023 # Install only runtime dependencies -RUN dnf install -y jq && \ +RUN dnf install -y jq python3 python3-libs && \ dnf clean all && \ rm -rf /var/cache/dnf @@ -32,6 +32,10 @@ RUN dnf install -y jq && \ COPY --from=builder /aws-cli-bin/aws /usr/local/bin/aws COPY --from=builder /aws-cli /usr/local/aws-cli +# Fix Python shared library path for AWS CLI v2 +RUN find /usr -name "libpython3*.so*" -exec cp {} /usr/local/bin/ \; 2>/dev/null || true + + # Copy http-cli COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli diff --git a/build b/build index 1b76218..0e661e0 100755 --- a/build +++ b/build @@ -7,7 +7,8 @@ set -e PLATFORM="linux/arm64" MODE="--load" TAG="lambda-shell-runtime" -VARIANTS="tiny slim full" +# VARIANTS="tiny slim full" +VARIANTS="tiny micro" # Parse arguments diff --git a/micro.Dockerfile b/micro.Dockerfile new file mode 100644 index 0000000..981dcb8 --- /dev/null +++ b/micro.Dockerfile @@ -0,0 +1,53 @@ +FROM public.ecr.aws/lambda/provided:al2023 AS builder + +RUN dnf install -y unzip python3-pip findutils && \ + dnf clean all + +# Download http-cli +RUN --mount=type=secret,id=github_token \ + curl -H "Authorization: token $(cat /run/secrets/github_token)" \ + -L https://github.com/ql4b/http-cli/archive/refs/heads/develop.zip \ + -o http-cli.zip && \ + unzip http-cli.zip && \ + mkdir -p /http-cli-bin && \ + mv http-cli-develop/http-cli /http-cli-bin/ && \ + chmod +x /http-cli-bin/http-cli && \ + rm -rf http-cli.zip http-cli-develop + +RUN pip3 install --no-cache-dir --target /tmp/awscurl awscurl && \ + find /tmp/awscurl -type d -name '__pycache__' -exec rm -rf {} + && \ + find /tmp/awscurl -type f -name '*.pyc' -delete && \ + find /tmp/awscurl -type d -name '*.dist-info' -exec rm -rf {} + + +# Stage 2: Runtime stage +FROM public.ecr.aws/lambda/provided:al2023 + +# Install only runtime dependencies +RUN dnf install -y jq python3 && \ + dnf clean all && \ + rm -rf /var/cache/dnf + +# Copy only what's needed +COPY --from=builder /tmp/awscurl /var/task/aws +# Clean up Python cache and metadata +RUN rm -rf \ + /var/task/aws/__pycache__ \ + /var/task/aws/*.dist-info \ + /var/task/aws/**/__pycache__ + +ENV PYTHONPATH="/var/task/aws" + +RUN mkdir -p /var/task/bin && \ + printf '#!/bin/sh\nexport PYTHONPATH=/var/task/aws\nexec python3 -m awscurl.awscurl "$@"\n' > /var/task/bin/awscurl && \ + chmod +x /var/task/bin/awscurl + +# Copy http-cli +COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli +ENV PATH="/var/task/bin:${PATH}" + +COPY runtime/bootstrap /var/runtime/bootstrap +RUN chmod +x /var/runtime/bootstrap + +WORKDIR /var/task + +COPY functions/handler.sh handler.sh diff --git a/test/run b/test/run index beff63d..23449b5 100644 --- a/test/run +++ b/test/run @@ -18,6 +18,31 @@ docker run \ lambda-shell-runtime:debug-tiny \ /var/runtime/bootstrap +docker run \ + -p 9000:8080 \ + --platform linux/arm64 \ + -v ~/.aws:/root/.aws:ro \ + --env AWS_PROFILE=${AWS_PROFILE} \ + --env AWS_REGION=${AWS_REGION} \ + --volume ~/.aws-lambda-rie:/aws-lambda \ + --entrypoint /aws-lambda/aws-lambda-rie \ + --env HANDLER="handler.hello" \ + lambda-shell-runtime:debug-tiny \ + /var/runtime/bootstrap + + +docker run -d --rm \ + -p 9000:8080 \ + --platform linux/arm64 \ + -v ~/.aws:/root/.aws:ro \ + --volume ~/.aws-lambda-rie:/aws-lambda \ + --entrypoint /aws-lambda/aws-lambda-rie \ + --env HANDLER="handler.hello" \ + lambda-shell-runtime:micro \ + /var/runtime/bootstrap + + + # Invoke test: basic curl request curl -XPOST \ "http://localhost:9000/2015-03-31/functions/function/invocations" \ From c71c12200a474b5b9e6407a3b62133cf28e0f46b Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Wed, 9 Jul 2025 13:33:10 +0200 Subject: [PATCH 21/58] chore: add examples --- .gitignore | 2 +- build | 1 + examples/Dockerfile | 7 +++ examples/README.md | 50 ++++++++++++++++++++ examples/service.sh | 111 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 examples/Dockerfile create mode 100644 examples/README.md create mode 100644 examples/service.sh diff --git a/.gitignore b/.gitignore index ee8e4bc..bafb4d6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ node_modules/ # VSCode settings .vscode/ -examples/ +#examples/ diff --git a/build b/build index 0e661e0..ce8ac67 100755 --- a/build +++ b/build @@ -47,6 +47,7 @@ for VARIANT in $VARIANTS; do echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." docker buildx build \ + --no-cache \ --platform "$PLATFORM" \ --secret id=github_token,env=GITHUB_TOKEN \ --tag $TAG:$VARIANT \ diff --git a/examples/Dockerfile b/examples/Dockerfile new file mode 100644 index 0000000..5aa6871 --- /dev/null +++ b/examples/Dockerfile @@ -0,0 +1,7 @@ +# FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.10-full +FROM lambda-shell-runtime:micro +# FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.10-slim + +WORKDIR /var/task + +COPY service.sh handler.sh \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..7cf1c8d --- /dev/null +++ b/examples/README.md @@ -0,0 +1,50 @@ +# examples + +## buld + +```bash +CONTAINER="shell-booking" +# TAG="shell-booking:dev" +TAG="703177223665.dkr.ecr.eu-central-1.amazonaws.com/ql4b-farecrumbs:booking" + +b () { + docker build \ + --no-cache \ + --platform linux/arm64 \ + -t "$TAG" \ + --secret id=github_token,env=GITHUB_TOKEN \ + . +} + +r () { + docker run -d --rm \ + -p 9000:8080 \ + --name $CONTAINER \ + --platform linux/arm64 \ + -v ~/.aws:/root/.aws:ro \ + --volume ~/.aws-lambda-rie:/aws-lambda \ + --entrypoint /aws-lambda/aws-lambda-rie \ + --env HANDLER="handler.booking" \ + "$TAG" \ + /var/runtime/bootstrap +} + +p () { + docker ps -q --filter name="$CONTAINER" +} + +s () { + docker stop $(p) +} + +e () { + docker exec -it \ + $(p) \ + $@ +} + +req () { + http-cli -d '{ "reservationNumber": "A3PJKR" }' \ + "http://localhost:9000/2015-03-31/functions/function/invocations" +} +``` \ No newline at end of file diff --git a/examples/service.sh b/examples/service.sh new file mode 100644 index 0000000..8375ed7 --- /dev/null +++ b/examples/service.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +export HOME=/tmp + +# set -x + +function booking () { + local body=$(echo "$1" | jq -r .body) + local reservationNumber=$(echo "$body" | jq -r .reservationNumber) + local email=$(echo "$body" | jq -r .email) + local data=$(GetBookingByReservationNumber "$reservationNumber" "$email" ) + # data=$(GetBookingInfoByReservationNumber "$reservationNumber") + + jq -n --argjson data "$data" '{ + statusCode: 200, + body: ($data | tostring), + headers: { + "Content-Type": "application/json" + } + }' +} + +# Get Booking info from a parsed job JSON object +GetBookingInfo () { + local info=${1} + # mapfile -t args < <(echo "$info"\ + # | jq -r '.pnr, .email') + # GetBookingByReservationNumber "${args[@]}" \ + # | jq + pnr=$(echo "$info" | jq -r '.pnr') + email=$(echo "$info" | jq -r '.email') + GetBookingByReservationNumber "$pnr" "$email" | jq +} + +# Given a PNR, fethes the Booking email from ddb and +# retrive airline Booking info +GetBookingInfoByReservationNumber () { + local reservationNumber=${1} + local job + job=$(GetJobByReservationNumber "$reservationNumber") + if [[ $? -ne 0 ]]; then + echo "Error: $job" >&2 + return 1 + fi + GetBookingInfo "$job" +} + + +GetJobByReservationNumber() { + local pnr="$1" + local table="flygo-flygo-booking-ryanair" + local region="eu-south-1" + local profile="flygo" + + awscurl \ + --service dynamodb \ + --region "$region" \ + --profile "$profile" \ + -X POST \ + -H "Content-Type: application/x-amz-json-1.0" \ + -H "X-Amz-Target: DynamoDB_20120810.Query" \ + -d "{ + \"TableName\": \"$table\", + \"IndexName\": \"StatusPNRIndex\", + \"KeyConditionExpression\": \"#s = :s AND #p = :p\", + \"ExpressionAttributeNames\": { + \"#s\": \"status\", + \"#p\": \"pnr\" + }, + \"ExpressionAttributeValues\": { + \":s\": {\"S\": \"success\"}, + \":p\": {\"S\": \"$pnr\"} + }, + \"ProjectionExpression\": \"pnr, email, Id, createdAt, authToken\", + \"Limit\": 1, + \"ScanIndexForward\": false + }" https://dynamodb."$region".amazonaws.com/ | + jq -r '.Items[0] | { + pnr: .pnr.S, + id: .Id.S, + email: .email.S, + timestamp: .createdAt.S, + sessionToken: .authToken.S + }' +} + +function GetBookingByReservationNumber () { + local reservationNumber=${1} + local email=${2} + + if [[ -z "$reservationNumber" ]]; then + echo "Reservation number is required" >&2 + return 1 + else + shift + fi + + if [[ -z "$email" ]]; then + echo "Email is required" >&2 + return 1 + else + shift + fi + + http-cli \ + --method POST \ + --url 'https://www.ryanair.com/api/bookingfa/it-it/graphql' \ + --user-agent 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36' \ + --data $'{"query":"query GetBookingByReservationNumber($bookingInfo: GetBookingByReservationNumberInputType\u0021) {\\n getBookingByReservationNumber(bookingInfo: $bookingInfo) {\\n addons {\\n ...AddonFrag\\n }\\n carHire {\\n ...CarHireFrag\\n }\\n contacts {\\n ...ContactsFrag\\n }\\n extras {\\n ...ExtrasOrFeesFrag\\n }\\n groundTransfer {\\n ...GroundTransferFrag\\n }\\n hotels {\\n ...HotelsFrag\\n }\\n info {\\n ...InfoFrag\\n }\\n journeys {\\n ...JourneysFrag\\n }\\n passengers {\\n ...PassengersFrag\\n }\\n payments {\\n ...PaymentInfoFrag\\n }\\n fees {\\n ...ExtrasOrFeesFrag\\n }\\n serverTimeUTC\\n sessionToken\\n tripId\\n }\\n}\\n\\n\\nfragment AddonFrag on AddOnResponseModelType {\\n carParkName\\n code\\n currLater\\n currNow\\n dropOffLocation\\n end\\n isSingleOffer\\n itemId\\n loc\\n name\\n pax\\n paxNum\\n pickUpLocation\\n provider\\n providerCode\\n qty\\n refNo\\n sold\\n src\\n start\\n status\\n total\\n type\\n}\\n\\nfragment CarHireFrag on CarHireResponseModelType {\\n carSupplierConfirmationId\\n carType\\n confirmationId\\n currencyCode\\n insurance\\n pickupDateTime\\n pickupLocation\\n returnDateTime\\n returnLocation\\n serviceProvider\\n sold\\n status\\n totalPrice\\n}\\n\\nfragment PassengerNameFrag on PassengerNameResponseModelType {\\n first\\n last\\n middle\\n suffix\\n title\\n}\\n\\nfragment ContactsFrag on ContactResponseModelType {\\n address\\n city\\n country\\n cultureCode\\n email\\n fax\\n homePhone\\n name {\\n ...PassengerNameFrag\\n }\\n otherPhone\\n postalCode\\n provinceState\\n type\\n workPhone\\n}\\n\\nfragment ExtrasOrFeesFrag on BookingExtraResponseModelType {\\n amt\\n code\\n includedSsrs\\n isPartOfBundle\\n isSeatChange\\n journeyNum\\n percentageDiscount\\n qty\\n segmentNum\\n sold\\n total\\n totalDiscount\\n totalWithoutDiscount\\n type\\n vat\\n}\\n\\nfragment GroundTransferFrag on GroundTransferResponseModelType {\\n confirmationId\\n currencyCode\\n dropoffDateTime\\n dropoffLocation\\n flightBookingId\\n isSold\\n pickupDateTime\\n pickupLocation\\n pnr\\n status\\n totalPrice\\n}\\n\\nfragment HotelsFrag on HotelsResponseModelType {\\n status\\n}\\n\\nfragment InfoFrag on BookingInfoResponseModelType {\\n allSeatsAutoAllocated\\n balanceDue\\n bookingAgent\\n bookingId\\n createdUtcDate\\n curr\\n currPrecision\\n domestic\\n holdDateTime\\n isConnectingFlight\\n isBuyOneGetOneDiscounted\\n isHoldable\\n isPrime\\n modifiedUtcDate\\n pnr\\n status\\n locationCode\\n locationCodeGroup\\n sourceOrganization\\n companyName\\n}\\n\\nfragment JourneyChangeFrag on JourneyChangeInfoResponseModelType {\\n freeMove\\n isChangeable\\n isChanged\\n reasonCode\\n}\\n\\nfragment FaresFrag on BookingFareResponseModelType {\\n amt\\n code\\n disc\\n fareKey\\n fat\\n includedSsrs\\n percentageDiscount\\n qty\\n sold\\n total\\n totalDiscount\\n totalWithoutDiscount\\n type\\n vat\\n}\\n\\nfragment FatsFrag on BookingFatResponseModelType {\\n amount\\n code\\n total\\n vat\\n description\\n qty\\n}\\n\\nfragment SeatRowDeltaFrag on PaxSeatRowDeltaResponseModelType {\\n rowDistance\\n segmentNum\\n}\\n\\nfragment SegmentsFrag on SegmentModelResponseModelType {\\n aircraft\\n arrive\\n arriveUTC\\n depart\\n departUTC\\n dest\\n duration\\n flown\\n flt\\n isCancelled\\n isDomestic\\n orig\\n segmentNum\\n vatRate\\n}\\n\\nfragment ZoneDiscountFrag on BookingZoneDiscountResponseModelType {\\n code\\n pct\\n total\\n zone\\n}\\n\\nfragment JourneysFrag on BookingJourneyResponseModelType {\\n arrive\\n arriveUTC\\n changeInfo {\\n ...JourneyChangeFrag\\n }\\n checkInCloseUtcDate\\n checkInFreeAllocateOpenUtcDate\\n checkInOpenUtcDate\\n depart\\n departUTC\\n dest\\n destCountry\\n duration\\n fareClass\\n fareOption\\n fares {\\n ...FaresFrag\\n }\\n fareType\\n fats {\\n ...FatsFrag\\n }\\n flt\\n infSsrs {\\n ...ExtrasOrFeesFrag\\n }\\n isRescheduledJourneyWithFreeMove\\n setaSsrs {\\n ...ExtrasOrFeesFrag\\n }\\n journeyNum\\n maxPaxSeatRowDistance {\\n ...SeatRowDeltaFrag\\n }\\n mobilebp\\n orig\\n origCountry\\n seatsLeft\\n segments {\\n ...SegmentsFrag\\n }\\n zoneDiscount {\\n ...ZoneDiscountFrag\\n }\\n}\\n\\nfragment ResidentInfoFrag on PassengerResidentInfoResponseModelType {\\n community\\n dateOfBirth\\n dob\\n docNum\\n docType\\n hasLargeFamilyDiscount\\n hasResidentDiscount\\n largeFamilyCert\\n municipality\\n saraValidationCode\\n}\\n\\nfragment SegmentCheckinFrag on PassengerSegmentCheckinResponseModelType {\\n journeyNum\\n segmentNum\\n status\\n}\\n\\nfragment TravelDocumentFrag on TravelDocumentResponseModelType {\\n countryOfIssue\\n dateOfBirth\\n dOB\\n docNumber\\n docType\\n expiryDate\\n nationality\\n specialVisaDetails {\\n countryOfIssue\\n docNumber\\n docType\\n }\\n}\\n\\nfragment PassengerWithInfantTravelDocumentsFrag on PassengerWithInfantTravelDocumentResponseModelType {\\n num\\n travelDocument {\\n ...TravelDocumentFrag\\n }\\n infantTravelDocument {\\n ...TravelDocumentFrag\\n }\\n}\\n\\nfragment PassengersFrag on PassengerResponseModelType {\\n dateOfBirth\\n doB\\n ins\\n inf {\\n dateOfBirth\\n dob\\n first\\n last\\n middle\\n suffix\\n title\\n }\\n name {\\n ...PassengerNameFrag\\n }\\n nationality\\n paxFees {\\n ...ExtrasOrFeesFrag\\n }\\n paxNum\\n residentInfo {\\n ...ResidentInfoFrag\\n }\\n segCheckin {\\n ...SegmentCheckinFrag\\n }\\n segFees {\\n ...ExtrasOrFeesFrag\\n }\\n segPrm {\\n ...ExtrasOrFeesFrag\\n }\\n segSeats {\\n ...ExtrasOrFeesFrag\\n }\\n segSsrs {\\n ...ExtrasOrFeesFrag\\n }\\n travelDocuments {\\n ...PassengerWithInfantTravelDocumentsFrag\\n }\\n type\\n}\\n\\nfragment PaymentInfoFrag on PaymentInfoResponseModelType {\\n accName\\n accNum\\n amt\\n code\\n currency\\n dccAmt\\n dccApplicable\\n dccCurrency\\n dccRate\\n discount\\n isReward\\n status\\n type\\n createdDate\\n invoiceNumber\\n}\\n\\n","variables":{"bookingInfo":{"reservationNumber":"'"$reservationNumber"'","emailAddress":"'"$email"'"}}}' \ + $@ +} \ No newline at end of file From 9ea3f182491793fad60794e7f828898920172bdf Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Thu, 10 Jul 2025 18:03:47 +0200 Subject: [PATCH 22/58] feat: add new real life example and refactor repository layout --- Dockerfile | 6 +- README.md | 57 ++++++++--- build | 1 - examples/Dockerfile | 8 +- examples/README.md | 9 +- examples/rrelay.sh | 228 +++++++++++++++++++++++++++++++++++++++++++ examples/run | 32 ++++++ functions/handler.sh | 7 -- micro.Dockerfile | 2 +- runtime/Dockerfile | 18 ---- runtime/bootstrap | 5 +- runtime/handler.sh | 3 - slim.Dockerfile | 2 +- task/handler.sh | 8 ++ test/run | 27 +---- tiny.Dockerfile | 2 +- 16 files changed, 332 insertions(+), 83 deletions(-) create mode 100644 examples/rrelay.sh create mode 100755 examples/run delete mode 100644 functions/handler.sh delete mode 100644 runtime/Dockerfile delete mode 100644 runtime/handler.sh create mode 100644 task/handler.sh diff --git a/Dockerfile b/Dockerfile index 39bd1f9..f1f6e31 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,10 +32,6 @@ RUN dnf install -y jq python3 python3-libs && \ COPY --from=builder /aws-cli-bin/aws /usr/local/bin/aws COPY --from=builder /aws-cli /usr/local/aws-cli -# Fix Python shared library path for AWS CLI v2 -RUN find /usr -name "libpython3*.so*" -exec cp {} /usr/local/bin/ \; 2>/dev/null || true - - # Copy http-cli COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli @@ -46,4 +42,4 @@ RUN chmod +x /var/runtime/bootstrap WORKDIR /var/task -COPY functions/handler.sh handler.sh \ No newline at end of file +COPY task/handler.sh handler.sh \ No newline at end of file diff --git a/README.md b/README.md index 5400b5f..fec7f52 100644 --- a/README.md +++ b/README.md @@ -6,27 +6,58 @@ Implement AWS Lambda functions in Bash, packaged as OCI-compliant container imag Inspired by: https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html -## Runtime Variants -Each runtime variant has its own Dockerfile: +This custom Lambda runtime enables Bash-based execution with minimal dependencies. We provide three image variants tailored to different needs: -- `tiny`: [`jq`](https://stedolan.github.io/jq/) + [`http-cli`](https://github.com/ql4b/http-cli) -- `slim`: same as `tiny` + AWS CLI v1 -- `full`: same as `slim` + AWS CLI v2 +## Runtime Image Variants -### Build locally +### 1. `tiny` +- Includes: `jq`, `curl` +- Use case: Lightweight data parsing and HTTP requests. -```bash -# Build tiny -docker build -f tiny.Dockerfile -t lambda-shell-runtime:tiny . +### 2. `micro` +- Based on: `tiny` +- Adds: `awscurl` +- Use case: AWS API calls using IAM credentials without full AWS CLI. + +### 3. `full` +- Based on: `tiny` +- Adds: **full AWS CLI** (official install) +- Use case: Complete access to AWS CLI features. +- Note: Previously experimented with intermediate setups (e.g., stripped-down AWS CLI), but they proved unstable. This variant now uses the official, standard installation of the AWS CLI. -# Build slim -docker build -f slim.Dockerfile -t lambda-shell-runtime:slim . +--- -# Build full -docker build -f Dockerfile -t lambda-shell-runtime:full . +Each image is built from the same base but optimized for different tasks. You can choose the right variant for your Lambda depending on the environment and tools required. + +Each runtime variant has its own Dockerfile: + +## Local build process + +To run the script locally: + +``` +./build --platform linux/arm64 --tag your-repo/lambda-shell --load micro ``` +``` +./build --platform linux/arm64 --tag your-repo/lambda-shell --push micro +``` + +Key Features: + +* Platform targeting: Defaults to linux/arm64, which is suitable for AWS Lambda ARM-based functions. +* Variants: You can build one or more of the supported variants: tiny, micro, or full. +* Secrets: Supports injecting a GitHub token via --secret id=github_token,env=GITHUB_TOKEN for private dependency access during build. + +Modes: +* `--load` (default): Loads the image into the local Docker engine. +* `--push`: Pushes the image to a remote registry. + +## GitHub Actions Build + +In a GitHub Actions environment, the build script is typically used in combination with Docker Buildx and secrets configured in the CI pipeline. + ## Usage To use this runtime in your own Lambda container image: diff --git a/build b/build index ce8ac67..859dcef 100755 --- a/build +++ b/build @@ -10,7 +10,6 @@ TAG="lambda-shell-runtime" # VARIANTS="tiny slim full" VARIANTS="tiny micro" - # Parse arguments while [ $# -gt 0 ]; do case "$1" in diff --git a/examples/Dockerfile b/examples/Dockerfile index 5aa6871..95d8249 100644 --- a/examples/Dockerfile +++ b/examples/Dockerfile @@ -1,7 +1,13 @@ # FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.10-full FROM lambda-shell-runtime:micro # FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.10-slim +FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.11-tiny WORKDIR /var/task -COPY service.sh handler.sh \ No newline at end of file +RUN dnf install -y util-linux && \ + cp /usr/bin/uuidgen /var/task/bin/ && \ + dnf remove -y util-linux && \ + dnf clean all + +COPY ./rrelay.sh handler.sh \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 7cf1c8d..5b71f88 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,13 +3,15 @@ ## buld ```bash -CONTAINER="shell-booking" +# CONTAINER="shell-booking" # TAG="shell-booking:dev" -TAG="703177223665.dkr.ecr.eu-central-1.amazonaws.com/ql4b-farecrumbs:booking" +# TAG="703177223665.dkr.ecr.eu-central-1.amazonaws.com/ql4b-farecrumbs:booking" +CONTAINER="rrlelay" +TAG="rrlelay:dev" + b () { docker build \ - --no-cache \ --platform linux/arm64 \ -t "$TAG" \ --secret id=github_token,env=GITHUB_TOKEN \ @@ -23,6 +25,7 @@ r () { --platform linux/arm64 \ -v ~/.aws:/root/.aws:ro \ --volume ~/.aws-lambda-rie:/aws-lambda \ + --volume ./rrelay.sh:/var/task/hander.sh:rw \ --entrypoint /aws-lambda/aws-lambda-rie \ --env HANDLER="handler.booking" \ "$TAG" \ diff --git a/examples/rrelay.sh b/examples/rrelay.sh new file mode 100644 index 0000000..f378071 --- /dev/null +++ b/examples/rrelay.sh @@ -0,0 +1,228 @@ +#!/bin/bash + +# GLOBALS + +market="${market-"it/it"}" +ua="Mozilla/5.0 (Windows CE/6.0; ARM; fr-FR) AppleWebKit/62.0 (KHTML, like Gecko) Samsung Internet/62.0 Safari/62.0" + +# mutations +# q_create_basket='{"query":"mutation CreateBasket {\n createBasket {\n ...BasketCommon\n gettingAround {\n ...GettingAroundPillar\n }\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\nfragment VariantCar on VariantUnionType {\n ... on Car {\n rentPrice\n carName\n refId\n engineLoadId\n pickUpTime\n pickUpLocation {\n countryCode\n code\n name\n }\n dropOffTime\n dropOffLocation {\n countryCode\n code\n name\n }\n insurance\n extras {\n totalPrice\n includedInRate\n code\n price\n selected\n type\n }\n residence\n age\n }\n}\n\nfragment VariantCarRental on VariantUnionType {\n ... on CarRental {\n rentPrice\n carName\n clientId\n refId\n pickUpTime\n pickUpLocation {\n countryCode\n code\n name\n }\n dropOffTime\n dropOffLocation {\n countryCode\n code\n name\n }\n insurance\n insuranceQuoteReference\n extras {\n code\n includedInRate\n payNow\n price\n selected\n totalPrice\n type\n }\n residence\n age\n language\n searchId\n supplier\n }\n}\n\nfragment GettingAroundPillar on GettingAroundType {\n price {\n amount\n discount\n amountWithTaxes\n total\n }\n payLater {\n ...PayLaterCommon\n }\n taxes {\n amount\n }\n components {\n ...ComponentCommon\n payLater {\n amountWithTaxes\n total\n }\n variant {\n ...VariantCar\n ...VariantCarRental\n ...VariantGroundTransfer\n }\n }\n}\n\n","operationName":"CreateBasket"}' +# jq -nr --arg data "$m_create_basket" '$data' | jq -c 'keys' + +# return 0 +# ["operationName","query"] +# d_create_basket='{"data":{"createBasket":{"id":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","tripId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","dotrezSessionId":null,"currency":"","gettingThere":{"isPrime":false,"price":{"total":0},"journeys":[],"discounts":[],"taxes":[],"vouchers":[],"components":[],"messages":[]},"price":{"total":0},"payLater":{"total":0},"totalToPay":0,"gettingAround":{"price":{"amount":0,"discount":0,"amountWithTaxes":0,"total":0},"payLater":{"total":0},"taxes":[],"components":[]}}}}' + + +# createBooking +# m_create_booking='{"query":"mutation CreateBooking($basketId: String, $createBooking: CreateBookingInput!, $culture: String!) {\n createBooking(basketId: $basketId, createBooking: $createBooking, culture: $culture) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","createBooking":{"adults":1,"children":0,"infants":0,"teens":0,"flights":[{"fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","fareOption":null}],"discount":0,"promoCode":""},"culture":"it-it"},"operationName":"CreateBooking"}' +# jq -nr --arg data "$m_create_booking" '$data' | jq -c 'keys' +# {"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","createBooking":{"adults":1,"children":0,"infants":0,"teens":0,"flights":[{"fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","fareOption":null}],"discount":0,"promoCode":""},"culture":"it-it"} +# ["operationName","query","variables"] + +# header +# jwt: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJCYXNrZXRJZCI6IjJmM2UxNGZhLWM3OGEtNDkwNi1iMDIxLWQ1ZWU0MjIyZTBkYiIsIlRyaXBJZCI6IjJmM2UxNGZhLWM3OGEtNDkwNi1iMDIxLWQ1ZWU0MjIyZTBkYiIsIm5iZiI6MTc1MTU0MDgyMiwiZXhwIjoxNzUxNTQ0NzIyLCJpYXQiOjE3NTE1NDA4MjJ9.LVUmrGRDbghK4EwCfndBQX98Rdkw169Ryq51k_H_adecPDqIdInNQ4Bg9Fo0Lmmm0MzEN5q4qCdjkj5UxxPNL2wN3u5xuTnLeGqXpgE6SraRVoi2itYEnLAvKFarBwDkaHfIDLC6d5RrKa_W3x6ZsIx4ILsW7slVD83qfMVvHJGZtg9zvVUKSBqY5wwDEE6ZYWo7t2qcCA21bUbpF9DaEanya9pxR0kb3cOfWp6OIayG9kTgglfe-iyexEG0fH5EyW6dXvM2ApA4vzbLrTffldGPb4Y16oi8NNl0HDbY4EQ_1ZwbqaHMU5sD255URrFDRlc_K4fAHKTw00Vq5Pnptw + +# d_create_booking='{"data":{"createBooking":{"id":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","tripId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","dotrezSessionId":null,"currency":"EUR","gettingThere":{"isPrime":false,"price":{"total":111.99},"journeys":[{"arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","fareClass":"W","fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","fareOption":null,"flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","flightNumber":"FR7763","isConnecting":false,"isDomestic":false,"journeyNum":0,"origin":"ALC","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"","arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","hasGovernmentTax":false,"flightNumber":"FR7763","segmentNum":0,"origin":"ALC","originCountry":"ES","destinationCountry":"IE"}]}],"discounts":[],"taxes":[],"vouchers":[],"components":[{"id":"455b8555-be62-40e0-85df-ccd5cd564766","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":111.99,"total":111.99,"discount":0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":0}}],"messages":[]},"price":{"total":111.99},"payLater":{"total":0},"totalToPay":111.99}}}' +# jq -nr --arg data "$d_create_booking" '$data' | jq -c 'keys' +# ["data"] + +# CommitBooking +# m_commit_booking='{"query":"mutation CommitBooking($basketId: String!) {\n commitBooking(basketId: $basketId) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"},"operationName":"CommitBooking"}' +# jq -nr --arg data "$m_commit_booking" '$data' | jq -c 'keys' +# ["operationName","query","variables"] + +# d_commit_booking='{"data":{"commitBooking":{"id":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","tripId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","dotrezSessionId":"wzfaqb5f113wkzy0od4yf41l","currency":"EUR","gettingThere":{"isPrime":false,"price":{"total":182.98},"journeys":[{"arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","fareClass":"W","fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","fareOption":null,"flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","flightNumber":"FR7763","isConnecting":false,"isDomestic":false,"journeyNum":0,"origin":"ALC","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"738","arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","hasGovernmentTax":false,"flightNumber":"FR7763","segmentNum":0,"origin":"ALC","originCountry":"ES","destinationCountry":"IE"}]},{"arrival":"2025-10-11T20:05:00","departure":"2025-10-11T16:20:00","destination":"ALC","duration":"02:45","fareClass":"J","fareKey":"BVXTTSHSD37C5T56MHXKUSHPNQQEWBGQ65FGBUYKS7UOS4PIID2WW4YZZHYUF4JYR4DCKMKCYYJXK5NR25OUPQWTPEE4NARDMM2XYZ7KO3Z2SZKW7L5EAG2JZ4YHGCQPLWJG2WUJTUNV3YAJ2UEFB2RFHTZYGWSP7NV43Y57LY45DSFTHRHIYPUULHO4WOD3UOHN6PBLDLTVH45XPRZOP26J7O34I65J362TDZZ3ZY5JKMQNVTNTPT44L33IZ6CXDXJAFMM5DN65PLFZHBWAULNVMKNT22E7QZOIEP6WZQMEYV7Y5WQMFCSEXJJQEHJYABFPBIK42X6IACOMTUUUBLSQI5LZMJESX6NPR3I","fareOption":null,"flightKey":"FR~7762~ ~~DUB~10/11/2025 16:20~ALC~10/11/2025 20:05~~","flightNumber":"FR7762","isConnecting":false,"isDomestic":false,"journeyNum":1,"origin":"DUB","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"738","arrival":"2025-10-11T20:05:00","departure":"2025-10-11T16:20:00","destination":"ALC","duration":"02:45","hasGovernmentTax":false,"flightNumber":"FR7762","segmentNum":0,"origin":"DUB","originCountry":"IE","destinationCountry":"ES"}]}],"discounts":[{"amount":5.0000,"code":"PD","journeyNum":1,"percentage":0,"zone":null,"description":null,"qty":0}],"taxes":[],"vouchers":[],"components":[{"id":"323c0eed-6562-48e5-9eee-1d05a9759154","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":111.99,"total":111.99,"discount":0.0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":0}},{"id":"fd5e43d3-0aa0-4d37-8d7a-874f30dd12ca","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":70.99,"total":70.99,"discount":0.0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":1}},{"id":"6a9d84c4-adab-4142-bc6a-aa9130b83a3b","parentId":"323c0eed-6562-48e5-9eee-1d05a9759154","code":"SBG","type":"SMALL_BAG","quantity":1,"removable":true,"hidden":false,"price":{"amountWithTaxes":0,"total":0,"discount":0,"discountCode":null},"variant":{"journeyNumber":0,"paxNumber":0,"segmentNumber":0}},{"id":"3d9e683e-86e5-46af-91dc-ee4a79880dad","parentId":"fd5e43d3-0aa0-4d37-8d7a-874f30dd12ca","code":"SBG","type":"SMALL_BAG","quantity":1,"removable":true,"hidden":false,"price":{"amountWithTaxes":0,"total":0,"discount":0,"discountCode":null},"variant":{"journeyNumber":1,"paxNumber":0,"segmentNumber":0}}],"messages":[]},"price":{"total":182.98},"payLater":{"total":0},"totalToPay":182.98}}}' +# jq -nr --arg data "$d_commit_booking" '$data' | jq -c 'keys' +# ["data"] + + +# POST https://www.ryanair.com/api/personapi/it-it/graphql + +# GetPassengerMandatoryFields +# m_passengers_fields='{"query":"query GetPassengerMandatoryFields($searchCriteria: InputSearchCriteria) {\n passengerMandatoryFields(searchCriteria: $searchCriteria) {\n ...PassengersMandatoryFieldsResponse\n }\n}\n\nfragment PassengersMandatoryFieldsResponse on PassengersMandatoryFieldsResponse {\n passengers {\n ...PassengerMandatoryFields\n }\n}\n\nfragment PassengerMandatoryFields on PassengerMandatoryFields {\n paxNum\n type\n title\n firstName\n firstSurname\n secondSurname\n first\n last\n middle\n dob\n inf {\n ...PassengersInfantMandatoryFields\n }\n dobConstraints {\n ...PassengersDobConstraints\n }\n}\n\nfragment PassengersInfantMandatoryFields on InfantMandatoryFields {\n firstName\n firstSurname\n secondSurname\n first\n last\n middle\n dob\n}\n\nfragment PassengersDobConstraints on MandatoryDateOfBirth {\n maxAgeDays\n maxAgeYears\n maxDateOfBirth\n minAgeDays\n minAgeYears\n minDateOfBirth\n}\n","variables":{"searchCriteria":{"adults":1,"teens":0,"children":0,"infants":0,"discountPercentage":0,"departureDate":"2025-10-10"}}}' +# jq -ncr --arg m_passengers_fields "$m_passengers_fields" '$m_passengers_fields|fromjson' +# d_passengers_fields='{"data":{"passengerMandatoryFields":{"passengers":[{"paxNum":0,"type":"ADT","title":"","firstName":"","firstSurname":null,"secondSurname":"","first":"","last":"","middle":null,"dob":null,"inf":null,"dobConstraints":null}]}}}' +# jq -ncr --arg d_passengers_fields "$d_passengers_fields" '$d_passengers_fields|fromjson' + +# AddPassengers +# m_passengers_add='{"query":"mutation AddPassengers($passengers: [InputPassenger] = null, $basketId: String!) {\n addPassengers(passengers: $passengers, basketId: $basketId) {\n ...PassengersResponse\n }\n}\n\nfragment PassengersResponse on PassengersResponse {\n passengers {\n ...PassengersPassenger\n }\n}\n\nfragment PassengersPassenger on Passenger {\n paxNum\n type\n title\n firstName\n firstSurname\n secondSurname\n first\n last\n middle\n dob\n inf {\n ...PassengersInfant\n }\n specialAssistance {\n ...PassengersPassengerPrmSsrType\n }\n}\n\nfragment PassengersInfant on Infant {\n firstName\n firstSurname\n secondSurname\n first\n last\n middle\n dob\n}\n\nfragment PassengersPassengerPrmSsrType on PassengerPrmSsrType {\n codes\n journeyNum\n segmentNum\n}\n","variables":{"passengers":[{"type":"ADT","dob":null,"first":"Esther","last":"Robles Casado","middle":null,"title":"MRS","paxNum":0,"specialAssistance":[]}],"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"}}' +# jq -nc --arg m_passengers_add $m_passengers_add '$m_passengers_add|fromjson' + +# GetPassengers +# q_get_passwengers='{"query":"query GetPassengersQuery($basketId: String!) {\n passengers(basketId: $basketId) {\n ...PassengersResponse\n }\n}\n\n\nfragment PassengersInfant on Infant {\n first\n middle\n last\n dob\n}\n\nfragment PassengersPassenger on Passenger {\n type\n title\n first\n middle\n last\n paxNum\n specialAssistance {\n codes\n journeyNum\n segmentNum\n }\n inf {\n ...PassengersInfant\n }\n}\n\nfragment PassengersResponse on PassengersResponse {\n passengers {\n ...PassengersPassenger\n }\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"}}' +# jq -ncr --arg q_get_passwengers "$q_get_passwengers" '$q_get_passwengers|fromjson' +# d_get_passengers='{"data":{"passengers":{"passengers":[{"type":"ADT","title":"MRS","first":"Esther","middle":"","last":"Robles Casado","paxNum":0,"specialAssistance":[],"inf":null}]}}}' +# jq -nrc --arg d_get_passengers "$d_get_passengers" '$d_get_passengers|fromjson' +# {"data":{"passengers":{"passengers":[{"type":"ADT","title":"MRS","first":"Esther","middle":"","last":"Robles Casado","paxNum":0,"specialAssistance":[],"inf":null}]}}} + +# GetSeatsQuery +# q_get_seats='{"query":"query GetSeatsQuery($basketId: String!) {\n seats(basketId: $basketId) {\n ...SeatsResponse\n }\n}\n\nfragment SeatsResponse on SeatAvailability {\n equipmentModel\n groups {\n fares {\n discount\n originalPrice\n paxNum\n price\n strikeThroughPrice\n }\n group\n type\n }\n groupsMinPrices {\n available\n fares {\n discount\n originalPrice\n paxNum\n price\n }\n firstRow\n type\n }\n includedSeatRows {\n includedRanges {\n end\n start\n }\n paxNum\n primeIncluded\n }\n journeyNum\n prmCompanions {\n paxNum\n }\n prmSeats {\n paxNum\n seats\n }\n seatsOffer {\n price\n seats {\n paxNum\n seat\n }\n }\n segmentNum\n unavailableSeats\n}\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"}}' +# jq -ncr --arg q_get_seats $q_get_seats '$q_get_seats|fromjson' +# d_get_seats='{"data":{"seats":[{"equipmentModel":"738","groups":[{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":30.50,"strikeThroughPrice":null}],"group":1,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":30.00,"strikeThroughPrice":null}],"group":13,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":19.50,"strikeThroughPrice":null}],"group":2,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":19.00,"strikeThroughPrice":null}],"group":14,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":18.50,"strikeThroughPrice":null}],"group":3,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":18.00,"strikeThroughPrice":null}],"group":15,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":17.50,"strikeThroughPrice":null}],"group":4,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":17.00,"strikeThroughPrice":null}],"group":16,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.50,"strikeThroughPrice":null}],"group":5,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.00,"strikeThroughPrice":null}],"group":17,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":15.50,"strikeThroughPrice":null}],"group":6,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":15.00,"strikeThroughPrice":null}],"group":18,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":14.50,"strikeThroughPrice":null}],"group":7,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":14.00,"strikeThroughPrice":null}],"group":19,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.50,"strikeThroughPrice":null}],"group":8,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.00,"strikeThroughPrice":null}],"group":20,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.50,"strikeThroughPrice":null}],"group":9,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.00,"strikeThroughPrice":null}],"group":21,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.50,"strikeThroughPrice":null}],"group":10,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.00,"strikeThroughPrice":null}],"group":22,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.50,"strikeThroughPrice":null}],"group":11,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.00,"strikeThroughPrice":null}],"group":23,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.50,"strikeThroughPrice":null}],"group":12,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.00,"strikeThroughPrice":null}],"group":24,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.50,"strikeThroughPrice":null}],"group":26,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.00,"strikeThroughPrice":null}],"group":30,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.50,"strikeThroughPrice":null}],"group":27,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.00,"strikeThroughPrice":null}],"group":31,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.50,"strikeThroughPrice":null}],"group":28,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.00,"strikeThroughPrice":null}],"group":32,"type":"STANDARD"}],"groupsMinPrices":[{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.50}],"firstRow":16,"type":"EXTRALEG"},{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.00}],"firstRow":6,"type":"PRIORITY"},{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.00}],"firstRow":21,"type":"STANDARD"}],"includedSeatRows":[{"includedRanges":[],"paxNum":0,"primeIncluded":false}],"journeyNum":0,"prmCompanions":[],"prmSeats":[],"seatsOffer":{"price":10.00,"seats":[{"paxNum":0,"seat":"24D"}]},"segmentNum":0,"unavailableSeats":["01A","01B","02A","02B","02C","02D","02E","02F","03D","03E","04A","04B","04C","05A","05B","06C","06D","06E","06F","07C","07D","11E","11F","14E","14F","15C","15D","16B","16C","16D","16E","17A","17B","17C","17D","17E","17F","18D","18E","18F","19D","21A","21B","21E","21F","22B","22C","27E","27F","29C","29D","29F","30A","30B","30C","30D","31B","31C","32A","32B"]},{"equipmentModel":"738","groups":[{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":30.50,"strikeThroughPrice":null}],"group":1,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":30.00,"strikeThroughPrice":null}],"group":13,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":19.50,"strikeThroughPrice":null}],"group":2,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":19.00,"strikeThroughPrice":null}],"group":14,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":18.50,"strikeThroughPrice":null}],"group":3,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":18.00,"strikeThroughPrice":null}],"group":15,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":17.50,"strikeThroughPrice":null}],"group":4,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":17.00,"strikeThroughPrice":null}],"group":16,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.50,"strikeThroughPrice":null}],"group":5,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.00,"strikeThroughPrice":null}],"group":17,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":15.50,"strikeThroughPrice":null}],"group":6,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":15.00,"strikeThroughPrice":null}],"group":18,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":14.50,"strikeThroughPrice":null}],"group":7,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":14.00,"strikeThroughPrice":null}],"group":19,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.50,"strikeThroughPrice":null}],"group":8,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.00,"strikeThroughPrice":null}],"group":20,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.50,"strikeThroughPrice":null}],"group":9,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.00,"strikeThroughPrice":null}],"group":21,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.50,"strikeThroughPrice":null}],"group":10,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.00,"strikeThroughPrice":null}],"group":22,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.50,"strikeThroughPrice":null}],"group":11,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.00,"strikeThroughPrice":null}],"group":23,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.50,"strikeThroughPrice":null}],"group":12,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.00,"strikeThroughPrice":null}],"group":24,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.50,"strikeThroughPrice":null}],"group":26,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.00,"strikeThroughPrice":null}],"group":30,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.50,"strikeThroughPrice":null}],"group":27,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.00,"strikeThroughPrice":null}],"group":31,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.50,"strikeThroughPrice":null}],"group":28,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.00,"strikeThroughPrice":null}],"group":32,"type":"STANDARD"}],"groupsMinPrices":[{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.00}],"firstRow":16,"type":"EXTRALEG"},{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.00}],"firstRow":6,"type":"PRIORITY"},{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.00}],"firstRow":21,"type":"STANDARD"}],"includedSeatRows":[{"includedRanges":[],"paxNum":0,"primeIncluded":false}],"journeyNum":1,"prmCompanions":[],"prmSeats":[],"seatsOffer":{"price":10.00,"seats":[{"paxNum":0,"seat":"24D"}]},"segmentNum":0,"unavailableSeats":["03C","04A","09D","09E","11E","11F","15D","18A","18B","18C","18D","18E","19B","19C","19D","19F","20A","20B","20C","20D","20E","20F","21B","21C","21D","21E","22A","22B","22C","22D","23A","23B","23C","23D","24A","24B","24C","24E","24F","25A","25B","25C","25D","25E","25F","26A","26B","26D","26E","26F","27A","27B","27E","27F","31A","32C","32D","33A","33B","33C","33D","33E"]}]}}' +# jq -ncr --arg d_get_seats $d_get_seats '$d_get_seats|fromjson' + +# FligthExtrasQuery +# q_flight_extras='{"variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"},"query":"query FligthExtrasQuery($basketId: String!) {\n flightExtras(basketId: $basketId, products: [FAST, SEATS]) {\n ...ExtraFrag\n }\n}\n\nfragment ExtraFrag on FlightExtra {\n code\n maxPerPassenger\n minPrice\n priceDetails {\n journeyNumber\n }\n}\n"}' +# jq -ncr --arg q_flight_extras "$q_flight_extras" '$q_flight_extras|fromjson' +# d_flight_extras='{"data":{"flightExtras":[{"code":"SEATS","maxPerPassenger":null,"minPrice":10,"priceDetails":[{"journeyNumber":0},{"journeyNumber":1}]},{"code":"FAST","maxPerPassenger":null,"minPrice":9.99,"priceDetails":[{"journeyNumber":0},{"journeyNumber":1}]}]}}' + +# AssignSeat +# m_assign_seats='{"query":"mutation AssignSeat($basketId: String!, $seats: [SeatInputType]!) {\n assignSeat(basketId: $basketId, seats: $seats) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","seats":[{"journeyNum":0,"segmentNum":0,"paxNum":0,"designator":""},{"journeyNum":1,"segmentNum":0,"paxNum":0,"designator":""}]},"operationName":"AssignSeat"}' +# jq -ncr --arg m_assign_seats "$m_assign_seats" '$m_assign_seats|fromjson' +# d_assign_seats='{"data":{"assignSeat":{"id":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","tripId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","dotrezSessionId":"wzfaqb5f113wkzy0od4yf41l","currency":"EUR","gettingThere":{"isPrime":false,"price":{"total":182.98},"journeys":[{"arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","fareClass":"W","fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","fareOption":null,"flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","flightNumber":"FR7763","isConnecting":false,"isDomestic":false,"journeyNum":0,"origin":"ALC","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"738","arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","hasGovernmentTax":false,"flightNumber":"FR7763","segmentNum":0,"origin":"ALC","originCountry":"ES","destinationCountry":"IE"}]},{"arrival":"2025-10-11T20:05:00","departure":"2025-10-11T16:20:00","destination":"ALC","duration":"02:45","fareClass":"J","fareKey":"BVXTTSHSD37C5T56MHXKUSHPNQQEWBGQ65FGBUYKS7UOS4PIID2WW4YZZHYUF4JYR4DCKMKCYYJXK5NR25OUPQWTPEE4NARDMM2XYZ7KO3Z2SZKW7L5EAG2JZ4YHGCQPLWJG2WUJTUNV3YAJ2UEFB2RFHTZYGWSP7NV43Y57LY45DSFTHRHIYPUULHO4WOD3UOHN6PBLDLTVH45XPRZOP26J7O34I65J362TDZZ3ZY5JKMQNVTNTPT44L33IZ6CXDXJAFMM5DN65PLFZHBWAULNVMKNT22E7QZOIEP6WZQMEYV7Y5WQMFCSEXJJQEHJYABFPBIK42X6IACOMTUUUBLSQI5LZMJESX6NPR3I","fareOption":null,"flightKey":"FR~7762~ ~~DUB~10/11/2025 16:20~ALC~10/11/2025 20:05~~","flightNumber":"FR7762","isConnecting":false,"isDomestic":false,"journeyNum":1,"origin":"DUB","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"738","arrival":"2025-10-11T20:05:00","departure":"2025-10-11T16:20:00","destination":"ALC","duration":"02:45","hasGovernmentTax":false,"flightNumber":"FR7762","segmentNum":0,"origin":"DUB","originCountry":"IE","destinationCountry":"ES"}]}],"discounts":[{"amount":5.0000,"code":"PD","journeyNum":1,"percentage":0,"zone":null,"description":null,"qty":0}],"taxes":[],"vouchers":[],"components":[{"id":"323c0eed-6562-48e5-9eee-1d05a9759154","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":111.99,"total":111.99,"discount":0.0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":0}},{"id":"fd5e43d3-0aa0-4d37-8d7a-874f30dd12ca","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":70.99,"total":70.99,"discount":0.0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":1}},{"id":"6a9d84c4-adab-4142-bc6a-aa9130b83a3b","parentId":"323c0eed-6562-48e5-9eee-1d05a9759154","code":"SBG","type":"SMALL_BAG","quantity":1,"removable":true,"hidden":false,"price":{"amountWithTaxes":0,"total":0,"discount":0,"discountCode":null},"variant":{"journeyNumber":0,"paxNumber":0,"segmentNumber":0}},{"id":"3d9e683e-86e5-46af-91dc-ee4a79880dad","parentId":"fd5e43d3-0aa0-4d37-8d7a-874f30dd12ca","code":"SBG","type":"SMALL_BAG","quantity":1,"removable":true,"hidden":false,"price":{"amountWithTaxes":0,"total":0,"discount":0,"discountCode":null},"variant":{"journeyNumber":1,"paxNumber":0,"segmentNumber":0}}],"messages":[]},"price":{"total":182.98},"payLater":{"total":0},"totalToPay":182.98}}}' + +# ProductQuery +# q_products='{"query":"query Products($basketId: String!, $productQuery: ProductQuery!, $isCheckInFlow: Boolean!) {\n products(basketId: $basketId, productQuery: $productQuery) {\n bags: bag {\n ...BagFrag\n }\n equipments: equipment {\n ...EquipmentFrag\n }\n flightExtras: flightExtra {\n ...ExtraFrag\n }\n priorityBoarding {\n ...PriorityBoardingFrag\n }\n }\n excessBag(basketId: $basketId, isCheckInFlow: $isCheckInFlow) {\n code\n price\n paxType\n paxNum\n journeyNum\n maxPerPassenger\n maxPerBag\n }\n}\n\nfragment BagFrag on Bag {\n journeyNum\n maxPerPassenger\n offers {\n ...BagOfferFrag\n }\n}\n\nfragment BagOfferFrag on BagOffer {\n code\n maxPerPassenger\n price {\n ...BagOfferPriceFrag\n }\n}\n\nfragment BagOfferPriceFrag on BagOfferPrice {\n discountPercentage\n discountType\n originalPrice\n paxType\n total\n totalDiscount\n strikeThrough\n}\n\nfragment EquipmentFrag on Equipment {\n journeyNum\n offers {\n code\n type\n maxPerPassenger\n availableItems\n prices {\n paxType\n total\n }\n }\n}\n\nfragment ExtraFrag on FlightExtra {\n code\n discountPercentage\n discountType\n maxPerPassenger\n minOriginalPrice\n minPrice\n totalDiscount\n priceDetails {\n journeyNumber\n segmentNumber\n minPrice\n minOriginalPrice\n discountType\n totalDiscount\n dsc\n }\n}\n\nfragment PriorityBoardingFrag on PriorityBoarding {\n code\n price\n paxType\n journeyNumber\n segmentNumber\n strikeThrough\n}\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","productQuery":{"productTypes":["PRIOBRDNG","CBAG","BAGS","EQUIPMENT"]},"isCheckInFlow":false}}' +# jq -ncr --arg q_products "$q_products" '$q_products' +# d_products='{"data":{"products":{"bags":[{"journeyNum":0,"maxPerPassenger":3,"offers":[{"code":"BBG","maxPerPassenger":3,"price":[{"discountPercentage":null,"discountType":"None","originalPrice":32.49,"paxType":"ADT","total":32.49,"totalDiscount":0.0,"strikeThrough":null}]},{"code":"CBAG","maxPerPassenger":1,"price":[{"discountPercentage":null,"discountType":"None","originalPrice":22.49,"paxType":"ADT","total":22.49,"totalDiscount":0.0,"strikeThrough":null}]}]},{"journeyNum":1,"maxPerPassenger":3,"offers":[{"code":"BBG","maxPerPassenger":3,"price":[{"discountPercentage":null,"discountType":"None","originalPrice":28.34,"paxType":"ADT","total":28.34,"totalDiscount":0.0,"strikeThrough":null}]},{"code":"CBAG","maxPerPassenger":1,"price":[{"discountPercentage":null,"discountType":"None","originalPrice":15.74,"paxType":"ADT","total":15.74,"totalDiscount":0.0,"strikeThrough":null}]}]}],"equipments":[{"journeyNum":0,"offers":[{"code":"BABY","type":"BABY","maxPerPassenger":3,"availableItems":-1,"prices":[{"paxType":"ADT","total":15.0}]},{"code":"GOLF","type":"SPORTS","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":45.0}]},{"code":"SPRT","type":"SPORTS","maxPerPassenger":2,"availableItems":189,"prices":[{"paxType":"ADT","total":45.0}]},{"code":"SKI","type":"SPORTS","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":45.0}]},{"code":"BULK","type":"SPORTS","maxPerPassenger":2,"availableItems":20,"prices":[{"paxType":"ADT","total":60.0}]},{"code":"BIKE","type":"SPORTS","maxPerPassenger":2,"availableItems":20,"prices":[{"paxType":"ADT","total":60.0}]},{"code":"MUSC","type":"MUSIC","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":60.0}]}]},{"journeyNum":1,"offers":[{"code":"BABY","type":"BABY","maxPerPassenger":3,"availableItems":-1,"prices":[{"paxType":"ADT","total":15.0}]},{"code":"GOLF","type":"SPORTS","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":40.0}]},{"code":"SPRT","type":"SPORTS","maxPerPassenger":2,"availableItems":189,"prices":[{"paxType":"ADT","total":40.0}]},{"code":"SKI","type":"SPORTS","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":45.0}]},{"code":"BULK","type":"SPORTS","maxPerPassenger":2,"availableItems":20,"prices":[{"paxType":"ADT","total":60.0}]},{"code":"BIKE","type":"SPORTS","maxPerPassenger":2,"availableItems":20,"prices":[{"paxType":"ADT","total":60.0}]},{"code":"MUSC","type":"MUSIC","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":60.0}]}]}],"flightExtras":[{"code":"BAGS","discountPercentage":0,"discountType":"None","maxPerPassenger":3,"minOriginalPrice":0,"minPrice":28.34,"totalDiscount":0,"priceDetails":[{"journeyNumber":0,"segmentNumber":null,"minPrice":32.49,"minOriginalPrice":0,"discountType":"None","totalDiscount":0,"dsc":null},{"journeyNumber":1,"segmentNumber":null,"minPrice":28.34,"minOriginalPrice":0,"discountType":"None","totalDiscount":0,"dsc":null}]},{"code":"CBAG","discountPercentage":0,"discountType":"None","maxPerPassenger":null,"minOriginalPrice":15.74,"minPrice":15.74,"totalDiscount":0,"priceDetails":[{"journeyNumber":0,"segmentNumber":null,"minPrice":22.49,"minOriginalPrice":22.49,"discountType":"None","totalDiscount":0,"dsc":null},{"journeyNumber":1,"segmentNumber":null,"minPrice":15.74,"minOriginalPrice":15.74,"discountType":"None","totalDiscount":0,"dsc":null}]},{"code":"EQUIPMENT","discountPercentage":0,"discountType":"None","maxPerPassenger":15,"minOriginalPrice":0,"minPrice":15,"totalDiscount":0,"priceDetails":[{"journeyNumber":0,"segmentNumber":null,"minPrice":15,"minOriginalPrice":null,"discountType":"None","totalDiscount":0,"dsc":null},{"journeyNumber":1,"segmentNumber":null,"minPrice":15,"minOriginalPrice":null,"discountType":"None","totalDiscount":0,"dsc":null}]},{"code":"PRIOBRDNG","discountPercentage":0,"discountType":"None","maxPerPassenger":null,"minOriginalPrice":20.25,"minPrice":20.25,"totalDiscount":0,"priceDetails":[{"journeyNumber":0,"segmentNumber":null,"minPrice":21.5,"minOriginalPrice":21.5,"discountType":"None","totalDiscount":0,"dsc":null},{"journeyNumber":1,"segmentNumber":null,"minPrice":20.25,"minOriginalPrice":20.25,"discountType":"None","totalDiscount":0,"dsc":null}]}],"priorityBoarding":[{"code":"PS","price":21.5,"paxType":"ADT","journeyNumber":0,"segmentNumber":0,"strikeThrough":23.5},{"code":"PS","price":20.25,"paxType":"ADT","journeyNumber":1,"segmentNumber":0,"strikeThrough":22.05}]},"excessBag":[]}}' + +function dashed_market() { + local market=${1-${market}} + local part1=$(echo "$market" | cut -d'/' -f2) + local part2=$(echo "$market" | cut -d'/' -f1) + echo "$part1-$part2" +} + +function availability () { + local req="$1" + local market="${2:-$market}" + local url=$(availability_url "$req" $market) + local correlation_id=$(uuidgen | tr '[:upper:]' '[:lower:]') + http-cli --url "$url" \ + --cookie "fr-correlation-id=$correlation_id" \ + --user-agent "$ua" +} + +function availability_url () { + local request=${1:-$(cat)} + local market="${2:-$market}" + local dashedMkt=$(dashed_market $market) + local adults=$(echo $request | jq -r '.adults') + local children=$(echo $request | jq -r '.children') + local teen=0 + local infants=$(echo $request | jq -r '.infants') + local DateOut=$(echo $request | jq -r '.dateOut') + local DateIn=$(echo $request | jq -r 'if .dateIn then .dateIn else "" end') + local Origin=$(echo $request | jq -r '.origin') + local Destination=$(echo $request | jq -r '.destination') + local RoundTrip + local IncludePrimeFares="false" + if [ -n "$DateIn" ]; then + RoundTrip="true" + else + RoundTrip="false" + fi + local url="https://www.ryanair.com/api/booking/v4/${dashedMkt}/availability?ADT=${adults}&TEEN=${teen}&CHD=${children}&INF=${infants}&Origin=${Origin}&Destination=${Destination}&promoCode=&IncludeConnectingFlights=false&DateOut=${DateOut}&DateIn=${DateIn}&FlexDaysBeforeOut=2&FlexDaysOut=2&FlexDaysBeforeIn=2&FlexDaysIn=2&RoundTrip=${RoundTrip}&IncludePrimeFares=${IncludePrimeFares}&ToUs=AGREED" + echo "$url" +} + +function booking_variables () { + local basketID="$1" + local request="$2" + local fligtht="$3" + local variables=$(jq -nc --arg basketId "$basketID" --argjson request "$request" --argjson flight "$flight" --arg culture "$culture" \ + '{ basketId: $basketId } + + { createBooking: ($request | { adults, children, infants, teens } + + { flights: [ { + flightKey: $flight.flightKey, + fareKey: $flight.regularFare.fareKey, + fareOption: null } ], + discount: 0, + promoCode: "" + }), + culture: $culture + }') + echo "$variables" +} + +function create_basket () { + local url="https://www.ryanair.com/api/basketapi/it-it/graphql" + local m_create_basket='{"query":"mutation CreateBasket {\n createBasket {\n ...BasketCommon\n gettingAround {\n ...GettingAroundPillar\n }\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\nfragment VariantCar on VariantUnionType {\n ... on Car {\n rentPrice\n carName\n refId\n engineLoadId\n pickUpTime\n pickUpLocation {\n countryCode\n code\n name\n }\n dropOffTime\n dropOffLocation {\n countryCode\n code\n name\n }\n insurance\n extras {\n totalPrice\n includedInRate\n code\n price\n selected\n type\n }\n residence\n age\n }\n}\n\nfragment VariantCarRental on VariantUnionType {\n ... on CarRental {\n rentPrice\n carName\n clientId\n refId\n pickUpTime\n pickUpLocation {\n countryCode\n code\n name\n }\n dropOffTime\n dropOffLocation {\n countryCode\n code\n name\n }\n insurance\n insuranceQuoteReference\n extras {\n code\n includedInRate\n payNow\n price\n selected\n totalPrice\n type\n }\n residence\n age\n language\n searchId\n supplier\n }\n}\n\nfragment GettingAroundPillar on GettingAroundType {\n price {\n amount\n discount\n amountWithTaxes\n total\n }\n payLater {\n ...PayLaterCommon\n }\n taxes {\n amount\n }\n components {\n ...ComponentCommon\n payLater {\n amountWithTaxes\n total\n }\n variant {\n ...VariantCar\n ...VariantCarRental\n ...VariantGroundTransfer\n }\n }\n}\n\n","operationName":"CreateBasket"}' + http-cli \ + --header 'accept: application/json, text/plain, */*' \ + --user-agent "$ua" \ + --cookie "fr-correlation-id=$correlation_id" \ + --url "$url" \ + --data "$m_create_basket" +} + +function create_booking () { + local variables=$(echo "$1" | jq -c) + # { + # "basketId": "2f3e14fa-c78a-4906-b021-d5ee4222e0db", + # "createBooking": { + # "adults": 1, + # "children": 0, + # "infants": 0, + # "teens": 0, + # "flights": [ + # { + # "fareKey": "2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A", + # "flightKey": "FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~", + # "fareOption": null + # } + # ], + # "discount": 0, + # "promoCode": "" + # }, + # "culture": "it-it" + # } + local url="https://www.ryanair.com/api/basketapi/it-it/graphql" + local m_create_booking='{"query":"mutation CreateBooking($basketId: String, $createBooking: CreateBookingInput!, $culture: String!) {\n createBooking(basketId: $basketId, createBooking: $createBooking, culture: $culture) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","createBooking":{"adults":1,"children":0,"infants":0,"teens":0,"flights":[{"fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","fareOption":null}],"discount":0,"promoCode":""},"culture":"it-it"},"operationName":"CreateBooking"}' + local data=$( + jq -rcn --arg m_create_booking "$m_create_booking" '$m_create_booking|fromjson' \ + | jq -rc --arg variables "$variables" '. + { variables: $variables|fromjson }' + ) + local output=$(http-cli \ + --silent \ + --user-agent "$ua" \ + --header 'accept: application/json, text/plain, */*' \ + --cookie "fr-correlation-id=$correlation_id" \ + --data "$data" \ + --url "$url" \ + -- -D "$JWT_HANDLER" + ) + export JWT=$(cat "$JWT_HANDLER" | grep 'jwt' | awk '{ print $2 }') + echo "$output" | jq + # echo "$JWT" >&2 +} + +function commit_booking () { + local basketId=${1:-$(cat)} + local url="https://www.ryanair.com/api/basketapi/it-it/graphql" + local m_commit_booking='{"query":"mutation CommitBooking($basketId: String!) {\n commitBooking(basketId: $basketId) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"},"operationName":"CommitBooking"}' + local data=$(jq -nrc --arg m_commit_booking "$m_commit_booking" '$m_commit_booking|fromjson' | jq -c --arg basketId "$basketId" '.variables.basketId|=$basketId') + echo "$data" | jq >&2 + http-cli --silent \ + --url "$url" \ + --header "authorization: Bearer $JWT" \ + --header 'accept: application/json, text/plain, */*' \ + --user-agent "$ua" \ + --cookie "fr-correlation-id=$correlation_id" \ + --data "$data" +} + +function catalog_products () { + local bookingId="$1" + local jwt="${2:-${JWT}}" + local url="https://www.ryanair.com/api/catalogapi/it-it/graphql" + local q_products='{"query":"query Products($basketId: String!, $productQuery: ProductQuery!, $isCheckInFlow: Boolean!) {\n products(basketId: $basketId, productQuery: $productQuery) {\n bags: bag {\n ...BagFrag\n }\n equipments: equipment {\n ...EquipmentFrag\n }\n flightExtras: flightExtra {\n ...ExtraFrag\n }\n priorityBoarding {\n ...PriorityBoardingFrag\n }\n }\n excessBag(basketId: $basketId, isCheckInFlow: $isCheckInFlow) {\n code\n price\n paxType\n paxNum\n journeyNum\n maxPerPassenger\n maxPerBag\n }\n}\n\nfragment BagFrag on Bag {\n journeyNum\n maxPerPassenger\n offers {\n ...BagOfferFrag\n }\n}\n\nfragment BagOfferFrag on BagOffer {\n code\n maxPerPassenger\n price {\n ...BagOfferPriceFrag\n }\n}\n\nfragment BagOfferPriceFrag on BagOfferPrice {\n discountPercentage\n discountType\n originalPrice\n paxType\n total\n totalDiscount\n strikeThrough\n}\n\nfragment EquipmentFrag on Equipment {\n journeyNum\n offers {\n code\n type\n maxPerPassenger\n availableItems\n prices {\n paxType\n total\n }\n }\n}\n\nfragment ExtraFrag on FlightExtra {\n code\n discountPercentage\n discountType\n maxPerPassenger\n minOriginalPrice\n minPrice\n totalDiscount\n priceDetails {\n journeyNumber\n segmentNumber\n minPrice\n minOriginalPrice\n discountType\n totalDiscount\n dsc\n }\n}\n\nfragment PriorityBoardingFrag on PriorityBoarding {\n code\n price\n paxType\n journeyNumber\n segmentNumber\n strikeThrough\n}\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","productQuery":{"productTypes":["PRIOBRDNG","CBAG","BAGS","EQUIPMENT"]},"isCheckInFlow":false}}' + local q_products_json=$(jq -ncr --arg q_products "$q_products" '$q_products|fromjson') + # echo "bookingId:$bookingId" >&2 + echo "$q_products_json" | jq >&2 + local data=$(jq -n --arg bookingId "$bookingId" \ + --argjson query "$q_products_json" ' + $query + { + variables: ($query.variables + {basketId: $bookingId}) + }') + echo "$data" | jq >&2 + http-cli \ + --silent \ + --url "$url" \ + --header "authorization: Bearer $jwt" \ + --user-agent "$ua" \ + --header 'accept: application/json, text/plain, */*' \ + --cookie "fr-correlation-id=$correlation_id" \ + --data "$data" +} diff --git a/examples/run b/examples/run new file mode 100755 index 0000000..207ce24 --- /dev/null +++ b/examples/run @@ -0,0 +1,32 @@ +#!/bin/bash + +source $(realpath $(dirname $0))/rrelay.sh + +request='{"adults":1,"children":1,"infants":1,"teens":0,"origin":"NAP","destination":"BCN","dateOut":"2025-07-13"}' +echo $request >&2 +correlation_id=$(uuidgen | tr '[:upper:]' '[:lower:]') +# export USER_AGENT=$(randua) + + +departureDate=$(echo "$request" | jq -r '.dateOut +"T00:00:00.000"') +# echo "$departureDate" >&2 + +flight=$(availability "$request" \ + | jq --arg dateOut "$departureDate" '.trips[].dates[]|select(.dateOut == $dateOut).flights|first|del(.segments)' +) +# {"faresLeft":4,"flightKey":"FR~ 59~ ~~NAP~07/10/2025 15:40~BCN~07/10/2025 17:40~~","infantsLeft":16,"regularFare":{"fareKey":"VES4L3AY6V7QCEKP25YZQOZOZFWRY6UDQRC75USHLWWP46O6ECSN3JO4DDEUY7PI6ZAYZLISSNP4PT65R2P2ARTBKRII4CWGMYQKF4XU5WT37LIAC6LWDC332SPT64QR3GVVX53FTHOJNJMUUVQHXIUMHUVE6N3XE3O7NOLV4B4LZPUNAUC6U7O3ZSFR246HBE4CAXKCCXWOHC45UGVSGFR4WCXUGBKVKW54IAFFT6EXQYIWSKSFUHLX2ESS3AYRWPRKDLH22KKRDX7HDT4XRZEEJZMLX5BS2WQ6M5ORROOZJ3FMAWVPPDOP3GY742C2OZDU5IEDFB3YAFOJPHSTPDAIG3IW6YDTHMNCHGI","fares":[{"type":"ADT","amount":197.99,"count":1,"hasDiscount":false,"publishedFare":197.99,"discountInPercent":0,"hasPromoDiscount":false,"discountAmount":0.0,"hasBogof":false,"isPrime":false}]},"operatedBy":"","isSSIMLoad":false,"flightNumber":"FR 59","time":["2025-07-10T15:40:00.000","2025-07-10T17:40:00.000"],"timeUTC":["2025-07-10T13:40:00.000Z","2025-07-10T15:40:00.000Z"],"duration":"02:00"} +echo "$flight" | jq -c >&2 + +basketID=$(create_basket | jq -r '.data.createBasket.id') +# echo "basketId:$basketID" >&2 +culture=$(dashed_market "$market") +# echo "$culture" >&2 +variables=$(booking_variables "$basketID" "$request" "$flight") +# echo "$variables" | jq >&2 +export JWT_HANDLER=$(mktemp) +create_booking "$variables" +echo "$JWT" >&2 +booking=$(commit_booking "$basketID" | jq .data.commitBooking) +echo "$booking" | jq >&2 +data=$(catalog_products "$basketID" "$JWT") +echo "$data" | jq >&2 \ No newline at end of file diff --git a/functions/handler.sh b/functions/handler.sh deleted file mode 100644 index 7f72d48..0000000 --- a/functions/handler.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -function hello() { - EVENT="$1" - echo "$EVENT" \ - | jq -} \ No newline at end of file diff --git a/micro.Dockerfile b/micro.Dockerfile index 981dcb8..ec0eddc 100644 --- a/micro.Dockerfile +++ b/micro.Dockerfile @@ -50,4 +50,4 @@ RUN chmod +x /var/runtime/bootstrap WORKDIR /var/task -COPY functions/handler.sh handler.sh +COPY task/handler.sh handler.sh diff --git a/runtime/Dockerfile b/runtime/Dockerfile deleted file mode 100644 index 280b7a6..0000000 --- a/runtime/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM public.ecr.aws/lambda/provided:al2023 - -# Set the working directory -WORKDIR /var/task - -# Copy bootstrap and handler files -COPY bootstrap /var/runtime/bootstrap -COPY handler.sh /var/task/handler.sh - -# Ensure bootstrap is executable -RUN chmod +x /var/runtime/bootstrap - -# Optional: copy additional CLI tools or libraries -# COPY bin/ /var/task/bin/ -# ENV PATH="/var/task/bin:${PATH}" - -# Set Lambda bootstrap as entrypoint -ENTRYPOINT ["/var/runtime/bootstrap"] diff --git a/runtime/bootstrap b/runtime/bootstrap index b6708fc..b8f1bd3 100755 --- a/runtime/bootstrap +++ b/runtime/bootstrap @@ -19,14 +19,11 @@ do REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) # Run the handler function from the script - # set -x RESPONSE=$($(echo "$HANDLER" | cut -d. -f2) "$EVENT_DATA") - # set +x + echo "$RESPONSE" | curl -sS -X POST \ "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${REQUEST_ID}/response" \ -H "Content-Type: application/json" \ -d @- - -# --data-binary "$RESPONSE" done \ No newline at end of file diff --git a/runtime/handler.sh b/runtime/handler.sh deleted file mode 100644 index ab41f35..0000000 --- a/runtime/handler.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -echo "Default handler โ€” override this in your runtime build context." >&2 -exit 1 \ No newline at end of file diff --git a/slim.Dockerfile b/slim.Dockerfile index 8589f61..483db66 100644 --- a/slim.Dockerfile +++ b/slim.Dockerfile @@ -38,6 +38,6 @@ RUN chmod +x /var/runtime/bootstrap WORKDIR /var/task -COPY functions/handler.sh handler.sh +COPY task/handler.sh handler.sh diff --git a/task/handler.sh b/task/handler.sh new file mode 100644 index 0000000..68c9630 --- /dev/null +++ b/task/handler.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +function f () { + EVENT="$1" + echo "$EVENT" | jq + echo "Default handler โ€” override this in your runtime build context." >&2 + exit 1 +} \ No newline at end of file diff --git a/test/run b/test/run index 23449b5..6a59b9c 100644 --- a/test/run +++ b/test/run @@ -1,38 +1,15 @@ #!/bin/sh # Setup the AWS Lambda Runtime Interface Emulator +# https://github.com/aws/aws-lambda-runtime-interface-emulator/ mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \ https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64 \ && chmod +x ~/.aws-lambda-rie/aws-lambda-rie # Start the Lambda shell container -docker run \ - -p 9000:8080 \ - --platform linux/arm64 \ - -v ~/.aws:/root/.aws:ro \ - --env AWS_PROFILE=${AWS_PROFILE} \ - --env AWS_REGION=${AWS_REGION} \ - --volume ~/.aws-lambda-rie:/aws-lambda \ - --entrypoint /aws-lambda/aws-lambda-rie \ - --env HANDLER="handler.hello" \ - lambda-shell-runtime:debug-tiny \ - /var/runtime/bootstrap - -docker run \ - -p 9000:8080 \ - --platform linux/arm64 \ - -v ~/.aws:/root/.aws:ro \ - --env AWS_PROFILE=${AWS_PROFILE} \ - --env AWS_REGION=${AWS_REGION} \ - --volume ~/.aws-lambda-rie:/aws-lambda \ - --entrypoint /aws-lambda/aws-lambda-rie \ - --env HANDLER="handler.hello" \ - lambda-shell-runtime:debug-tiny \ - /var/runtime/bootstrap - - docker run -d --rm \ -p 9000:8080 \ + --name lambda-shell-runtime \ --platform linux/arm64 \ -v ~/.aws:/root/.aws:ro \ --volume ~/.aws-lambda-rie:/aws-lambda \ diff --git a/tiny.Dockerfile b/tiny.Dockerfile index 416bddd..3f86d4f 100644 --- a/tiny.Dockerfile +++ b/tiny.Dockerfile @@ -31,4 +31,4 @@ RUN chmod +x /var/runtime/bootstrap WORKDIR /var/task -COPY functions/handler.sh handler.sh +COPY task/handler.sh handler.sh From 53a49a71b50929829003947d5bfcf2203710b099 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Thu, 10 Jul 2025 18:19:05 +0200 Subject: [PATCH 23/58] chore: fix FROM statment in examples Dockerfile --- examples/Dockerfile | 2 +- examples/run | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/Dockerfile b/examples/Dockerfile index 95d8249..5f79881 100644 --- a/examples/Dockerfile +++ b/examples/Dockerfile @@ -1,5 +1,5 @@ # FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.10-full -FROM lambda-shell-runtime:micro +# FROM lambda-shell-runtime:micro # FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.10-slim FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.11-tiny diff --git a/examples/run b/examples/run index 207ce24..d01d55a 100755 --- a/examples/run +++ b/examples/run @@ -7,7 +7,6 @@ echo $request >&2 correlation_id=$(uuidgen | tr '[:upper:]' '[:lower:]') # export USER_AGENT=$(randua) - departureDate=$(echo "$request" | jq -r '.dateOut +"T00:00:00.000"') # echo "$departureDate" >&2 From ca1bd8e661a0eb121d7b51b64f6db86169a06027 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Fri, 11 Jul 2025 16:23:28 +0200 Subject: [PATCH 24/58] feat: updated examples functions --- examples/Dockerfile | 3 --- examples/rrelay.sh | 46 ++++++++++++++++++++++----------------------- examples/run | 16 ++++++++++++---- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/examples/Dockerfile b/examples/Dockerfile index 5f79881..6ab1ea7 100644 --- a/examples/Dockerfile +++ b/examples/Dockerfile @@ -1,6 +1,3 @@ -# FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.10-full -# FROM lambda-shell-runtime:micro -# FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.10-slim FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.11-tiny WORKDIR /var/task diff --git a/examples/rrelay.sh b/examples/rrelay.sh index f378071..665d503 100644 --- a/examples/rrelay.sh +++ b/examples/rrelay.sh @@ -149,43 +149,41 @@ function create_basket () { function create_booking () { local variables=$(echo "$1" | jq -c) - # { - # "basketId": "2f3e14fa-c78a-4906-b021-d5ee4222e0db", - # "createBooking": { - # "adults": 1, - # "children": 0, - # "infants": 0, - # "teens": 0, - # "flights": [ - # { - # "fareKey": "2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A", - # "flightKey": "FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~", - # "fareOption": null - # } - # ], - # "discount": 0, - # "promoCode": "" - # }, - # "culture": "it-it" - # } local url="https://www.ryanair.com/api/basketapi/it-it/graphql" local m_create_booking='{"query":"mutation CreateBooking($basketId: String, $createBooking: CreateBookingInput!, $culture: String!) {\n createBooking(basketId: $basketId, createBooking: $createBooking, culture: $culture) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","createBooking":{"adults":1,"children":0,"infants":0,"teens":0,"flights":[{"fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","fareOption":null}],"discount":0,"promoCode":""},"culture":"it-it"},"operationName":"CreateBooking"}' local data=$( jq -rcn --arg m_create_booking "$m_create_booking" '$m_create_booking|fromjson' \ | jq -rc --arg variables "$variables" '. + { variables: $variables|fromjson }' ) - local output=$(http-cli \ + local headers_file=$(mktemp) + local response=$(http-cli \ --silent \ --user-agent "$ua" \ --header 'accept: application/json, text/plain, */*' \ --cookie "fr-correlation-id=$correlation_id" \ --data "$data" \ --url "$url" \ - -- -D "$JWT_HANDLER" + -D "$headers_file" \ + -D code ) - export JWT=$(cat "$JWT_HANDLER" | grep 'jwt' | awk '{ print $2 }') - echo "$output" | jq - # echo "$JWT" >&2 + + # echo "$response" >&2 + code=$(echo "$response" | grep '^server=' | cut -d '=' -f2) + + if [[ $code -ne 200 ]];then + echo "error" >&2 + echo "$response" >&2 + exit 1 + fi + + local jwt=$(cat "$headers_file" | grep '^jwt: ' | cut -d ' ' -f2) + local body=$(echo "$response" | grep -v "^server=" | grep -v "^proxy=" | jq ) + + local output=$(jq -n --arg jwt "$jwt" --argjson body "$body" '{ + body: $body , + jwt: $jwt + }') + echo "$output" } function commit_booking () { diff --git a/examples/run b/examples/run index d01d55a..944cff8 100755 --- a/examples/run +++ b/examples/run @@ -2,7 +2,7 @@ source $(realpath $(dirname $0))/rrelay.sh -request='{"adults":1,"children":1,"infants":1,"teens":0,"origin":"NAP","destination":"BCN","dateOut":"2025-07-13"}' +request='{"adults":2,"children":1,"infants":0,"teens":0,"origin":"FCO","destination":"CTA","dateOut":"2025-07-15"}' echo $request >&2 correlation_id=$(uuidgen | tr '[:upper:]' '[:lower:]') # export USER_AGENT=$(randua) @@ -22,9 +22,17 @@ culture=$(dashed_market "$market") # echo "$culture" >&2 variables=$(booking_variables "$basketID" "$request" "$flight") # echo "$variables" | jq >&2 -export JWT_HANDLER=$(mktemp) -create_booking "$variables" -echo "$JWT" >&2 +booking=$(create_booking "$variables") + +if [ $? -ne 0 ]; then + echo "Error creating booking: $booking" >&2 + exit 1 +fi +export JWT=$(echo "$booking" | jq -r .jwt) +data=$(echo "$booking" | jq -r .body) +echo "$data | jq '.data.createBooking|{ currency, price }'" + +# echo "$JWT" >&2 booking=$(commit_booking "$basketID" | jq .data.commitBooking) echo "$booking" | jq >&2 data=$(catalog_products "$basketID" "$JWT") From fe7464d825984634973a30b099f48f44c37e2265 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Fri, 11 Jul 2025 18:11:25 +0200 Subject: [PATCH 25/58] feat: update publish script --- scripts/publish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/publish b/scripts/publish index 74a6959..a2a761d 100755 --- a/scripts/publish +++ b/scripts/publish @@ -5,7 +5,7 @@ VERSION="$1" echo "Publishing Docker images with version $VERSION" -for VARIANT in tiny slim full; do +for VARIANT in tiny micro full; do BASE_IMAGE="ghcr.io/ql4b/lambda-shell-runtime" TAG="$VERSION-$VARIANT" docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$TAG" From e4587e73e0819712ab4b10810c6750ea2a4f8f51 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Fri, 11 Jul 2025 18:38:14 +0200 Subject: [PATCH 26/58] feat: update publish script --- scripts/publish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/publish b/scripts/publish index a2a761d..217fc8a 100755 --- a/scripts/publish +++ b/scripts/publish @@ -5,7 +5,7 @@ VERSION="$1" echo "Publishing Docker images with version $VERSION" -for VARIANT in tiny micro full; do +for VARIANT in tiny micro; do BASE_IMAGE="ghcr.io/ql4b/lambda-shell-runtime" TAG="$VERSION-$VARIANT" docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$TAG" From fdd34f818d6694bb0b6a2db5e4d69ad8931301d3 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sat, 12 Jul 2025 15:24:59 +0200 Subject: [PATCH 27/58] feat: full variant uses aws-cli installation as dnf package --- .github/workflows/release.yml | 3 +- Dockerfile | 19 +++++++----- build | 4 +-- scripts/publish | 2 +- task/handler.sh | 2 +- test/functions.sh | 58 +++++++++++++++++++++++++++++++++++ test/run | 30 ------------------ 7 files changed, 73 insertions(+), 45 deletions(-) create mode 100644 test/functions.sh delete mode 100644 test/run diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c644061..fc102e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,8 +36,7 @@ jobs: export DOCKER_BUILDKIT=1 docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:micro -f micro.Dockerfile . - # docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:slim -f slim.Dockerfile . - # docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . + docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . shell: bash - name: Log in to GHCR run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin diff --git a/Dockerfile b/Dockerfile index f1f6e31..c4714cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,11 +3,12 @@ FROM public.ecr.aws/lambda/provided:al2023 AS builder RUN dnf install -y unzip && \ dnf clean all -# Install AWS CLI v2 (minimal installation) -RUN curl -sL "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o awscliv2.zip && \ - unzip awscliv2.zip && \ - ./aws/install --bin-dir /aws-cli-bin --install-dir /aws-cli && \ - rm -rf aws awscliv2.zip + +# # Install AWS CLI v2 (official installation) +# RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" && \ +# unzip awscliv2.zip && \ +# ./aws/install && \ +# rm -rf awscliv2.zip aws # Download http-cli RUN --mount=type=secret,id=github_token \ @@ -24,13 +25,15 @@ RUN --mount=type=secret,id=github_token \ FROM public.ecr.aws/lambda/provided:al2023 # Install only runtime dependencies -RUN dnf install -y jq python3 python3-libs && \ +RUN dnf install -y \ + jq \ + aws-cli && \ dnf clean all && \ rm -rf /var/cache/dnf # Copy AWS CLI binaries only -COPY --from=builder /aws-cli-bin/aws /usr/local/bin/aws -COPY --from=builder /aws-cli /usr/local/aws-cli +# COPY --from=builder /usr/local/bin/aws /usr/local/bin/aws +# COPY --from=builder /usr/local/aws-cli /usr/local/aws-cli # Copy http-cli COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli diff --git a/build b/build index 859dcef..137af54 100755 --- a/build +++ b/build @@ -7,8 +7,7 @@ set -e PLATFORM="linux/arm64" MODE="--load" TAG="lambda-shell-runtime" -# VARIANTS="tiny slim full" -VARIANTS="tiny micro" +VARIANTS="tiny micro full" # Parse arguments while [ $# -gt 0 ]; do @@ -46,7 +45,6 @@ for VARIANT in $VARIANTS; do echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." docker buildx build \ - --no-cache \ --platform "$PLATFORM" \ --secret id=github_token,env=GITHUB_TOKEN \ --tag $TAG:$VARIANT \ diff --git a/scripts/publish b/scripts/publish index 217fc8a..a2a761d 100755 --- a/scripts/publish +++ b/scripts/publish @@ -5,7 +5,7 @@ VERSION="$1" echo "Publishing Docker images with version $VERSION" -for VARIANT in tiny micro; do +for VARIANT in tiny micro full; do BASE_IMAGE="ghcr.io/ql4b/lambda-shell-runtime" TAG="$VERSION-$VARIANT" docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$TAG" diff --git a/task/handler.sh b/task/handler.sh index 68c9630..2d8e63e 100644 --- a/task/handler.sh +++ b/task/handler.sh @@ -4,5 +4,5 @@ function f () { EVENT="$1" echo "$EVENT" | jq echo "Default handler โ€” override this in your runtime build context." >&2 - exit 1 + # exit 1 } \ No newline at end of file diff --git a/test/functions.sh b/test/functions.sh new file mode 100644 index 0000000..edcf41d --- /dev/null +++ b/test/functions.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +VARIANT=${VARIANT:-"tiny"} +HANDLER=${HANDLER:-"handler.f"} + +# Setup the AWS Lambda Runtime Interface Emulator +# https://github.com/aws/aws-lambda-runtime-interface-emulator/ +em () { + mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \ + https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64 \ + && chmod +x ~/.aws-lambda-rie/aws-lambda-rie +} + +# Start the Lambda shell container +# Optionally: +# +# * add --detach flag to run the container in the background +# * mount --volume ./task/handler.sh:/var/task/handler.sh +r () { + docker run --rm \ + --detach \ + -p 9000:8080 \ + --name lambda-shell-runtime \ + --platform linux/arm64 \ + -v ~/.aws:/root/.aws:ro \ + --volume ~/.aws-lambda-rie:/aws-lambda \ + --entrypoint /aws-lambda/aws-lambda-rie \ + --env HANDLER="$HANDLER" \ + "lambda-shell-runtime:$VARIANT" \ + /var/runtime/bootstrap +} + +# Get running container +p () { + docker ps --filter "name=^lambda-shell-runtime$" \ + "$@" +} + +# logs +l () { + p -q | xargs -I {} docker logs -f {} +} + +# stop +s () { + docker stop $(p -q ) +} + +# exec bash (into running container) +e () { + docker exec -it $(p -q ) /bin/bash +} + +i () { + # Invoke test using http-bin + http-cli -d '{}' \ + "http://localhost:9000/2015-03-31/functions/function/invocations" +} \ No newline at end of file diff --git a/test/run b/test/run deleted file mode 100644 index 6a59b9c..0000000 --- a/test/run +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -# Setup the AWS Lambda Runtime Interface Emulator -# https://github.com/aws/aws-lambda-runtime-interface-emulator/ -mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \ - https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64 \ - && chmod +x ~/.aws-lambda-rie/aws-lambda-rie - -# Start the Lambda shell container -docker run -d --rm \ - -p 9000:8080 \ - --name lambda-shell-runtime \ - --platform linux/arm64 \ - -v ~/.aws:/root/.aws:ro \ - --volume ~/.aws-lambda-rie:/aws-lambda \ - --entrypoint /aws-lambda/aws-lambda-rie \ - --env HANDLER="handler.hello" \ - lambda-shell-runtime:micro \ - /var/runtime/bootstrap - - - -# Invoke test: basic curl request -curl -XPOST \ - "http://localhost:9000/2015-03-31/functions/function/invocations" \ - -d '{}' - -# Invoke test using http-bin -http-bin -d '{}' \ - "http://localhost:9000/2015-03-31/functions/function/invocations" From bffba7b0f9e01369e0153fca006ad4f9b71e4e4b Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sat, 12 Jul 2025 15:29:48 +0200 Subject: [PATCH 28/58] docs: update README.md --- README.md | 2 +- slim.Dockerfile | 43 ------------------------------------------- 2 files changed, 1 insertion(+), 44 deletions(-) delete mode 100644 slim.Dockerfile diff --git a/README.md b/README.md index fec7f52..b027e0d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This custom Lambda runtime enables Bash-based execution with minimal dependencie ## Runtime Image Variants ### 1. `tiny` -- Includes: `jq`, `curl` +- Includes: `jq`, `curl`, `http-cli` - Use case: Lightweight data parsing and HTTP requests. ### 2. `micro` diff --git a/slim.Dockerfile b/slim.Dockerfile deleted file mode 100644 index 483db66..0000000 --- a/slim.Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -# Ultra-minimal AWS CLI (CLI v1 via pip) -FROM public.ecr.aws/lambda/python:3.11 AS builder - -RUN yum install -y unzip && \ - yum clean all - -RUN pip install awscli --target /aws-cli - -# Download http-cli -RUN --mount=type=secret,id=github_token \ - curl -H "Authorization: token $(cat /run/secrets/github_token)" \ - -L https://github.com/ql4b/http-cli/archive/refs/heads/develop.zip \ - -o http-cli.zip && \ - unzip http-cli.zip && \ - mkdir -p /http-cli-bin && \ - mv http-cli-develop/http-cli /http-cli-bin/ && \ - chmod +x /http-cli-bin/http-cli && \ - rm -rf http-cli.zip http-cli-develop - -# Stage 2: Runtime stage -FROM public.ecr.aws/lambda/provided:al2023 - -# Install only runtime dependencies -RUN dnf install -y jq python3 && \ - dnf clean all && \ - rm -rf /var/cache/dnf - -COPY --from=builder /aws-cli /opt/python -ENV PYTHONPATH=/opt/python - -# Copy http-cli -COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli - -ENV PATH="/var/task/bin:${PATH}" - -COPY runtime/bootstrap /var/runtime/bootstrap -RUN chmod +x /var/runtime/bootstrap - -WORKDIR /var/task - -COPY task/handler.sh handler.sh - - From 0c6f18855cdcf08d9b8ff48dcda8b59b2140ad40 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sat, 12 Jul 2025 15:35:10 +0200 Subject: [PATCH 29/58] ci: add docker caching layer --- .github/workflows/release.yml | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc102e6..5a73c79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,13 +30,37 @@ jobs: uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- - name: Build Docker images run: | echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 - docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:tiny -f tiny.Dockerfile . - docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:micro -f micro.Dockerfile . - docker buildx build --builder default --platform linux/arm64 --output type=docker --progress=plain --secret id=github_token,src=github_token -t lambda-shell-runtime:full -f Dockerfile . + docker buildx build --builder default --platform linux/arm64 \ + --output type=docker --progress=plain \ + --secret id=github_token,src=github_token \ + --cache-from type=local,src=/tmp/.buildx-cache \ + --cache-to type=local,dest=/tmp/.buildx-cache \ + -t lambda-shell-runtime:tiny -f tiny.Dockerfile . + + docker buildx build --builder default --platform linux/arm64 \ + --output type=docker --progress=plain \ + --secret id=github_token,src=github_token \ + --cache-from type=local,src=/tmp/.buildx-cache \ + --cache-to type=local,dest=/tmp/.buildx-cache \ + -t lambda-shell-runtime:micro -f micro.Dockerfile . + + docker buildx build --builder default --platform linux/arm64 \ + --output type=docker --progress=plain \ + --secret id=github_token,src=github_token \ + --cache-from type=local,src=/tmp/.buildx-cache \ + --cache-to type=local,dest=/tmp/.buildx-cache \ + -t lambda-shell-runtime:full -f Dockerfile . shell: bash - name: Log in to GHCR run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin From a845f39535742eee3ac7962c7c3cbe31d835833a Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sat, 12 Jul 2025 15:39:38 +0200 Subject: [PATCH 30/58] ci: enable docker layer caching using buildx builder --- .github/workflows/release.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a73c79..15f1d42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,6 +30,10 @@ jobs: uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Create and use buildx builder + run: | + docker buildx create --name shell-runtime-builder --driver docker-container --use + docker buildx inspect shell-runtime-builder --bootstrap - name: Cache Docker layers uses: actions/cache@v3 with: @@ -41,21 +45,21 @@ jobs: run: | echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 - docker buildx build --builder default --platform linux/arm64 \ + docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --output type=docker --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t lambda-shell-runtime:tiny -f tiny.Dockerfile . - docker buildx build --builder default --platform linux/arm64 \ + docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --output type=docker --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t lambda-shell-runtime:micro -f micro.Dockerfile . - docker buildx build --builder default --platform linux/arm64 \ + docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --output type=docker --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ From 0b3cdfd6406af78e1ce21669f268fe261a8f0b16 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sat, 26 Jul 2025 19:56:02 +0200 Subject: [PATCH 31/58] feat: add HTTP_CLI_VERSION build ARG and set default into the github workflow --- .github/workflows/release.yml | 5 +++++ Dockerfile | 4 +++- micro.Dockerfile | 4 +++- tiny.Dockerfile | 4 +++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15f1d42..3deaeb8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,8 @@ permissions: jobs: release: runs-on: ubuntu-latest + env: + HTTP_CLI_VERSION: v1.0.1 steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 @@ -46,6 +48,7 @@ jobs: echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ + --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ --output type=docker --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ @@ -53,6 +56,7 @@ jobs: -t lambda-shell-runtime:tiny -f tiny.Dockerfile . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ + --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ --output type=docker --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ @@ -60,6 +64,7 @@ jobs: -t lambda-shell-runtime:micro -f micro.Dockerfile . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ + --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ --output type=docker --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ diff --git a/Dockerfile b/Dockerfile index c4714cd..dea184f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ # Stage 1: Build stage FROM public.ecr.aws/lambda/provided:al2023 AS builder +ARG HTTP_CLI_VERSION=v1.0.1 + RUN dnf install -y unzip && \ dnf clean all @@ -13,7 +15,7 @@ RUN dnf install -y unzip && \ # Download http-cli RUN --mount=type=secret,id=github_token \ curl -H "Authorization: token $(cat /run/secrets/github_token)" \ - -L https://github.com/ql4b/http-cli/archive/refs/heads/develop.zip \ + -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ -o http-cli.zip && \ unzip http-cli.zip && \ mkdir -p /http-cli-bin && \ diff --git a/micro.Dockerfile b/micro.Dockerfile index ec0eddc..3f43fa4 100644 --- a/micro.Dockerfile +++ b/micro.Dockerfile @@ -1,12 +1,14 @@ FROM public.ecr.aws/lambda/provided:al2023 AS builder +ARG HTTP_CLI_VERSION=v1.0.1 + RUN dnf install -y unzip python3-pip findutils && \ dnf clean all # Download http-cli RUN --mount=type=secret,id=github_token \ curl -H "Authorization: token $(cat /run/secrets/github_token)" \ - -L https://github.com/ql4b/http-cli/archive/refs/heads/develop.zip \ + -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ -o http-cli.zip && \ unzip http-cli.zip && \ mkdir -p /http-cli-bin && \ diff --git a/tiny.Dockerfile b/tiny.Dockerfile index 3f86d4f..a21f7d1 100644 --- a/tiny.Dockerfile +++ b/tiny.Dockerfile @@ -1,12 +1,14 @@ FROM public.ecr.aws/lambda/provided:al2023 AS builder +ARG HTTP_CLI_VERSION=v1.0.1 + RUN dnf install -y unzip && \ dnf clean all # Download http-cli RUN --mount=type=secret,id=github_token \ curl -H "Authorization: token $(cat /run/secrets/github_token)" \ - -L https://github.com/ql4b/http-cli/archive/refs/heads/develop.zip \ + -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ -o http-cli.zip && \ unzip http-cli.zip && \ mkdir -p /http-cli-bin && \ From a8eaa8e51c6ed08b575e0bb5e76ebaaf297a2d96 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sat, 26 Jul 2025 20:22:50 +0200 Subject: [PATCH 32/58] fix: fix how the unzipped http-cli content is handled during build (#5) --- Dockerfile | 4 ++-- micro.Dockerfile | 4 ++-- tiny.Dockerfile | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index dea184f..3d7a989 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,9 +19,9 @@ RUN --mount=type=secret,id=github_token \ -o http-cli.zip && \ unzip http-cli.zip && \ mkdir -p /http-cli-bin && \ - mv http-cli-develop/http-cli /http-cli-bin/ && \ + mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ chmod +x /http-cli-bin/http-cli && \ - rm -rf http-cli.zip http-cli-develop + rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} # Stage 2: Runtime stage FROM public.ecr.aws/lambda/provided:al2023 diff --git a/micro.Dockerfile b/micro.Dockerfile index 3f43fa4..d1facd5 100644 --- a/micro.Dockerfile +++ b/micro.Dockerfile @@ -12,9 +12,9 @@ RUN --mount=type=secret,id=github_token \ -o http-cli.zip && \ unzip http-cli.zip && \ mkdir -p /http-cli-bin && \ - mv http-cli-develop/http-cli /http-cli-bin/ && \ + mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ chmod +x /http-cli-bin/http-cli && \ - rm -rf http-cli.zip http-cli-develop + rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} RUN pip3 install --no-cache-dir --target /tmp/awscurl awscurl && \ find /tmp/awscurl -type d -name '__pycache__' -exec rm -rf {} + && \ diff --git a/tiny.Dockerfile b/tiny.Dockerfile index a21f7d1..c510b19 100644 --- a/tiny.Dockerfile +++ b/tiny.Dockerfile @@ -12,9 +12,9 @@ RUN --mount=type=secret,id=github_token \ -o http-cli.zip && \ unzip http-cli.zip && \ mkdir -p /http-cli-bin && \ - mv http-cli-develop/http-cli /http-cli-bin/ && \ + mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ chmod +x /http-cli-bin/http-cli && \ - rm -rf http-cli.zip http-cli-develop + rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} # Stage 2: Runtime stage FROM public.ecr.aws/lambda/provided:al2023 From ad036fd2a29a654ce05753c43872fdb675855ba5 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sat, 26 Jul 2025 23:40:05 +0200 Subject: [PATCH 33/58] feat: make helpers function available in the LAMBDA_TASK_ROOT folder (#6) --- Dockerfile | 3 +- micro.Dockerfile | 1 + task/helpers.sh | 102 +++++++++++++++++++++++++++++++++++++++++++++++ tiny.Dockerfile | 1 + 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 task/helpers.sh diff --git a/Dockerfile b/Dockerfile index 3d7a989..45b317f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,4 +47,5 @@ RUN chmod +x /var/runtime/bootstrap WORKDIR /var/task -COPY task/handler.sh handler.sh \ No newline at end of file +COPY task/handler.sh handler.sh +COPY task/helpers.sh helpers.sh \ No newline at end of file diff --git a/micro.Dockerfile b/micro.Dockerfile index d1facd5..938c756 100644 --- a/micro.Dockerfile +++ b/micro.Dockerfile @@ -53,3 +53,4 @@ RUN chmod +x /var/runtime/bootstrap WORKDIR /var/task COPY task/handler.sh handler.sh +COPY task/helpers.sh helpers.sh diff --git a/task/helpers.sh b/task/helpers.sh new file mode 100644 index 0000000..48a66af --- /dev/null +++ b/task/helpers.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html + + +# https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html#apigateway-example-event +# https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format +# https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format + +lambda_parse_event () { + local EVENT="$1" + # Extract commonly used fields + if echo "$EVENT" | jq -e '.requestContext' >/dev/null; then + export EVENT_BODY=$(echo "$EVENT" | jq -r '.body // empty') + export EVENT_QUERY=$(echo "$EVENT" | jq -c '.queryStringParameters // {}') + export EVENT_HEADERS=$(echo "$EVENT" | jq -c '.headers // {}') + export EVENT_PATH=$(echo "$EVENT" | jq -r '.path // empty') + export EVENT_HTTPMETHOD=$(echo "$EVENT" | jq -r '.httpMethod // empty') + else + # Handle other event types + export EVENT_TYPE=$(echo "$EVENT" | jq -r 'keys[0]' 2>/dev/null || echo "unknown") + fi +} + + +lambda_require_http_event () { + if [ -z "$EVENT_HTTPMETHOD" ]; then + lambda_error_response "Not an HTTP event" 400 + exit 1 + fi +} + +# JSON response with CORS +lambda_json_response() { + local data="$1" + local status="${2:-200}" + + jq -n \ + --arg data "$data" \ + --arg status "$status" \ + '{ + statusCode: ($status|tonumber), + body: $data, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }' +} + +# Error response +lambda_error_response() { + local message="$1" + local status="${2:-400}" + lambda_json_response "$(jq -n --arg msg "$message" '{error: $msg}')" "$status" +} + +# Success with data +lambda_ok_response() { + local data="$1" + lambda_json_response "$data" 200 +} + +# Not found +lambda_not_found_response() { + lambda_ok_response "Not found" 404 +} + +# Method not allowed +lambda_method_not_allowed_response() { + lambda_ok_response "Method not allowed" 405 +} + +# lambda_log function +lambda_log() { + local message="$1" + local level="${2:-INFO}" + local timestamp=$(date +"%Y-%m-%d %H:%M:%S") + echo "$timestamp [$level] $message" >&2 + +} + +# lambda_log_error +lambda_log_error() { + lambda_log "$1" "ERROR" +} + +# lambda_log_info +lambda_log_info() { + lambda_log "$1" "INFO" +} + +# lambda_log_debug +lambda_log_debug() { + lambda_log "$1" "DEBUG" +} + +# lambda_log_warn +lambda_log_warn() { + lambda_log "$1" "WARN" +} + diff --git a/tiny.Dockerfile b/tiny.Dockerfile index c510b19..c734a2b 100644 --- a/tiny.Dockerfile +++ b/tiny.Dockerfile @@ -34,3 +34,4 @@ RUN chmod +x /var/runtime/bootstrap WORKDIR /var/task COPY task/handler.sh handler.sh +COPY task/helpers.sh helpers.sh From 84a85a9a07911788d6edac76336e24dba8282561 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 08:15:40 +0200 Subject: [PATCH 34/58] chore: removed examples --- Dockerfile | 15 +-- README.md | 1 - examples/Dockerfile | 10 -- examples/README.md | 53 ----------- examples/rrelay.sh | 226 -------------------------------------------- examples/run | 39 -------- examples/service.sh | 111 ---------------------- task/helpers.sh | 2 - 8 files changed, 2 insertions(+), 455 deletions(-) delete mode 100644 examples/Dockerfile delete mode 100644 examples/README.md delete mode 100644 examples/rrelay.sh delete mode 100755 examples/run delete mode 100644 examples/service.sh diff --git a/Dockerfile b/Dockerfile index 45b317f..6abe60f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,12 +6,6 @@ ARG HTTP_CLI_VERSION=v1.0.1 RUN dnf install -y unzip && \ dnf clean all -# # Install AWS CLI v2 (official installation) -# RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" && \ -# unzip awscliv2.zip && \ -# ./aws/install && \ -# rm -rf awscliv2.zip aws - # Download http-cli RUN --mount=type=secret,id=github_token \ curl -H "Authorization: token $(cat /run/secrets/github_token)" \ @@ -23,20 +17,15 @@ RUN --mount=type=secret,id=github_token \ chmod +x /http-cli-bin/http-cli && \ rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} -# Stage 2: Runtime stage -FROM public.ecr.aws/lambda/provided:al2023 -# Install only runtime dependencies + FROM public.ecr.aws/lambda/provided:al2023 + RUN dnf install -y \ jq \ aws-cli && \ dnf clean all && \ rm -rf /var/cache/dnf -# Copy AWS CLI binaries only -# COPY --from=builder /usr/local/bin/aws /usr/local/bin/aws -# COPY --from=builder /usr/local/aws-cli /usr/local/aws-cli - # Copy http-cli COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli diff --git a/README.md b/README.md index b027e0d..62a4378 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ Implement AWS Lambda functions in Bash, packaged as OCI-compliant container imag Inspired by: https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html - This custom Lambda runtime enables Bash-based execution with minimal dependencies. We provide three image variants tailored to different needs: ## Runtime Image Variants diff --git a/examples/Dockerfile b/examples/Dockerfile deleted file mode 100644 index 6ab1ea7..0000000 --- a/examples/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM ghcr.io/ql4b/lambda-shell-runtime:0.1.0-develop.11-tiny - -WORKDIR /var/task - -RUN dnf install -y util-linux && \ - cp /usr/bin/uuidgen /var/task/bin/ && \ - dnf remove -y util-linux && \ - dnf clean all - -COPY ./rrelay.sh handler.sh \ No newline at end of file diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 5b71f88..0000000 --- a/examples/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# examples - -## buld - -```bash -# CONTAINER="shell-booking" -# TAG="shell-booking:dev" -# TAG="703177223665.dkr.ecr.eu-central-1.amazonaws.com/ql4b-farecrumbs:booking" -CONTAINER="rrlelay" -TAG="rrlelay:dev" - - -b () { - docker build \ - --platform linux/arm64 \ - -t "$TAG" \ - --secret id=github_token,env=GITHUB_TOKEN \ - . -} - -r () { - docker run -d --rm \ - -p 9000:8080 \ - --name $CONTAINER \ - --platform linux/arm64 \ - -v ~/.aws:/root/.aws:ro \ - --volume ~/.aws-lambda-rie:/aws-lambda \ - --volume ./rrelay.sh:/var/task/hander.sh:rw \ - --entrypoint /aws-lambda/aws-lambda-rie \ - --env HANDLER="handler.booking" \ - "$TAG" \ - /var/runtime/bootstrap -} - -p () { - docker ps -q --filter name="$CONTAINER" -} - -s () { - docker stop $(p) -} - -e () { - docker exec -it \ - $(p) \ - $@ -} - -req () { - http-cli -d '{ "reservationNumber": "A3PJKR" }' \ - "http://localhost:9000/2015-03-31/functions/function/invocations" -} -``` \ No newline at end of file diff --git a/examples/rrelay.sh b/examples/rrelay.sh deleted file mode 100644 index 665d503..0000000 --- a/examples/rrelay.sh +++ /dev/null @@ -1,226 +0,0 @@ -#!/bin/bash - -# GLOBALS - -market="${market-"it/it"}" -ua="Mozilla/5.0 (Windows CE/6.0; ARM; fr-FR) AppleWebKit/62.0 (KHTML, like Gecko) Samsung Internet/62.0 Safari/62.0" - -# mutations -# q_create_basket='{"query":"mutation CreateBasket {\n createBasket {\n ...BasketCommon\n gettingAround {\n ...GettingAroundPillar\n }\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\nfragment VariantCar on VariantUnionType {\n ... on Car {\n rentPrice\n carName\n refId\n engineLoadId\n pickUpTime\n pickUpLocation {\n countryCode\n code\n name\n }\n dropOffTime\n dropOffLocation {\n countryCode\n code\n name\n }\n insurance\n extras {\n totalPrice\n includedInRate\n code\n price\n selected\n type\n }\n residence\n age\n }\n}\n\nfragment VariantCarRental on VariantUnionType {\n ... on CarRental {\n rentPrice\n carName\n clientId\n refId\n pickUpTime\n pickUpLocation {\n countryCode\n code\n name\n }\n dropOffTime\n dropOffLocation {\n countryCode\n code\n name\n }\n insurance\n insuranceQuoteReference\n extras {\n code\n includedInRate\n payNow\n price\n selected\n totalPrice\n type\n }\n residence\n age\n language\n searchId\n supplier\n }\n}\n\nfragment GettingAroundPillar on GettingAroundType {\n price {\n amount\n discount\n amountWithTaxes\n total\n }\n payLater {\n ...PayLaterCommon\n }\n taxes {\n amount\n }\n components {\n ...ComponentCommon\n payLater {\n amountWithTaxes\n total\n }\n variant {\n ...VariantCar\n ...VariantCarRental\n ...VariantGroundTransfer\n }\n }\n}\n\n","operationName":"CreateBasket"}' -# jq -nr --arg data "$m_create_basket" '$data' | jq -c 'keys' - -# return 0 -# ["operationName","query"] -# d_create_basket='{"data":{"createBasket":{"id":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","tripId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","dotrezSessionId":null,"currency":"","gettingThere":{"isPrime":false,"price":{"total":0},"journeys":[],"discounts":[],"taxes":[],"vouchers":[],"components":[],"messages":[]},"price":{"total":0},"payLater":{"total":0},"totalToPay":0,"gettingAround":{"price":{"amount":0,"discount":0,"amountWithTaxes":0,"total":0},"payLater":{"total":0},"taxes":[],"components":[]}}}}' - - -# createBooking -# m_create_booking='{"query":"mutation CreateBooking($basketId: String, $createBooking: CreateBookingInput!, $culture: String!) {\n createBooking(basketId: $basketId, createBooking: $createBooking, culture: $culture) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","createBooking":{"adults":1,"children":0,"infants":0,"teens":0,"flights":[{"fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","fareOption":null}],"discount":0,"promoCode":""},"culture":"it-it"},"operationName":"CreateBooking"}' -# jq -nr --arg data "$m_create_booking" '$data' | jq -c 'keys' -# {"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","createBooking":{"adults":1,"children":0,"infants":0,"teens":0,"flights":[{"fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","fareOption":null}],"discount":0,"promoCode":""},"culture":"it-it"} -# ["operationName","query","variables"] - -# header -# jwt: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJCYXNrZXRJZCI6IjJmM2UxNGZhLWM3OGEtNDkwNi1iMDIxLWQ1ZWU0MjIyZTBkYiIsIlRyaXBJZCI6IjJmM2UxNGZhLWM3OGEtNDkwNi1iMDIxLWQ1ZWU0MjIyZTBkYiIsIm5iZiI6MTc1MTU0MDgyMiwiZXhwIjoxNzUxNTQ0NzIyLCJpYXQiOjE3NTE1NDA4MjJ9.LVUmrGRDbghK4EwCfndBQX98Rdkw169Ryq51k_H_adecPDqIdInNQ4Bg9Fo0Lmmm0MzEN5q4qCdjkj5UxxPNL2wN3u5xuTnLeGqXpgE6SraRVoi2itYEnLAvKFarBwDkaHfIDLC6d5RrKa_W3x6ZsIx4ILsW7slVD83qfMVvHJGZtg9zvVUKSBqY5wwDEE6ZYWo7t2qcCA21bUbpF9DaEanya9pxR0kb3cOfWp6OIayG9kTgglfe-iyexEG0fH5EyW6dXvM2ApA4vzbLrTffldGPb4Y16oi8NNl0HDbY4EQ_1ZwbqaHMU5sD255URrFDRlc_K4fAHKTw00Vq5Pnptw - -# d_create_booking='{"data":{"createBooking":{"id":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","tripId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","dotrezSessionId":null,"currency":"EUR","gettingThere":{"isPrime":false,"price":{"total":111.99},"journeys":[{"arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","fareClass":"W","fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","fareOption":null,"flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","flightNumber":"FR7763","isConnecting":false,"isDomestic":false,"journeyNum":0,"origin":"ALC","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"","arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","hasGovernmentTax":false,"flightNumber":"FR7763","segmentNum":0,"origin":"ALC","originCountry":"ES","destinationCountry":"IE"}]}],"discounts":[],"taxes":[],"vouchers":[],"components":[{"id":"455b8555-be62-40e0-85df-ccd5cd564766","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":111.99,"total":111.99,"discount":0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":0}}],"messages":[]},"price":{"total":111.99},"payLater":{"total":0},"totalToPay":111.99}}}' -# jq -nr --arg data "$d_create_booking" '$data' | jq -c 'keys' -# ["data"] - -# CommitBooking -# m_commit_booking='{"query":"mutation CommitBooking($basketId: String!) {\n commitBooking(basketId: $basketId) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"},"operationName":"CommitBooking"}' -# jq -nr --arg data "$m_commit_booking" '$data' | jq -c 'keys' -# ["operationName","query","variables"] - -# d_commit_booking='{"data":{"commitBooking":{"id":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","tripId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","dotrezSessionId":"wzfaqb5f113wkzy0od4yf41l","currency":"EUR","gettingThere":{"isPrime":false,"price":{"total":182.98},"journeys":[{"arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","fareClass":"W","fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","fareOption":null,"flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","flightNumber":"FR7763","isConnecting":false,"isDomestic":false,"journeyNum":0,"origin":"ALC","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"738","arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","hasGovernmentTax":false,"flightNumber":"FR7763","segmentNum":0,"origin":"ALC","originCountry":"ES","destinationCountry":"IE"}]},{"arrival":"2025-10-11T20:05:00","departure":"2025-10-11T16:20:00","destination":"ALC","duration":"02:45","fareClass":"J","fareKey":"BVXTTSHSD37C5T56MHXKUSHPNQQEWBGQ65FGBUYKS7UOS4PIID2WW4YZZHYUF4JYR4DCKMKCYYJXK5NR25OUPQWTPEE4NARDMM2XYZ7KO3Z2SZKW7L5EAG2JZ4YHGCQPLWJG2WUJTUNV3YAJ2UEFB2RFHTZYGWSP7NV43Y57LY45DSFTHRHIYPUULHO4WOD3UOHN6PBLDLTVH45XPRZOP26J7O34I65J362TDZZ3ZY5JKMQNVTNTPT44L33IZ6CXDXJAFMM5DN65PLFZHBWAULNVMKNT22E7QZOIEP6WZQMEYV7Y5WQMFCSEXJJQEHJYABFPBIK42X6IACOMTUUUBLSQI5LZMJESX6NPR3I","fareOption":null,"flightKey":"FR~7762~ ~~DUB~10/11/2025 16:20~ALC~10/11/2025 20:05~~","flightNumber":"FR7762","isConnecting":false,"isDomestic":false,"journeyNum":1,"origin":"DUB","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"738","arrival":"2025-10-11T20:05:00","departure":"2025-10-11T16:20:00","destination":"ALC","duration":"02:45","hasGovernmentTax":false,"flightNumber":"FR7762","segmentNum":0,"origin":"DUB","originCountry":"IE","destinationCountry":"ES"}]}],"discounts":[{"amount":5.0000,"code":"PD","journeyNum":1,"percentage":0,"zone":null,"description":null,"qty":0}],"taxes":[],"vouchers":[],"components":[{"id":"323c0eed-6562-48e5-9eee-1d05a9759154","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":111.99,"total":111.99,"discount":0.0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":0}},{"id":"fd5e43d3-0aa0-4d37-8d7a-874f30dd12ca","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":70.99,"total":70.99,"discount":0.0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":1}},{"id":"6a9d84c4-adab-4142-bc6a-aa9130b83a3b","parentId":"323c0eed-6562-48e5-9eee-1d05a9759154","code":"SBG","type":"SMALL_BAG","quantity":1,"removable":true,"hidden":false,"price":{"amountWithTaxes":0,"total":0,"discount":0,"discountCode":null},"variant":{"journeyNumber":0,"paxNumber":0,"segmentNumber":0}},{"id":"3d9e683e-86e5-46af-91dc-ee4a79880dad","parentId":"fd5e43d3-0aa0-4d37-8d7a-874f30dd12ca","code":"SBG","type":"SMALL_BAG","quantity":1,"removable":true,"hidden":false,"price":{"amountWithTaxes":0,"total":0,"discount":0,"discountCode":null},"variant":{"journeyNumber":1,"paxNumber":0,"segmentNumber":0}}],"messages":[]},"price":{"total":182.98},"payLater":{"total":0},"totalToPay":182.98}}}' -# jq -nr --arg data "$d_commit_booking" '$data' | jq -c 'keys' -# ["data"] - - -# POST https://www.ryanair.com/api/personapi/it-it/graphql - -# GetPassengerMandatoryFields -# m_passengers_fields='{"query":"query GetPassengerMandatoryFields($searchCriteria: InputSearchCriteria) {\n passengerMandatoryFields(searchCriteria: $searchCriteria) {\n ...PassengersMandatoryFieldsResponse\n }\n}\n\nfragment PassengersMandatoryFieldsResponse on PassengersMandatoryFieldsResponse {\n passengers {\n ...PassengerMandatoryFields\n }\n}\n\nfragment PassengerMandatoryFields on PassengerMandatoryFields {\n paxNum\n type\n title\n firstName\n firstSurname\n secondSurname\n first\n last\n middle\n dob\n inf {\n ...PassengersInfantMandatoryFields\n }\n dobConstraints {\n ...PassengersDobConstraints\n }\n}\n\nfragment PassengersInfantMandatoryFields on InfantMandatoryFields {\n firstName\n firstSurname\n secondSurname\n first\n last\n middle\n dob\n}\n\nfragment PassengersDobConstraints on MandatoryDateOfBirth {\n maxAgeDays\n maxAgeYears\n maxDateOfBirth\n minAgeDays\n minAgeYears\n minDateOfBirth\n}\n","variables":{"searchCriteria":{"adults":1,"teens":0,"children":0,"infants":0,"discountPercentage":0,"departureDate":"2025-10-10"}}}' -# jq -ncr --arg m_passengers_fields "$m_passengers_fields" '$m_passengers_fields|fromjson' -# d_passengers_fields='{"data":{"passengerMandatoryFields":{"passengers":[{"paxNum":0,"type":"ADT","title":"","firstName":"","firstSurname":null,"secondSurname":"","first":"","last":"","middle":null,"dob":null,"inf":null,"dobConstraints":null}]}}}' -# jq -ncr --arg d_passengers_fields "$d_passengers_fields" '$d_passengers_fields|fromjson' - -# AddPassengers -# m_passengers_add='{"query":"mutation AddPassengers($passengers: [InputPassenger] = null, $basketId: String!) {\n addPassengers(passengers: $passengers, basketId: $basketId) {\n ...PassengersResponse\n }\n}\n\nfragment PassengersResponse on PassengersResponse {\n passengers {\n ...PassengersPassenger\n }\n}\n\nfragment PassengersPassenger on Passenger {\n paxNum\n type\n title\n firstName\n firstSurname\n secondSurname\n first\n last\n middle\n dob\n inf {\n ...PassengersInfant\n }\n specialAssistance {\n ...PassengersPassengerPrmSsrType\n }\n}\n\nfragment PassengersInfant on Infant {\n firstName\n firstSurname\n secondSurname\n first\n last\n middle\n dob\n}\n\nfragment PassengersPassengerPrmSsrType on PassengerPrmSsrType {\n codes\n journeyNum\n segmentNum\n}\n","variables":{"passengers":[{"type":"ADT","dob":null,"first":"Esther","last":"Robles Casado","middle":null,"title":"MRS","paxNum":0,"specialAssistance":[]}],"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"}}' -# jq -nc --arg m_passengers_add $m_passengers_add '$m_passengers_add|fromjson' - -# GetPassengers -# q_get_passwengers='{"query":"query GetPassengersQuery($basketId: String!) {\n passengers(basketId: $basketId) {\n ...PassengersResponse\n }\n}\n\n\nfragment PassengersInfant on Infant {\n first\n middle\n last\n dob\n}\n\nfragment PassengersPassenger on Passenger {\n type\n title\n first\n middle\n last\n paxNum\n specialAssistance {\n codes\n journeyNum\n segmentNum\n }\n inf {\n ...PassengersInfant\n }\n}\n\nfragment PassengersResponse on PassengersResponse {\n passengers {\n ...PassengersPassenger\n }\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"}}' -# jq -ncr --arg q_get_passwengers "$q_get_passwengers" '$q_get_passwengers|fromjson' -# d_get_passengers='{"data":{"passengers":{"passengers":[{"type":"ADT","title":"MRS","first":"Esther","middle":"","last":"Robles Casado","paxNum":0,"specialAssistance":[],"inf":null}]}}}' -# jq -nrc --arg d_get_passengers "$d_get_passengers" '$d_get_passengers|fromjson' -# {"data":{"passengers":{"passengers":[{"type":"ADT","title":"MRS","first":"Esther","middle":"","last":"Robles Casado","paxNum":0,"specialAssistance":[],"inf":null}]}}} - -# GetSeatsQuery -# q_get_seats='{"query":"query GetSeatsQuery($basketId: String!) {\n seats(basketId: $basketId) {\n ...SeatsResponse\n }\n}\n\nfragment SeatsResponse on SeatAvailability {\n equipmentModel\n groups {\n fares {\n discount\n originalPrice\n paxNum\n price\n strikeThroughPrice\n }\n group\n type\n }\n groupsMinPrices {\n available\n fares {\n discount\n originalPrice\n paxNum\n price\n }\n firstRow\n type\n }\n includedSeatRows {\n includedRanges {\n end\n start\n }\n paxNum\n primeIncluded\n }\n journeyNum\n prmCompanions {\n paxNum\n }\n prmSeats {\n paxNum\n seats\n }\n seatsOffer {\n price\n seats {\n paxNum\n seat\n }\n }\n segmentNum\n unavailableSeats\n}\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"}}' -# jq -ncr --arg q_get_seats $q_get_seats '$q_get_seats|fromjson' -# d_get_seats='{"data":{"seats":[{"equipmentModel":"738","groups":[{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":30.50,"strikeThroughPrice":null}],"group":1,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":30.00,"strikeThroughPrice":null}],"group":13,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":19.50,"strikeThroughPrice":null}],"group":2,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":19.00,"strikeThroughPrice":null}],"group":14,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":18.50,"strikeThroughPrice":null}],"group":3,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":18.00,"strikeThroughPrice":null}],"group":15,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":17.50,"strikeThroughPrice":null}],"group":4,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":17.00,"strikeThroughPrice":null}],"group":16,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.50,"strikeThroughPrice":null}],"group":5,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.00,"strikeThroughPrice":null}],"group":17,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":15.50,"strikeThroughPrice":null}],"group":6,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":15.00,"strikeThroughPrice":null}],"group":18,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":14.50,"strikeThroughPrice":null}],"group":7,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":14.00,"strikeThroughPrice":null}],"group":19,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.50,"strikeThroughPrice":null}],"group":8,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.00,"strikeThroughPrice":null}],"group":20,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.50,"strikeThroughPrice":null}],"group":9,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.00,"strikeThroughPrice":null}],"group":21,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.50,"strikeThroughPrice":null}],"group":10,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.00,"strikeThroughPrice":null}],"group":22,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.50,"strikeThroughPrice":null}],"group":11,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.00,"strikeThroughPrice":null}],"group":23,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.50,"strikeThroughPrice":null}],"group":12,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.00,"strikeThroughPrice":null}],"group":24,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.50,"strikeThroughPrice":null}],"group":26,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.00,"strikeThroughPrice":null}],"group":30,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.50,"strikeThroughPrice":null}],"group":27,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.00,"strikeThroughPrice":null}],"group":31,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.50,"strikeThroughPrice":null}],"group":28,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.00,"strikeThroughPrice":null}],"group":32,"type":"STANDARD"}],"groupsMinPrices":[{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.50}],"firstRow":16,"type":"EXTRALEG"},{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.00}],"firstRow":6,"type":"PRIORITY"},{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.00}],"firstRow":21,"type":"STANDARD"}],"includedSeatRows":[{"includedRanges":[],"paxNum":0,"primeIncluded":false}],"journeyNum":0,"prmCompanions":[],"prmSeats":[],"seatsOffer":{"price":10.00,"seats":[{"paxNum":0,"seat":"24D"}]},"segmentNum":0,"unavailableSeats":["01A","01B","02A","02B","02C","02D","02E","02F","03D","03E","04A","04B","04C","05A","05B","06C","06D","06E","06F","07C","07D","11E","11F","14E","14F","15C","15D","16B","16C","16D","16E","17A","17B","17C","17D","17E","17F","18D","18E","18F","19D","21A","21B","21E","21F","22B","22C","27E","27F","29C","29D","29F","30A","30B","30C","30D","31B","31C","32A","32B"]},{"equipmentModel":"738","groups":[{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":30.50,"strikeThroughPrice":null}],"group":1,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":30.00,"strikeThroughPrice":null}],"group":13,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":19.50,"strikeThroughPrice":null}],"group":2,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":19.00,"strikeThroughPrice":null}],"group":14,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":18.50,"strikeThroughPrice":null}],"group":3,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":18.00,"strikeThroughPrice":null}],"group":15,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":17.50,"strikeThroughPrice":null}],"group":4,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":17.00,"strikeThroughPrice":null}],"group":16,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.50,"strikeThroughPrice":null}],"group":5,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.00,"strikeThroughPrice":null}],"group":17,"type":"PRIORITY"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":15.50,"strikeThroughPrice":null}],"group":6,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":15.00,"strikeThroughPrice":null}],"group":18,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":14.50,"strikeThroughPrice":null}],"group":7,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":14.00,"strikeThroughPrice":null}],"group":19,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.50,"strikeThroughPrice":null}],"group":8,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.00,"strikeThroughPrice":null}],"group":20,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.50,"strikeThroughPrice":null}],"group":9,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.00,"strikeThroughPrice":null}],"group":21,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.50,"strikeThroughPrice":null}],"group":10,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.00,"strikeThroughPrice":null}],"group":22,"type":"EXTRALEG"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.50,"strikeThroughPrice":null}],"group":11,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.00,"strikeThroughPrice":null}],"group":23,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.50,"strikeThroughPrice":null}],"group":12,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.00,"strikeThroughPrice":null}],"group":24,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.50,"strikeThroughPrice":null}],"group":26,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":11.00,"strikeThroughPrice":null}],"group":30,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.50,"strikeThroughPrice":null}],"group":27,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":12.00,"strikeThroughPrice":null}],"group":31,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.50,"strikeThroughPrice":null}],"group":28,"type":"STANDARD"},{"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":13.00,"strikeThroughPrice":null}],"group":32,"type":"STANDARD"}],"groupsMinPrices":[{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":21.00}],"firstRow":16,"type":"EXTRALEG"},{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":16.00}],"firstRow":6,"type":"PRIORITY"},{"available":true,"fares":[{"discount":null,"originalPrice":null,"paxNum":0,"price":10.00}],"firstRow":21,"type":"STANDARD"}],"includedSeatRows":[{"includedRanges":[],"paxNum":0,"primeIncluded":false}],"journeyNum":1,"prmCompanions":[],"prmSeats":[],"seatsOffer":{"price":10.00,"seats":[{"paxNum":0,"seat":"24D"}]},"segmentNum":0,"unavailableSeats":["03C","04A","09D","09E","11E","11F","15D","18A","18B","18C","18D","18E","19B","19C","19D","19F","20A","20B","20C","20D","20E","20F","21B","21C","21D","21E","22A","22B","22C","22D","23A","23B","23C","23D","24A","24B","24C","24E","24F","25A","25B","25C","25D","25E","25F","26A","26B","26D","26E","26F","27A","27B","27E","27F","31A","32C","32D","33A","33B","33C","33D","33E"]}]}}' -# jq -ncr --arg d_get_seats $d_get_seats '$d_get_seats|fromjson' - -# FligthExtrasQuery -# q_flight_extras='{"variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"},"query":"query FligthExtrasQuery($basketId: String!) {\n flightExtras(basketId: $basketId, products: [FAST, SEATS]) {\n ...ExtraFrag\n }\n}\n\nfragment ExtraFrag on FlightExtra {\n code\n maxPerPassenger\n minPrice\n priceDetails {\n journeyNumber\n }\n}\n"}' -# jq -ncr --arg q_flight_extras "$q_flight_extras" '$q_flight_extras|fromjson' -# d_flight_extras='{"data":{"flightExtras":[{"code":"SEATS","maxPerPassenger":null,"minPrice":10,"priceDetails":[{"journeyNumber":0},{"journeyNumber":1}]},{"code":"FAST","maxPerPassenger":null,"minPrice":9.99,"priceDetails":[{"journeyNumber":0},{"journeyNumber":1}]}]}}' - -# AssignSeat -# m_assign_seats='{"query":"mutation AssignSeat($basketId: String!, $seats: [SeatInputType]!) {\n assignSeat(basketId: $basketId, seats: $seats) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","seats":[{"journeyNum":0,"segmentNum":0,"paxNum":0,"designator":""},{"journeyNum":1,"segmentNum":0,"paxNum":0,"designator":""}]},"operationName":"AssignSeat"}' -# jq -ncr --arg m_assign_seats "$m_assign_seats" '$m_assign_seats|fromjson' -# d_assign_seats='{"data":{"assignSeat":{"id":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","tripId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","dotrezSessionId":"wzfaqb5f113wkzy0od4yf41l","currency":"EUR","gettingThere":{"isPrime":false,"price":{"total":182.98},"journeys":[{"arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","fareClass":"W","fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","fareOption":null,"flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","flightNumber":"FR7763","isConnecting":false,"isDomestic":false,"journeyNum":0,"origin":"ALC","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"738","arrival":"2025-10-10T16:25:00","departure":"2025-10-10T14:30:00","destination":"DUB","duration":"02:55","hasGovernmentTax":false,"flightNumber":"FR7763","segmentNum":0,"origin":"ALC","originCountry":"ES","destinationCountry":"IE"}]},{"arrival":"2025-10-11T20:05:00","departure":"2025-10-11T16:20:00","destination":"ALC","duration":"02:45","fareClass":"J","fareKey":"BVXTTSHSD37C5T56MHXKUSHPNQQEWBGQ65FGBUYKS7UOS4PIID2WW4YZZHYUF4JYR4DCKMKCYYJXK5NR25OUPQWTPEE4NARDMM2XYZ7KO3Z2SZKW7L5EAG2JZ4YHGCQPLWJG2WUJTUNV3YAJ2UEFB2RFHTZYGWSP7NV43Y57LY45DSFTHRHIYPUULHO4WOD3UOHN6PBLDLTVH45XPRZOP26J7O34I65J362TDZZ3ZY5JKMQNVTNTPT44L33IZ6CXDXJAFMM5DN65PLFZHBWAULNVMKNT22E7QZOIEP6WZQMEYV7Y5WQMFCSEXJJQEHJYABFPBIK42X6IACOMTUUUBLSQI5LZMJESX6NPR3I","fareOption":null,"flightKey":"FR~7762~ ~~DUB~10/11/2025 16:20~ALC~10/11/2025 20:05~~","flightNumber":"FR7762","isConnecting":false,"isDomestic":false,"journeyNum":1,"origin":"DUB","changeInfo":{"isChangeable":false,"freeMove":false,"isChanged":false},"segments":[{"aircraft":"738","arrival":"2025-10-11T20:05:00","departure":"2025-10-11T16:20:00","destination":"ALC","duration":"02:45","hasGovernmentTax":false,"flightNumber":"FR7762","segmentNum":0,"origin":"DUB","originCountry":"IE","destinationCountry":"ES"}]}],"discounts":[{"amount":5.0000,"code":"PD","journeyNum":1,"percentage":0,"zone":null,"description":null,"qty":0}],"taxes":[],"vouchers":[],"components":[{"id":"323c0eed-6562-48e5-9eee-1d05a9759154","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":111.99,"total":111.99,"discount":0.0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":0}},{"id":"fd5e43d3-0aa0-4d37-8d7a-874f30dd12ca","parentId":null,"code":"ADT","type":"FARE","quantity":1,"removable":false,"hidden":false,"price":{"amountWithTaxes":70.99,"total":70.99,"discount":0.0,"discountCode":null},"variant":{"fareOption":"REGULAR","journeyNumber":1}},{"id":"6a9d84c4-adab-4142-bc6a-aa9130b83a3b","parentId":"323c0eed-6562-48e5-9eee-1d05a9759154","code":"SBG","type":"SMALL_BAG","quantity":1,"removable":true,"hidden":false,"price":{"amountWithTaxes":0,"total":0,"discount":0,"discountCode":null},"variant":{"journeyNumber":0,"paxNumber":0,"segmentNumber":0}},{"id":"3d9e683e-86e5-46af-91dc-ee4a79880dad","parentId":"fd5e43d3-0aa0-4d37-8d7a-874f30dd12ca","code":"SBG","type":"SMALL_BAG","quantity":1,"removable":true,"hidden":false,"price":{"amountWithTaxes":0,"total":0,"discount":0,"discountCode":null},"variant":{"journeyNumber":1,"paxNumber":0,"segmentNumber":0}}],"messages":[]},"price":{"total":182.98},"payLater":{"total":0},"totalToPay":182.98}}}' - -# ProductQuery -# q_products='{"query":"query Products($basketId: String!, $productQuery: ProductQuery!, $isCheckInFlow: Boolean!) {\n products(basketId: $basketId, productQuery: $productQuery) {\n bags: bag {\n ...BagFrag\n }\n equipments: equipment {\n ...EquipmentFrag\n }\n flightExtras: flightExtra {\n ...ExtraFrag\n }\n priorityBoarding {\n ...PriorityBoardingFrag\n }\n }\n excessBag(basketId: $basketId, isCheckInFlow: $isCheckInFlow) {\n code\n price\n paxType\n paxNum\n journeyNum\n maxPerPassenger\n maxPerBag\n }\n}\n\nfragment BagFrag on Bag {\n journeyNum\n maxPerPassenger\n offers {\n ...BagOfferFrag\n }\n}\n\nfragment BagOfferFrag on BagOffer {\n code\n maxPerPassenger\n price {\n ...BagOfferPriceFrag\n }\n}\n\nfragment BagOfferPriceFrag on BagOfferPrice {\n discountPercentage\n discountType\n originalPrice\n paxType\n total\n totalDiscount\n strikeThrough\n}\n\nfragment EquipmentFrag on Equipment {\n journeyNum\n offers {\n code\n type\n maxPerPassenger\n availableItems\n prices {\n paxType\n total\n }\n }\n}\n\nfragment ExtraFrag on FlightExtra {\n code\n discountPercentage\n discountType\n maxPerPassenger\n minOriginalPrice\n minPrice\n totalDiscount\n priceDetails {\n journeyNumber\n segmentNumber\n minPrice\n minOriginalPrice\n discountType\n totalDiscount\n dsc\n }\n}\n\nfragment PriorityBoardingFrag on PriorityBoarding {\n code\n price\n paxType\n journeyNumber\n segmentNumber\n strikeThrough\n}\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","productQuery":{"productTypes":["PRIOBRDNG","CBAG","BAGS","EQUIPMENT"]},"isCheckInFlow":false}}' -# jq -ncr --arg q_products "$q_products" '$q_products' -# d_products='{"data":{"products":{"bags":[{"journeyNum":0,"maxPerPassenger":3,"offers":[{"code":"BBG","maxPerPassenger":3,"price":[{"discountPercentage":null,"discountType":"None","originalPrice":32.49,"paxType":"ADT","total":32.49,"totalDiscount":0.0,"strikeThrough":null}]},{"code":"CBAG","maxPerPassenger":1,"price":[{"discountPercentage":null,"discountType":"None","originalPrice":22.49,"paxType":"ADT","total":22.49,"totalDiscount":0.0,"strikeThrough":null}]}]},{"journeyNum":1,"maxPerPassenger":3,"offers":[{"code":"BBG","maxPerPassenger":3,"price":[{"discountPercentage":null,"discountType":"None","originalPrice":28.34,"paxType":"ADT","total":28.34,"totalDiscount":0.0,"strikeThrough":null}]},{"code":"CBAG","maxPerPassenger":1,"price":[{"discountPercentage":null,"discountType":"None","originalPrice":15.74,"paxType":"ADT","total":15.74,"totalDiscount":0.0,"strikeThrough":null}]}]}],"equipments":[{"journeyNum":0,"offers":[{"code":"BABY","type":"BABY","maxPerPassenger":3,"availableItems":-1,"prices":[{"paxType":"ADT","total":15.0}]},{"code":"GOLF","type":"SPORTS","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":45.0}]},{"code":"SPRT","type":"SPORTS","maxPerPassenger":2,"availableItems":189,"prices":[{"paxType":"ADT","total":45.0}]},{"code":"SKI","type":"SPORTS","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":45.0}]},{"code":"BULK","type":"SPORTS","maxPerPassenger":2,"availableItems":20,"prices":[{"paxType":"ADT","total":60.0}]},{"code":"BIKE","type":"SPORTS","maxPerPassenger":2,"availableItems":20,"prices":[{"paxType":"ADT","total":60.0}]},{"code":"MUSC","type":"MUSIC","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":60.0}]}]},{"journeyNum":1,"offers":[{"code":"BABY","type":"BABY","maxPerPassenger":3,"availableItems":-1,"prices":[{"paxType":"ADT","total":15.0}]},{"code":"GOLF","type":"SPORTS","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":40.0}]},{"code":"SPRT","type":"SPORTS","maxPerPassenger":2,"availableItems":189,"prices":[{"paxType":"ADT","total":40.0}]},{"code":"SKI","type":"SPORTS","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":45.0}]},{"code":"BULK","type":"SPORTS","maxPerPassenger":2,"availableItems":20,"prices":[{"paxType":"ADT","total":60.0}]},{"code":"BIKE","type":"SPORTS","maxPerPassenger":2,"availableItems":20,"prices":[{"paxType":"ADT","total":60.0}]},{"code":"MUSC","type":"MUSIC","maxPerPassenger":2,"availableItems":-1,"prices":[{"paxType":"ADT","total":60.0}]}]}],"flightExtras":[{"code":"BAGS","discountPercentage":0,"discountType":"None","maxPerPassenger":3,"minOriginalPrice":0,"minPrice":28.34,"totalDiscount":0,"priceDetails":[{"journeyNumber":0,"segmentNumber":null,"minPrice":32.49,"minOriginalPrice":0,"discountType":"None","totalDiscount":0,"dsc":null},{"journeyNumber":1,"segmentNumber":null,"minPrice":28.34,"minOriginalPrice":0,"discountType":"None","totalDiscount":0,"dsc":null}]},{"code":"CBAG","discountPercentage":0,"discountType":"None","maxPerPassenger":null,"minOriginalPrice":15.74,"minPrice":15.74,"totalDiscount":0,"priceDetails":[{"journeyNumber":0,"segmentNumber":null,"minPrice":22.49,"minOriginalPrice":22.49,"discountType":"None","totalDiscount":0,"dsc":null},{"journeyNumber":1,"segmentNumber":null,"minPrice":15.74,"minOriginalPrice":15.74,"discountType":"None","totalDiscount":0,"dsc":null}]},{"code":"EQUIPMENT","discountPercentage":0,"discountType":"None","maxPerPassenger":15,"minOriginalPrice":0,"minPrice":15,"totalDiscount":0,"priceDetails":[{"journeyNumber":0,"segmentNumber":null,"minPrice":15,"minOriginalPrice":null,"discountType":"None","totalDiscount":0,"dsc":null},{"journeyNumber":1,"segmentNumber":null,"minPrice":15,"minOriginalPrice":null,"discountType":"None","totalDiscount":0,"dsc":null}]},{"code":"PRIOBRDNG","discountPercentage":0,"discountType":"None","maxPerPassenger":null,"minOriginalPrice":20.25,"minPrice":20.25,"totalDiscount":0,"priceDetails":[{"journeyNumber":0,"segmentNumber":null,"minPrice":21.5,"minOriginalPrice":21.5,"discountType":"None","totalDiscount":0,"dsc":null},{"journeyNumber":1,"segmentNumber":null,"minPrice":20.25,"minOriginalPrice":20.25,"discountType":"None","totalDiscount":0,"dsc":null}]}],"priorityBoarding":[{"code":"PS","price":21.5,"paxType":"ADT","journeyNumber":0,"segmentNumber":0,"strikeThrough":23.5},{"code":"PS","price":20.25,"paxType":"ADT","journeyNumber":1,"segmentNumber":0,"strikeThrough":22.05}]},"excessBag":[]}}' - -function dashed_market() { - local market=${1-${market}} - local part1=$(echo "$market" | cut -d'/' -f2) - local part2=$(echo "$market" | cut -d'/' -f1) - echo "$part1-$part2" -} - -function availability () { - local req="$1" - local market="${2:-$market}" - local url=$(availability_url "$req" $market) - local correlation_id=$(uuidgen | tr '[:upper:]' '[:lower:]') - http-cli --url "$url" \ - --cookie "fr-correlation-id=$correlation_id" \ - --user-agent "$ua" -} - -function availability_url () { - local request=${1:-$(cat)} - local market="${2:-$market}" - local dashedMkt=$(dashed_market $market) - local adults=$(echo $request | jq -r '.adults') - local children=$(echo $request | jq -r '.children') - local teen=0 - local infants=$(echo $request | jq -r '.infants') - local DateOut=$(echo $request | jq -r '.dateOut') - local DateIn=$(echo $request | jq -r 'if .dateIn then .dateIn else "" end') - local Origin=$(echo $request | jq -r '.origin') - local Destination=$(echo $request | jq -r '.destination') - local RoundTrip - local IncludePrimeFares="false" - if [ -n "$DateIn" ]; then - RoundTrip="true" - else - RoundTrip="false" - fi - local url="https://www.ryanair.com/api/booking/v4/${dashedMkt}/availability?ADT=${adults}&TEEN=${teen}&CHD=${children}&INF=${infants}&Origin=${Origin}&Destination=${Destination}&promoCode=&IncludeConnectingFlights=false&DateOut=${DateOut}&DateIn=${DateIn}&FlexDaysBeforeOut=2&FlexDaysOut=2&FlexDaysBeforeIn=2&FlexDaysIn=2&RoundTrip=${RoundTrip}&IncludePrimeFares=${IncludePrimeFares}&ToUs=AGREED" - echo "$url" -} - -function booking_variables () { - local basketID="$1" - local request="$2" - local fligtht="$3" - local variables=$(jq -nc --arg basketId "$basketID" --argjson request "$request" --argjson flight "$flight" --arg culture "$culture" \ - '{ basketId: $basketId } + - { createBooking: ($request | { adults, children, infants, teens } + - { flights: [ { - flightKey: $flight.flightKey, - fareKey: $flight.regularFare.fareKey, - fareOption: null } ], - discount: 0, - promoCode: "" - }), - culture: $culture - }') - echo "$variables" -} - -function create_basket () { - local url="https://www.ryanair.com/api/basketapi/it-it/graphql" - local m_create_basket='{"query":"mutation CreateBasket {\n createBasket {\n ...BasketCommon\n gettingAround {\n ...GettingAroundPillar\n }\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\nfragment VariantCar on VariantUnionType {\n ... on Car {\n rentPrice\n carName\n refId\n engineLoadId\n pickUpTime\n pickUpLocation {\n countryCode\n code\n name\n }\n dropOffTime\n dropOffLocation {\n countryCode\n code\n name\n }\n insurance\n extras {\n totalPrice\n includedInRate\n code\n price\n selected\n type\n }\n residence\n age\n }\n}\n\nfragment VariantCarRental on VariantUnionType {\n ... on CarRental {\n rentPrice\n carName\n clientId\n refId\n pickUpTime\n pickUpLocation {\n countryCode\n code\n name\n }\n dropOffTime\n dropOffLocation {\n countryCode\n code\n name\n }\n insurance\n insuranceQuoteReference\n extras {\n code\n includedInRate\n payNow\n price\n selected\n totalPrice\n type\n }\n residence\n age\n language\n searchId\n supplier\n }\n}\n\nfragment GettingAroundPillar on GettingAroundType {\n price {\n amount\n discount\n amountWithTaxes\n total\n }\n payLater {\n ...PayLaterCommon\n }\n taxes {\n amount\n }\n components {\n ...ComponentCommon\n payLater {\n amountWithTaxes\n total\n }\n variant {\n ...VariantCar\n ...VariantCarRental\n ...VariantGroundTransfer\n }\n }\n}\n\n","operationName":"CreateBasket"}' - http-cli \ - --header 'accept: application/json, text/plain, */*' \ - --user-agent "$ua" \ - --cookie "fr-correlation-id=$correlation_id" \ - --url "$url" \ - --data "$m_create_basket" -} - -function create_booking () { - local variables=$(echo "$1" | jq -c) - local url="https://www.ryanair.com/api/basketapi/it-it/graphql" - local m_create_booking='{"query":"mutation CreateBooking($basketId: String, $createBooking: CreateBookingInput!, $culture: String!) {\n createBooking(basketId: $basketId, createBooking: $createBooking, culture: $culture) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","createBooking":{"adults":1,"children":0,"infants":0,"teens":0,"flights":[{"fareKey":"2FGVMXHMTBPQ73LTTFGTOXOWCG23NTHUGQ7TCVFLESZKDQLXBEM7ENS336VRY76Z6HBQD7JC5UCFVDAM7CXNPGZCKD3A6SSQQLKOLF7HAGIG2KXSIVBQUBOWNEJOWJ7SE3KKZ3QV6LNPMI3KHKU5CE62TOJBWMEITNYPFA7ICELQJXW2RHBE6IGSIQDKHERC35QRM67MAHVXOLBXQNXPL7WOZPYQNG4L3YIUWW6KFRBJGON3QLUYETUFHICGF7PKYLA6Y7QIREYGGRZWDOYDFQJLJHDZ4AHEHHUZ7LAGXQHRNOAU4JMCABKRO4DED2HTJYP2HHQQOASYRENHOQ26SCMY43Q445DNVOPMB2A","flightKey":"FR~7763~ ~~ALC~10/10/2025 14:30~DUB~10/10/2025 16:25~~","fareOption":null}],"discount":0,"promoCode":""},"culture":"it-it"},"operationName":"CreateBooking"}' - local data=$( - jq -rcn --arg m_create_booking "$m_create_booking" '$m_create_booking|fromjson' \ - | jq -rc --arg variables "$variables" '. + { variables: $variables|fromjson }' - ) - local headers_file=$(mktemp) - local response=$(http-cli \ - --silent \ - --user-agent "$ua" \ - --header 'accept: application/json, text/plain, */*' \ - --cookie "fr-correlation-id=$correlation_id" \ - --data "$data" \ - --url "$url" \ - -D "$headers_file" \ - -D code - ) - - # echo "$response" >&2 - code=$(echo "$response" | grep '^server=' | cut -d '=' -f2) - - if [[ $code -ne 200 ]];then - echo "error" >&2 - echo "$response" >&2 - exit 1 - fi - - local jwt=$(cat "$headers_file" | grep '^jwt: ' | cut -d ' ' -f2) - local body=$(echo "$response" | grep -v "^server=" | grep -v "^proxy=" | jq ) - - local output=$(jq -n --arg jwt "$jwt" --argjson body "$body" '{ - body: $body , - jwt: $jwt - }') - echo "$output" -} - -function commit_booking () { - local basketId=${1:-$(cat)} - local url="https://www.ryanair.com/api/basketapi/it-it/graphql" - local m_commit_booking='{"query":"mutation CommitBooking($basketId: String!) {\n commitBooking(basketId: $basketId) {\n ...BasketCommon\n }\n}\n\n\nfragment TotalCommon on PriceType {\n total\n}\n\nfragment PriceCommon on PriceType {\n amountWithTaxes\n total\n discount\n discountCode\n}\n\nfragment ComponentCommon on ComponentType {\n id\n parentId\n code\n type\n quantity\n removable\n hidden\n price {\n ...PriceCommon\n }\n}\n\nfragment VariantUnionAddOn on VariantUnionType {\n ... on AddOn {\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionFare on VariantUnionType {\n ... on Fare {\n fareOption\n journeyNumber\n }\n}\n\nfragment VariantUnionSsr on VariantUnionType {\n ... on Ssr {\n journeyNumber\n paxNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionSeat on VariantUnionType {\n ... on Seat {\n paxNumber\n journeyNumber\n segmentNumber\n seatType\n designator\n childSeatsWithAdult\n hasAdditionalSeatCost\n primeIncluded\n }\n}\n\nfragment VariantUnionBundle on VariantUnionType {\n ... on Bundle {\n journeyNumber\n segmentNumber\n }\n}\n\nfragment VariantUnionParkingAddOn on VariantUnionType {\n ... on ParkingAddOn {\n carParkName\n itemId\n provider\n paxNumber\n pax\n src\n start\n end\n }\n}\n\nfragment VariantUnionVoucher on VariantUnionType {\n ... on Voucher {\n firstName\n lastName\n email\n }\n}\n\nfragment VariantUnionPhysicalVoucher on VariantUnionType {\n ... on PhysicalVoucher {\n sequenceNumber\n firstName\n lastName\n address1\n address2\n city\n postalCode\n country\n countryName\n scheduleDate\n message\n }\n}\n\nfragment VariantUnionDigitalVoucher on VariantUnionType {\n ... on DigitalVoucher {\n sequenceNumber\n firstName\n lastName\n email\n theme\n scheduleDate\n scheduleTime\n message\n }\n}\n\nfragment VariantUnionPhysicalVoucherShippingAddress on VariantUnionType {\n ... on PhyscialVoucherShippingAddress {\n address1\n address2\n city\n postalCode\n country\n countryName\n firstName\n lastName\n }\n}\n\nfragment VariantUnionChangePaxType on VariantUnionType {\n ... on ChangePassengerType {\n passengerNumber\n invalidJourneys {\n journeyNumber\n passengers {\n passengerNumber\n passengerType\n }\n }\n mandatorySeatPricesWithoutDiscount {\n journeyNumber\n passengerNumber\n feePriceWithoutDiscount\n cost\n }\n }\n}\n\nfragment VariantUnionAddInfant on VariantUnionType {\n ... on AddInfant {\n journeyNumber\n invalidPassengers {\n passengerNumber\n passengerType\n }\n segmentNumber\n paxNumber\n }\n}\n\nfragment VariantGroundTransfer on VariantUnionType {\n ... on GroundTransfer {\n locationPickUp\n locationDropOff\n routeType\n startDate\n endDate\n itemId\n location\n }\n}\n\nfragment GettingTherePillar on GettingThereType {\n isPrime\n price {\n ...TotalCommon\n }\n journeys {\n ... on JourneyType {\n arrival\n departure\n destination\n duration\n fareClass\n fareKey\n fareOption\n flightKey\n flightNumber\n isConnecting\n isDomestic\n journeyNum\n origin\n changeInfo {\n ... on ChangeInfoType {\n isChangeable\n freeMove\n isChanged\n }\n }\n segments {\n ... on SegmentType {\n aircraft\n arrival\n departure\n destination\n duration\n hasGovernmentTax\n flightNumber\n segmentNum\n origin\n originCountry\n destinationCountry\n }\n }\n }\n }\n discounts {\n ... on DiscountType {\n amount\n code\n journeyNum\n percentage\n zone\n description\n qty\n }\n }\n taxes {\n ... on TaxType {\n amount\n code\n journeyNum\n percentage\n zone\n }\n }\n vouchers {\n ... on VoucherType {\n amount\n code\n status\n accountNumber\n voucherBasisCode\n }\n }\n components {\n ... on ComponentType {\n ...ComponentCommon\n variant {\n ...VariantUnionAddOn\n ...VariantUnionFare\n ...VariantUnionSsr\n ...VariantUnionSeat\n ...VariantGroundTransfer\n ...VariantUnionBundle\n ...VariantUnionParkingAddOn\n ...VariantUnionVoucher\n ...VariantUnionDigitalVoucher\n ...VariantUnionPhysicalVoucher\n ...VariantUnionPhysicalVoucherShippingAddress\n ...VariantUnionChangePaxType\n ...VariantUnionAddInfant\n }\n }\n }\n messages {\n ... on MessageType {\n type\n journeyNum\n key\n message\n }\n }\n}\n\nfragment PayLaterCommon on PriceType {\n total\n}\n\nfragment BasketCommon on BasketType {\n id\n tripId\n dotrezSessionId\n currency\n gettingThere {\n ...GettingTherePillar\n }\n price {\n ...TotalCommon\n }\n payLater {\n ...PayLaterCommon\n }\n totalToPay\n}\n\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db"},"operationName":"CommitBooking"}' - local data=$(jq -nrc --arg m_commit_booking "$m_commit_booking" '$m_commit_booking|fromjson' | jq -c --arg basketId "$basketId" '.variables.basketId|=$basketId') - echo "$data" | jq >&2 - http-cli --silent \ - --url "$url" \ - --header "authorization: Bearer $JWT" \ - --header 'accept: application/json, text/plain, */*' \ - --user-agent "$ua" \ - --cookie "fr-correlation-id=$correlation_id" \ - --data "$data" -} - -function catalog_products () { - local bookingId="$1" - local jwt="${2:-${JWT}}" - local url="https://www.ryanair.com/api/catalogapi/it-it/graphql" - local q_products='{"query":"query Products($basketId: String!, $productQuery: ProductQuery!, $isCheckInFlow: Boolean!) {\n products(basketId: $basketId, productQuery: $productQuery) {\n bags: bag {\n ...BagFrag\n }\n equipments: equipment {\n ...EquipmentFrag\n }\n flightExtras: flightExtra {\n ...ExtraFrag\n }\n priorityBoarding {\n ...PriorityBoardingFrag\n }\n }\n excessBag(basketId: $basketId, isCheckInFlow: $isCheckInFlow) {\n code\n price\n paxType\n paxNum\n journeyNum\n maxPerPassenger\n maxPerBag\n }\n}\n\nfragment BagFrag on Bag {\n journeyNum\n maxPerPassenger\n offers {\n ...BagOfferFrag\n }\n}\n\nfragment BagOfferFrag on BagOffer {\n code\n maxPerPassenger\n price {\n ...BagOfferPriceFrag\n }\n}\n\nfragment BagOfferPriceFrag on BagOfferPrice {\n discountPercentage\n discountType\n originalPrice\n paxType\n total\n totalDiscount\n strikeThrough\n}\n\nfragment EquipmentFrag on Equipment {\n journeyNum\n offers {\n code\n type\n maxPerPassenger\n availableItems\n prices {\n paxType\n total\n }\n }\n}\n\nfragment ExtraFrag on FlightExtra {\n code\n discountPercentage\n discountType\n maxPerPassenger\n minOriginalPrice\n minPrice\n totalDiscount\n priceDetails {\n journeyNumber\n segmentNumber\n minPrice\n minOriginalPrice\n discountType\n totalDiscount\n dsc\n }\n}\n\nfragment PriorityBoardingFrag on PriorityBoarding {\n code\n price\n paxType\n journeyNumber\n segmentNumber\n strikeThrough\n}\n","variables":{"basketId":"2f3e14fa-c78a-4906-b021-d5ee4222e0db","productQuery":{"productTypes":["PRIOBRDNG","CBAG","BAGS","EQUIPMENT"]},"isCheckInFlow":false}}' - local q_products_json=$(jq -ncr --arg q_products "$q_products" '$q_products|fromjson') - # echo "bookingId:$bookingId" >&2 - echo "$q_products_json" | jq >&2 - local data=$(jq -n --arg bookingId "$bookingId" \ - --argjson query "$q_products_json" ' - $query + { - variables: ($query.variables + {basketId: $bookingId}) - }') - echo "$data" | jq >&2 - http-cli \ - --silent \ - --url "$url" \ - --header "authorization: Bearer $jwt" \ - --user-agent "$ua" \ - --header 'accept: application/json, text/plain, */*' \ - --cookie "fr-correlation-id=$correlation_id" \ - --data "$data" -} diff --git a/examples/run b/examples/run deleted file mode 100755 index 944cff8..0000000 --- a/examples/run +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -source $(realpath $(dirname $0))/rrelay.sh - -request='{"adults":2,"children":1,"infants":0,"teens":0,"origin":"FCO","destination":"CTA","dateOut":"2025-07-15"}' -echo $request >&2 -correlation_id=$(uuidgen | tr '[:upper:]' '[:lower:]') -# export USER_AGENT=$(randua) - -departureDate=$(echo "$request" | jq -r '.dateOut +"T00:00:00.000"') -# echo "$departureDate" >&2 - -flight=$(availability "$request" \ - | jq --arg dateOut "$departureDate" '.trips[].dates[]|select(.dateOut == $dateOut).flights|first|del(.segments)' -) -# {"faresLeft":4,"flightKey":"FR~ 59~ ~~NAP~07/10/2025 15:40~BCN~07/10/2025 17:40~~","infantsLeft":16,"regularFare":{"fareKey":"VES4L3AY6V7QCEKP25YZQOZOZFWRY6UDQRC75USHLWWP46O6ECSN3JO4DDEUY7PI6ZAYZLISSNP4PT65R2P2ARTBKRII4CWGMYQKF4XU5WT37LIAC6LWDC332SPT64QR3GVVX53FTHOJNJMUUVQHXIUMHUVE6N3XE3O7NOLV4B4LZPUNAUC6U7O3ZSFR246HBE4CAXKCCXWOHC45UGVSGFR4WCXUGBKVKW54IAFFT6EXQYIWSKSFUHLX2ESS3AYRWPRKDLH22KKRDX7HDT4XRZEEJZMLX5BS2WQ6M5ORROOZJ3FMAWVPPDOP3GY742C2OZDU5IEDFB3YAFOJPHSTPDAIG3IW6YDTHMNCHGI","fares":[{"type":"ADT","amount":197.99,"count":1,"hasDiscount":false,"publishedFare":197.99,"discountInPercent":0,"hasPromoDiscount":false,"discountAmount":0.0,"hasBogof":false,"isPrime":false}]},"operatedBy":"","isSSIMLoad":false,"flightNumber":"FR 59","time":["2025-07-10T15:40:00.000","2025-07-10T17:40:00.000"],"timeUTC":["2025-07-10T13:40:00.000Z","2025-07-10T15:40:00.000Z"],"duration":"02:00"} -echo "$flight" | jq -c >&2 - -basketID=$(create_basket | jq -r '.data.createBasket.id') -# echo "basketId:$basketID" >&2 -culture=$(dashed_market "$market") -# echo "$culture" >&2 -variables=$(booking_variables "$basketID" "$request" "$flight") -# echo "$variables" | jq >&2 -booking=$(create_booking "$variables") - -if [ $? -ne 0 ]; then - echo "Error creating booking: $booking" >&2 - exit 1 -fi -export JWT=$(echo "$booking" | jq -r .jwt) -data=$(echo "$booking" | jq -r .body) -echo "$data | jq '.data.createBooking|{ currency, price }'" - -# echo "$JWT" >&2 -booking=$(commit_booking "$basketID" | jq .data.commitBooking) -echo "$booking" | jq >&2 -data=$(catalog_products "$basketID" "$JWT") -echo "$data" | jq >&2 \ No newline at end of file diff --git a/examples/service.sh b/examples/service.sh deleted file mode 100644 index 8375ed7..0000000 --- a/examples/service.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash - -export HOME=/tmp - -# set -x - -function booking () { - local body=$(echo "$1" | jq -r .body) - local reservationNumber=$(echo "$body" | jq -r .reservationNumber) - local email=$(echo "$body" | jq -r .email) - local data=$(GetBookingByReservationNumber "$reservationNumber" "$email" ) - # data=$(GetBookingInfoByReservationNumber "$reservationNumber") - - jq -n --argjson data "$data" '{ - statusCode: 200, - body: ($data | tostring), - headers: { - "Content-Type": "application/json" - } - }' -} - -# Get Booking info from a parsed job JSON object -GetBookingInfo () { - local info=${1} - # mapfile -t args < <(echo "$info"\ - # | jq -r '.pnr, .email') - # GetBookingByReservationNumber "${args[@]}" \ - # | jq - pnr=$(echo "$info" | jq -r '.pnr') - email=$(echo "$info" | jq -r '.email') - GetBookingByReservationNumber "$pnr" "$email" | jq -} - -# Given a PNR, fethes the Booking email from ddb and -# retrive airline Booking info -GetBookingInfoByReservationNumber () { - local reservationNumber=${1} - local job - job=$(GetJobByReservationNumber "$reservationNumber") - if [[ $? -ne 0 ]]; then - echo "Error: $job" >&2 - return 1 - fi - GetBookingInfo "$job" -} - - -GetJobByReservationNumber() { - local pnr="$1" - local table="flygo-flygo-booking-ryanair" - local region="eu-south-1" - local profile="flygo" - - awscurl \ - --service dynamodb \ - --region "$region" \ - --profile "$profile" \ - -X POST \ - -H "Content-Type: application/x-amz-json-1.0" \ - -H "X-Amz-Target: DynamoDB_20120810.Query" \ - -d "{ - \"TableName\": \"$table\", - \"IndexName\": \"StatusPNRIndex\", - \"KeyConditionExpression\": \"#s = :s AND #p = :p\", - \"ExpressionAttributeNames\": { - \"#s\": \"status\", - \"#p\": \"pnr\" - }, - \"ExpressionAttributeValues\": { - \":s\": {\"S\": \"success\"}, - \":p\": {\"S\": \"$pnr\"} - }, - \"ProjectionExpression\": \"pnr, email, Id, createdAt, authToken\", - \"Limit\": 1, - \"ScanIndexForward\": false - }" https://dynamodb."$region".amazonaws.com/ | - jq -r '.Items[0] | { - pnr: .pnr.S, - id: .Id.S, - email: .email.S, - timestamp: .createdAt.S, - sessionToken: .authToken.S - }' -} - -function GetBookingByReservationNumber () { - local reservationNumber=${1} - local email=${2} - - if [[ -z "$reservationNumber" ]]; then - echo "Reservation number is required" >&2 - return 1 - else - shift - fi - - if [[ -z "$email" ]]; then - echo "Email is required" >&2 - return 1 - else - shift - fi - - http-cli \ - --method POST \ - --url 'https://www.ryanair.com/api/bookingfa/it-it/graphql' \ - --user-agent 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36' \ - --data $'{"query":"query GetBookingByReservationNumber($bookingInfo: GetBookingByReservationNumberInputType\u0021) {\\n getBookingByReservationNumber(bookingInfo: $bookingInfo) {\\n addons {\\n ...AddonFrag\\n }\\n carHire {\\n ...CarHireFrag\\n }\\n contacts {\\n ...ContactsFrag\\n }\\n extras {\\n ...ExtrasOrFeesFrag\\n }\\n groundTransfer {\\n ...GroundTransferFrag\\n }\\n hotels {\\n ...HotelsFrag\\n }\\n info {\\n ...InfoFrag\\n }\\n journeys {\\n ...JourneysFrag\\n }\\n passengers {\\n ...PassengersFrag\\n }\\n payments {\\n ...PaymentInfoFrag\\n }\\n fees {\\n ...ExtrasOrFeesFrag\\n }\\n serverTimeUTC\\n sessionToken\\n tripId\\n }\\n}\\n\\n\\nfragment AddonFrag on AddOnResponseModelType {\\n carParkName\\n code\\n currLater\\n currNow\\n dropOffLocation\\n end\\n isSingleOffer\\n itemId\\n loc\\n name\\n pax\\n paxNum\\n pickUpLocation\\n provider\\n providerCode\\n qty\\n refNo\\n sold\\n src\\n start\\n status\\n total\\n type\\n}\\n\\nfragment CarHireFrag on CarHireResponseModelType {\\n carSupplierConfirmationId\\n carType\\n confirmationId\\n currencyCode\\n insurance\\n pickupDateTime\\n pickupLocation\\n returnDateTime\\n returnLocation\\n serviceProvider\\n sold\\n status\\n totalPrice\\n}\\n\\nfragment PassengerNameFrag on PassengerNameResponseModelType {\\n first\\n last\\n middle\\n suffix\\n title\\n}\\n\\nfragment ContactsFrag on ContactResponseModelType {\\n address\\n city\\n country\\n cultureCode\\n email\\n fax\\n homePhone\\n name {\\n ...PassengerNameFrag\\n }\\n otherPhone\\n postalCode\\n provinceState\\n type\\n workPhone\\n}\\n\\nfragment ExtrasOrFeesFrag on BookingExtraResponseModelType {\\n amt\\n code\\n includedSsrs\\n isPartOfBundle\\n isSeatChange\\n journeyNum\\n percentageDiscount\\n qty\\n segmentNum\\n sold\\n total\\n totalDiscount\\n totalWithoutDiscount\\n type\\n vat\\n}\\n\\nfragment GroundTransferFrag on GroundTransferResponseModelType {\\n confirmationId\\n currencyCode\\n dropoffDateTime\\n dropoffLocation\\n flightBookingId\\n isSold\\n pickupDateTime\\n pickupLocation\\n pnr\\n status\\n totalPrice\\n}\\n\\nfragment HotelsFrag on HotelsResponseModelType {\\n status\\n}\\n\\nfragment InfoFrag on BookingInfoResponseModelType {\\n allSeatsAutoAllocated\\n balanceDue\\n bookingAgent\\n bookingId\\n createdUtcDate\\n curr\\n currPrecision\\n domestic\\n holdDateTime\\n isConnectingFlight\\n isBuyOneGetOneDiscounted\\n isHoldable\\n isPrime\\n modifiedUtcDate\\n pnr\\n status\\n locationCode\\n locationCodeGroup\\n sourceOrganization\\n companyName\\n}\\n\\nfragment JourneyChangeFrag on JourneyChangeInfoResponseModelType {\\n freeMove\\n isChangeable\\n isChanged\\n reasonCode\\n}\\n\\nfragment FaresFrag on BookingFareResponseModelType {\\n amt\\n code\\n disc\\n fareKey\\n fat\\n includedSsrs\\n percentageDiscount\\n qty\\n sold\\n total\\n totalDiscount\\n totalWithoutDiscount\\n type\\n vat\\n}\\n\\nfragment FatsFrag on BookingFatResponseModelType {\\n amount\\n code\\n total\\n vat\\n description\\n qty\\n}\\n\\nfragment SeatRowDeltaFrag on PaxSeatRowDeltaResponseModelType {\\n rowDistance\\n segmentNum\\n}\\n\\nfragment SegmentsFrag on SegmentModelResponseModelType {\\n aircraft\\n arrive\\n arriveUTC\\n depart\\n departUTC\\n dest\\n duration\\n flown\\n flt\\n isCancelled\\n isDomestic\\n orig\\n segmentNum\\n vatRate\\n}\\n\\nfragment ZoneDiscountFrag on BookingZoneDiscountResponseModelType {\\n code\\n pct\\n total\\n zone\\n}\\n\\nfragment JourneysFrag on BookingJourneyResponseModelType {\\n arrive\\n arriveUTC\\n changeInfo {\\n ...JourneyChangeFrag\\n }\\n checkInCloseUtcDate\\n checkInFreeAllocateOpenUtcDate\\n checkInOpenUtcDate\\n depart\\n departUTC\\n dest\\n destCountry\\n duration\\n fareClass\\n fareOption\\n fares {\\n ...FaresFrag\\n }\\n fareType\\n fats {\\n ...FatsFrag\\n }\\n flt\\n infSsrs {\\n ...ExtrasOrFeesFrag\\n }\\n isRescheduledJourneyWithFreeMove\\n setaSsrs {\\n ...ExtrasOrFeesFrag\\n }\\n journeyNum\\n maxPaxSeatRowDistance {\\n ...SeatRowDeltaFrag\\n }\\n mobilebp\\n orig\\n origCountry\\n seatsLeft\\n segments {\\n ...SegmentsFrag\\n }\\n zoneDiscount {\\n ...ZoneDiscountFrag\\n }\\n}\\n\\nfragment ResidentInfoFrag on PassengerResidentInfoResponseModelType {\\n community\\n dateOfBirth\\n dob\\n docNum\\n docType\\n hasLargeFamilyDiscount\\n hasResidentDiscount\\n largeFamilyCert\\n municipality\\n saraValidationCode\\n}\\n\\nfragment SegmentCheckinFrag on PassengerSegmentCheckinResponseModelType {\\n journeyNum\\n segmentNum\\n status\\n}\\n\\nfragment TravelDocumentFrag on TravelDocumentResponseModelType {\\n countryOfIssue\\n dateOfBirth\\n dOB\\n docNumber\\n docType\\n expiryDate\\n nationality\\n specialVisaDetails {\\n countryOfIssue\\n docNumber\\n docType\\n }\\n}\\n\\nfragment PassengerWithInfantTravelDocumentsFrag on PassengerWithInfantTravelDocumentResponseModelType {\\n num\\n travelDocument {\\n ...TravelDocumentFrag\\n }\\n infantTravelDocument {\\n ...TravelDocumentFrag\\n }\\n}\\n\\nfragment PassengersFrag on PassengerResponseModelType {\\n dateOfBirth\\n doB\\n ins\\n inf {\\n dateOfBirth\\n dob\\n first\\n last\\n middle\\n suffix\\n title\\n }\\n name {\\n ...PassengerNameFrag\\n }\\n nationality\\n paxFees {\\n ...ExtrasOrFeesFrag\\n }\\n paxNum\\n residentInfo {\\n ...ResidentInfoFrag\\n }\\n segCheckin {\\n ...SegmentCheckinFrag\\n }\\n segFees {\\n ...ExtrasOrFeesFrag\\n }\\n segPrm {\\n ...ExtrasOrFeesFrag\\n }\\n segSeats {\\n ...ExtrasOrFeesFrag\\n }\\n segSsrs {\\n ...ExtrasOrFeesFrag\\n }\\n travelDocuments {\\n ...PassengerWithInfantTravelDocumentsFrag\\n }\\n type\\n}\\n\\nfragment PaymentInfoFrag on PaymentInfoResponseModelType {\\n accName\\n accNum\\n amt\\n code\\n currency\\n dccAmt\\n dccApplicable\\n dccCurrency\\n dccRate\\n discount\\n isReward\\n status\\n type\\n createdDate\\n invoiceNumber\\n}\\n\\n","variables":{"bookingInfo":{"reservationNumber":"'"$reservationNumber"'","emailAddress":"'"$email"'"}}}' \ - $@ -} \ No newline at end of file diff --git a/task/helpers.sh b/task/helpers.sh index 48a66af..7df4588 100644 --- a/task/helpers.sh +++ b/task/helpers.sh @@ -1,8 +1,6 @@ #!/bin/bash # https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html - - # https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html#apigateway-example-event # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format # https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format From ceb62797ec950337a9281b9361834e79dd252053 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 08:51:49 +0200 Subject: [PATCH 35/58] chore(ci): add base image build step to release workflow --- .github/workflows/release.yml | 11 +++++++++- Dockerfile | 35 +------------------------------- base.Dockerfile | 38 +++++++++++++++++++++++++++++++++++ build | 8 +++++--- micro.Dockerfile | 27 ++----------------------- tiny.Dockerfile | 38 +++-------------------------------- 6 files changed, 59 insertions(+), 98 deletions(-) create mode 100644 base.Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3deaeb8..a2d96dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,9 +44,18 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- - name: Build Docker images - run: | + run: | echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 + + docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ + --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ + --output type=docker --progress=plain \ + --secret id=github_token,src=github_token \ + --cache-from type=local,src=/tmp/.buildx-cache \ + --cache-to type=local,dest=/tmp/.buildx-cache \ + -t lambda-shell-runtime:base -f base.Dockerfile . + docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ --output type=docker --progress=plain \ diff --git a/Dockerfile b/Dockerfile index 6abe60f..8ecc84c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,7 @@ -# Stage 1: Build stage -FROM public.ecr.aws/lambda/provided:al2023 AS builder - -ARG HTTP_CLI_VERSION=v1.0.1 - -RUN dnf install -y unzip && \ - dnf clean all - -# Download http-cli -RUN --mount=type=secret,id=github_token \ - curl -H "Authorization: token $(cat /run/secrets/github_token)" \ - -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ - -o http-cli.zip && \ - unzip http-cli.zip && \ - mkdir -p /http-cli-bin && \ - mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ - chmod +x /http-cli-bin/http-cli && \ - rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} - - - FROM public.ecr.aws/lambda/provided:al2023 +FROM lamda-shell-runtime:tiny RUN dnf install -y \ jq \ aws-cli && \ dnf clean all && \ rm -rf /var/cache/dnf - -# Copy http-cli -COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli - -ENV PATH="/var/task/bin:${PATH}" - -COPY runtime/bootstrap /var/runtime/bootstrap -RUN chmod +x /var/runtime/bootstrap - -WORKDIR /var/task - -COPY task/handler.sh handler.sh -COPY task/helpers.sh helpers.sh \ No newline at end of file diff --git a/base.Dockerfile b/base.Dockerfile new file mode 100644 index 0000000..08611cb --- /dev/null +++ b/base.Dockerfile @@ -0,0 +1,38 @@ +FROM public.ecr.aws/lambda/provided:al2023 AS builder + +ARG HTTP_CLI_VERSION=v1.0.1 + +RUN dnf install -y unzip && \ + dnf clean all + +# Download http-cli +RUN --mount=type=secret,id=github_token \ + curl -H "Authorization: token $(cat /run/secrets/github_token)" \ + -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ + -o http-cli.zip && \ + unzip http-cli.zip && \ + mkdir -p /http-cli-bin && \ + mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ + chmod +x /http-cli-bin/http-cli && \ + rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} + +FROM public.ecr.aws/lambda/provided:al2023 AS base + +# Install only runtime dependencies +RUN dnf install -y jq && \ + dnf clean all && \ + rm -rf /var/cache/dnf + +# Copy http-cli +COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli +ENV PATH="/var/task/bin:${PATH}" + +COPY runtime/bootstrap /var/runtime/bootstrap +RUN chmod +x /var/runtime/bootstrap + +WORKDIR /var/task + +COPY task/handler.sh handler.sh + +# Label for documentation/reference +LABEL org.opencontainers.image.title="lambda-shell-runtime:base" \ No newline at end of file diff --git a/build b/build index 137af54..c7905e9 100755 --- a/build +++ b/build @@ -1,13 +1,13 @@ #!/bin/sh -set -e +set -ex # Default config PLATFORM="linux/arm64" MODE="--load" TAG="lambda-shell-runtime" -VARIANTS="tiny micro full" +VARIANTS="base tiny micro full" # Parse arguments while [ $# -gt 0 ]; do @@ -28,7 +28,7 @@ while [ $# -gt 0 ]; do MODE="--load" shift ;; - tiny|slim|full) + full|tiny|micro|base) VARIANTS="$1" shift ;; @@ -43,6 +43,8 @@ for VARIANT in $VARIANTS; do DOCKERFILE="./${VARIANT}.Dockerfile" [ "$VARIANT" = "full" ] && DOCKERFILE="./Dockerfile" + [ "$VARIANT" = "base" ] && MODE="--load" + echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." docker buildx build \ --platform "$PLATFORM" \ diff --git a/micro.Dockerfile b/micro.Dockerfile index 938c756..6fec6c3 100644 --- a/micro.Dockerfile +++ b/micro.Dockerfile @@ -1,28 +1,15 @@ FROM public.ecr.aws/lambda/provided:al2023 AS builder -ARG HTTP_CLI_VERSION=v1.0.1 - RUN dnf install -y unzip python3-pip findutils && \ dnf clean all -# Download http-cli -RUN --mount=type=secret,id=github_token \ - curl -H "Authorization: token $(cat /run/secrets/github_token)" \ - -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ - -o http-cli.zip && \ - unzip http-cli.zip && \ - mkdir -p /http-cli-bin && \ - mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ - chmod +x /http-cli-bin/http-cli && \ - rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} - RUN pip3 install --no-cache-dir --target /tmp/awscurl awscurl && \ find /tmp/awscurl -type d -name '__pycache__' -exec rm -rf {} + && \ find /tmp/awscurl -type f -name '*.pyc' -delete && \ find /tmp/awscurl -type d -name '*.dist-info' -exec rm -rf {} + # Stage 2: Runtime stage -FROM public.ecr.aws/lambda/provided:al2023 +FROM lambda-shell-runtime:tiny AS micro # Install only runtime dependencies RUN dnf install -y jq python3 && \ @@ -43,14 +30,4 @@ RUN mkdir -p /var/task/bin && \ printf '#!/bin/sh\nexport PYTHONPATH=/var/task/aws\nexec python3 -m awscurl.awscurl "$@"\n' > /var/task/bin/awscurl && \ chmod +x /var/task/bin/awscurl -# Copy http-cli -COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli -ENV PATH="/var/task/bin:${PATH}" - -COPY runtime/bootstrap /var/runtime/bootstrap -RUN chmod +x /var/runtime/bootstrap - -WORKDIR /var/task - -COPY task/handler.sh handler.sh -COPY task/helpers.sh helpers.sh +LABEL org.opencontainers.image.title="lambda-shell-runtime:micro" \ No newline at end of file diff --git a/tiny.Dockerfile b/tiny.Dockerfile index c734a2b..58a134b 100644 --- a/tiny.Dockerfile +++ b/tiny.Dockerfile @@ -1,37 +1,5 @@ -FROM public.ecr.aws/lambda/provided:al2023 AS builder +FROM lambda-shell-runtime:base AS tiny -ARG HTTP_CLI_VERSION=v1.0.1 - -RUN dnf install -y unzip && \ - dnf clean all - -# Download http-cli -RUN --mount=type=secret,id=github_token \ - curl -H "Authorization: token $(cat /run/secrets/github_token)" \ - -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ - -o http-cli.zip && \ - unzip http-cli.zip && \ - mkdir -p /http-cli-bin && \ - mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ - chmod +x /http-cli-bin/http-cli && \ - rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} - -# Stage 2: Runtime stage -FROM public.ecr.aws/lambda/provided:al2023 - -# Install only runtime dependencies -RUN dnf install -y jq && \ - dnf clean all && \ - rm -rf /var/cache/dnf - -# Copy http-cli -COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli -ENV PATH="/var/task/bin:${PATH}" - -COPY runtime/bootstrap /var/runtime/bootstrap -RUN chmod +x /var/runtime/bootstrap - -WORKDIR /var/task - -COPY task/handler.sh handler.sh COPY task/helpers.sh helpers.sh + +LABEL org.opencontainers.image.title="lambda-shell-runtime:tiny" \ No newline at end of file From dde782d6903bc469d53f11207e9b20bde36b6756 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 09:46:47 +0200 Subject: [PATCH 36/58] build: use a single Dockerfile for all targets --- .github/workflows/release.yml | 39 +++++++++---- Dockerfile | 107 +++++++++++++++++++++++++++++++++- build | 17 ++++-- micro.Dockerfile | 33 ----------- tiny.Dockerfile | 5 -- 5 files changed, 143 insertions(+), 58 deletions(-) delete mode 100644 micro.Dockerfile delete mode 100644 tiny.Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2d96dd..1e31704 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,42 +43,57 @@ jobs: key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx- + - name: Get version from semantic-release + id: version + run: | + VERSION=$(npx semantic-release --dry-run | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+') + echo "VERSION=$VERSION" >> $GITHUB_ENV - name: Build Docker images run: | echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 + VERSION=$VERSION + IMAGE=ghcr.io/ql4b/lambda-shell-runtime docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ - --output type=docker --progress=plain \ + --build-arg HTTP_CLI_VERSION=$VERSION \ + --output type=registry --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ - -t lambda-shell-runtime:base -f base.Dockerfile . + -t $IMAGE:base-$VERSION \ + -t $IMAGE:base-latest \ + -f base.Dockerfile . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ - --output type=docker --progress=plain \ + --build-arg HTTP_CLI_VERSION=$VERSION \ + --output type=registry --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ - -t lambda-shell-runtime:tiny -f tiny.Dockerfile . + -t $IMAGE:tiny-$VERSION \ + -t $IMAGE:tiny-latest \ + -f tiny.Dockerfile . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ - --output type=docker --progress=plain \ + --build-arg HTTP_CLI_VERSION=$VERSION \ + --output type=registry --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ - -t lambda-shell-runtime:micro -f micro.Dockerfile . + -t $IMAGE:micro-$VERSION \ + -t $IMAGE:micro-latest \ + -f micro.Dockerfile . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg HTTP_CLI_VERSION=${{ env.HTTP_CLI_VERSION }} \ - --output type=docker --progress=plain \ + --build-arg HTTP_CLI_VERSION=$VERSION \ + --output type=registry --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ - -t lambda-shell-runtime:full -f Dockerfile . + -t $IMAGE:full-$VERSION \ + -t $IMAGE:full-latest \ + -f Dockerfile . shell: bash - name: Log in to GHCR run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin diff --git a/Dockerfile b/Dockerfile index 8ecc84c..27da39b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,108 @@ -FROM lamda-shell-runtime:tiny +FROM public.ecr.aws/lambda/provided:al2023 AS builder + +ARG HTTP_CLI_VERSION=v1.0.1 + +RUN dnf install -y unzip && \ + dnf clean all + +# Download http-cli +RUN --mount=type=secret,id=github_token \ + curl -H "Authorization: token $(cat /run/secrets/github_token)" \ + -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ + -o http-cli.zip && \ + unzip http-cli.zip && \ + mkdir -p /http-cli-bin && \ + mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ + chmod +x /http-cli-bin/http-cli && \ + rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} + +LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" + +# base: minimal runtime setup with jq +FROM public.ecr.aws/lambda/provided:al2023 AS base + +ARG VERSION=develop +ARG HTTP_CLI_VERSION + +# Install only runtime dependencies +RUN dnf install -y jq && \ + dnf clean all && \ + rm -rf /var/cache/dnf + +# Copy http-cli +COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli +ENV PATH="/var/task/bin:${PATH}" + +COPY runtime/bootstrap /var/runtime/bootstrap +RUN chmod +x /var/runtime/bootstrap + +WORKDIR /var/task + +COPY task/handler.sh handler.sh + +LABEL org.opencontainers.image.source="https://github.com/ql4b/lambda-shell-runtime" +LABEL org.opencontainers.image.version="${VERSION}" + +# tiny: add lamnda helper functions +FROM base AS tiny + +ARG VERSION +ARG HTTP_CLI_VERSION + +COPY task/helpers.sh helpers.sh + +LABEL org.opencontainers.image.title="lambda-shell-runtime:tiny" +LABEL org.opencontainers.image.version="${VERSION}" +LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" + +FROM public.ecr.aws/lambda/provided:al2023 AS awscurl-installer + +RUN dnf install -y unzip python3-pip findutils && \ + dnf clean all + +RUN pip3 install --no-cache-dir --target /tmp/awscurl awscurl && \ + find /tmp/awscurl -type d -name '__pycache__' -exec rm -rf {} + && \ + find /tmp/awscurl -type f -name '*.pyc' -delete && \ + find /tmp/awscurl -type d -name '*.dist-info' -exec rm -rf {} + + +# micro: inclues awscurl +FROM tiny AS micro + +ARG VERSION +ARG HTTP_CLI_VERSION + +RUN dnf install -y python3 && \ + dnf clean all && \ + rm -rf /var/cache/dnf + +COPY --from=awscurl-installer /tmp/awscurl /var/task/aws +# Clean up Python cache and metadata +RUN rm -rf \ + /var/task/aws/__pycache__ \ + /var/task/aws/*.dist-info \ + /var/task/aws/**/__pycache__ + +ENV PYTHONPATH="/var/task/aws" + +RUN mkdir -p /var/task/bin && \ + printf '#!/bin/sh\nexport PYTHONPATH=/var/task/aws\nexec python3 -m awscurl.awscurl "$@"\n' > /var/task/bin/awscurl && \ + chmod +x /var/task/bin/awscurl + +LABEL org.opencontainers.image.title="lambda-shell-runtime:micro" +LABEL org.opencontainers.image.version="${VERSION}" +LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" + +# full: includes aws-cli for complete AWS functionality +FROM tiny AS full + +ARG VERSION +ARG HTTP_CLI_VERSION RUN dnf install -y \ - jq \ aws-cli && \ dnf clean all && \ - rm -rf /var/cache/dnf + rm -rf /var/cache/dnf + +LABEL org.opencontainers.image.title="lambda-shell-runtime:full" +LABEL org.opencontainers.image.version="${VERSION}" +LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" \ No newline at end of file diff --git a/build b/build index c7905e9..c89594a 100755 --- a/build +++ b/build @@ -1,4 +1,3 @@ - #!/bin/sh set -ex @@ -7,6 +6,7 @@ set -ex PLATFORM="linux/arm64" MODE="--load" TAG="lambda-shell-runtime" +VERSION="${VERSION:-dev}" VARIANTS="base tiny micro full" # Parse arguments @@ -40,10 +40,10 @@ while [ $# -gt 0 ]; do done for VARIANT in $VARIANTS; do - DOCKERFILE="./${VARIANT}.Dockerfile" - [ "$VARIANT" = "full" ] && DOCKERFILE="./Dockerfile" + DOCKERFILE="./Dockerfile" + TARGET="$VARIANT" - [ "$VARIANT" = "base" ] && MODE="--load" + [ "$VARIANT" = "base" ] && TARGET="" echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." docker buildx build \ @@ -51,6 +51,13 @@ for VARIANT in $VARIANTS; do --secret id=github_token,env=GITHUB_TOKEN \ --tag $TAG:$VARIANT \ --file "$DOCKERFILE" \ + ${TARGET:+--target "$TARGET"} \ $MODE \ . -done \ No newline at end of file + + if [ -n "$VERSION" ]; then + echo "Tagging $TAG:$VARIANT-$VERSION" + docker tag $TAG:$VARIANT $TAG:$VARIANT-$VERSION + fi +done + diff --git a/micro.Dockerfile b/micro.Dockerfile deleted file mode 100644 index 6fec6c3..0000000 --- a/micro.Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -FROM public.ecr.aws/lambda/provided:al2023 AS builder - -RUN dnf install -y unzip python3-pip findutils && \ - dnf clean all - -RUN pip3 install --no-cache-dir --target /tmp/awscurl awscurl && \ - find /tmp/awscurl -type d -name '__pycache__' -exec rm -rf {} + && \ - find /tmp/awscurl -type f -name '*.pyc' -delete && \ - find /tmp/awscurl -type d -name '*.dist-info' -exec rm -rf {} + - -# Stage 2: Runtime stage -FROM lambda-shell-runtime:tiny AS micro - -# Install only runtime dependencies -RUN dnf install -y jq python3 && \ - dnf clean all && \ - rm -rf /var/cache/dnf - -# Copy only what's needed -COPY --from=builder /tmp/awscurl /var/task/aws -# Clean up Python cache and metadata -RUN rm -rf \ - /var/task/aws/__pycache__ \ - /var/task/aws/*.dist-info \ - /var/task/aws/**/__pycache__ - -ENV PYTHONPATH="/var/task/aws" - -RUN mkdir -p /var/task/bin && \ - printf '#!/bin/sh\nexport PYTHONPATH=/var/task/aws\nexec python3 -m awscurl.awscurl "$@"\n' > /var/task/bin/awscurl && \ - chmod +x /var/task/bin/awscurl - -LABEL org.opencontainers.image.title="lambda-shell-runtime:micro" \ No newline at end of file diff --git a/tiny.Dockerfile b/tiny.Dockerfile deleted file mode 100644 index 58a134b..0000000 --- a/tiny.Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM lambda-shell-runtime:base AS tiny - -COPY task/helpers.sh helpers.sh - -LABEL org.opencontainers.image.title="lambda-shell-runtime:tiny" \ No newline at end of file From a217fb64c7f5ffd95b1b01c19c6825871e78f6c5 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 09:51:35 +0200 Subject: [PATCH 37/58] feat: add base image build step to release workflow --- .github/workflows/release.yml | 15 ++++++++++++++- Dockerfile | 2 +- base.Dockerfile | 10 ++++++++-- build | 2 ++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1e31704..bdf4357 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,8 +45,21 @@ jobs: ${{ runner.os }}-buildx- - name: Get version from semantic-release id: version + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - VERSION=$(npx semantic-release --dry-run | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+') + set -euo pipefail + + echo "Running semantic-release dry-run to extract next version..." + VERSION=$(npx semantic-release --no-ci --dry-run 2>&1 | tee semantic-output.log | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+' || true) + + if [ -z "$VERSION" ]; then + echo "โŒ Failed to extract VERSION from semantic-release output:" + cat semantic-output.log + exit 1 + fi + + echo "โœ… Detected VERSION: $VERSION" echo "VERSION=$VERSION" >> $GITHUB_ENV - name: Build Docker images run: | diff --git a/Dockerfile b/Dockerfile index 27da39b..2936186 100644 --- a/Dockerfile +++ b/Dockerfile @@ -105,4 +105,4 @@ RUN dnf install -y \ LABEL org.opencontainers.image.title="lambda-shell-runtime:full" LABEL org.opencontainers.image.version="${VERSION}" -LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" \ No newline at end of file +LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" diff --git a/base.Dockerfile b/base.Dockerfile index 08611cb..97decff 100644 --- a/base.Dockerfile +++ b/base.Dockerfile @@ -1,5 +1,6 @@ -FROM public.ecr.aws/lambda/provided:al2023 AS builder +FROM lambda-shell-runtime:base AS tiny +<<<<<<<< HEAD:base.Dockerfile ARG HTTP_CLI_VERSION=v1.0.1 RUN dnf install -y unzip && \ @@ -35,4 +36,9 @@ WORKDIR /var/task COPY task/handler.sh handler.sh # Label for documentation/reference -LABEL org.opencontainers.image.title="lambda-shell-runtime:base" \ No newline at end of file +LABEL org.opencontainers.image.title="lambda-shell-runtime:base" +======== +COPY task/helpers.sh helpers.sh + +LABEL org.opencontainers.image.title="lambda-shell-runtime:tiny" +>>>>>>>> 09d81b0 (chore(ci): add base image build step to release workflow):tiny.Dockerfile diff --git a/build b/build index c89594a..f8479ee 100755 --- a/build +++ b/build @@ -45,6 +45,8 @@ for VARIANT in $VARIANTS; do [ "$VARIANT" = "base" ] && TARGET="" + [ "$VARIANT" = "base" ] && MODE="--load" + echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." docker buildx build \ --platform "$PLATFORM" \ From 62ee31a913b835cd71ae18bda2ca4703ae8a1535 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 10:06:24 +0200 Subject: [PATCH 38/58] fix: fix workflow using target in the build commands --- .github/workflows/release.yml | 8 +++---- base.Dockerfile | 44 ----------------------------------- 2 files changed, 4 insertions(+), 48 deletions(-) delete mode 100644 base.Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bdf4357..d5cb368 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -76,7 +76,7 @@ jobs: --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:base-$VERSION \ -t $IMAGE:base-latest \ - -f base.Dockerfile . + --target base . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ @@ -86,7 +86,7 @@ jobs: --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:tiny-$VERSION \ -t $IMAGE:tiny-latest \ - -f tiny.Dockerfile . + --target tiny . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ @@ -96,7 +96,7 @@ jobs: --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:micro-$VERSION \ -t $IMAGE:micro-latest \ - -f micro.Dockerfile . + --target micro . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ @@ -106,7 +106,7 @@ jobs: --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:full-$VERSION \ -t $IMAGE:full-latest \ - -f Dockerfile . + . shell: bash - name: Log in to GHCR run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin diff --git a/base.Dockerfile b/base.Dockerfile deleted file mode 100644 index 97decff..0000000 --- a/base.Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -FROM lambda-shell-runtime:base AS tiny - -<<<<<<<< HEAD:base.Dockerfile -ARG HTTP_CLI_VERSION=v1.0.1 - -RUN dnf install -y unzip && \ - dnf clean all - -# Download http-cli -RUN --mount=type=secret,id=github_token \ - curl -H "Authorization: token $(cat /run/secrets/github_token)" \ - -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ - -o http-cli.zip && \ - unzip http-cli.zip && \ - mkdir -p /http-cli-bin && \ - mv http-cli-${HTTP_CLI_VERSION#v}/http-cli /http-cli-bin/ && \ - chmod +x /http-cli-bin/http-cli && \ - rm -rf http-cli.zip http-cli-${HTTP_CLI_VERSION#v} - -FROM public.ecr.aws/lambda/provided:al2023 AS base - -# Install only runtime dependencies -RUN dnf install -y jq && \ - dnf clean all && \ - rm -rf /var/cache/dnf - -# Copy http-cli -COPY --from=builder /http-cli-bin/http-cli /var/task/bin/http-cli -ENV PATH="/var/task/bin:${PATH}" - -COPY runtime/bootstrap /var/runtime/bootstrap -RUN chmod +x /var/runtime/bootstrap - -WORKDIR /var/task - -COPY task/handler.sh handler.sh - -# Label for documentation/reference -LABEL org.opencontainers.image.title="lambda-shell-runtime:base" -======== -COPY task/helpers.sh helpers.sh - -LABEL org.opencontainers.image.title="lambda-shell-runtime:tiny" ->>>>>>>> 09d81b0 (chore(ci): add base image build step to release workflow):tiny.Dockerfile From 5a540e6a7795885ee3ae8a577b4855ec947199b8 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 10:20:52 +0200 Subject: [PATCH 39/58] fix: fix how to pass the GITHUB tocken as secret --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d5cb368..1dc7726 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ --output type=registry --progress=plain \ - --secret id=github_token,src=github_token \ + --secret id=github_token,env=GITHUB_TOKEN \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:base-$VERSION \ @@ -81,7 +81,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ --output type=registry --progress=plain \ - --secret id=github_token,src=github_token \ + --secret id=github_token,env=GITHUB_TOKEN \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:tiny-$VERSION \ @@ -91,7 +91,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ --output type=registry --progress=plain \ - --secret id=github_token,src=github_token \ + --secret id=github_token,env=GITHUB_TOKEN \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:micro-$VERSION \ @@ -101,7 +101,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ --output type=registry --progress=plain \ - --secret id=github_token,src=github_token \ + --secret id=github_token,env=GITHUB_TOKEN \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:full-$VERSION \ From 8baaab47b4baaf6c44ec2ca3a353dcf054805b06 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 10:35:42 +0200 Subject: [PATCH 40/58] fix: fix how to pass the GITHUB tocken as secret --- .github/workflows/release.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1dc7726..e9bafd5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,12 +54,12 @@ jobs: VERSION=$(npx semantic-release --no-ci --dry-run 2>&1 | tee semantic-output.log | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+' || true) if [ -z "$VERSION" ]; then - echo "โŒ Failed to extract VERSION from semantic-release output:" + echo "Failed to extract VERSION from semantic-release output:" cat semantic-output.log exit 1 fi - echo "โœ… Detected VERSION: $VERSION" + echo "Detected VERSION: $VERSION" echo "VERSION=$VERSION" >> $GITHUB_ENV - name: Build Docker images run: | @@ -71,7 +71,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ --output type=registry --progress=plain \ - --secret id=github_token,env=GITHUB_TOKEN \ + --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:base-$VERSION \ @@ -81,7 +81,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ --output type=registry --progress=plain \ - --secret id=github_token,env=GITHUB_TOKEN \ + --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:tiny-$VERSION \ @@ -91,7 +91,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ --output type=registry --progress=plain \ - --secret id=github_token,env=GITHUB_TOKEN \ + --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:micro-$VERSION \ @@ -101,7 +101,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg HTTP_CLI_VERSION=$VERSION \ --output type=registry --progress=plain \ - --secret id=github_token,env=GITHUB_TOKEN \ + --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ -t $IMAGE:full-$VERSION \ From 650fcead7411e04493f082faa93d6556ee2509c1 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 10:51:03 +0200 Subject: [PATCH 41/58] fix: fix the mess with VERSION and HTTP_CLI_VERSION --- .github/workflows/release.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e9bafd5..5be3a05 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,8 @@ jobs: IMAGE=ghcr.io/ql4b/lambda-shell-runtime docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg HTTP_CLI_VERSION=$VERSION \ + --build-arg VERSION="$VERSION" \ + --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ --output type=registry --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ @@ -79,7 +80,8 @@ jobs: --target base . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg HTTP_CLI_VERSION=$VERSION \ + --build-arg VERSION="$VERSION" \ + --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ --output type=registry --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ @@ -89,7 +91,8 @@ jobs: --target tiny . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg HTTP_CLI_VERSION=$VERSION \ + --build-arg VERSION="$VERSION" \ + --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ --output type=registry --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ @@ -99,7 +102,8 @@ jobs: --target micro . docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg HTTP_CLI_VERSION=$VERSION \ + --build-arg VERSION="$VERSION" \ + --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ --output type=registry --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ From 12c2b660b710b0d96ba599b9fb97833324764da3 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 11:05:47 +0200 Subject: [PATCH 42/58] fix: fix the mess with VERSION and HTTP_CLI_VERSION --- .github/workflows/release.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5be3a05..d5959b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,11 +61,12 @@ jobs: echo "Detected VERSION: $VERSION" echo "VERSION=$VERSION" >> $GITHUB_ENV + - name: Log in to GHCR + run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - name: Build Docker images run: | echo "${{ secrets.GHCR_PAT }}" > github_token export DOCKER_BUILDKIT=1 - VERSION=$VERSION IMAGE=ghcr.io/ql4b/lambda-shell-runtime docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ @@ -112,8 +113,6 @@ jobs: -t $IMAGE:full-latest \ . shell: bash - - name: Log in to GHCR - run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - run: npx semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d381ad61e9efe5276ac5be7b27a7b470b4d8a2e3 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 12:28:18 +0200 Subject: [PATCH 43/58] feat: fix workflow --- .github/workflows/release.yml | 52 ++++++++++++++++------------------- scripts/publish | 28 +++++++++++++------ 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d5959b4..0b85f90 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,20 +49,14 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail - - echo "Running semantic-release dry-run to extract next version..." - VERSION=$(npx semantic-release --no-ci --dry-run 2>&1 | tee semantic-output.log | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+' || true) - + VERSION=$(npx semantic-release --no-ci --dry-run --branch ${{ github.ref_name }} 2>&1 | tee semantic-output.log | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.]+)?' || true) if [ -z "$VERSION" ]; then echo "Failed to extract VERSION from semantic-release output:" cat semantic-output.log exit 1 fi - echo "Detected VERSION: $VERSION" echo "VERSION=$VERSION" >> $GITHUB_ENV - - name: Log in to GHCR - run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - name: Build Docker images run: | echo "${{ secrets.GHCR_PAT }}" > github_token @@ -72,7 +66,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg VERSION="$VERSION" \ --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ - --output type=registry --progress=plain \ + --output type=docker --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ @@ -83,7 +77,7 @@ jobs: docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ --build-arg VERSION="$VERSION" \ --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ - --output type=registry --progress=plain \ + --output type=docker --progress=plain \ --secret id=github_token,src=github_token \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache \ @@ -91,28 +85,30 @@ jobs: -t $IMAGE:tiny-latest \ --target tiny . - docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg VERSION="$VERSION" \ - --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ - --output type=registry --progress=plain \ - --secret id=github_token,src=github_token \ - --cache-from type=local,src=/tmp/.buildx-cache \ - --cache-to type=local,dest=/tmp/.buildx-cache \ - -t $IMAGE:micro-$VERSION \ - -t $IMAGE:micro-latest \ - --target micro . + # docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ + # --build-arg VERSION="$VERSION" \ + # --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ + # --output type=docker --progress=plain \ + # --secret id=github_token,src=github_token \ + # --cache-from type=local,src=/tmp/.buildx-cache \ + # --cache-to type=local,dest=/tmp/.buildx-cache \ + # -t $IMAGE:micro-$VERSION \ + # -t $IMAGE:micro-latest \ + # --target micro . - docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg VERSION="$VERSION" \ - --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ - --output type=registry --progress=plain \ - --secret id=github_token,src=github_token \ - --cache-from type=local,src=/tmp/.buildx-cache \ - --cache-to type=local,dest=/tmp/.buildx-cache \ - -t $IMAGE:full-$VERSION \ - -t $IMAGE:full-latest \ + # docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ + # --build-arg VERSION="$VERSION" \ + # --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ + # --output type=docker --progress=plain \ + # --secret id=github_token,src=github_token \ + # --cache-from type=local,src=/tmp/.buildx-cache \ + # --cache-to type=local,dest=/tmp/.buildx-cache \ + # -t $IMAGE:full-$VERSION \ + # -t $IMAGE:full-latest \ . shell: bash + - name: Log in to GHCR + run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - run: npx semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/publish b/scripts/publish index a2a761d..4f94952 100755 --- a/scripts/publish +++ b/scripts/publish @@ -1,15 +1,25 @@ #!/bin/sh -set -e +set -euo pipefail -VERSION="$1" +echo "Logging in to GitHub Container Registry..." +echo "${GITHUB_TOKEN:?Environment variable GITHUB_TOKEN not set}" | docker login ghcr.io -u "${GITHUB_USER:-$(whoami)}" --password-stdin + +# Get version argument +VERSION="${1:-"dev"}" +BASE_IMAGE="ghcr.io/ql4b/lambda-shell-runtime" echo "Publishing Docker images with version $VERSION" -for VARIANT in tiny micro full; do - BASE_IMAGE="ghcr.io/ql4b/lambda-shell-runtime" - TAG="$VERSION-$VARIANT" - docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$TAG" - docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$VARIANT-latest" - docker push "$BASE_IMAGE:$TAG" - docker push "$BASE_IMAGE:$VARIANT-latest" +# for VARIANT in tiny micro full; do +for VARIANT in tiny; do + echo "Building and pushing $VARIANT image..." + + TAG="$VERSION-$VARIANT" + docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$TAG" + docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$VARIANT-latest" + docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$VARIANT" + docker push "$BASE_IMAGE:$TAG" + docker push "$BASE_IMAGE:$VARIANT-latest" + docker push "$BASE_IMAGE:$VARIANT" + done From 1b86371883130aee76fafc0fac500f7a30418261 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 15:59:08 +0200 Subject: [PATCH 44/58] refactor: refactor the build process --- .github/workflows/build-and-release.yml | 86 ++++++++++++++++++ .github/workflows/build-base.yml | 38 ++++++++ .github/workflows/release.yml | 115 ------------------------ Dockerfile | 18 ++-- Makefile | 32 +++++++ build | 29 +++--- 6 files changed, 186 insertions(+), 132 deletions(-) create mode 100644 .github/workflows/build-and-release.yml create mode 100644 .github/workflows/build-base.yml delete mode 100644 .github/workflows/release.yml create mode 100644 Makefile diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml new file mode 100644 index 0000000..8ba2245 --- /dev/null +++ b/.github/workflows/build-and-release.yml @@ -0,0 +1,86 @@ +name: Build and Release + +on: + push: + branches: + - develop + - main + pull_request: + branches: + - main + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + build: + runs-on: ubuntu-latest + env: + HTTP_CLI_VERSION: v1.0.1 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - run: npm ci + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Create and use buildx builder + run: | + docker buildx create --name shell-runtime-builder --driver docker-container --use + docker buildx inspect shell-runtime-builder --bootstrap + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Set version + id: version + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [ "${{ github.ref_name }}" = "main" ]; then + # Get semantic version for main branch + VERSION=$(npx semantic-release --no-ci --dry-run --branch main 2>&1 | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+' || echo "") + if [ -z "$VERSION" ]; then + echo "No release needed" + echo "VERSION=develop" >> $GITHUB_ENV + echo "SHOULD_RELEASE=false" >> $GITHUB_ENV + else + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "SHOULD_RELEASE=true" >> $GITHUB_ENV + fi + else + # Use branch name for develop + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + echo "SHOULD_RELEASE=false" >> $GITHUB_ENV + fi + echo "Detected VERSION: $VERSION" + - name: Build and push images + run: | + echo "${{ secrets.GHCR_PAT }}" > github_token + export GITHUB_TOKEN="${{ secrets.GHCR_PAT }}" + + # Build and push all variants + make push VERSION="$VERSION" REGISTRY="ghcr.io/${{ github.repository_owner }}" + shell: bash + - name: Log in to GHCR + run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin + - name: Create release + if: env.SHOULD_RELEASE == 'true' + run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GHCR_PAT: ${{ secrets.GHCR_PAT }} \ No newline at end of file diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml new file mode 100644 index 0000000..36a4959 --- /dev/null +++ b/.github/workflows/build-base.yml @@ -0,0 +1,38 @@ +name: Build Base Image + +on: + push: + branches: [ main, develop ] + paths: + - 'Dockerfile' + - 'runtime/**' + - 'task/handler.sh' + - '.github/workflows/build-base.yml' + pull_request: + branches: [ main ] + paths: + - 'Dockerfile' + - 'runtime/**' + - 'task/handler.sh' + +jobs: + build-base: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push base + run: | + ./build --platform linux/arm64 --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime --push base + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 0b85f90..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: Release - -on: - push: - branches: - - develop - - main - -permissions: - contents: write - issues: write - pull-requests: write - -jobs: - release: - runs-on: ubuntu-latest - env: - HTTP_CLI_VERSION: v1.0.1 - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 20 - - uses: actions/cache@v3 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - - run: npm ci - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Create and use buildx builder - run: | - docker buildx create --name shell-runtime-builder --driver docker-container --use - docker buildx inspect shell-runtime-builder --bootstrap - - name: Cache Docker layers - uses: actions/cache@v3 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - name: Get version from semantic-release - id: version - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - VERSION=$(npx semantic-release --no-ci --dry-run --branch ${{ github.ref_name }} 2>&1 | tee semantic-output.log | grep -oP 'The next release version is \K[0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.]+)?' || true) - if [ -z "$VERSION" ]; then - echo "Failed to extract VERSION from semantic-release output:" - cat semantic-output.log - exit 1 - fi - echo "Detected VERSION: $VERSION" - echo "VERSION=$VERSION" >> $GITHUB_ENV - - name: Build Docker images - run: | - echo "${{ secrets.GHCR_PAT }}" > github_token - export DOCKER_BUILDKIT=1 - IMAGE=ghcr.io/ql4b/lambda-shell-runtime - - docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg VERSION="$VERSION" \ - --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ - --output type=docker --progress=plain \ - --secret id=github_token,src=github_token \ - --cache-from type=local,src=/tmp/.buildx-cache \ - --cache-to type=local,dest=/tmp/.buildx-cache \ - -t $IMAGE:base-$VERSION \ - -t $IMAGE:base-latest \ - --target base . - - docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - --build-arg VERSION="$VERSION" \ - --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ - --output type=docker --progress=plain \ - --secret id=github_token,src=github_token \ - --cache-from type=local,src=/tmp/.buildx-cache \ - --cache-to type=local,dest=/tmp/.buildx-cache \ - -t $IMAGE:tiny-$VERSION \ - -t $IMAGE:tiny-latest \ - --target tiny . - - # docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - # --build-arg VERSION="$VERSION" \ - # --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ - # --output type=docker --progress=plain \ - # --secret id=github_token,src=github_token \ - # --cache-from type=local,src=/tmp/.buildx-cache \ - # --cache-to type=local,dest=/tmp/.buildx-cache \ - # -t $IMAGE:micro-$VERSION \ - # -t $IMAGE:micro-latest \ - # --target micro . - - # docker buildx build --builder shell-runtime-builder --platform linux/arm64 \ - # --build-arg VERSION="$VERSION" \ - # --build-arg HTTP_CLI_VERSION="$HTTP_CLI_VERSION" \ - # --output type=docker --progress=plain \ - # --secret id=github_token,src=github_token \ - # --cache-from type=local,src=/tmp/.buildx-cache \ - # --cache-to type=local,dest=/tmp/.buildx-cache \ - # -t $IMAGE:full-$VERSION \ - # -t $IMAGE:full-latest \ - . - shell: bash - - name: Log in to GHCR - run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - - run: npx semantic-release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GHCR_PAT: ${{ secrets.GHCR_PAT }} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 2936186..52acc04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,8 +43,8 @@ COPY task/handler.sh handler.sh LABEL org.opencontainers.image.source="https://github.com/ql4b/lambda-shell-runtime" LABEL org.opencontainers.image.version="${VERSION}" -# tiny: add lamnda helper functions -FROM base AS tiny +# tiny: add lambda helper functions +FROM ghcr.io/ql4b/lambda-shell-runtime:base AS tiny ARG VERSION ARG HTTP_CLI_VERSION @@ -65,8 +65,12 @@ RUN pip3 install --no-cache-dir --target /tmp/awscurl awscurl && \ find /tmp/awscurl -type f -name '*.pyc' -delete && \ find /tmp/awscurl -type d -name '*.dist-info' -exec rm -rf {} + -# micro: inclues awscurl -FROM tiny AS micro +# micro: includes awscurl +FROM ghcr.io/ql4b/lambda-shell-runtime:base AS micro-base + +COPY task/helpers.sh helpers.sh + +FROM micro-base AS micro ARG VERSION ARG HTTP_CLI_VERSION @@ -93,7 +97,11 @@ LABEL org.opencontainers.image.version="${VERSION}" LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" # full: includes aws-cli for complete AWS functionality -FROM tiny AS full +FROM ghcr.io/ql4b/lambda-shell-runtime:base AS full-base + +COPY task/helpers.sh helpers.sh + +FROM full-base AS full ARG VERSION ARG HTTP_CLI_VERSION diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eda0f51 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +.PHONY: help build push clean base tiny micro full + +PLATFORM ?= linux/arm64 +TAG ?= lambda-shell-runtime +VERSION ?= develop +REGISTRY ?= ghcr.io/ql4b + +help: ## Show this help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}' + +build: tiny micro full ## Build all variants locally + +base: ## Build base image + VERSION=$(VERSION) ./build --platform $(PLATFORM) --tag $(TAG) --load base + +tiny: base ## Build tiny variant + VERSION=$(VERSION) ./build --platform $(PLATFORM) --tag $(TAG) --load tiny + +micro: base ## Build micro variant + VERSION=$(VERSION) ./build --platform $(PLATFORM) --tag $(TAG) --load micro + +full: base ## Build full variant + VERSION=$(VERSION) ./build --platform $(PLATFORM) --tag $(TAG) --load full + +push-base: ## Push base to registry + VERSION=$(VERSION) ./build --platform $(PLATFORM) --tag $(REGISTRY)/$(TAG) --push base + +push: ## Push all variants to registry + VERSION=$(VERSION) ./build --platform $(PLATFORM) --tag $(REGISTRY)/$(TAG) --push tiny micro full + +clean: ## Remove local images + docker rmi -f $(TAG):base $(TAG):tiny $(TAG):micro $(TAG):full 2>/dev/null || true \ No newline at end of file diff --git a/build b/build index f8479ee..6e16786 100755 --- a/build +++ b/build @@ -1,13 +1,14 @@ #!/bin/sh -set -ex +set -e # Default config PLATFORM="linux/arm64" MODE="--load" TAG="lambda-shell-runtime" -VERSION="${VERSION:-dev}" +VERSION="${VERSION:-develop}" VARIANTS="base tiny micro full" +# VARIANTS="base tiny" # Parse arguments while [ $# -gt 0 ]; do @@ -28,13 +29,10 @@ while [ $# -gt 0 ]; do MODE="--load" shift ;; - full|tiny|micro|base) - VARIANTS="$1" - shift - ;; *) - echo "Unknown option: $1" - exit 1 + # Remaining arguments are variants + VARIANTS="$*" + break ;; esac done @@ -45,19 +43,26 @@ for VARIANT in $VARIANTS; do [ "$VARIANT" = "base" ] && TARGET="" - [ "$VARIANT" = "base" ] && MODE="--load" - echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." + + # Build tags + TAGS="--tag $TAG:$VARIANT" + if [ -n "$VERSION" ]; then + TAGS="$TAGS --tag $TAG:$VARIANT-$VERSION" + fi + docker buildx build \ --platform "$PLATFORM" \ + --provenance=false \ --secret id=github_token,env=GITHUB_TOKEN \ - --tag $TAG:$VARIANT \ + $TAGS \ --file "$DOCKERFILE" \ ${TARGET:+--target "$TARGET"} \ $MODE \ . - if [ -n "$VERSION" ]; then + # Only do local tagging for --load mode if version wasn't already tagged + if [ -n "$VERSION" ] && [ "$MODE" = "--load" ]; then echo "Tagging $TAG:$VARIANT-$VERSION" docker tag $TAG:$VARIANT $TAG:$VARIANT-$VERSION fi From 1d252798c18a74b7feeda74edb1bda7a7a2e6d91 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 16:02:11 +0200 Subject: [PATCH 45/58] fix: fix logging to ghcr step --- .github/workflows/build-and-release.yml | 2 ++ .github/workflows/build-ghcr.base.yml | 0 .github/workflows/publish-dockerhub.yml | 24 +++++++++++++++++ .github/workflows/publish-ecr.yml | 34 +++++++++++++++++++++++++ .github/workflows/publish-optimized.yml | 33 ++++++++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 .github/workflows/build-ghcr.base.yml create mode 100644 .github/workflows/publish-dockerhub.yml create mode 100644 .github/workflows/publish-ecr.yml create mode 100644 .github/workflows/publish-optimized.yml diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 8ba2245..318e98b 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -68,6 +68,8 @@ jobs: echo "SHOULD_RELEASE=false" >> $GITHUB_ENV fi echo "Detected VERSION: $VERSION" + - name: Log in to GHCR + run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - name: Build and push images run: | echo "${{ secrets.GHCR_PAT }}" > github_token diff --git a/.github/workflows/build-ghcr.base.yml b/.github/workflows/build-ghcr.base.yml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/publish-dockerhub.yml b/.github/workflows/publish-dockerhub.yml new file mode 100644 index 0000000..3a0eebe --- /dev/null +++ b/.github/workflows/publish-dockerhub.yml @@ -0,0 +1,24 @@ +name: Publish to Docker Hub + +on: + push: + tags: ['v*'] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + run: | + ./build --platform linux/arm64,linux/amd64 --tag your-username/lambda-shell-runtime --push tiny micro full \ No newline at end of file diff --git a/.github/workflows/publish-ecr.yml b/.github/workflows/publish-ecr.yml new file mode 100644 index 0000000..e4b6915 --- /dev/null +++ b/.github/workflows/publish-ecr.yml @@ -0,0 +1,34 @@ +name: Publish to ECR Public + +on: + push: + tags: ['v*'] + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Login to ECR Public + run: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws + + - name: Build and push variants + run: | + for variant in tiny micro full; do + docker buildx build \ + --platform linux/arm64,linux/amd64 \ + --target $variant \ + --secret id=github_token,env=GITHUB_TOKEN \ + --tag public.ecr.aws/j5r7n1v7/lambda-shell-runtime:$variant \ + --push \ + . + done \ No newline at end of file diff --git a/.github/workflows/publish-optimized.yml b/.github/workflows/publish-optimized.yml new file mode 100644 index 0000000..a1acf11 --- /dev/null +++ b/.github/workflows/publish-optimized.yml @@ -0,0 +1,33 @@ +name: Fast Build & Publish + +on: + push: + tags: ['v*'] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + image=moby/buildkit:v0.12.0 + network=host + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Login to ECR Public + run: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws + + - name: Build with cache + run: | + export BUILDX_EXPERIMENTAL=1 + ./build --platform linux/arm64,linux/amd64 --tag public.ecr.aws/your-alias/lambda-shell-runtime --push --cache-from type=gha --cache-to type=gha,mode=max tiny micro full \ No newline at end of file From 4177949ae7e01ee0fb58e985f1e1aaa46cd174b5 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 16:02:40 +0200 Subject: [PATCH 46/58] chore: cleanup unused flows --- .github/workflows/publish-dockerhub.yml | 24 ----------------- .github/workflows/publish-ecr.yml | 34 ------------------------- .github/workflows/publish-optimized.yml | 33 ------------------------ 3 files changed, 91 deletions(-) delete mode 100644 .github/workflows/publish-dockerhub.yml delete mode 100644 .github/workflows/publish-ecr.yml delete mode 100644 .github/workflows/publish-optimized.yml diff --git a/.github/workflows/publish-dockerhub.yml b/.github/workflows/publish-dockerhub.yml deleted file mode 100644 index 3a0eebe..0000000 --- a/.github/workflows/publish-dockerhub.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Publish to Docker Hub - -on: - push: - tags: ['v*'] - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push - run: | - ./build --platform linux/arm64,linux/amd64 --tag your-username/lambda-shell-runtime --push tiny micro full \ No newline at end of file diff --git a/.github/workflows/publish-ecr.yml b/.github/workflows/publish-ecr.yml deleted file mode 100644 index e4b6915..0000000 --- a/.github/workflows/publish-ecr.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Publish to ECR Public - -on: - push: - tags: ['v*'] - workflow_dispatch: - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-east-1 - - - name: Login to ECR Public - run: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws - - - name: Build and push variants - run: | - for variant in tiny micro full; do - docker buildx build \ - --platform linux/arm64,linux/amd64 \ - --target $variant \ - --secret id=github_token,env=GITHUB_TOKEN \ - --tag public.ecr.aws/j5r7n1v7/lambda-shell-runtime:$variant \ - --push \ - . - done \ No newline at end of file diff --git a/.github/workflows/publish-optimized.yml b/.github/workflows/publish-optimized.yml deleted file mode 100644 index a1acf11..0000000 --- a/.github/workflows/publish-optimized.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Fast Build & Publish - -on: - push: - tags: ['v*'] - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver-opts: | - image=moby/buildkit:v0.12.0 - network=host - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-east-1 - - - name: Login to ECR Public - run: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws - - - name: Build with cache - run: | - export BUILDX_EXPERIMENTAL=1 - ./build --platform linux/arm64,linux/amd64 --tag public.ecr.aws/your-alias/lambda-shell-runtime --push --cache-from type=gha --cache-to type=gha,mode=max tiny micro full \ No newline at end of file From 91efb6573e9efb7e76141a6d034520deff6ee5f9 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 16:03:05 +0200 Subject: [PATCH 47/58] chore: cleanup unused flows --- .github/workflows/build-ghcr.base.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .github/workflows/build-ghcr.base.yml diff --git a/.github/workflows/build-ghcr.base.yml b/.github/workflows/build-ghcr.base.yml deleted file mode 100644 index e69de29..0000000 From 61c0c8beccfc1f49d7dc1be10526caaa7c38698f Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 16:24:31 +0200 Subject: [PATCH 48/58] refactor: remove bottlenck on slow builders --- .github/workflows/build-installers.yml | 58 ++++++++++++++++++++++++++ Dockerfile | 16 +------ scripts/publish | 25 ----------- 3 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 .github/workflows/build-installers.yml delete mode 100755 scripts/publish diff --git a/.github/workflows/build-installers.yml b/.github/workflows/build-installers.yml new file mode 100644 index 0000000..0587459 --- /dev/null +++ b/.github/workflows/build-installers.yml @@ -0,0 +1,58 @@ +name: Build Installers + +on: + push: + branches: [ main, develop ] + paths: + - 'Dockerfile' + - '.github/workflows/build-installers.yml' + pull_request: + branches: [ main ] + paths: + - 'Dockerfile' + +jobs: + build-installers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push installers + run: | + # Build awscurl-installer + docker buildx build \ + --platform linux/arm64 \ + --provenance=false \ + --target awscurl-installer \ + --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime:awscurl-installer \ + --push \ + -f - . << 'EOF' + FROM public.ecr.aws/lambda/provided:al2023 AS awscurl-installer + RUN dnf install -y unzip python3-pip findutils && dnf clean all + RUN pip3 install --no-cache-dir --target /tmp/awscurl awscurl && \ + find /tmp/awscurl -type d -name '__pycache__' -exec rm -rf {} + && \ + find /tmp/awscurl -type f -name '*.pyc' -delete && \ + find /tmp/awscurl -type d -name '*.dist-info' -exec rm -rf {} + + EOF + + # Build awscli-installer + docker buildx build \ + --platform linux/arm64 \ + --provenance=false \ + --target awscli-installer \ + --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime:awscli-installer \ + --push \ + -f - . << 'EOF' + FROM public.ecr.aws/lambda/provided:al2023 AS awscli-installer + RUN dnf install -y aws-cli && dnf clean all + EOF \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 52acc04..3b25f37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,15 +55,7 @@ LABEL org.opencontainers.image.title="lambda-shell-runtime:tiny" LABEL org.opencontainers.image.version="${VERSION}" LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" -FROM public.ecr.aws/lambda/provided:al2023 AS awscurl-installer -RUN dnf install -y unzip python3-pip findutils && \ - dnf clean all - -RUN pip3 install --no-cache-dir --target /tmp/awscurl awscurl && \ - find /tmp/awscurl -type d -name '__pycache__' -exec rm -rf {} + && \ - find /tmp/awscurl -type f -name '*.pyc' -delete && \ - find /tmp/awscurl -type d -name '*.dist-info' -exec rm -rf {} + # micro: includes awscurl FROM ghcr.io/ql4b/lambda-shell-runtime:base AS micro-base @@ -79,8 +71,7 @@ RUN dnf install -y python3 && \ dnf clean all && \ rm -rf /var/cache/dnf -COPY --from=awscurl-installer /tmp/awscurl /var/task/aws -# Clean up Python cache and metadata +COPY --from=ghcr.io/ql4b/lambda-shell-runtime:awscurl-installer /tmp/awscurl /var/task/aws RUN rm -rf \ /var/task/aws/__pycache__ \ /var/task/aws/*.dist-info \ @@ -106,10 +97,7 @@ FROM full-base AS full ARG VERSION ARG HTTP_CLI_VERSION -RUN dnf install -y \ - aws-cli && \ - dnf clean all && \ - rm -rf /var/cache/dnf +COPY --from=ghcr.io/ql4b/lambda-shell-runtime:awscli-installer /usr/bin/aws /usr/bin/aws LABEL org.opencontainers.image.title="lambda-shell-runtime:full" LABEL org.opencontainers.image.version="${VERSION}" diff --git a/scripts/publish b/scripts/publish deleted file mode 100755 index 4f94952..0000000 --- a/scripts/publish +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -set -euo pipefail - -echo "Logging in to GitHub Container Registry..." -echo "${GITHUB_TOKEN:?Environment variable GITHUB_TOKEN not set}" | docker login ghcr.io -u "${GITHUB_USER:-$(whoami)}" --password-stdin - -# Get version argument -VERSION="${1:-"dev"}" -BASE_IMAGE="ghcr.io/ql4b/lambda-shell-runtime" - -echo "Publishing Docker images with version $VERSION" - -# for VARIANT in tiny micro full; do -for VARIANT in tiny; do - echo "Building and pushing $VARIANT image..." - - TAG="$VERSION-$VARIANT" - docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$TAG" - docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$VARIANT-latest" - docker tag lambda-shell-runtime:$VARIANT "$BASE_IMAGE:$VARIANT" - docker push "$BASE_IMAGE:$TAG" - docker push "$BASE_IMAGE:$VARIANT-latest" - docker push "$BASE_IMAGE:$VARIANT" - -done From 53226a97e6aaf0e6a4b4306197ccdde9f74d3b12 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 16:32:57 +0200 Subject: [PATCH 49/58] feat: add concurrency control to workflows --- .github/workflows/build-and-release.yml | 6 ++++-- .github/workflows/build-base.yml | 13 ++++++++++++- .github/workflows/build-installers.yml | 4 ++++ build | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 318e98b..af88bef 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -14,6 +14,10 @@ permissions: issues: write pull-requests: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest @@ -78,8 +82,6 @@ jobs: # Build and push all variants make push VERSION="$VERSION" REGISTRY="ghcr.io/${{ github.repository_owner }}" shell: bash - - name: Log in to GHCR - run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u skunxicat --password-stdin - name: Create release if: env.SHOULD_RELEASE == 'true' run: npx semantic-release diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml index 36a4959..b7305c6 100644 --- a/.github/workflows/build-base.yml +++ b/.github/workflows/build-base.yml @@ -15,6 +15,10 @@ on: - 'runtime/**' - 'task/handler.sh' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-base: runs-on: ubuntu-latest @@ -33,6 +37,13 @@ jobs: - name: Build and push base run: | - ./build --platform linux/arm64 --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime --push base + docker buildx build \ + --platform linux/arm64 \ + --provenance=false \ + --secret id=github_token,env=GITHUB_TOKEN \ + --target base \ + --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime:base \ + --push \ + . env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/build-installers.yml b/.github/workflows/build-installers.yml index 0587459..a1a1cdc 100644 --- a/.github/workflows/build-installers.yml +++ b/.github/workflows/build-installers.yml @@ -11,6 +11,10 @@ on: paths: - 'Dockerfile' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-installers: runs-on: ubuntu-latest diff --git a/build b/build index 6e16786..8b7e895 100755 --- a/build +++ b/build @@ -41,7 +41,7 @@ for VARIANT in $VARIANTS; do DOCKERFILE="./Dockerfile" TARGET="$VARIANT" - [ "$VARIANT" = "base" ] && TARGET="" + [ "$VARIANT" = "base" ] && TARGET="base" echo "Building $VARIANT ($DOCKERFILE) with platform $PLATFORM..." From dc83c0c4aa9a6cd6946c47e4bb23ed03617c98ad Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 16:37:58 +0200 Subject: [PATCH 50/58] feat: add concurrency control to workflows --- .github/workflows/build-and-release.yml | 26 +++++++++++++++++++++++++ .github/workflows/build-base.yml | 4 ++-- .github/workflows/build-installers.yml | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index af88bef..7fbf812 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -19,7 +19,33 @@ concurrency: cancel-in-progress: true jobs: + build-base: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GHCR_PAT }} + - name: Build and push base + run: | + docker buildx build \ + --platform linux/arm64 \ + --provenance=false \ + --secret id=github_token,env=GITHUB_TOKEN \ + --target base \ + --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime:base \ + --push \ + . + env: + GITHUB_TOKEN: ${{ secrets.GHCR_PAT }} + build: + needs: build-base runs-on: ubuntu-latest env: HTTP_CLI_VERSION: v1.0.1 diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml index b7305c6..1350131 100644 --- a/.github/workflows/build-base.yml +++ b/.github/workflows/build-base.yml @@ -33,7 +33,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.GHCR_PAT }} - name: Build and push base run: | @@ -46,4 +46,4 @@ jobs: --push \ . env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GHCR_PAT }} \ No newline at end of file diff --git a/.github/workflows/build-installers.yml b/.github/workflows/build-installers.yml index a1a1cdc..36455e5 100644 --- a/.github/workflows/build-installers.yml +++ b/.github/workflows/build-installers.yml @@ -29,7 +29,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.GHCR_PAT }} - name: Build and push installers run: | From a77b46e9c68ac62736a1eac490762ea2ef028af1 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 16:41:08 +0200 Subject: [PATCH 51/58] fix: fix authentication issues --- .github/workflows/build-and-release.yml | 3 ++- .github/workflows/build-base.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 7fbf812..0c093a7 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -33,10 +33,11 @@ jobs: password: ${{ secrets.GHCR_PAT }} - name: Build and push base run: | + echo "${{ secrets.GHCR_PAT }}" > github_token docker buildx build \ --platform linux/arm64 \ --provenance=false \ - --secret id=github_token,env=GITHUB_TOKEN \ + --secret id=github_token,src=github_token \ --target base \ --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime:base \ --push \ diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml index 1350131..81dedb6 100644 --- a/.github/workflows/build-base.yml +++ b/.github/workflows/build-base.yml @@ -37,10 +37,11 @@ jobs: - name: Build and push base run: | + echo "${{ secrets.GHCR_PAT }}" > github_token docker buildx build \ --platform linux/arm64 \ --provenance=false \ - --secret id=github_token,env=GITHUB_TOKEN \ + --secret id=github_token,src=github_token \ --target base \ --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime:base \ --push \ From 51d618f6079017c380059c9b0332648b7dce4083 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 17:40:34 +0200 Subject: [PATCH 52/58] fix: remove authentication for downloading http-cli --- .github/workflows/build-base.yml | 2 -- Dockerfile | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml index 81dedb6..571f759 100644 --- a/.github/workflows/build-base.yml +++ b/.github/workflows/build-base.yml @@ -37,11 +37,9 @@ jobs: - name: Build and push base run: | - echo "${{ secrets.GHCR_PAT }}" > github_token docker buildx build \ --platform linux/arm64 \ --provenance=false \ - --secret id=github_token,src=github_token \ --target base \ --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime:base \ --push \ diff --git a/Dockerfile b/Dockerfile index 3b25f37..24f4ed5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,8 +6,7 @@ RUN dnf install -y unzip && \ dnf clean all # Download http-cli -RUN --mount=type=secret,id=github_token \ - curl -H "Authorization: token $(cat /run/secrets/github_token)" \ +RUN curl \ -L "https://github.com/ql4b/http-cli/archive/refs/tags/${HTTP_CLI_VERSION}.zip" \ -o http-cli.zip && \ unzip http-cli.zip && \ From a163bfd774d9a117232349cff499474deed6bda3 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 17:43:19 +0200 Subject: [PATCH 53/58] fix: add permission to the build-base workflow --- .github/workflows/build-base.yml | 8 ++++++-- README.md | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml index 571f759..670e9d1 100644 --- a/.github/workflows/build-base.yml +++ b/.github/workflows/build-base.yml @@ -15,6 +15,10 @@ on: - 'runtime/**' - 'task/handler.sh' +permissions: + contents: read + packages: write + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -33,7 +37,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GHCR_PAT }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push base run: | @@ -45,4 +49,4 @@ jobs: --push \ . env: - GITHUB_TOKEN: ${{ secrets.GHCR_PAT }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index 62a4378..84568d2 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ In a GitHub Actions environment, the build script is typically used in combinati To use this runtime in your own Lambda container image: ```Dockerfile -FROM ghcr.io/ql4b/lambda-shell-runtime:tiny +FROM public.ecr.aws/j5r7n1v7/lambda-shell-runtime:tiny WORKDIR /var/task From 8c7f073a664f75ecc3739d188f7d4ac430d2580f Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Sun, 27 Jul 2025 18:14:14 +0200 Subject: [PATCH 54/58] ci: add permission to build-base workflow --- .github/workflows/build-base.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml index 670e9d1..22d55ad 100644 --- a/.github/workflows/build-base.yml +++ b/.github/workflows/build-base.yml @@ -16,7 +16,9 @@ on: - 'task/handler.sh' permissions: - contents: read + contents: write + issues: write + pull-requests: write packages: write concurrency: @@ -37,16 +39,18 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.GHCR_PAT }} - name: Build and push base run: | + echo "${{ secrets.GHCR_PAT }}" > github_token docker buildx build \ --platform linux/arm64 \ --provenance=false \ + --secret id=github_token,src=github_token \ --target base \ --tag ghcr.io/${{ github.repository_owner }}/lambda-shell-runtime:base \ --push \ . env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GHCR_PAT }} \ No newline at end of file From 9198d12bd07804acfb01b23b080bc9455678a122 Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Mon, 28 Jul 2025 19:15:20 +0200 Subject: [PATCH 55/58] docs: update README.md --- README.md | 283 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 226 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 84568d2..66bf318 100644 --- a/README.md +++ b/README.md @@ -1,102 +1,271 @@ # lambda-shell-runtime -Custom AWS Lambda runtime environment for executing shell/bash functions with AWS Lambda. +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=flat&logo=docker&logoColor=white)](https://github.com/ql4b/lambda-shell-runtime/pkgs/container/lambda-shell-runtime) +[![AWS Lambda](https://img.shields.io/badge/AWS%20Lambda-FF9900?style=flat&logo=awslambda&logoColor=white)](https://aws.amazon.com/lambda/) -Implement AWS Lambda functions in Bash, packaged as OCI-compliant container images that interface with the Lambda Runtime API and follow the custom runtime execution flow. +> **Turn shell scripts into serverless functions in minutes** -Inspired by: https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html +Custom AWS Lambda runtime for executing Bash functions as serverless applications. Deploy shell scripts directly to AWS Lambda with full access to common CLI tools like `jq`, `curl`, and AWS CLI. -This custom Lambda runtime enables Bash-based execution with minimal dependencies. We provide three image variants tailored to different needs: +## Features -## Runtime Image Variants +- ๐Ÿš€ **Zero-config deployment** - Just write Bash, deploy to Lambda +- ๐Ÿ“ฆ **Three optimized variants** - Choose the right tools for your use case +- ๐Ÿ”ง **Built-in utilities** - `jq`, `curl`, `http-cli`, and optional AWS CLI +- ๐Ÿ—๏ธ **Multi-platform support** - ARM64 and x86_64 architectures +- ๐Ÿงช **Local testing** - Full Lambda Runtime Interface Emulator support +- ๐Ÿ“‹ **Production ready** - Based on official AWS Lambda base images -### 1. `tiny` -- Includes: `jq`, `curl`, `http-cli` -- Use case: Lightweight data parsing and HTTP requests. +## Quick Start -### 2. `micro` -- Based on: `tiny` -- Adds: `awscurl` -- Use case: AWS API calls using IAM credentials without full AWS CLI. +1. **Create your handler function:** -### 3. `full` -- Based on: `tiny` -- Adds: **full AWS CLI** (official install) -- Use case: Complete access to AWS CLI features. -- Note: Previously experimented with intermediate setups (e.g., stripped-down AWS CLI), but they proved unstable. This variant now uses the official, standard installation of the AWS CLI. +```bash +# handler.sh +hello() { + local event="$1" + echo '{"message": "Hello from Bash Lambda!", "input": '"$event"'}' +} +``` ---- +2. **Create your Dockerfile:** -Each image is built from the same base but optimized for different tasks. You can choose the right variant for your Lambda depending on the environment and tools required. +```dockerfile +FROM ghcr.io/ql4b/lambda-shell-runtime:tiny +COPY handler.sh . +``` -Each runtime variant has its own Dockerfile: +3. **Deploy to AWS Lambda** using container images -## Local build process +## Runtime Variants -To run the script locally: +Choose the variant that matches your requirements: -``` -./build --platform linux/arm64 --tag your-repo/lambda-shell --load micro -``` +| Variant | Size | Tools Included | Best For | +|---------|------|----------------|----------| +| **`tiny`** | ~50MB | `jq`, `curl`, `http-cli` | HTTP APIs, JSON processing | +| **`micro`** | ~80MB | `tiny` + `awscurl` | AWS API calls without full CLI | +| **`full`** | ~200MB | `micro` + AWS CLI | Complete AWS operations | + +### Available Images +```bash +# From GitHub Container Registry +ghcr.io/ql4b/lambda-shell-runtime:tiny +ghcr.io/ql4b/lambda-shell-runtime:micro +ghcr.io/ql4b/lambda-shell-runtime:full + +# From AWS Public ECR +public.ecr.aws/j5r7n1v7/lambda-shell-runtime:tiny +public.ecr.aws/j5r7n1v7/lambda-shell-runtime:micro +public.ecr.aws/j5r7n1v7/lambda-shell-runtime:full ``` -./build --platform linux/arm64 --tag your-repo/lambda-shell --push micro + +## Examples + +### HTTP API with JSON Processing + +```bash +# handler.sh +api_handler() { + local event="$1" + local name=$(echo "$event" | jq -r '.queryStringParameters.name // "World"') + + echo '{ + "statusCode": 200, + "headers": {"Content-Type": "application/json"}, + "body": '{"greeting": "Hello, '"$name"'!"}' + }' +} ``` -Key Features: +### AWS API Integration + +```bash +# handler.sh (using micro variant) +list_buckets() { + local buckets=$(awscurl --service s3 https://s3.amazonaws.com/ | jq '.ListAllMyBucketsResult.Buckets') + echo '{"buckets": '"$buckets"'}' +} +``` -* Platform targeting: Defaults to linux/arm64, which is suitable for AWS Lambda ARM-based functions. -* Variants: You can build one or more of the supported variants: tiny, micro, or full. -* Secrets: Supports injecting a GitHub token via --secret id=github_token,env=GITHUB_TOKEN for private dependency access during build. +### File Processing with AWS CLI -Modes: -* `--load` (default): Loads the image into the local Docker engine. -* `--push`: Pushes the image to a remote registry. +```bash +# handler.sh (using full variant) +process_s3_file() { + local event="$1" + local bucket=$(echo "$event" | jq -r '.Records[0].s3.bucket.name') + local key=$(echo "$event" | jq -r '.Records[0].s3.object.key') + + aws s3 cp "s3://$bucket/$key" /tmp/input.json + local result=$(jq '.data | length' /tmp/input.json) + + echo '{"processed": true, "count": '"$result"'}' +} +``` -## GitHub Actions Build +## Function Handler Format -In a GitHub Actions environment, the build script is typically used in combination with Docker Buildx and secrets configured in the CI pipeline. +Your handler functions receive the Lambda event as the first argument: -## Usage +```bash +# handler.sh +my_function() { + local event="$1" # Lambda event JSON + local context="$2" # Lambda context (optional) + + # Process the event + local result=$(echo "$event" | jq '.key') + + # Return JSON response + echo '{"result": '"$result"'}' +} +``` -To use this runtime in your own Lambda container image: +**Handler naming:** Set your Lambda handler to `handler.my_function` (filename.function_name) -```Dockerfile -FROM public.ecr.aws/j5r7n1v7/lambda-shell-runtime:tiny +## Deployment -WORKDIR /var/task +### Using AWS CLI -COPY relay.sh handler.sh . +```bash +# Build and push your image +docker build -t my-lambda . +docker tag my-lambda:latest 123456789012.dkr.ecr.region.amazonaws.com/my-lambda:latest +docker push 123456789012.dkr.ecr.region.amazonaws.com/my-lambda:latest + +# Create/update Lambda function +aws lambda create-function \ + --function-name my-bash-function \ + --code ImageUri=123456789012.dkr.ecr.region.amazonaws.com/my-lambda:latest \ + --role arn:aws:iam::123456789012:role/lambda-execution-role \ + --package-type Image \ + --timeout 30 ``` -Your `handler.sh` file should define bash functions, and the handler name passed to Lambda should match `filename.functionname`, e.g., `handler.hello`. +### Using Terraform -## Local testing +```hcl +resource "aws_lambda_function" "bash_function" { + function_name = "my-bash-function" + role = aws_iam_role.lambda_role.arn + package_type = "Image" + image_uri = "123456789012.dkr.ecr.region.amazonaws.com/my-lambda:latest" + timeout = 30 +} +``` -Use [aws-lambda-runtime-interface-emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator) for local testing. +## Local Testing + +### Using Lambda Runtime Interface Emulator ```bash -docker run -d \ +# Download RIE (one time setup) +mkdir -p ~/.aws-lambda-rie +curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \ + https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie +chmod +x ~/.aws-lambda-rie/aws-lambda-rie + +# Run your function locally +docker run --rm -p 9000:8080 \ -v ~/.aws-lambda-rie:/aws-lambda \ - -p 9000:8080 \ - --env HANDLER="handler.hello" \ + -e HANDLER="handler.hello" \ --entrypoint /aws-lambda/aws-lambda-rie \ - lambda-shell-runtime:tiny \ - /var/runtime/bootstrap + my-lambda:latest /var/runtime/bootstrap -curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' +# Test your function +curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" \ + -d '{"name": "World"}' ``` -## Publishing +### Debug Mode + +```bash +# Run with debug output +docker run --rm -p 9000:8080 \ + -e HANDLER="handler.hello" \ + -e _LAMBDA_RUNTIME_DEBUG=1 \ + my-lambda:latest +``` -Images are published to GitHub Container Registry (GHCR): +## Building from Source ```bash -./scripts/publish +# Clone the repository +git clone https://github.com/ql4b/lambda-shell-runtime.git +cd lambda-shell-runtime + +# Build specific variant +./build --platform linux/arm64 --tag my-runtime --load tiny + +# Build and push to registry +./build --platform linux/arm64 --tag my-registry/lambda-shell --push micro +``` + +### Build Options + +- `--platform`: Target platform (default: `linux/arm64`) +- `--tag`: Image tag prefix +- `--load`: Load image locally (default) +- `--push`: Push to registry +- `--secret`: Inject build secrets (e.g., GitHub token) + +## Performance & Limitations + +- **Cold start**: ~100-300ms depending on variant +- **Memory usage**: 64MB minimum recommended +- **Timeout**: Standard Lambda limits apply (15 minutes max) +- **Package size**: Varies by variant (50MB-200MB) +- **Concurrent executions**: Standard Lambda limits + +## Troubleshooting + +### Common Issues + +**Function not found:** +```bash +# Ensure your function is defined and handler matches +HANDLER="handler.my_function" # filename.function_name +``` + +**Permission errors:** +```bash +# Ensure Lambda execution role has required permissions +# For AWS API calls, add appropriate IAM policies +``` + +**Timeout issues:** +```bash +# Increase Lambda timeout setting +# Optimize shell script performance +``` + +## Contributing + +We welcome contributions! Please see our [contributing guidelines](CONTRIBUTING.md) for details. + +### Development Setup + +```bash +git clone https://github.com/ql4b/lambda-shell-runtime.git +cd lambda-shell-runtime +./build --load tiny # Build and test locally +``` + +### Project Structure + +``` +lambda-shell-runtime/ +โ”œโ”€โ”€ runtime/ # Custom bootstrap and runtime logic +โ”œโ”€โ”€ task/ # Helper functions and example handlers +โ”œโ”€โ”€ test/ # Test functions and runners +โ”œโ”€โ”€ scripts/ # Build and publishing utilities +โ”œโ”€โ”€ Dockerfile # Multi-stage build definitions +โ””โ”€โ”€ build # Build script for all variants ``` -## Layout +## License -- `runtime/` โ€” custom bootstrap and core loop -- `test/` โ€” simple example test function + runner -- `scripts/` โ€” utilities like publishing +MIT License - see [LICENSE](LICENSE) file for details. From 36976c8c24099600a1cd0a30c051b40572c8dcfb Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Mon, 28 Jul 2025 19:38:58 +0200 Subject: [PATCH 56/58] fix: fix awscli installation in the full variant --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 24f4ed5..cc0177e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -96,7 +96,9 @@ FROM full-base AS full ARG VERSION ARG HTTP_CLI_VERSION -COPY --from=ghcr.io/ql4b/lambda-shell-runtime:awscli-installer /usr/bin/aws /usr/bin/aws +RUN dnf install -y awscli-2 && \ + dnf clean all && \ + rm -rf /var/cache/dnf LABEL org.opencontainers.image.title="lambda-shell-runtime:full" LABEL org.opencontainers.image.version="${VERSION}" From 068e24a4f11313b9c39a1649ac0bb83bba34243d Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Mon, 28 Jul 2025 19:39:22 +0200 Subject: [PATCH 57/58] docs: update README --- README.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 66bf318..2cd9418 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,9 @@ Choose the variant that matches your requirements: | Variant | Size | Tools Included | Best For | |---------|------|----------------|----------| -| **`tiny`** | ~50MB | `jq`, `curl`, `http-cli` | HTTP APIs, JSON processing | -| **`micro`** | ~80MB | `tiny` + `awscurl` | AWS API calls without full CLI | -| **`full`** | ~200MB | `micro` + AWS CLI | Complete AWS operations | +| **`tiny`** | ~132MB | `jq`, `curl`, `http-cli` | HTTP APIs, JSON processing | +| **`micro`** | ~221MB | `tiny` + `awscurl` | AWS API calls without full CLI | +| **`full`** | ~417MB | `tiny` + AWS CLI | Complete AWS operations | ### Available Images @@ -217,9 +217,22 @@ cd lambda-shell-runtime - **Cold start**: ~100-300ms depending on variant - **Memory usage**: 64MB minimum recommended - **Timeout**: Standard Lambda limits apply (15 minutes max) -- **Package size**: Varies by variant (50MB-200MB) +- **Package size**: Varies by variant (132MB-417MB) - **Concurrent executions**: Standard Lambda limits +### Size Comparison with Official AWS Lambda Runtimes + +| Runtime | Size | Notes | +|---------|------|-------| +| **AWS Python 3.12** | 534MB | Official Python runtime | +| **lambda-shell-runtime:full** | 417MB | **22% smaller** with complete AWS CLI | +| **AWS Node.js 20** | 410MB | Official Node.js runtime | +| **lambda-shell-runtime:micro** | 221MB | **46% smaller** with AWS API access | +| **lambda-shell-runtime:tiny** | 132MB | **75% smaller** with essential tools | +| **AWS provided** | 128MB | Bare custom runtime base | + +The shell runtime is **highly competitive** with official runtimes while providing the simplicity and power of Bash scripting. + ## Troubleshooting ### Common Issues From 7568524d2d970ec83b3d9bb4248607c579b1701a Mon Sep 17 00:00:00 2001 From: Carlo D'Ambrosio Date: Mon, 28 Jul 2025 19:49:23 +0200 Subject: [PATCH 58/58] fix: fix micro and full image hierarchy --- Dockerfile | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index cc0177e..da3d30f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,14 +54,8 @@ LABEL org.opencontainers.image.title="lambda-shell-runtime:tiny" LABEL org.opencontainers.image.version="${VERSION}" LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" - - # micro: includes awscurl -FROM ghcr.io/ql4b/lambda-shell-runtime:base AS micro-base - -COPY task/helpers.sh helpers.sh - -FROM micro-base AS micro +FROM tiny AS micro ARG VERSION ARG HTTP_CLI_VERSION @@ -87,11 +81,7 @@ LABEL org.opencontainers.image.version="${VERSION}" LABEL org.opencontainers.image.http_cli_version="${HTTP_CLI_VERSION}" # full: includes aws-cli for complete AWS functionality -FROM ghcr.io/ql4b/lambda-shell-runtime:base AS full-base - -COPY task/helpers.sh helpers.sh - -FROM full-base AS full +FROM tiny AS full ARG VERSION ARG HTTP_CLI_VERSION