diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 945ae3a08..0eb3d18c0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -6,16 +6,10 @@ # Set the matching branch protection rule on `dev` and `main` to # "Require review from Code Owners" or this file is advisory only. -# Fall-through: any change needs a team review. -* @mulgadc/mulgadc-engineers - # Network layer is governed by ADR-0006 invariant tests. -# Changes here must respect those clauses — see -# spinifex/network/invariants/ and +# Changes here must respect those clauses +# see spinifex/network/invariants/ # docs/development/proposals/az-local-architecture/0006-spinifex-network-layer-contract.md /spinifex/network/ @joshsiv-mulga /spinifex/vpcd/ @joshsiv-mulga - -# Invariants suite is the executable ADR-0006 fence — any widening or -# narrowing of the scan roots needs an explicit owner review. /spinifex/network/invariants/ @joshsiv-mulga diff --git a/.github/workflows/e2e-baremetal.yml b/.github/workflows/e2e-baremetal.yml index c4b8bfd79..fdfc56208 100644 --- a/.github/workflows/e2e-baremetal.yml +++ b/.github/workflows/e2e-baremetal.yml @@ -2,6 +2,13 @@ name: E2E Bare-Metal on: workflow_dispatch: + workflow_call: + inputs: + ref: + description: "spinifex ref to checkout/test (default: triggering ref)" + required: false + type: string + default: "" permissions: contents: read @@ -12,7 +19,7 @@ env: jobs: e2e_baremetal: - name: Bare-Metal Multi-Node E2E + name: Bare-Metal Single-Node E2E runs-on: [ self-hosted, ci-baremetal ] timeout-minutes: 120 concurrency: @@ -20,11 +27,17 @@ jobs: cancel-in-progress: false env: BM_ISO_CACHE: /var/cache/spinifex-iso-builder + ARTIFACT_DIR: /tmp/spinifex-e2e-baremetal-artifacts-${{ github.run_id }} steps: - name: Clean Workspace run: sudo rm -rf "$GITHUB_WORKSPACE"/* - uses: actions/checkout@v6 + with: + # When called (nightly/release) the caller picks the ref (dev on + # schedule, the tag on release); standalone dispatch defaults to the + # triggering ref. + ref: ${{ inputs.ref || github.ref }} - uses: actions/create-github-app-token@v3 id: app-token @@ -92,6 +105,9 @@ jobs: fi + - name: Install go-junit-report on runner + run: go install github.com/jstemmer/go-junit-report/v2@latest + - name: Run Bare-Metal E2E env: BM_CI_PASSWORD: ${{ secrets.BM_CI_PASSWORD }} @@ -100,8 +116,38 @@ jobs: BM_UBUNTU_IMAGE: ${{ env.BM_ISO_CACHE }}/ubuntu-26.04-cloudimg-amd64.img SPX_EXTERNAL_POOL: ${{ env.SPINIFEX_CI_POOL }} run: | + mkdir -p "${ARTIFACT_DIR}" + # Capture combined bootstrap + e2e output so failures produce a + # downloadable, analyzable artifact (the suites run go test -v on the + # init node and stream back over SSH). pipefail keeps the script's + # real exit code through the tee. + set -o pipefail sudo -E mulga/scripts/baremetal/run-baremetal-e2e.sh \ --vmlinuz "${BM_ISO_CACHE}/vmlinuz" \ --initrd "${BM_ISO_CACHE}/initrd.img" \ --squashfs "${BM_ISO_CACHE}/spinifex.squashfs" \ - --tests-tar "${RUNNER_TEMP}/spinifex-tests.tar" + --tests-tar "${RUNNER_TEMP}/spinifex-tests.tar" \ + 2>&1 | tee "${ARTIFACT_DIR}/test-baremetal.log" + + - name: Generate JUnit report + if: always() + run: | + LOG="${ARTIFACT_DIR}/test-baremetal.log" + [ -f "$LOG" ] || exit 0 + go-junit-report -in "$LOG" -out "${ARTIFACT_DIR}/junit-baremetal.xml" || true + + - name: Analyze e2e failures + if: always() + uses: ./.github/actions/e2e-analyze + with: + junit-glob: ${{ env.ARTIFACT_DIR }}/junit-*.xml + log-dir: ${{ env.ARTIFACT_DIR }} + title: "Bare-Metal E2E failure analysis" + + - uses: actions/upload-artifact@v7 + if: always() + with: + name: e2e-baremetal-artifacts-${{ github.run_id }} + path: ${{ env.ARTIFACT_DIR }} + if-no-files-found: warn + retention-days: 14 diff --git a/.github/workflows/e2e-nightly.yml b/.github/workflows/e2e-nightly.yml index 39282bbd0..ed9385c8b 100644 --- a/.github/workflows/e2e-nightly.yml +++ b/.github/workflows/e2e-nightly.yml @@ -216,3 +216,22 @@ jobs: tofu workspace select -or-create "${SPINIFEX_CI_ENV}" >> "$TOFU_LOG" 2>&1 tofu destroy -var-file=envs/${SPINIFEX_CI_ENV}.tfvars ${TOFU_VAR_ARGS} -auto-approve -input=false >> "$TOFU_LOG" 2>&1 ./scrub-env.sh "${SPINIFEX_CI_ENV}" >> "$TOFU_LOG" 2>&1 || true + + # Bare-metal single-node suite (PXE-reimaged physical hardware). Doesn't fit + # the tofu matrix shape (different runner/provisioning/120m timeout), so it + # runs as a sibling job via the reusable e2e-baremetal workflow. Gated like a + # matrix cell ("cell 20"): every weeknight cron, or dispatch with 20 selected + # (blank cells = all). Tests `dev` on schedule to match GIT_BRANCH above. + baremetal: + name: cell-20 (baremetal/pxe/real-hw) + if: >- + github.event_name == 'schedule' || + inputs.cells == '' || + contains(format(',{0},', inputs.cells), ',20,') + permissions: + contents: read + statuses: write + uses: ./.github/workflows/e2e-baremetal.yml + with: + ref: ${{ github.event_name == 'schedule' && 'dev' || github.ref_name }} + secrets: inherit diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 9f745a773..02110e4b9 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -18,7 +18,7 @@ on: description: "PR head SHA to report status on" required: false suites: - description: "Space-separated Make targets to run (e.g. 'e2e-single e2e-multinode e2e-cert e2e-lb')" + description: "Space-separated Make targets to run (e.g. 'e2e-single e2e-multinode e2e-cert e2e-lb e2e-eks')" required: false default: "e2e-single e2e-multinode e2e-cert e2e-lb" test_run: @@ -30,13 +30,6 @@ permissions: contents: read statuses: write -# Pushes to the same ref stack on the single `ci-single` self-hosted runner, -# and a cancelled run can leave orphan `tofu`/`go test -c` procs behind that -# wedge the runner host. Auto-cancel keeps at most one in-flight run per ref. -concurrency: - group: e2e-${{ github.ref }} - cancel-in-progress: true - env: GIT_BRANCH: ${{ github.ref_name }} SUITES: ${{ inputs.suites || 'e2e-single e2e-multinode e2e-cert e2e-lb' }} @@ -49,7 +42,7 @@ jobs: # suites='e2e-reboot' only). Push triggers and the default SUITES list # both include single/cert/lb so the inputs.suites null-check keeps the # job running on plain PR pushes. - if: github.event_name != 'workflow_dispatch' || contains(github.event.inputs.suites, 'e2e-single') || contains(github.event.inputs.suites, 'e2e-cert') || contains(github.event.inputs.suites, 'e2e-lb') + if: github.event_name != 'workflow_dispatch' || contains(github.event.inputs.suites, 'e2e-single') || contains(github.event.inputs.suites, 'e2e-cert') || contains(github.event.inputs.suites, 'e2e-lb') || contains(github.event.inputs.suites, 'e2e-eks') runs-on: [ self-hosted, ci-single ] timeout-minutes: 45 env: @@ -77,7 +70,15 @@ jobs: true - name: Clean Workspace - run: sudo rm -rf "$GITHUB_WORKSPACE"/* + run: | + # Detach any leaked mounts under the workspace before deleting. A + # killed image build can leave a `mount --bind /dev` under its build + # dir; if `rm -rf` traverses it as root it wipes the host's /dev + # (deletes /dev/null and wedges the box). umount deepest-first, then + # forbid rm from crossing any mount boundary as a hard backstop. + awk -v w="$GITHUB_WORKSPACE" '$2 ~ "^"w {print $2}' /proc/mounts \ + | sort -r | xargs -r -I{} sudo umount -l -R {} || true + sudo rm -rf --one-file-system "$GITHUB_WORKSPACE"/* - uses: actions/create-github-app-token@v3 id: app-token @@ -153,6 +154,85 @@ jobs: echo "wan_ip=${WAN_IP}" >> "$GITHUB_OUTPUT" echo "ssh_key=${SSH_KEY}" >> "$GITHUB_OUTPUT" + # The eks suite's CreateCluster boots a control-plane VM from the + # eks-node AMI, so that AMI must exist in the VM's image store before the + # suite runs. build-system-image.sh customizes the image with libguestfs + # (an isolated userspace appliance) — it touches no host block device, so + # it is safe on the shared runner. The build needs the spinifex source + + # network (runner only); the import needs predastore/NATS (VM only), so + # build on the runner then ship the raw and import on the VM. + - name: Build eks-node AMI (libguestfs, runner) + if: contains(env.SUITES, 'e2e-eks') + working-directory: spinifex + run: | + set -euo pipefail + source "${GITHUB_WORKSPACE}/spinifex/.github/scripts/ci-fmt.sh" + banner "build eks-node AMI (libguestfs — no host devices)" + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends libguestfs-tools qemu-utils + # Debian/Ubuntu ship /boot/vmlinuz-* mode 0600; the libguestfs + # appliance needs to read a host kernel. direct backend = no libvirt. + export LIBGUESTFS_BACKEND=direct + sudo chmod 0644 /boot/vmlinuz-* || true + group_open "make build-eks-node-image" + make build-eks-node-image + group_close + ok "raw: $(du -h /tmp/eks-node-image-build/eks-node-alpine.raw | cut -f1)" + + - name: Import eks-node AMI on VM + if: contains(env.SUITES, 'e2e-eks') + run: | + set -euo pipefail + source "${GITHUB_WORKSPACE}/spinifex/.github/scripts/ci-fmt.sh" + # Pull import args straight from the manifest so they never drift. + eval "$(grep -E '^(ALPINE_VERSION|AMI_NAME|SYSTEM_TAG)=' \ + spinifex/scripts/images/eks-node/manifest.conf)" + SSH_OPTS=(-o StrictHostKeyChecking=no -i "${{ steps.vm.outputs.ssh_key }}") + VM=tf-user@${{ steps.vm.outputs.wan_ip }} + banner "import eks-node AMI to VM image store" + scp "${SSH_OPTS[@]}" \ + /tmp/eks-node-image-build/eks-node-alpine.raw \ + "$VM":/tmp/eks-node-alpine.raw + # Drop the import progress bar (pterm redraws to stdout — thousands of + # lines over the SSH pipe). Errors go to stderr, so they survive; the + # exit code still gates the step. + ssh "${SSH_OPTS[@]}" "$VM" \ + "spx admin images import --file /tmp/eks-node-alpine.raw \ + --distro alpine --version ${ALPINE_VERSION} --arch x86_64 \ + --boot-mode bios --ami-name ${AMI_NAME} --tag ${SYSTEM_TAG}" >/dev/null + ok "eks-node AMI imported" + + # The eks data-plane subtest (TestEKS/KubectlGetNodes) drives the full + # `aws eks get-token` → kubectl → apiserver → token-webhook → gateway + # broker → STS verify path. The e2e binaries run ON the VM, so kubectl + # must be on the VM's PATH or harness.NewKubectl t.Skips the subtest. + # Pin to the k3s server version (setup.sh K3S_VERSION) — kubectl is + # ±1-minor skew tolerant; matching is simplest. Checksum-verified. + - name: Install kubectl on VM + if: contains(env.SUITES, 'e2e-eks') + run: | + set -euo pipefail + source "${GITHUB_WORKSPACE}/spinifex/.github/scripts/ci-fmt.sh" + SSH_OPTS=(-o StrictHostKeyChecking=no -i "${{ steps.vm.outputs.ssh_key }}") + VM=tf-user@${{ steps.vm.outputs.wan_ip }} + banner "install kubectl on VM (eks data-plane subtest)" + ssh "${SSH_OPTS[@]}" "$VM" 'bash -s' <<'EOF' + set -euo pipefail + KVER=v1.32.5 + case "$(uname -m)" in + x86_64) ARCH=amd64 ;; + aarch64) ARCH=arm64 ;; + *) echo "unsupported arch $(uname -m)" >&2; exit 1 ;; + esac + cd /tmp + curl -fsSLO "https://dl.k8s.io/release/${KVER}/bin/linux/${ARCH}/kubectl" + curl -fsSLO "https://dl.k8s.io/release/${KVER}/bin/linux/${ARCH}/kubectl.sha256" + echo "$(cat kubectl.sha256) kubectl" | sha256sum -c - + sudo install -m 0755 kubectl /usr/local/bin/kubectl + kubectl version --client + EOF + ok "kubectl installed on VM" + - name: Build E2E test binaries on runner working-directory: spinifex/tests/e2e run: | @@ -277,6 +357,15 @@ jobs: ssh "${SSH_OPTS[@]}" "$VM" 'sudo journalctl -u "spinifex-*" --no-pager --output=short-iso --since "60 min ago"' \ > "${ARTIFACT_DIR}/spinifex-journal.log" 2>/dev/null ok "journal: $(wc -l < "${ARTIFACT_DIR}/spinifex-journal.log" 2>/dev/null || echo 0) lines" + # LB/system microVM serial consoles. The lb-agent logs its nginx (NLB) + # engine activation and any `nginx -t`/reload error + active-probe + # results to the guest serial console, which the host journal never + # captures. These files persist in /run/spinifex past VM teardown, so + # collecting them here makes an NLB "0/N healthy" timeout diagnosable. + mkdir -p "${ARTIFACT_DIR}/lb-console" + ssh "${SSH_OPTS[@]}" "$VM" 'sudo sh -c "cd /run/spinifex 2>/dev/null && tar -cf - console-*.log 2>/dev/null"' \ + | tar -C "${ARTIFACT_DIR}/lb-console" -xf - 2>/dev/null + ok "lb-console: $(ls "${ARTIFACT_DIR}/lb-console" 2>/dev/null | wc -l) files" - name: Convert test logs to JUnit XML if: always() @@ -383,7 +472,15 @@ jobs: true - name: Clean Workspace - run: sudo rm -rf "$GITHUB_WORKSPACE"/* + run: | + # Detach any leaked mounts under the workspace before deleting. A + # killed image build can leave a `mount --bind /dev` under its build + # dir; if `rm -rf` traverses it as root it wipes the host's /dev + # (deletes /dev/null and wedges the box). umount deepest-first, then + # forbid rm from crossing any mount boundary as a hard backstop. + awk -v w="$GITHUB_WORKSPACE" '$2 ~ "^"w {print $2}' /proc/mounts \ + | sort -r | xargs -r -I{} sudo umount -l -R {} || true + sudo rm -rf --one-file-system "$GITHUB_WORKSPACE"/* - uses: actions/create-github-app-token@v3 id: app-token @@ -471,7 +568,12 @@ jobs: run: | set -euo pipefail source "${GITHUB_WORKSPACE}/spinifex/.github/scripts/ci-fmt.sh" - SUITES_BUILD=$(echo "${SUITES}" | sed 's/e2e-//g' | tr ' ' '\n' | grep -vE '^(reboot|single)$' | tr '\n' ' ') + # eks is excluded on multinode: EKS KV is a clustered (Raft) JetStream + # stream and create-cluster needs an elected leader, but multinode + # JetStream clustering isn't solid yet (mulga-omq/z2z/5yg). EKS is + # validated single-node only for now; re-enable here once clustered + # JetStream + multinode EKS are a deliberate target. + SUITES_BUILD=$(echo "${SUITES}" | sed 's/e2e-//g' | tr ' ' '\n' | grep -vE '^(reboot|single|eks)$' | tr '\n' ' ') if [ -z "$(echo "${SUITES_BUILD}" | tr -d ' ')" ]; then echo "no multinode-eligible suites requested (SUITES=${SUITES}); nothing to build" exit 0 @@ -554,7 +656,7 @@ jobs: id: e2e run: | set +e - SUITES_RUN=$(echo "${SUITES}" | sed 's/e2e-//g' | tr ' ' '\n' | grep -vE '^(reboot|single)$' | tr '\n' ' ') + SUITES_RUN=$(echo "${SUITES}" | sed 's/e2e-//g' | tr ' ' '\n' | grep -vE '^(reboot|single|eks)$' | tr '\n' ' ') if [ -z "$(echo "${SUITES_RUN}" | tr -d ' ')" ]; then echo "no multinode-eligible suites requested (SUITES=${SUITES}); skipping" echo "rc=0" >> "$GITHUB_OUTPUT" @@ -664,6 +766,18 @@ jobs: ssh "${SSH_OPTS[@]}" tf-user@"$ip" 'sudo journalctl -u openvswitch-ipsec.service --no-pager --output=short-iso --since "60 min ago"' \ > "${ARTIFACT_DIR}/openvswitch-ipsec-node${idx}.log" 2>/dev/null done + # LB/system microVM serial consoles, per node. The lb-agent logs its + # nginx (NLB) engine activation and any `nginx -t`/reload error + + # active-probe results to the guest serial console (never in the host + # journal). Files persist in /run/spinifex past VM teardown. + for i in "${!NODE_LIST[@]}"; do + idx=$((i + 1)) + ip="${NODE_LIST[$i]}" + mkdir -p "${ARTIFACT_DIR}/lb-console/node${idx}" + ssh "${SSH_OPTS[@]}" tf-user@"$ip" 'sudo sh -c "cd /run/spinifex 2>/dev/null && tar -cf - console-*.log 2>/dev/null"' \ + | tar -C "${ARTIFACT_DIR}/lb-console/node${idx}" -xf - 2>/dev/null + ok "lb-console node${idx}: $(ls "${ARTIFACT_DIR}/lb-console/node${idx}" 2>/dev/null | wc -l) files" + done - name: Convert test logs to JUnit XML if: always() @@ -779,7 +893,15 @@ jobs: true - name: Clean Workspace - run: sudo rm -rf "$GITHUB_WORKSPACE"/* + run: | + # Detach any leaked mounts under the workspace before deleting. A + # killed image build can leave a `mount --bind /dev` under its build + # dir; if `rm -rf` traverses it as root it wipes the host's /dev + # (deletes /dev/null and wedges the box). umount deepest-first, then + # forbid rm from crossing any mount boundary as a hard backstop. + awk -v w="$GITHUB_WORKSPACE" '$2 ~ "^"w {print $2}' /proc/mounts \ + | sort -r | xargs -r -I{} sudo umount -l -R {} || true + sudo rm -rf --one-file-system "$GITHUB_WORKSPACE"/* - uses: actions/create-github-app-token@v3 id: app-token @@ -1004,7 +1126,7 @@ jobs: name: Report E2E Status if: always() needs: [ e2e_single, e2e_real_multi, e2e_reboot ] - runs-on: [ self-hosted, ci-single ] + runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Report Status to PR diff --git a/.github/workflows/publish-eks-ami.yml b/.github/workflows/publish-eks-ami.yml new file mode 100644 index 000000000..89b0d6483 --- /dev/null +++ b/.github/workflows/publish-eks-ami.yml @@ -0,0 +1,92 @@ +name: Publish EKS node AMI + +# Builds the eks-node system AMI (libguestfs, on the runner) and publishes the +# compressed qcow2 + .sha256 sidecar to the Cloudflare R2 bucket that backs +# iso.mulgadc.com/system-ami/. Operators then install it on any cluster with: +# spx admin images import --name spinifex-eks-node +# +# Manual-only: publishing overwrites the mirror artifact, so it is a deliberate +# action, not something every push should trigger. + +on: + workflow_dispatch: + inputs: + dry_run: + description: "Build + convert only; skip the R2 upload" + type: boolean + default: false + +permissions: + contents: read + +concurrency: + group: publish-eks-ami + cancel-in-progress: false + +jobs: + publish: + name: Build + publish eks-node AMI + runs-on: ubuntu-24.04 + + steps: + - name: Checkout Spinifex + uses: actions/checkout@v6 + with: + path: spinifex + + - name: Checkout Viperblock + uses: actions/checkout@v6 + with: + repository: mulgadc/viperblock + ref: dev + path: viperblock + + - name: Checkout Predastore + uses: actions/checkout@v6 + with: + repository: mulgadc/predastore + ref: dev + path: predastore + + - name: Setup Go + uses: actions/setup-go@v6 + with: + cache-dependency-path: "spinifex/go.sum" + go-version-file: "spinifex/go.mod" + + - name: Setup Go Workspace + run: go work init ./spinifex ./viperblock ./predastore + + - name: Install build + upload tooling + run: | + set -euo pipefail + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends libguestfs-tools qemu-utils + if ! command -v aws &>/dev/null; then + curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip + unzip -q /tmp/awscliv2.zip -d /tmp/aws-install + sudo /tmp/aws-install/aws/install + rm -rf /tmp/awscliv2.zip /tmp/aws-install + fi + + - name: Build + publish to R2 + working-directory: spinifex + env: + # Map the repo's R2 secrets onto the names publish-system-image.sh reads. + AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }} + R2_ENDPOINT: ${{ secrets.CF_R2_ENDPOINT_URL }} + R2_BUCKET: spinifex-iso + DRY_RUN: ${{ inputs.dry_run }} + run: | + set -euo pipefail + # libguestfs needs to read a host kernel; Ubuntu ships it mode 0600. + # direct backend = no libvirt on the runner. + export LIBGUESTFS_BACKEND=direct + sudo chmod 0644 /boot/vmlinuz-* || true + FLAGS=(--build) + if [[ "${DRY_RUN}" == "true" ]]; then + FLAGS+=(--dry-run) + fi + ./scripts/publish-system-image.sh \ + scripts/images/eks-node/manifest.conf "${FLAGS[@]}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 732a2656d..09d6c3c43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -131,7 +131,16 @@ jobs: REPO: ${{ github.repository }} steps: - name: Clean Workspace - run: rm -rf "$GITHUB_WORKSPACE"/* + run: | + # This runner (ci-single) is shared with the iso job, which mounts a + # kernel-global devtmpfs at /mulga/scripts/iso-builder/.cache/rootfs/dev. + # If that job is killed its EXIT-trap unmount never runs and the bind + # leaks; a plain rm -rf then traverses it and deletes the host's /dev + # (wipes /dev/null, wedges the box). Detach any leaked mounts under the + # workspace first, then forbid rm from crossing a mount boundary. + awk -v w="$GITHUB_WORKSPACE" '$2 ~ "^"w {print $2}' /proc/mounts \ + | sort -r | xargs -r -I{} sudo umount -l -R {} || true + sudo rm -rf --one-file-system "$GITHUB_WORKSPACE"/* - uses: actions/create-github-app-token@v3 id: app-token @@ -169,8 +178,36 @@ jobs: TARBALL=$(find "${GITHUB_WORKSPACE}/release-assets" -name "*-linux-amd64.tar.gz" | head -1) ./bootstrap-install.sh "${SPINIFEX_CI_ENV}" "$SETUP_SH" "$TARBALL" + - name: Teardown + if: always() + working-directory: mulga/scripts/tofu-cluster + run: | + tofu workspace select -or-create "${SPINIFEX_CI_ENV}" >> "$TOFU_LOG" 2>&1 + tofu destroy -var-file=envs/${SPINIFEX_CI_ENV}.tfvars -auto-approve -input=false >> "$TOFU_LOG" 2>&1 + ./scrub-env.sh "${SPINIFEX_CI_ENV}" >> "$TOFU_LOG" 2>&1 || true + + # Bare-metal single-node suite on the real ISO/squashfs + PXE-boot path — + # the only release check that exercises hardware install. Runs in parallel + # with `verify` (both need: release). Promotion and ISO upload both hard-gate + # on this passing. + baremetal: + needs: release + permissions: + contents: read + statuses: write + uses: ./.github/workflows/e2e-baremetal.yml + secrets: inherit + + # Promote the draft only after BOTH the tofu install (`verify`) and the + # bare-metal hardware boot (`baremetal`) pass — runs only if both succeed. + promote: + needs: [ verify, baremetal ] + runs-on: ubuntu-24.04 + env: + TAG: ${{ github.ref_name }} + REPO: ${{ github.repository }} + steps: - name: Promote Release - if: success() env: GH_TOKEN: ${{ github.token }} run: | @@ -182,29 +219,31 @@ jobs: echo "Release ${TAG} promoted to latest" fi + # Annotate the (still-draft) release if either gate failed. + mark-failed: + needs: [ verify, baremetal ] + if: failure() + runs-on: ubuntu-24.04 + env: + TAG: ${{ github.ref_name }} + REPO: ${{ github.repository }} + steps: - name: Mark Release Failed - if: failure() env: GH_TOKEN: ${{ github.token }} run: | EXISTING_BODY=$(gh release view "$TAG" --repo "$REPO" --json body -q .body) NOTES_FILE=$(mktemp) - printf 'Verification failed — see workflow run for details.\n\n%s' "$EXISTING_BODY" > "$NOTES_FILE" + printf 'Verification failed (tofu install or bare-metal boot) — see workflow run for details.\n\n%s' "$EXISTING_BODY" > "$NOTES_FILE" gh release edit "$TAG" --repo "$REPO" --notes-file "$NOTES_FILE" rm -f "$NOTES_FILE" - - name: Teardown - if: always() - working-directory: mulga/scripts/tofu-cluster - run: | - tofu workspace select -or-create "${SPINIFEX_CI_ENV}" >> "$TOFU_LOG" 2>&1 - tofu destroy -var-file=envs/${SPINIFEX_CI_ENV}.tfvars -auto-approve -input=false >> "$TOFU_LOG" 2>&1 - ./scrub-env.sh "${SPINIFEX_CI_ENV}" >> "$TOFU_LOG" 2>&1 || true - iso: needs: release runs-on: [ self-hosted, ci-single ] - timeout-minutes: 90 + # ISO build (~60-90m) overlaps the parallel baremetal run (~120m); the + # upload gate then waits for baremetal to finish, so the job must outlast it. + timeout-minutes: 180 steps: - name: Clean Workspace run: | @@ -216,7 +255,7 @@ jobs: sudo umount -l "${ROOTFS}/dev" 2>/dev/null || true sudo umount -l "${ROOTFS}/sys" 2>/dev/null || true sudo umount -l "${ROOTFS}/proc" 2>/dev/null || true - sudo rm -rf "$GITHUB_WORKSPACE"/* + sudo rm -rf --one-file-system "$GITHUB_WORKSPACE"/* - uses: actions/create-github-app-token@v3 id: app-token @@ -289,24 +328,28 @@ jobs: SPINIFEX_BIN="${GITHUB_WORKSPACE}/spinifex/bin/spx" \ INSTALLER_BIN="${GITHUB_WORKSPACE}/spinifex/bin/spinifex-installer" - - name: Wait for verify + - name: Wait for verify + baremetal id: wait-verify if: steps.build-iso.outcome == 'success' continue-on-error: true env: GH_TOKEN: ${{ github.token }} run: | - echo "Waiting for verify job to complete..." + # Gate the R2 upload on BOTH the tofu install (`verify`) and the + # real-hardware boot (`baremetal`, a reusable workflow whose job shows + # as "baremetal / ..."). Upload only if both conclude success. + echo "Waiting for verify + baremetal jobs to complete..." while true; do - CONCLUSION=$(gh api \ - "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs" \ - --jq '.jobs[] | select(.name == "verify") | .conclusion // empty') - if [ -n "$CONCLUSION" ]; then - echo "Verify conclusion: $CONCLUSION" - [ "$CONCLUSION" = "success" ] && exit 0 + JOBS=$(gh api \ + "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs" --paginate) + VERIFY=$(echo "$JOBS" | jq -r '.jobs[] | select(.name == "verify") | .conclusion // empty') + BAREMETAL=$(echo "$JOBS" | jq -r '.jobs[] | select(.name | startswith("baremetal")) | .conclusion // empty') + if [ -n "$VERIFY" ] && [ -n "$BAREMETAL" ]; then + echo "verify=$VERIFY baremetal=$BAREMETAL" + { [ "$VERIFY" = "success" ] && [ "$BAREMETAL" = "success" ]; } && exit 0 exit 1 fi - echo "Verify still running, retrying in 30s..." + echo "Still waiting (verify=${VERIFY:-pending} baremetal=${BAREMETAL:-pending}), retry 30s..." sleep 30 done @@ -327,14 +370,13 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }} CF_R2_ENDPOINT_URL: ${{ secrets.CF_R2_ENDPOINT_URL }} - VERSION: ${{ github.ref_name }} run: | aws --endpoint-url="${CF_R2_ENDPOINT_URL}" \ - s3 cp "mulga/scripts/iso-builder/dist/spinifex-${VERSION}.iso" \ - "s3://spinifex-iso/spinifex-${VERSION}.iso" + s3 cp "mulga/scripts/iso-builder/dist/spinifex.iso" \ + "s3://spinifex-iso/spinifex.iso" aws --endpoint-url="${CF_R2_ENDPOINT_URL}" \ - s3 cp "mulga/scripts/iso-builder/dist/spinifex-${VERSION}.iso.sha256" \ - "s3://spinifex-iso/spinifex-${VERSION}.iso.sha256" + s3 cp "mulga/scripts/iso-builder/dist/spinifex.iso.sha256" \ + "s3://spinifex-iso/spinifex.iso.sha256" - name: Mark ISO failed if: always() && (steps.build-iso.outcome == 'failure' || steps.upload.outcome == 'failure') diff --git a/Makefile b/Makefile index f7764d23d..889b39853 100644 --- a/Makefile +++ b/Makefile @@ -73,24 +73,27 @@ build-lb-agent: @echo -e "\n....Building lb-agent (static)" CGO_ENABLED=0 GOFIPS140=v1.0.0 go build -ldflags "-s -w" -o ./bin/lb-agent cmd/lb-agent/main.go -build-system-image: ## Build a system image from manifest (use IMAGE=lb or IMAGE=eks-server) +build-system-image: ## Build a system image from manifest (use IMAGE=lb or IMAGE=eks-node) ifndef IMAGE $(error IMAGE is required. Usage: make build-system-image IMAGE=lb) endif @if [ -f scripts/images/$(IMAGE).conf ]; then \ - ./scripts/build-system-image.sh scripts/images/$(IMAGE).conf; \ + ./scripts/build-system-image.sh scripts/images/$(IMAGE).conf $(if $(IMPORT),--import); \ elif [ -f scripts/images/$(IMAGE)/manifest.conf ]; then \ - ./scripts/build-system-image.sh scripts/images/$(IMAGE)/manifest.conf; \ + ./scripts/build-system-image.sh scripts/images/$(IMAGE)/manifest.conf $(if $(IMPORT),--import); \ else \ echo "ERROR: no manifest at scripts/images/$(IMAGE).conf or scripts/images/$(IMAGE)/manifest.conf"; \ exit 1; \ fi -build-eks-server-image: ## Build the eks-server AMI (K3s control-plane variant) - $(MAKE) build-system-image IMAGE=eks-server +build-eks-node-image: ## Build the unified eks-node AMI (K3s server+agent; role at first boot; IMPORT=1 to register) + $(MAKE) build-system-image IMAGE=eks-node -build-eks-agent-image: ## Build the eks-agent AMI (K3s worker variant) - $(MAKE) build-system-image IMAGE=eks-agent +import-eks-node-image: ## Build + register the eks-node AMI (requires a running cluster) + $(MAKE) build-system-image IMAGE=eks-node IMPORT=1 + +publish-eks-node-image: ## Build + publish the eks-node AMI to Cloudflare R2 (needs R2_ENDPOINT + AWS_* env) + ./scripts/publish-system-image.sh scripts/images/eks-node/manifest.conf --build MICROVM_OUT_DIR := build/microvm MICROVM_ARTIFACTS := $(MICROVM_OUT_DIR)/vmlinuz $(MICROVM_OUT_DIR)/initramfs.cpio.gz @@ -338,9 +341,20 @@ ansible-dev-version: ansible-dev-vpc: cd scripts/ansible && ansible-playbook playbooks/dev-vpc.yml $(ANSIBLE_EXTRA) -.PHONY: build build-ui build-installer build-lb-agent build-system-image build-eks-server-image build-eks-agent-image build-microvm-image install-microvm go_build go_run preflight test test-cover test-race diff-coverage bench run test-actions test-harness manifest-check diff-coverage bench run \ +# Multi-node cluster bootstrap against a tofu env (env1, env2, bryn, etc). +# Inventory is generated from scripts/tofu-cluster/envs/.tfvars. +# make ansible-cluster-bootstrap ENV=env1 POOL=192.168.1.150-192.168.1.159 +ENV ?= +POOL ?= +ansible-cluster-bootstrap: + @test -n "$(ENV)" || { echo "ENV= required (e.g. env1, env2)"; exit 64; } + cd scripts/ansible && CLUSTER_ENV=$(ENV) ansible-playbook -i inventory/tofu.py playbooks/cluster-bootstrap.yml \ + -e cluster_env=$(ENV) \ + $(if $(POOL),-e cluster_external_pool=$(POOL),) \ + $(_ANSIBLE_EXTRA) + +.PHONY: build build-ui build-installer build-lb-agent build-system-image build-eks-node-image import-eks-node-image publish-eks-node-image build-microvm-image install-microvm go_build go_run preflight test test-cover test-race diff-coverage bench run test-actions test-harness manifest-check diff-coverage bench run \ deploy reinstall clean \ install-system install-go install-aws quickinstall \ lint fix govulncheck \ - distro distro-amd64 distro-arm64 distro-clean \ - ansible-dev-preflight ansible-dev-teardown ansible-dev-install ansible-dev-reset ansible-dev-deploy ansible-dev-status ansible-dev-logs ansible-dev-snapshot ansible-dev-restore ansible-dev-version ansible-dev-vpc + distro distro-amd64 distro-arm64 distro-clean diff --git a/build/microvm/init.sh b/build/microvm/init.sh index 869fb59fe..0cf893ad7 100755 --- a/build/microvm/init.sh +++ b/build/microvm/init.sh @@ -6,20 +6,58 @@ mount -t devtmpfs dev /dev # virtio_mmio is built-in (CONFIG_VIRTIO_MMIO=y) so virtio-net devices are # already enumerated by the time PID 1 runs — no modprobe needed. # qemu_fw_cfg and virtio_net are =m in stock Alpine linux-virt, so load them. -modprobe qemu_fw_cfg 2>/dev/null || modprobe fw_cfg_sysfs 2>/dev/null || true -modprobe virtio_net 2>/dev/null || true +# +# modprobe needs a usable modules.dep; when the image is built on a host whose +# depmod can't produce one, busybox modprobe silently no-ops and the module +# never loads — fw_cfg then never appears and init panics. insmod by explicit +# path needs no modules.dep, so fall back to it. Handles .ko and .ko.gz. +load_mod() { + modprobe "$1" 2>/dev/null && return 0 + for ko in $(find /lib/modules -name "$1.ko" -o -name "$1.ko.gz" 2>/dev/null); do + insmod "$ko" 2>/dev/null && return 0 + case "$ko" in + *.gz) gunzip -c "$ko" >/tmp/mod.ko 2>/dev/null \ + && insmod /tmp/mod.ko 2>/dev/null && return 0 ;; + esac + done + return 1 +} +load_mod qemu_fw_cfg || load_mod fw_cfg_sysfs || true +# virtio_net depends on net_failover, which depends on failover. The load_mod +# insmod fallback loads a single .ko with no dependency resolution, and +# modules.dep is absent when the image is built on a kmod-less host (depmod +# skipped), so modprobe can't resolve the chain either. Load it explicitly +# bottom-up so the NIC comes up regardless of how the image was built — +# without this the agent has no network and the LB hangs in provisioning. +load_mod failover || true +load_mod net_failover || true +load_mod virtio_net || true -for i in 1 2 3 4 5; do - [ -f /sys/firmware/qemu_fw_cfg/by_name/opt/spinifex/netcfg/raw ] && break - sleep 0.02 +NETCFG=/sys/firmware/qemu_fw_cfg/by_name/opt/spinifex/netcfg/raw +i=0 +while [ "$i" -lt 25 ]; do + [ -f "$NETCFG" ] && break + sleep 0.04 + i=$((i + 1)) done -if [ ! -f /sys/firmware/qemu_fw_cfg/by_name/opt/spinifex/netcfg/raw ]; then +if [ ! -f "$NETCFG" ]; then + # Dump host/guest fw_cfg state to the serial console before init exits so a + # "netcfg not available" panic is diagnosable from the captured console log + # (module-not-loaded vs qemu-not-exposing-by_name vs wrong machine type). echo "[init] FATAL: fw_cfg netcfg not available" >&2 + echo "[init] dmi: vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) product=$(cat /sys/class/dmi/id/product_name 2>/dev/null) bios=$(cat /sys/class/dmi/id/bios_version 2>/dev/null)" >&2 + echo "[init] qemu_fw_cfg dir present: $([ -d /sys/firmware/qemu_fw_cfg ] && echo yes || echo NO)" >&2 + echo "[init] modules: $(lsmod 2>/dev/null | awk 'NR>1{print $1}' | tr '\n' ',')" >&2 + echo "[init] by_name listing:" >&2 + ls -la /sys/firmware/qemu_fw_cfg/by_name/ 2>&1 | sed 's/^/[init] /' >&2 + ls -la /sys/firmware/qemu_fw_cfg/by_name/opt/ 2>&1 | sed 's/^/[init] /' >&2 + echo "[init] dmesg fw_cfg:" >&2 + dmesg 2>/dev/null | grep -i fw_cfg | sed 's/^/[init] /' >&2 exit 1 fi -. /sys/firmware/qemu_fw_cfg/by_name/opt/spinifex/netcfg/raw +. "$NETCFG" for n in 0 1 2 3 4 5; do eval mac=\$NIC${n}_MAC diff --git a/build/systemd/spinifex-vpcd.service b/build/systemd/spinifex-vpcd.service index 4c3582779..9e68bd7cc 100644 --- a/build/systemd/spinifex-vpcd.service +++ b/build/systemd/spinifex-vpcd.service @@ -16,25 +16,34 @@ OOMScoreAdjust=-500 Environment=SPINIFEX_CONFIG_PATH=/etc/spinifex/spinifex.toml Environment=SPINIFEX_BASE_DIR=/var/lib/spinifex/vpcd/ -# Filesystem access — NoNewPrivileges omitted (vpcd uses sudo for ovs-vsctl, ovn-nbctl, systemctl) -ProtectSystem=full +# vpcd MUST share the HOST mount namespace. IMDS binds one listener per VPC inside +# a per-VPC netns entered in-process via setns(CLONE_NEWNET); the /run/netns/ +# bind-mount that `ip netns add` creates must be host-visible (for diagnostics) and +# must survive a vpcd restart (so the listener can rebind). ANY filesystem-sandbox +# directive — ProtectSystem / ProtectHome / PrivateTmp / ProtectKernel* / +# ProtectControlGroups / ProtectProc / Read{Only,Write}Paths — forks a PRIVATE +# mount namespace that traps that bind-mount inside vpcd, leaving the host a stale +# handle that fails setns(2) with EINVAL (CI runs 26920490454, 26922587471). Those +# directives are therefore omitted by design, and so is the MountFlags=shared / +# spinifex-netns.service propagation workaround they required. +# +# Boundary that REMAINS: DAC (User=spinifex-vpcd) plus the seccomp / capability / +# namespace / address-family restrictions below. The durable fix that restores +# filesystem sandboxing AND drops CAP_SYS_ADMIN is the process-per-netns model +# (`ip netns exec` helper instead of in-process setns) — see +# docs/development/improvements/imds-l2-datapath.md Phase 5. PrivateDevices=no -ReadOnlyPaths=/etc/spinifex/spinifex.toml /etc/spinifex/ca.pem -ReadWritePaths=/var/lib/spinifex/vpcd /var/log/spinifex -# Hardening -ProtectHome=yes -PrivateTmp=yes +# Hardening that does NOT fork a mount namespace (kept) RestrictSUIDSGID=no LockPersonality=yes RestrictRealtime=yes -RestrictNamespaces=yes ProtectClock=yes -ProtectKernelTunables=yes -ProtectKernelModules=yes -ProtectKernelLogs=yes -ProtectControlGroups=yes -ProtectProc=invisible +# IMDS enters the per-VPC netns via setns(CLONE_NEWNET); the `ip -n ` / +# `ip netns exec` plumbing also unshares a mount namespace (CLONE_NEWNS, to give +# the netns its own /sys), so `mnt` must be allowlisted alongside `net` or the +# first in-netns command fails with EPERM. +RestrictNamespaces=net mnt SystemCallArchitectures=native MemoryDenyWriteExecute=no # AF_PACKET: required by the in-process DHCP client (github.com/insomniacslk/dhcp @@ -44,9 +53,10 @@ RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK AF_PACKET # CAP_NET_RAW: open AF_PACKET sockets. # CAP_NET_BIND_SERVICE: covers unicast RENEW to UDP :68. # CAP_NET_ADMIN: already used for ovs-vsctl / ip link wiring on the bridge. +# CAP_SYS_ADMIN: setns(CLONE_NEWNET) into per-VPC IMDS netns to bind the listener. # Note, disabled for now, sudo issue with ovs -AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID -CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID CAP_SYS_ADMIN +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID CAP_SYS_ADMIN NoNewPrivileges=no ProtectHostname=yes KeyringMode=private diff --git a/cmd/eks-gateway-publish/main.go b/cmd/eks-gateway-publish/main.go new file mode 100644 index 000000000..5cd33a7b2 --- /dev/null +++ b/cmd/eks-gateway-publish/main.go @@ -0,0 +1,134 @@ +// eks-gateway-publish runs inside the EKS K3s control-plane VM and relays a +// single publication to the host through the AWS gateway, instead of dialing +// core NATS directly. +// +// It reads a JSON payload on stdin, wraps it as +// {accountId, channel, kind, payload}, SigV4-signs (service "eks") an HTTPS +// POST to {gateway}/clusters/{cluster}/internal-publish, and retries with +// backoff until the gateway returns 2xx or the attempt budget is exhausted — +// so a degraded link surfaces as a non-zero exit rather than a silently +// dropped message (the failure mode of fire-and-forget `nats pub`). +// +// Usage: +// +// echo '{"token":"..."}' | eks-gateway-publish -channel bootstrap -kind k3s-bootstrap-token +// kubectl ... | eks-gateway-publish -channel state +// +// Flags default to environment variables seeded by cloud-init: +// EKS_GATEWAY_URL, EKS_GATEWAY_CA, EKS_ACCESS_KEY, EKS_SECRET_KEY, EKS_REGION, +// EKS_ACCOUNT_ID, EKS_CLUSTER_NAME. +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "log/slog" + "os" + "time" + + "github.com/mulgadc/spinifex/internal/eksgw" + + _ "github.com/mulgadc/spinifex/internal/fipsboot" +) + +const ( + // Retry budget: the control plane reaches readiness before this runs, so a + // failing POST means a degraded link, not a cold start. Bounded so a stuck + // boot still terminates the OpenRC service. + maxAttempts = 30 + retryDelay = 5 * time.Second +) + +type publishBody struct { + AccountID string `json:"accountId"` + Channel string `json:"channel"` + Kind string `json:"kind"` + Payload json.RawMessage `json:"payload"` +} + +func main() { + var ( + gatewayURL string + gatewayCA string + accessKey string + secretKey string + region string + accountID string + clusterName string + channel string + kind string + ) + + flag.StringVar(&gatewayURL, "gateway", os.Getenv("EKS_GATEWAY_URL"), "Gateway URL (e.g. https://10.15.8.1:9999)") + flag.StringVar(&gatewayCA, "gateway-ca", os.Getenv("EKS_GATEWAY_CA"), "Path to gateway TLS CA PEM (optional; falls back to system trust)") + flag.StringVar(&accessKey, "access-key", os.Getenv("EKS_ACCESS_KEY"), "AWS access key ID") + flag.StringVar(&secretKey, "secret-key", os.Getenv("EKS_SECRET_KEY"), "AWS secret access key") + flag.StringVar(®ion, "region", os.Getenv("EKS_REGION"), "AWS region for SigV4 signing") + flag.StringVar(&accountID, "account-id", os.Getenv("EKS_ACCOUNT_ID"), "Cluster account ID") + flag.StringVar(&clusterName, "cluster", os.Getenv("EKS_CLUSTER_NAME"), "Cluster name") + flag.StringVar(&channel, "channel", "", "Publish channel: bootstrap|state") + flag.StringVar(&kind, "kind", "", "Bootstrap subject kind (bootstrap channel only)") + flag.Parse() + + switch { + case accountID == "": + fatal("--account-id is required (or set EKS_ACCOUNT_ID)") + case clusterName == "": + fatal("--cluster is required (or set EKS_CLUSTER_NAME)") + case channel != "bootstrap" && channel != "state": + fatal("--channel must be bootstrap or state") + case channel == "bootstrap" && kind == "": + fatal("--kind is required for the bootstrap channel") + } + + payload, err := io.ReadAll(os.Stdin) + if err != nil { + fatal(fmt.Sprintf("read stdin payload: %v", err)) + } + if len(bytes.TrimSpace(payload)) == 0 { + fatal("empty stdin payload") + } + if !json.Valid(payload) { + fatal("stdin payload is not valid JSON") + } + + body, err := json.Marshal(publishBody{ + AccountID: accountID, + Channel: channel, + Kind: kind, + Payload: json.RawMessage(payload), + }) + if err != nil { + fatal(fmt.Sprintf("marshal request body: %v", err)) + } + + client, err := eksgw.New(gatewayURL, gatewayCA, accessKey, secretKey, region) + if err != nil { + fatal(fmt.Sprintf("build gateway client: %v", err)) + } + path := "/clusters/" + clusterName + "/internal-publish" + + var lastErr error + for attempt := 1; attempt <= maxAttempts; attempt++ { + if _, err := client.Post(path, body); err != nil { + lastErr = err + slog.Warn("eks-gateway-publish: attempt failed", + "channel", channel, "kind", kind, "attempt", attempt, "err", err) + if attempt < maxAttempts { + time.Sleep(retryDelay) + } + continue + } + slog.Info("eks-gateway-publish: published", "channel", channel, "kind", kind) + return + } + fatal(fmt.Sprintf("publish failed after %d attempts: %v", maxAttempts, lastErr)) +} + +func fatal(msg string) { + slog.Error("eks-gateway-publish: " + msg) + os.Exit(1) +} diff --git a/cmd/eks-token-webhook/cert.go b/cmd/eks-token-webhook/cert.go new file mode 100644 index 000000000..3a4763197 --- /dev/null +++ b/cmd/eks-token-webhook/cert.go @@ -0,0 +1,137 @@ +package main + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/pem" + "fmt" + "math/big" + "net" + "os" + "time" + + "github.com/mulgadc/spinifex/internal/tlsconfig" +) + +// tlsCertificate is the webhook's loopback serving certificate. +type tlsCertificate struct { + cert tls.Certificate +} + +func (c tlsCertificate) serverTLSConfig() *tls.Config { + return &tls.Config{ + Certificates: []tls.Certificate{c.cert}, + MinVersion: tls.VersionTLS13, + CurvePreferences: tlsconfig.Curves, + } +} + +// ensureServingCert loads the persisted self-signed serving cert/key, or mints +// a fresh ECDSA-P256 pair (SAN 127.0.0.1 / localhost) and persists it. Reusing +// across restarts keeps the CA the apiserver loaded into its webhook kubeconfig +// valid even if the webhook process is restarted. Returns the parsed cert and +// the certificate PEM (for embedding as the kubeconfig CA bundle). +func ensureServingCert(certPath, keyPath string) (tlsCertificate, []byte, error) { + if certPEM, keyPEM, ok := readCertPair(certPath, keyPath); ok { + cert, err := tls.X509KeyPair(certPEM, keyPEM) + if err == nil { + return tlsCertificate{cert: cert}, certPEM, nil + } + // Fall through to regenerate on a corrupt/incompatible persisted pair. + } + + certPEM, keyPEM, err := generateSelfSigned() + if err != nil { + return tlsCertificate{}, nil, err + } + if err := os.WriteFile(certPath, certPEM, 0o644); err != nil { + return tlsCertificate{}, nil, fmt.Errorf("write cert %s: %w", certPath, err) + } + if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil { + return tlsCertificate{}, nil, fmt.Errorf("write key %s: %w", keyPath, err) + } + cert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + return tlsCertificate{}, nil, fmt.Errorf("parse minted keypair: %w", err) + } + return tlsCertificate{cert: cert}, certPEM, nil +} + +func readCertPair(certPath, keyPath string) (certPEM, keyPEM []byte, ok bool) { + c, err := os.ReadFile(certPath) + if err != nil { + return nil, nil, false + } + k, err := os.ReadFile(keyPath) + if err != nil { + return nil, nil, false + } + return c, k, true +} + +func generateSelfSigned() (certPEM, keyPEM []byte, err error) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, fmt.Errorf("generate key: %w", err) + } + serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + if err != nil { + return nil, nil, fmt.Errorf("serial: %w", err) + } + now := time.Now().UTC() + tmpl := x509.Certificate{ + SerialNumber: serial, + Subject: pkix.Name{CommonName: "eks-token-webhook"}, + NotBefore: now.Add(-time.Hour), + NotAfter: now.AddDate(10, 0, 0), + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback}, + DNSNames: []string{"localhost"}, + } + der, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv) + if err != nil { + return nil, nil, fmt.Errorf("create cert: %w", err) + } + keyDER, err := x509.MarshalECPrivateKey(priv) + if err != nil { + return nil, nil, fmt.Errorf("marshal key: %w", err) + } + certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) + keyPEM = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER}) + return certPEM, keyPEM, nil +} + +// writeAPIServerKubeconfig writes the kubeconfig kube-apiserver loads via +// --authentication-token-webhook-config-file. It points the apiserver at the +// loopback webhook and trusts its self-signed serving cert as the CA bundle. +func writeAPIServerKubeconfig(path, addr string, certPEM []byte) error { + caB64 := base64.StdEncoding.EncodeToString(certPEM) + kubeconfig := fmt.Sprintf(`apiVersion: v1 +kind: Config +clusters: +- name: mulga-eks-token-webhook + cluster: + certificate-authority-data: %s + server: https://%s/authenticate +users: +- name: kube-apiserver +contexts: +- name: webhook + context: + cluster: mulga-eks-token-webhook + user: kube-apiserver +current-context: webhook +`, caB64, addr) + if err := os.WriteFile(path, []byte(kubeconfig), 0o600); err != nil { + return fmt.Errorf("write %s: %w", path, err) + } + return nil +} diff --git a/cmd/eks-token-webhook/main.go b/cmd/eks-token-webhook/main.go index b2b28ba55..a314a2051 100644 --- a/cmd/eks-token-webhook/main.go +++ b/cmd/eks-token-webhook/main.go @@ -1,38 +1,227 @@ // eks-token-webhook is the K8s TokenReview webhook authenticator baked into -// the eks-server AMI. It decodes the SigV4-presigned GetCallerIdentity URL -// produced by `aws eks get-token`, enforces the X-K8s-Aws-Id cross-cluster -// pin, calls Mulga STS over NATS, and resolves the principal to a TokenReview -// response via the cluster's AccessEntry KV. +// the eks-server AMI. Per kubectl call the kube-apiserver POSTs the bearer +// token (produced by `aws eks get-token`); the webhook relays it through the +// AWS gateway broker (SigV4 HTTPS POST to /clusters/{name}/token-review), which +// host-side runs the STS verify (cross-cluster x-k8s-aws-id pin) + AccessEntry +// KV lookup and returns the resolved identity. The webhook never speaks core +// NATS — same gateway-broker model as ELBv2's lb-agent and eks-gateway-publish. // -// This is a placeholder build that returns 503 Service Unavailable on every -// path. It exists so the eks-server AMI build succeeds (INSTALL_BINARIES is -// satisfied) and the K3s server VM boots cleanly. Real implementation lands -// with bead mulga-cs-eks-6c. +// It binds 127.0.0.1 only; the kube-apiserver is the sole client (loopback). +// On startup it writes the apiserver webhook kubeconfig (with its self-signed +// serving CA) so k3s can be pointed at it via +// --authentication-token-webhook-config-file. package main import ( + "encoding/json" + "errors" "flag" + "fmt" "log/slog" "net/http" "os" + "time" + + "github.com/mulgadc/spinifex/internal/eksgw" + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" ) +// config is the webhook's runtime configuration, sourced from the first-boot +// env (/etc/spinifex-eks/first-boot.env) cloud-init seeds onto the VM. +type config struct { + addr string + gatewayURL string + gatewayCA string + accessKey string + secretKey string + region string + accountID string + clusterName string + certPath string + keyPath string + kubeconfig string +} + +func loadConfig() (config, error) { + c := config{ + addr: flagAddr, + gatewayURL: os.Getenv("EKS_GATEWAY_URL"), + gatewayCA: os.Getenv("EKS_GATEWAY_CA"), + accessKey: os.Getenv("EKS_ACCESS_KEY"), + secretKey: os.Getenv("EKS_SECRET_KEY"), + region: os.Getenv("EKS_REGION"), + accountID: os.Getenv("EKS_ACCOUNT_ID"), + clusterName: os.Getenv("EKS_CLUSTER_NAME"), + certPath: envOr("EKS_WEBHOOK_CERT", "/etc/spinifex-eks/token-webhook.crt"), + keyPath: envOr("EKS_WEBHOOK_KEY", "/etc/spinifex-eks/token-webhook.key"), + kubeconfig: envOr("EKS_WEBHOOK_KUBECONFIG", "/etc/spinifex-eks/token-webhook.kubeconfig"), + } + switch { + case c.gatewayURL == "": + return c, errors.New("EKS_GATEWAY_URL not set") + case c.accessKey == "" || c.secretKey == "": + return c, errors.New("EKS_ACCESS_KEY / EKS_SECRET_KEY not set") + case c.accountID == "": + return c, errors.New("EKS_ACCOUNT_ID not set") + case c.clusterName == "": + return c, errors.New("EKS_CLUSTER_NAME not set") + } + return c, nil +} + +func envOr(key, def string) string { + if v := os.Getenv(key); v != "" { + return v + } + return def +} + +var flagAddr string + func main() { - addr := flag.String("addr", "127.0.0.1:8443", "listen address") + flag.StringVar(&flagAddr, "addr", "127.0.0.1:8443", "listen address (loopback only)") flag.Parse() slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo}))) - mux := http.NewServeMux() - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Retry-After", "30") - w.Header().Set("Content-Type", "application/json") - http.Error(w, `{"error":"eks-token-webhook stub: real implementation not yet deployed"}`, http.StatusServiceUnavailable) - }) + cfg, err := loadConfig() + if err != nil { + slog.Error("eks-token-webhook config error", "err", err) + os.Exit(1) + } - slog.Info("eks-token-webhook stub starting", "addr", *addr) - if err := http.ListenAndServe(*addr, mux); err != nil { - slog.Error("listen failed", "err", err) + if err := run(cfg); err != nil { + slog.Error("eks-token-webhook fatal", "err", err) os.Exit(1) } } + +func run(cfg config) error { + // Serving cert + apiserver kubeconfig. Persisted and reused across restarts + // so a webhook crash does not invalidate the CA the apiserver already loaded. + tlsCert, certPEM, err := ensureServingCert(cfg.certPath, cfg.keyPath) + if err != nil { + return fmt.Errorf("serving cert: %w", err) + } + if err := writeAPIServerKubeconfig(cfg.kubeconfig, cfg.addr, certPEM); err != nil { + return fmt.Errorf("write apiserver kubeconfig: %w", err) + } + + client, err := eksgw.New(cfg.gatewayURL, cfg.gatewayCA, cfg.accessKey, cfg.secretKey, cfg.region) + if err != nil { + return fmt.Errorf("build gateway client: %w", err) + } + + authr := &authenticator{ + accountID: cfg.accountID, + clusterName: cfg.clusterName, + review: gatewayReviewer(client, cfg.clusterName, cfg.accountID), + } + + mux := http.NewServeMux() + mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) + }) + mux.HandleFunc("/authenticate", authr.handle) + + server := newServer(cfg.addr, mux, tlsCert) + slog.Info("eks-token-webhook listening", "addr", cfg.addr, "cluster", cfg.clusterName) + if err := server.ListenAndServeTLS("", ""); err != nil && !errors.Is(err, http.ErrServerClosed) { + return fmt.Errorf("serve: %w", err) + } + return nil +} + +// tokenReviewRequest is the body POSTed to /clusters/{name}/token-review. The +// webhook holds system SigV4 creds (system account, not the cluster account), +// so accountId names the cluster account explicitly — same as PublishInternal. +type tokenReviewRequest struct { + AccountID string `json:"accountId"` + Token string `json:"token"` +} + +// gatewayReviewer returns the per-call relay: SigV4-POST {accountId,token} to +// the gateway and decode the resolved identity. A non-2xx / transport error is +// surfaced (handle() turns it into a 5xx so the apiserver retries) rather than a +// silent deny, distinguishing "webhook broker down" from "token rejected". +func gatewayReviewer(client *eksgw.Client, clusterName, accountID string) func(string) (handlers_eks.WebhookTokenReviewResult, error) { + path := "/clusters/" + clusterName + "/token-review" + return func(token string) (handlers_eks.WebhookTokenReviewResult, error) { + body, err := json.Marshal(tokenReviewRequest{AccountID: accountID, Token: token}) + if err != nil { + return handlers_eks.WebhookTokenReviewResult{}, fmt.Errorf("marshal request: %w", err) + } + respBody, err := client.Post(path, body) + if err != nil { + return handlers_eks.WebhookTokenReviewResult{}, err + } + var res handlers_eks.WebhookTokenReviewResult + if err := json.Unmarshal(respBody, &res); err != nil { + return handlers_eks.WebhookTokenReviewResult{}, fmt.Errorf("decode response: %w", err) + } + return res, nil + } +} + +// authenticator resolves a TokenReview by relaying to the gateway broker. +type authenticator struct { + accountID string + clusterName string + review func(token string) (handlers_eks.WebhookTokenReviewResult, error) +} + +func (a *authenticator) handle(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + var review tokenReview + if err := json.NewDecoder(r.Body).Decode(&review); err != nil { + http.Error(w, "bad request", http.StatusBadRequest) + return + } + + res, err := a.review(review.Spec.Token) + if err != nil { + // Broker fault (gateway down, transport error): a 5xx tells the apiserver + // to retry rather than treating a transient outage as a hard deny. + slog.Error("TokenReview relay failed", "cluster", a.clusterName, "err", err) + http.Error(w, "token review unavailable", http.StatusServiceUnavailable) + return + } + + status := tokenReviewStatus{Authenticated: res.Authenticated} + if res.Authenticated { + status.User = userInfo{ + Username: res.Username, + UID: res.UID, + Groups: res.Groups, + } + // Confirm the token for the audiences the apiserver asked about; an empty + // status.audiences is rejected when --api-audiences is configured. + status.Audiences = review.Spec.Audiences + } + slog.Info("TokenReview decision", "authenticated", status.Authenticated, "username", status.User.Username, "audiences", status.Audiences) + + resp := tokenReview{ + APIVersion: "authentication.k8s.io/v1", + Kind: "TokenReview", + Status: status, + } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(resp); err != nil { + slog.Error("encode TokenReview response", "err", err) + } +} + +// newServer builds the loopback TLS server. Split out so tests can construct it +// without binding a port. +func newServer(addr string, handler http.Handler, cert tlsCertificate) *http.Server { + return &http.Server{ + Addr: addr, + Handler: handler, + ReadHeaderTimeout: 5 * time.Second, + TLSConfig: cert.serverTLSConfig(), + } +} diff --git a/cmd/eks-token-webhook/main_test.go b/cmd/eks-token-webhook/main_test.go new file mode 100644 index 000000000..889ae0832 --- /dev/null +++ b/cmd/eks-token-webhook/main_test.go @@ -0,0 +1,123 @@ +package main + +import ( + "encoding/base64" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// postReview drives authenticator.handle with a TokenReview carrying token and +// audiences, returning the decoded response status and HTTP code. +func postReview(t *testing.T, a *authenticator, token string, audiences []string) (tokenReviewStatus, int) { + t.Helper() + body, err := json.Marshal(tokenReview{ + APIVersion: "authentication.k8s.io/v1", + Kind: "TokenReview", + Spec: tokenReviewSpec{Token: token, Audiences: audiences}, + }) + require.NoError(t, err) + + req := httptest.NewRequest(http.MethodPost, "/authenticate", strings.NewReader(string(body))) + w := httptest.NewRecorder() + a.handle(w, req) + + if w.Code != http.StatusOK { + return tokenReviewStatus{}, w.Code + } + var resp tokenReview + require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp)) + return resp.Status, w.Code +} + +func TestHandle_GrantsAndStampsAudiences(t *testing.T) { + a := &authenticator{ + accountID: "111122223333", + clusterName: "alpha", + review: func(string) (handlers_eks.WebhookTokenReviewResult, error) { + return handlers_eks.WebhookTokenReviewResult{ + Authenticated: true, + Username: "arn:aws:iam::111122223333:role/admin", + UID: "AROAEXAMPLE:session", + Groups: []string{"system:masters"}, + }, nil + }, + } + + status, code := postReview(t, a, "tok", []string{"https://kubernetes.default.svc"}) + require.Equal(t, http.StatusOK, code) + require.True(t, status.Authenticated) + assert.Equal(t, "arn:aws:iam::111122223333:role/admin", status.User.Username) + assert.Equal(t, "AROAEXAMPLE:session", status.User.UID) + assert.Equal(t, []string{"system:masters"}, status.User.Groups) + assert.Equal(t, []string{"https://kubernetes.default.svc"}, status.Audiences) +} + +func TestHandle_DeniesWithoutStampingAudiences(t *testing.T) { + a := &authenticator{ + clusterName: "alpha", + review: func(string) (handlers_eks.WebhookTokenReviewResult, error) { + return handlers_eks.WebhookTokenReviewResult{Authenticated: false}, nil + }, + } + + status, code := postReview(t, a, "tok", []string{"https://kubernetes.default.svc"}) + require.Equal(t, http.StatusOK, code) + assert.False(t, status.Authenticated) + assert.Empty(t, status.User.Username) + assert.Empty(t, status.Audiences, "unauthenticated review must not stamp audiences") +} + +// A broker fault is a 5xx (retryable), not a silent deny — a transient gateway +// outage must not look identical to a rejected token. +func TestHandle_BrokerFaultReturns5xx(t *testing.T) { + a := &authenticator{ + clusterName: "alpha", + review: func(string) (handlers_eks.WebhookTokenReviewResult, error) { + return handlers_eks.WebhookTokenReviewResult{}, errors.New("gateway returned 503") + }, + } + + _, code := postReview(t, a, "tok", nil) + assert.Equal(t, http.StatusServiceUnavailable, code) +} + +func TestEnsureServingCert_PersistsAndReuses(t *testing.T) { + dir := t.TempDir() + certPath := filepath.Join(dir, "wh.crt") + keyPath := filepath.Join(dir, "wh.key") + + c1, pem1, err := ensureServingCert(certPath, keyPath) + require.NoError(t, err) + require.NotEmpty(t, pem1) + require.NotNil(t, c1.serverTLSConfig()) + + // Second call reuses the persisted pair (same cert bytes). + _, pem2, err := ensureServingCert(certPath, keyPath) + require.NoError(t, err) + assert.Equal(t, pem1, pem2) +} + +func TestWriteAPIServerKubeconfig(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "wh.kubeconfig") + certPEM := []byte("-----BEGIN CERTIFICATE-----\nMIIB\n-----END CERTIFICATE-----\n") + + require.NoError(t, writeAPIServerKubeconfig(path, "127.0.0.1:8443", certPEM)) + + data, err := os.ReadFile(path) + require.NoError(t, err) + body := string(data) + assert.Contains(t, body, "server: https://127.0.0.1:8443/authenticate") + assert.Contains(t, body, base64.StdEncoding.EncodeToString(certPEM)) + assert.Contains(t, body, "current-context: webhook") +} diff --git a/cmd/eks-token-webhook/tokenreview.go b/cmd/eks-token-webhook/tokenreview.go new file mode 100644 index 000000000..3b2addad2 --- /dev/null +++ b/cmd/eks-token-webhook/tokenreview.go @@ -0,0 +1,34 @@ +package main + +// Minimal authentication.k8s.io/v1 TokenReview wire types. Hand-rolled to avoid +// pulling k8s.io/api into the spinifex module for these few fields; the shapes +// match what kube-apiserver sends and expects on the webhook contract. + +type tokenReview struct { + APIVersion string `json:"apiVersion"` + Kind string `json:"kind"` + Spec tokenReviewSpec `json:"spec,omitzero"` + Status tokenReviewStatus `json:"status"` +} + +type tokenReviewSpec struct { + Token string `json:"token"` + Audiences []string `json:"audiences,omitempty"` +} + +type tokenReviewStatus struct { + Authenticated bool `json:"authenticated"` + User userInfo `json:"user,omitzero"` + // Audiences echoes the request's spec.audiences on success. When the + // apiserver runs with --api-audiences, it sends spec.audiences and discards + // an authenticated=true response that does not confirm a matching audience — + // so an empty Audiences here surfaces as a 401 despite a valid principal. + Audiences []string `json:"audiences,omitempty"` + Error string `json:"error,omitempty"` +} + +type userInfo struct { + Username string `json:"username,omitempty"` + UID string `json:"uid,omitempty"` + Groups []string `json:"groups,omitempty"` +} diff --git a/cmd/spinifex/cmd/admin.go b/cmd/spinifex/cmd/admin.go index 44f201de7..655e5ded0 100644 --- a/cmd/spinifex/cmd/admin.go +++ b/cmd/spinifex/cmd/admin.go @@ -325,6 +325,7 @@ func init() { imagesImportCmd.Flags().String("tmp-dir", os.TempDir(), "Temporary directory for image import processing") imagesImportCmd.Flags().String("name", "", "Import specified image by name") + imagesImportCmd.Flags().String("ami-name", "", "Override the registered AMI name (DescribeImages name). Defaults to ami-{distro}-{version}-{arch}. Use for locally-built appliances (e.g. --ami-name spinifex-eks-node).") imagesImportCmd.Flags().String("file", "", "Import file from specified path (raw, qcow2, compressed)") imagesImportCmd.Flags().String("distro", "", "Specified distro name (e.g debian)") imagesImportCmd.Flags().String("version", "", "Specified distro version (e.g 12)") @@ -380,6 +381,7 @@ func runimagesImportCmd(cmd *cobra.Command, args []string) { // and the file is used directly (no download). When only --file is set, // metadata comes from flags and no catalog tags are applied. imageName, _ := cmd.Flags().GetString("name") + amiNameOverride, _ := cmd.Flags().GetString("ami-name") localFile, _ := cmd.Flags().GetString("file") if imageName == "" && localFile == "" { @@ -535,6 +537,9 @@ func runimagesImportCmd(cmd *cobra.Command, args []string) { // Calculate the size manifest.AMIMetadata.Name = fmt.Sprintf("ami-%s-%s-%s", image.Distro, image.Version, image.Arch) + if amiNameOverride != "" { + manifest.AMIMetadata.Name = amiNameOverride + } volumeId := utils.GenerateResourceID("ami") manifest.AMIMetadata.ImageID = volumeId diff --git a/cmd/spinifex/cmd/service.go b/cmd/spinifex/cmd/service.go index 8cdb0d879..10724e8f9 100644 --- a/cmd/spinifex/cmd/service.go +++ b/cmd/spinifex/cmd/service.go @@ -538,7 +538,7 @@ var awsgwStartCmd = &cobra.Command{ awsgwHost := viper.GetString("host") if awsgwHost != "" { fmt.Println("Overwriting awsgw host to:", awsgwHost) - //nodeConfig.AWSGW.Host = awsgwHost + nodeConfig.AWSGW.Host = awsgwHost } awsgwTlsCert := viper.GetString("tls-cert") diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index d01dae7bf..fb6bbbddc 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -8,162 +8,102 @@ Platform management commands not exposed via the AWS gateway API. These are CLI- Service lifecycle commands for starting, stopping, and checking status of all Spinifex cluster services. Each service subcommand supports `start`, `stop`, and `status` operations. -| Command | Flags | Prerequisites | Basic Logic | Status | -|---------|-------|---------------|-------------|--------| -| `spx service predastore start` | `--port` (default: 8443), `--host` (default: 0.0.0.0), `--base-path`, `--config-path`, `--debug`, `--tls-cert`, `--tls-key`, `--backend` (distributed/filesystem, default: distributed), `--node-id` (default: 0), `--pprof`, `--pprof-output` | TLS cert/key, base-path, config-path required | Creates predastore service instance with S3-compatible storage backend → starts service | **DONE** | -| `spx service predastore stop` | — | Predastore must be running | Stops the predastore service | **DONE** | -| `spx service predastore status` | — | None | Reports predastore service status | **DONE** | -| `spx service viperblock start` | `--s3-host` (default: 0.0.0.0:8443), `--s3-bucket` (default: predastore), `--s3-region` (default: ap-southeast-2), `--plugin-path` (auto-detected via `nbdkit --dump-config plugindir`; typically `/usr/lib/x86_64-linux-gnu/nbdkit/plugins/nbdkit-viperblock-plugin.so` on amd64, overridable via `SPINIFEX_VIPERBLOCK_PLUGIN_PATH` in `/etc/spinifex/systemd.env`) | `--config` required, plugin-path must exist on disk | Loads cluster config → connects to NATS and Predastore → starts viperblock block storage service with NBD plugin | **DONE** | -| `spx service viperblock stop` | — | Viperblock must be running | Stops the viperblock service | **DONE** | -| `spx service viperblock status` | — | None | Reports viperblock service status | **DONE** | -| `spx service nats start` | `--port` (default: 4222), `--host` (default: 0.0.0.0), `--debug`, `--data-dir`, `--jetstream` | None | Starts embedded NATS server with optional JetStream | **DONE** | -| `spx service nats stop` | — | NATS must be running | Stops the NATS service | **DONE** | -| `spx service nats status` | — | None | Reports NATS service status | **DONE** | -| `spx service spinifex start` | `--wal-dir` | `--config` required | Loads cluster config → starts spinifex daemon (VM orchestration, NATS subscriptions, health endpoint) | **DONE** | -| `spx service spinifex stop` | — | Spinifex daemon must be running | Stops the spinifex daemon service | **DONE** | -| `spx service spinifex status` | — | None | Reports spinifex daemon service status | **DONE** | -| `spx service awsgw start` | `--host` (default: 0.0.0.0:9999), `--tls-cert`, `--tls-key`, `--debug` | `--config` required | Loads cluster config → starts AWS-compatible gateway with SigV4 auth, IAM policy enforcement, TLS | **DONE** | -| `spx service awsgw stop` | — | AWS gateway must be running | Stops the AWS gateway service | **DONE** | -| `spx service awsgw status` | — | None | Reports AWS gateway service status | **DONE** | -| `spx service spinifex-ui start` | `--port` (default: 3000), `--host` (default: 0.0.0.0), `--tls-cert`, `--tls-key` | None | Starts embedded web UI server serving the React frontend. Aliases: `ui`, `spinifexui` | **DONE** | -| `spx service spinifex-ui stop` | — | spinifex-ui must be running | Stops the spinifex-ui service | **DONE** | -| `spx service spinifex-ui status` | — | None | Reports spinifex-ui service status | **DONE** | -| `spx service vpcd start` | — | `--config` required, OVN/OVS installed | Loads cluster config → starts VPC daemon (subscribes to `vpc.*` NATS events, translates to OVN logical switches/ports/routers) | **DONE** | -| `spx service vpcd stop` | — | vpcd must be running | Stops the vpcd service | **DONE** | -| `spx service vpcd status` | — | None | Reports vpcd service status | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx service predastore start` | `--port` (default: 8443), `--host` (default: 0.0.0.0), `--base-path`, `--config-path`, `--debug`, `--tls-cert`, `--tls-key`, `--backend` (distributed/filesystem, default: distributed), `--node-id` (default: 0), `--pprof`, `--pprof-output` | Creates predastore service instance with S3-compatible storage backend → starts service | +| `spx service predastore stop` | — | Stops the predastore service | +| `spx service predastore status` | — | Reports predastore service status | +| `spx service viperblock start` | `--s3-host` (default: 0.0.0.0:8443), `--s3-bucket` (default: predastore), `--s3-region` (default: ap-southeast-2), `--plugin-path` (auto-detected via `nbdkit --dump-config plugindir`; typically `/usr/lib/x86_64-linux-gnu/nbdkit/plugins/nbdkit-viperblock-plugin.so` on amd64, overridable via `SPINIFEX_VIPERBLOCK_PLUGIN_PATH` in `/etc/spinifex/systemd.env`) | Loads cluster config → connects to NATS and Predastore → starts viperblock block storage service with NBD plugin | +| `spx service viperblock stop` | — | Stops the viperblock service | +| `spx service viperblock status` | — | Reports viperblock service status | +| `spx service nats start` | `--port` (default: 4222), `--host` (default: 0.0.0.0), `--debug`, `--data-dir`, `--jetstream` | Starts embedded NATS server with optional JetStream | +| `spx service nats stop` | — | Stops the NATS service | +| `spx service nats status` | — | Reports NATS service status | +| `spx service spinifex start` | `--wal-dir` | Loads cluster config → starts spinifex daemon (VM orchestration, NATS subscriptions, health endpoint) | +| `spx service spinifex stop` | — | Stops the spinifex daemon service | +| `spx service spinifex status` | — | Reports spinifex daemon service status | +| `spx service awsgw start` | `--host` (default: 0.0.0.0:9999), `--tls-cert`, `--tls-key`, `--debug` | Loads cluster config → starts AWS-compatible gateway with SigV4 auth, IAM policy enforcement, TLS | +| `spx service awsgw stop` | — | Stops the AWS gateway service | +| `spx service awsgw status` | — | Reports AWS gateway service status | +| `spx service spinifex-ui start` | `--port` (default: 3000), `--host` (default: 0.0.0.0), `--tls-cert`, `--tls-key` | Starts embedded web UI server serving the React frontend. Aliases: `ui`, `spinifexui` | +| `spx service spinifex-ui stop` | — | Stops the spinifex-ui service | +| `spx service spinifex-ui status` | — | Reports spinifex-ui service status | +| `spx service vpcd start` | — | Loads cluster config → starts VPC daemon (subscribes to `vpc.*` NATS events, translates to OVN logical switches/ports/routers) | +| `spx service vpcd stop` | — | Stops the vpcd service | +| `spx service vpcd status` | — | Reports vpcd service status | ### Cluster Inspection Operational commands for inspecting cluster state. These fan out NATS requests to all nodes and aggregate responses. -| Command | Flags | Prerequisites | Basic Logic | Output Columns | Status | -|---------|-------|---------------|-------------|----------------|--------| -| `spx get nodes` | `--timeout` (default: 3s) | Cluster must be running (NATS) | Loads config → publishes to `spinifex.node.status` fan-out topic → collects responses within timeout → merges config-known nodes with NATS responders → nodes that don't respond shown as `NotReady` | NAME, STATUS, IP, REGION, AZ, UPTIME, VMs, SERVICES | **DONE** | -| `spx get vms` | `--timeout` (default: 3s) | Cluster must be running (NATS) | Publishes to `spinifex.node.vms` fan-out topic → collects VM info from all nodes → sorts by node then instance ID → prints table. Alias: `spx get instances` | INSTANCE, STATUS, TYPE, VCPU, MEM, NODE, IP, AGE | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx get nodes` | `--timeout` (default: 3s) | Loads config → publishes to `spinifex.node.status` fan-out topic → collects responses within timeout → merges config-known nodes with NATS responders → nodes that don't respond shown as `NotReady` | +| `spx get vms` | `--timeout` (default: 3s) | Publishes to `spinifex.node.vms` fan-out topic → collects VM info from all nodes → sorts by node then instance ID → prints table. Alias: `spx get instances` | ### Resource Monitoring -| Command | Flags | Prerequisites | Basic Logic | Output | Status | -|---------|-------|---------------|-------------|--------|--------| -| `spx top nodes` | `--timeout` (default: 3s) | Cluster must be running (NATS) | Publishes to `spinifex.node.status` fan-out topic → collects CPU/memory usage per node → aggregates instance type capacity across all nodes → prints two tables: per-node resource usage and cluster-wide instance type availability | Table 1: NAME, CPU (used/total), MEM (used/total), GPU (used/total), VMs — GPU column shows `allocated/total` when passthrough is enabled, `0/count*` when the node is GPU-capable but passthrough is disabled, or `-` when no GPU is present. Table 2: INSTANCE TYPE, AVAILABLE, VCPU, MEMORY | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx top nodes` | `--timeout` (default: 3s) | Publishes to `spinifex.node.status` fan-out topic → collects CPU/memory usage per node → aggregates instance type capacity across all nodes → prints two tables: per-node resource usage and cluster-wide instance type availability | ### Cluster Initialization -| Command | Flags | Prerequisites | Basic Logic | Test Cases | Status | -|---------|-------|---------------|-------------|------------|--------| -| `spx admin init` | `--nodes`, `--node`, `--bind`, `--port`, `--region`, `--az`, `--cluster-name`, `--cluster-bind`, `--cluster-routes`, `--predastore-nodes`, `--services`, `--formation-timeout`, `--token-ttl`, `--force` | None (first-time setup) | Generates root IAM credentials (AKIA-prefixed access key + secret) → creates master.key (AES-256, 32 bytes, 0600) → writes bootstrap.json (consumed on first start) → generates CA + server TLS certificates → generates join token (written to `join-token` file, displayed in join command) → creates NATS config with auth token → writes spinifex.toml, awsgw.toml, predastore.toml → configures AWS CLI `spx` profile → creates directory structure under `~/spinifex/` | 1. Init creates all config files
2. Root credentials printed once
3. master.key is 32 bytes, mode 0600
4. bootstrap.json consumed on first start
5. `--force` re-initializes existing config
6. AWS CLI profile `spx` auto-configured
7. Multi-node init generates join token and writes to `/join-token` | **DONE** | -| `spx admin join` | `--host` (required), `--node` (required), `--token` (required), `--bind`, `--port`, `--region`, `--az`, `--cluster-bind`, `--cluster-routes`, `--data-dir`, `--services` | Leader node must be running | Connects to leader node with join token (Authorization: Bearer header) → retrieves cluster configuration → configures local node to join cluster and participate in distributed operations | 1. Join existing cluster
2. Missing host (error)
3. Missing node name (error)
4. Missing token (error)
5. Invalid token (401)
6. Expired token (401) | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx admin init` | `--nodes`, `--node`, `--bind`, `--port`, `--region`, `--az`, `--cluster-name`, `--cluster-bind`, `--cluster-routes`, `--predastore-nodes`, `--services`, `--formation-timeout`, `--token-ttl`, `--force` | Generates root IAM credentials (AKIA-prefixed access key + secret) → creates master.key (AES-256, 32 bytes, 0600) → writes bootstrap.json (consumed on first start) → generates CA + server TLS certificates → generates join token (written to `join-token` file, displayed in join command) → creates NATS config with auth token → writes spinifex.toml, awsgw.toml, predastore.toml → configures AWS CLI `spx` profile → creates directory structure under `~/spinifex/` | +| `spx admin join` | `--host` (required), `--node` (required), `--token` (required), `--bind`, `--port`, `--region`, `--az`, `--cluster-bind`, `--cluster-routes`, `--data-dir`, `--services` | Connects to leader node with join token (Authorization: Bearer header) → retrieves cluster configuration → configures local node to join cluster and participate in distributed operations | ### Version -| Command | Flags | Prerequisites | Basic Logic | Status | -|---------|-------|---------------|-------------|--------| -| `spx version` | — | None | Prints Spinifex version, commit hash, OS, and architecture (populated via build-time ldflags) | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx version` | — | Prints Spinifex version, commit hash, OS, and architecture (populated via build-time ldflags) | ### Cluster Operations -| Command | Flags | Prerequisites | Basic Logic | Test Cases | Status | -|---------|-------|---------------|-------------|------------|--------| -| `spx admin cluster shutdown` | `--force` (shutdown even if nodes don't respond), `--timeout` (max wait per phase, default 120s), `--dry-run` (print phase plan without executing) | Cluster must be running | Performs coordinated, phased shutdown of entire cluster. Phases execute in order: GATE (stop API/UI) → DRAIN (stop VMs) → STORAGE (stop viperblock) → PERSIST (stop predastore) → INFRA (stop NATS/daemon). Each phase waits for all nodes to ACK before proceeding. Uses JetStream state tracking. | 1. Shutdown running cluster
2. All nodes stop cleanly
3. Force shutdown with unresponsive nodes
4. Dry-run prints plan | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx admin cluster shutdown` | `--force` (shutdown even if nodes don't respond), `--timeout` (max wait per phase, default 120s), `--dry-run` (print phase plan without executing) | Performs coordinated, phased shutdown of entire cluster. Phases execute in order: GATE (stop API/UI) → DRAIN (stop VMs) → STORAGE (stop viperblock) → PERSIST (stop predastore) → INFRA (stop NATS/daemon). Each phase waits for all nodes to ACK before proceeding. Uses JetStream state tracking. | ### Certificate Management -| Command | Flags | Prerequisites | Basic Logic | Test Cases | Status | -|---------|-------|---------------|-------------|------------|--------| -| `spx admin cert renew` | `--extra-ip` (additional IPs for SANs), `--extra-dns` (additional DNS names for SANs) | Existing CA from `spx admin init` | Reads existing CA → regenerates server certificate with all current network interface IPs and machine hostname in SANs → writes new cert. Use after adding a new network interface or changing IP addresses. | 1. Renew with auto-detected IPs
2. Renew with extra IPs
3. Renew with extra DNS names | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx admin cert renew` | `--extra-ip` (additional IPs for SANs), `--extra-dns` (additional DNS names for SANs) | Reads existing CA → regenerates server certificate with all current network interface IPs and machine hostname in SANs → writes new cert. Use after adding a new network interface or changing IP addresses. | ### Upgrade Management -| Command | Flags | Prerequisites | Basic Logic | Test Cases | Status | -|---------|-------|---------------|-------------|------------|--------| -| `spx admin upgrade` | `--yes` (apply without prompting), `--config-dir` (persistent, default: `~/spinifex/config`), `--spinifex-dir` (persistent, default: `~/spinifex`) | Existing installation (`spinifex.toml` must exist in config dir) | Reads current config versions from registry → computes pending config migrations (from→to per target) → prints version summary and pending list → prompts for confirmation unless `--yes` → runs `migrate.DefaultRegistry.RunAllConfig()` to apply migrations to config files. Intended for upgrades between Spinifex versions. Operators can skip migrations during install by setting `INSTALL_SPINIFEX_SKIP_MIGRATE=1`, then run `spx admin upgrade` manually to review and apply. After completion, services must be restarted with `sudo systemctl restart spinifex.target`. Invoked non-interactively by `scripts/setup.sh` with `--yes`. | 1. No installation present (error, suggests `spx admin init`)
2. No pending migrations (prints "No pending config migrations")
3. Pending migrations with interactive confirmation (y/yes applies, anything else aborts)
4. Pending migrations with `--yes` (applies without prompt)
5. Migration failure surfaces error and exits non-zero | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx admin upgrade` | `--yes` (apply without prompting), `--config-dir` (persistent, default: `~/spinifex/config`), `--spinifex-dir` (persistent, default: `~/spinifex`) | Reads current config versions from registry → computes pending config migrations (from→to per target) → prints version summary and pending list → prompts for confirmation unless `--yes` → runs `migrate.DefaultRegistry.RunAllConfig()` to apply migrations to config files. Intended for upgrades between Spinifex versions. Operators can skip migrations during install by setting `INSTALL_SPINIFEX_SKIP_MIGRATE=1`, then run `spx admin upgrade` manually to review and apply. After completion, services must be restarted with `sudo systemctl restart spinifex.target`. Invoked non-interactively by `scripts/setup.sh` with `--yes`. | ### Account Management -| Command | Flags | Prerequisites | Basic Logic | Test Cases | Status | -|---------|-------|---------------|-------------|------------|--------| -| `spx admin account create` | `--name` | Cluster must be running (NATS), master key must exist | Connects to NATS → CAS loop on `spinifex-account-counter:next_id` for sequential 12-digit ID → creates Account record in `spinifex-accounts` KV → creates `admin` user in new account → creates access key for admin → creates AdministratorAccess policy (Action:*, Resource:*) → attaches policy → prints credentials | 1. Create first account (ID 000000000001)
2. Sequential IDs (000000000002, etc.)
3. Admin user has full access
4. Credentials printed once | **DONE** | -| `spx admin account list` | — | Cluster must be running (NATS), master key must exist | Connects to NATS → IAMService.ListAccounts() → prints table with Account ID, Name, Status, Created | 1. List all accounts
2. Empty list (only global account) | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx admin account create` | `--name` | Connects to NATS → CAS loop on `spinifex-account-counter:next_id` for sequential 12-digit ID → creates Account record in `spinifex-accounts` KV → creates `admin` user in new account → creates access key for admin → creates AdministratorAccess policy (Action:*, Resource:*) → attaches policy → prints credentials | +| `spx admin account list` | — | Connects to NATS → IAMService.ListAccounts() → prints table with Account ID, Name, Status, Created | ### Image Management -| Command | Flags | Prerequisites | Basic Logic | Test Cases | Status | -|---------|-------|---------------|-------------|------------|--------| -| `spx admin images import` | `--name`, `--file`, `--distro`, `--version`, `--arch`, `--platform`, `--boot-mode` (bios/uefi/uefi-preferred), `--tag`, `--force`, `--skip-verify` | Cluster must be running; either `--name` (catalog download) or `--file` (operator-supplied media). `--file` mode additionally requires `--distro`, `--version`, `--arch`, `--boot-mode`. | Catalog imports (`--name`) download the image, fetch the catalog `Checksum` URL, verify the SHA-256/SHA-512 digest, and inherit `BootMode` from the catalog entry. `--boot-mode` overrides the catalog value when set. Mismatch fails closed; the cached file is left on disk and `--force` re-downloads. `--file` imports skip checksum verification (operator-supplied media is outside Spinifex's trust boundary, the skip is logged at INFO for audit) and require an explicit `--boot-mode` because there is no catalog metadata to inherit from. `--skip-verify` bypasses verification for catalog imports and emits a WARN slog + stderr notice; use only for debugging or when upstream mirrors are confirmed-broken. | 1. Import valid catalog image (verifies checksum)
2. Tampered cache hit fails with `ErrChecksumMismatch`
3. `--force` recovers after a mismatch
4. `--file` import skips verification
5. `--skip-verify` bypasses checksum with WARN
6. `--file` without `--boot-mode` rejected
7. `--boot-mode=invalid` rejected | **DONE** | -| `spx admin images list` | — | None | Lists available OS images that can be imported or downloaded | 1. List available images | **DONE** | -| `spx admin images remove` | `--image-id` (required), `--force`, `--yes` | Cluster must be running; predastore reachable. Targets AMIs imported via `spx admin images import` (non-account `ImageOwnerAlias`, e.g. `"system"`). | Loads `ami-/config.json`, walks transitive dependents — copied snapshots whose `VolumeID == imageID`, volumes whose `SnapshotID` references the internal `snap-ami-` or any derived snap, and account AMIs created via `CopyImage` whose `SnapshotID` is a derived snap — then prompts (skipped with `--yes`) before deleting `ami-/config.json` (the DescribeImages barrier) followed by the rest of `ami-/` and `snap-ami-/`. Account-owned AMIs are refused with a hint pointing at `aws ec2 deregister-image` + `aws ec2 delete-snapshot`. `--force` bypasses the dependency, ownership and config-corrupt checks for salvage of orphaned blocks. | 1. Happy path: system AMI, no deps → deleted
2. Account-owned → refused with AWS-flow hint
3. Missing/corrupt config → `InvalidAMIID.NotFound` (salvageable with `--force`)
4. Dependent volume (direct or via copied snap) → refused
5. Dependent account AMI → refused
6. `--force` overrides dependents
7. Idempotent re-run after partial delete | **DONE** | - -#### Image integrity verification (CMMC SI.L1-3.14.2) - -Catalog imports (`spx admin images import --name `) verify the image -against the catalog-declared SHA-256/SHA-512 digest before extraction. The sums -file is fetched from the catalog `Checksum` URL over HTTPS only (cross-scheme -redirects refused), and verification runs on both fresh downloads and cache -hits so a poisoned cache is caught on the next import. - -On mismatch the import exits non-zero, the cached file is left on disk for -inspection, and the printed guidance is `spx admin images import --name ---force` to re-download. - -`--file` imports skip verification by design: operator-supplied media is -outside Spinifex's trust boundary and the operator is responsible for -integrity (e.g. `sha256sum` against a trusted upstream digest before import). -The skip is recorded as an INFO `slog` event with `reason=local-file-import` -so a CMMC assessor can audit the decision from journald. - -`--skip-verify` bypasses the checksum step for catalog imports. The command -still downloads via the catalog URL but does not compare the image digest -against the sums file. Intended for narrow cases such as debugging upstream -mirror issues or running against a transiently-broken `latest/` path; the -skip is logged at WARN with `reason=skip-verify-flag` and printed to stderr -so operators and assessors see it. Prefer `--file` with an out-of-band -verified image over `--skip-verify` whenever possible. - -**Limitation:** verification confirms the image matches the digest the mirror -served. A mirror compromise that swaps both image and sums file is not -detected; closing that gap requires GPG signature verification of the sums -file, deferred to a later phase. - -#### `spx admin images remove` caveats - -Admin-imported AMIs (`ImageOwnerAlias = "system"`) live -under the `ami-/` S3 prefix and use a viperblock-internal snap checkpoint -at `snap-ami-/` — there is no `snap-/metadata.json`. The AWS handlers -(`DeregisterImage`, `DeleteSnapshot`) reject system owners with -`UnauthorizedOperation`, which is the right behaviour for tenant API callers -but leaves no AWS-flow path to reclaim space. `spx admin images remove` is -the admin-trust-boundary counterpart that performs the dependency walk and -hard-deletes the blocks directly against predastore. - -`CopyImage` of a system AMI is metadata-only: it writes a fresh -`snap-/metadata.json` whose `VolumeID` points at `ami-` and a new -`ami-/config.json` referencing that snap. Volumes launched from the -copied AMI read transitively from `ami-/chunks/...`. The remove command -walks this transitive set and refuses if anything references the target. - -**TOCTOU window:** between the safety scan and the `config.json` delete a -concurrent `RunInstances` against the AMI could create a new dependent -volume. The window is sub-second on a healthy cluster. The admin running -this command is expected to know the fleet's operational state; if the race -fires the result is a `vol-` with deleted backing blocks, recovered by -terminating the orphaned instance. - -**`--force` bypasses every safety check** (dependents, ownership, missing / -corrupt `config.json`). Use only for salvage of orphaned blocks. Running it -against a live system AMI corrupts every dependent volume on the next disk -read. +| Command | Flags | Description | +|---------|-------|-------------| +| `spx admin images import` | `--name`, `--file`, `--distro`, `--version`, `--arch`, `--platform`, `--boot-mode` (bios/uefi/uefi-preferred), `--tag`, `--force`, `--skip-verify` | Catalog imports (`--name`) download the image, fetch the catalog `Checksum` URL, verify the SHA-256/SHA-512 digest, and inherit `BootMode` from the catalog entry. `--boot-mode` overrides the catalog value when set. Mismatch fails closed; the cached file is left on disk and `--force` re-downloads. `--file` imports skip checksum verification (operator-supplied media is outside Spinifex's trust boundary, the skip is logged at INFO for audit) and require an explicit `--boot-mode` because there is no catalog metadata to inherit from. `--skip-verify` bypasses verification for catalog imports and emits a WARN slog + stderr notice; use only for debugging or when upstream mirrors are confirmed-broken. | +| `spx admin images list` | — | Lists available OS images that can be imported or downloaded | +| `spx admin images remove` | `--image-id` (required), `--force`, `--yes` | Loads `ami-/config.json`, walks transitive dependents — copied snapshots whose `VolumeID == imageID`, volumes whose `SnapshotID` references the internal `snap-ami-` or any derived snap, and account AMIs created via `CopyImage` whose `SnapshotID` is a derived snap — then prompts (skipped with `--yes`) before deleting `ami-/config.json` (the DescribeImages barrier) followed by the rest of `ami-/` and `snap-ami-/`. Account-owned AMIs are refused with a hint pointing at `aws ec2 deregister-image` + `aws ec2 delete-snapshot`. `--force` bypasses the dependency, ownership and config-corrupt checks for salvage of orphaned blocks. | ### GPU Management -| Command | Flags | Prerequisites | Basic Logic | Test Cases | Status | -|---------|-------|---------------|-------------|------------|--------| -| `spx admin gpu status` | `--node` (default: local node) | Cluster must be running (NATS) | Queries `spinifex.node.status` fan-out → finds the target node response → prints Node, GPU hardware (model list or "none detected"), IOMMU state, vfio-pci state, passthrough enabled/disabled, and GPU pool allocation (`allocated/total`). Also lists GPU-capable instance types when passthrough is active. | 1. Local node with passthrough enabled (shows pool and instance types)
2. Local node capable but passthrough disabled (shows "run enable")
3. Node without GPU hardware (shows "none detected")
4. `--node` targets a remote node
5. Node not found or not responding (error) | **DONE** | -| `spx admin gpu enable` | — | Cluster must be running (NATS); node must be GPU-capable (IOMMU + vfio-pci configured) | Checks current passthrough state via NATS → errors if already enabled or prerequisites not met (directs to `setup`) → writes `gpu_passthrough = true` to `spinifex.toml` via `admin.SetGPUPassthrough` → sends SIGHUP to `spinifex-daemon` via `systemctl kill -s HUP` → polls node status for up to 30 s until daemon confirms new state → prints final `gpu status` output. Must be run directly on the target host. | 1. Enable on capable node
2. Already enabled (no-op)
3. Prerequisites not met (error, suggests `setup`)
4. Daemon does not confirm within 30 s (timeout error) | **DONE** | -| `spx admin gpu disable` | — | Cluster must be running (NATS); no GPU instances may be running | Checks current passthrough state via NATS → errors if already disabled or if `AllocGPUs > 0` (must terminate GPU instances first) → writes `gpu_passthrough = false` → sends SIGHUP to `spinifex-daemon` → polls for up to 30 s → prints final `gpu status` output. Must be run directly on the target host. | 1. Disable with no GPU instances running
2. Already disabled (no-op)
3. GPU instances running (error with count)
4. Daemon does not confirm within 30 s (timeout error) | **DONE** | -| `spx admin gpu setup` | — | Must be run as root; NVIDIA or AMD GPU must be present | Idempotent host configuration for GPU passthrough. Steps (skipped if already applied): detect GPUs via `gpu.Discover` → collect PCI IDs for all IOMMU-group siblings → check/enable IOMMU in GRUB (`intel_iommu=on iommu=pt` or `amd_iommu=on iommu=pt`) → write vfio udev rule (`/etc/udev/rules.d/99-spinifex-vfio.rules`) → blacklist nouveau (`/etc/modprobe.d/blacklist-nouveau.conf`) → blacklist amdgpu if AMD GPU present (`/etc/modprobe.d/blacklist-amdgpu.conf`) → write vfio-pci early binding config (`/etc/modprobe.d/vfio-pci.conf`) → add vfio modules to initramfs. If any change requires a reboot: runs `update-initramfs -u` and exits with reboot instructions. After reboot: verifies `vfio_pci` module is loaded → verifies each GPU is bound to `vfio-pci` (binds explicitly via `driver_override` if unbound) → calls `gpu enable` to activate passthrough. | 1. First run on unconfigured host (GRUB updated, reboot required)
2. Re-run after reboot (verifies bindings, enables passthrough)
3. Already fully configured (all steps skipped, enables passthrough)
4. AMD GPU present (blacklists amdgpu in addition to nouveau)
5. Not run as root (error)
6. No GPU hardware detected (error) | **DONE** | -| `spx admin gpu mig status` | — (must be run on target host) | NVIDIA GPU with MIG support present | Runs `gpu.Discover()` locally → for each GPU prints: PCI address, model, MIG capability, MIG mode (enabled/disabled/N/A); for GPUs with MIG enabled lists active MIG slices with GI ID, profile name, and mdev path. | 1. Node with MIG enabled (shows slices)
2. MIG capable but not enabled (shows disabled)
3. No MIG-capable GPU (shows N/A for MIG fields) | **DONE** | -| `spx admin gpu mig enable` | `--profile ` (required, e.g. `1g.10gb`), `--gpu ` (optional, default: all MIG-capable GPUs) | Must be run as root; MIG-capable GPU present; no GPU instances running | Checks no GPU instances running (NATS) → discovers MIG-capable GPUs via `gpu.Discover()` (filtered by `--gpu` if set) → enables MIG mode on each target (`gpu.EnableMIGMode`) → lists available profiles (`gpu.ListProfiles`) and validates requested profile name → destroys any existing instances (`gpu.DestroyAllInstances`) → creates new instances filling GPU capacity (`gpu.CreateInstances`) → writes `mig_profile` to `spinifex.toml` via `admin.SetMIGProfile` → sends SIGHUP to `spinifex-daemon`. Must be run directly on the target host. | 1. Enable MIG with valid profile
2. Invalid profile name (error with available profile list)
3. No MIG-capable GPU (error)
4. GPU instances running (error)
5. `--gpu` targets specific GPU
6. Not run as root (error) | **DONE** | -| `spx admin gpu mig disable` | `--gpu ` (optional, default: all MIG-capable GPUs) | Must be run as root; no GPU instances running | Checks no GPU instances running (NATS) → discovers MIG-capable GPUs via `gpu.Discover()` (filtered by `--gpu` if set) → destroys all GPU instances (`gpu.DestroyAllInstances`) → disables MIG mode (`gpu.DisableMIGMode`) → clears `mig_profile` in `spinifex.toml` via `admin.SetMIGProfile` → sends SIGHUP to `spinifex-daemon`. Must be run directly on the target host. | 1. Disable with no running GPU instances
2. GPU instances running (error)
3. MIG not enabled on GPU (skips that GPU)
4. `--gpu` targets specific GPU
5. Not run as root (error) | **DONE** | +| Command | Flags | Description | +|---------|-------|-------------| +| `spx admin gpu status` | `--node` (default: local node) | Queries `spinifex.node.status` fan-out → finds the target node response → prints Node, GPU hardware (model list or "none detected"), IOMMU state, vfio-pci state, passthrough enabled/disabled, and GPU pool allocation (`allocated/total`). Also lists GPU-capable instance types when passthrough is active. | +| `spx admin gpu enable` | — | Checks current passthrough state via NATS → errors if already enabled or prerequisites not met (directs to `setup`) → writes `gpu_passthrough = true` to `spinifex.toml` via `admin.SetGPUPassthrough` → sends SIGHUP to `spinifex-daemon` via `systemctl kill -s HUP` → polls node status for up to 30 s until daemon confirms new state → prints final `gpu status` output. Must be run directly on the target host. | +| `spx admin gpu disable` | — | Checks current passthrough state via NATS → errors if already disabled or if `AllocGPUs > 0` (must terminate GPU instances first) → writes `gpu_passthrough = false` → sends SIGHUP to `spinifex-daemon` → polls for up to 30 s → prints final `gpu status` output. Must be run directly on the target host. | +| `spx admin gpu setup` | — | Idempotent host configuration for GPU passthrough. Steps (skipped if already applied): detect GPUs via `gpu.Discover` → collect PCI IDs for all IOMMU-group siblings → check/enable IOMMU in GRUB (`intel_iommu=on iommu=pt` or `amd_iommu=on iommu=pt`) → write vfio udev rule (`/etc/udev/rules.d/99-spinifex-vfio.rules`) → blacklist nouveau (`/etc/modprobe.d/blacklist-nouveau.conf`) → blacklist amdgpu if AMD GPU present (`/etc/modprobe.d/blacklist-amdgpu.conf`) → write vfio-pci early binding config (`/etc/modprobe.d/vfio-pci.conf`) → add vfio modules to initramfs. If any change requires a reboot: runs `update-initramfs -u` and exits with reboot instructions. After reboot: verifies `vfio_pci` module is loaded → verifies each GPU is bound to `vfio-pci` (binds explicitly via `driver_override` if unbound) → calls `gpu enable` to activate passthrough. | +| `spx admin gpu mig status` | — (must be run on target host) | Runs `gpu.Discover()` locally → for each GPU prints: PCI address, model, MIG capability, MIG mode (enabled/disabled/N/A); for GPUs with MIG enabled lists active MIG slices with GI ID, profile name, and mdev path. | +| `spx admin gpu mig enable` | `--profile ` (required, e.g. `1g.10gb`), `--gpu ` (optional, default: all MIG-capable GPUs) | Checks no GPU instances running (NATS) → discovers MIG-capable GPUs via `gpu.Discover()` (filtered by `--gpu` if set) → enables MIG mode on each target (`gpu.EnableMIGMode`) → lists available profiles (`gpu.ListProfiles`) and validates requested profile name → destroys any existing instances (`gpu.DestroyAllInstances`) → creates new instances filling GPU capacity (`gpu.CreateInstances`) → writes `mig_profile` to `spinifex.toml` via `admin.SetMIGProfile` → sends SIGHUP to `spinifex-daemon`. Must be run directly on the target host. | +| `spx admin gpu mig disable` | `--gpu ` (optional, default: all MIG-capable GPUs) | Checks no GPU instances running (NATS) → discovers MIG-capable GPUs via `gpu.Discover()` (filtered by `--gpu` if set) → destroys all GPU instances (`gpu.DestroyAllInstances`) → disables MIG mode (`gpu.DisableMIGMode`) → clears `mig_profile` in `spinifex.toml` via `admin.SetMIGProfile` → sends SIGHUP to `spinifex-daemon`. Must be run directly on the target host. | ## AWS-Compatible API @@ -295,9 +235,6 @@ by downstream services. ### EC2 — VPC Core -VPC/Subnet/ENI/SG CRUD stores metadata in NATS KV and publishes events to -vpcd for OVN translation. Single AZ for Spinifex v1. - | Command | Implemented Flags | Missing Flags | Status | |---------|-------------------|---------------|--------| | `create-vpc` | `--cidr-block`, `--tag-specifications` | `--instance-tenancy`, `--dry-run` | **DONE** | @@ -393,9 +330,6 @@ EIP commands are only registered when an external IPAM pool is configured. ### EC2 — NAT Gateway -Deleted gateways move to a separate KV bucket with 1-hour TTL for Terraform -polling. - | Command | Implemented Flags | Missing Flags | Status | |---------|-------------------|---------------|--------| | `create-nat-gateway` | `--subnet-id`, `--allocation-id` | `--connectivity-type`, `--tag-specifications`, `--dry-run` | **DONE** | @@ -406,9 +340,6 @@ polling. ### EC2 — Placement Groups -Strategies: `spread` (1 instance per node, strict) and `cluster` (all pinned -to single node). `partition` rejected. - | Command | Implemented Flags | Missing Flags | Status | |---------|-------------------|---------------|--------| | `create-placement-group` | `--group-name`, `--strategy` (spread/cluster) | `--partition-count`, `--spread-level`, `--tag-specifications`, `--dry-run` | **DONE** | @@ -543,9 +474,6 @@ bypasses policy evaluation entirely. ### IAM — Access Keys -Max 2 keys per user. Secrets encrypted with AES-256-GCM using master key, -returned plaintext only at creation. - | Command | Implemented Flags | Missing Flags | Status | |---------|-------------------|---------------|--------| | `create-access-key` | `--user-name` | — | **DONE** | @@ -555,10 +483,6 @@ returned plaintext only at creation. ### IAM — Policies -Policy documents require `Version: "2012-10-17"` and a valid Statement array. -Wildcard action matching supported (`ec2:*`, `ec2:Describe*`, `*`). Evaluation -order: explicit Deny > explicit Allow > implicit Deny. - | Command | Implemented Flags | Missing Flags | Status | |---------|-------------------|---------------|--------| | `create-policy` | `--policy-name`, `--policy-document`, `--path`, `--description` | `--tags` | **DONE** | @@ -570,19 +494,6 @@ order: explicit Deny > explicit Allow > implicit Deny. | `detach-user-policy` | `--user-name`, `--policy-arn` | — | **DONE** | | `list-attached-user-policies` | `--user-name` | `--path-prefix`, `--max-items`, `--marker` | **DONE** | -### IAM — Policy Evaluation (internal — not an AWS API) - -| Feature | Status | -|---------|--------| -| Root user bypass | **DONE** | -| Default deny | **DONE** | -| Explicit allow | **DONE** | -| Explicit deny | **DONE** | -| Wildcard matching (`*`, `ec2:*`, `ec2:Describe*`) | **DONE** | -| Account-scoped evaluation | **DONE** | -| EC2 action mapping | **DONE** | -| IAM action mapping (16 actions) | **DONE** | - ### IAM — Roles | Command | Implemented Flags | Missing Flags | Status | @@ -599,9 +510,6 @@ order: explicit Deny > explicit Allow > implicit Deny. ### IAM — Instance Profiles -Containers for IAM roles that allow EC2 instances to assume a role. Required -for `run-instances --iam-instance-profile`. Max 1 role per profile. - | Command | Implemented Flags | Missing Flags | Status | |---------|-------------------|---------------|--------| | `create-instance-profile` | `--instance-profile-name`, `--path`, `--tags` | — | **DONE** | @@ -631,11 +539,11 @@ for `run-instances --iam-instance-profile`. Max 1 role per profile. ## STS -| Command | Implemented Flags | Missing Flags (rejected if supplied) | Status | +| Command | Implemented Flags | Missing Flags | Status | |---------|-------------------|--------------------------------------|--------| | `get-caller-identity` | — | — | **DONE** | | `assume-role` | `--role-arn`, `--role-session-name`, `--duration-seconds` (900–min(role MaxSessionDuration, 43200)) | `--policy`, `--policy-arns` (→ `PackedPolicyTooLarge`); `--tags`, `--transitive-tag-keys` (→ `InvalidParameterValue`); `--serial-number`, `--token-code` (→ `InvalidParameterValue`); `--external-id`, `--source-identity` (accepted and logged, **not enforced** — no Condition evaluator in v1) | **DONE** | -| `get-session-token` | — | `--duration-seconds`, `--serial-number`, `--token-code` | **NOT STARTED** | +| `get-session-token` | `--duration-seconds` (900–129600, default 43200 = 12h; clamped, not rejected) | `--serial-number`, `--token-code` (MFA → `InvalidParameterValue`) | **DONE** | | `assume-role-with-web-identity` | — | `--role-arn`, `--role-session-name`, `--web-identity-token`, `--provider-id`, `--policy`, `--policy-arns`, `--duration-seconds` | **NOT STARTED** | | `assume-role-with-saml` | — | `--role-arn`, `--principal-arn`, `--saml-assertion`, `--policy`, `--policy-arns`, `--duration-seconds` | **NOT STARTED** | | `get-access-key-info` | — | `--access-key-id` | **NOT STARTED** | @@ -649,9 +557,52 @@ Condition evaluator so accepting them would silently allow. --- +## IMDS (Instance Metadata Service) + +Available at `169.254.169.254` from inside every running guest VM, matching AWS. The endpoint is reached from within a guest over plain HTTP, exactly as on EC2, with no in-VM agent to install. DHCP and fully static guests reach it identically, with no in-guest route configuration. + +**IMDSv2-only.** Every read requires a session token. A tokenless (v1-style) `GET` returns `401 Unauthorized` with an empty body. Obtain a token with a `PUT /latest/api/token` carrying `X-aws-ec2-metadata-token-ttl-seconds` (1–21600), then send it back in `X-aws-ec2-metadata-token` on every read. + +```bash +# Inside the guest VM: +TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \ + -H "X-aws-ec2-metadata-token-ttl-seconds: 60") +curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \ + http://169.254.169.254/latest/meta-data/instance-id + +# Tokenless GET → 401 Unauthorized (empty body) +curl -i http://169.254.169.254/latest/meta-data/instance-id +``` + +### IMDS — Supported Paths + +| Path | Method | Source | Status | +|------|--------|--------|--------| +| `/latest/api/token` | PUT | Issues an ENI-bound IMDSv2 token; `X-aws-ec2-metadata-token-ttl-seconds` ∈ [1, 21600] required | **DONE** | +| `/latest/meta-data/` | GET | Directory listing of supported children | **DONE** | +| `/latest/meta-data/instance-id` | GET | `vm.ID` | **DONE** | +| `/latest/meta-data/instance-type` | GET | `vm.InstanceType` | **DONE** | +| `/latest/meta-data/ami-id` | GET | launch `ImageId` | **DONE** | +| `/latest/meta-data/local-ipv4` | GET | `ENIRecord.PrivateIpAddress` (== request source IP) | **DONE** | +| `/latest/meta-data/public-ipv4` | GET | EIP, else instance public IP; empty body if none | **DONE** | +| `/latest/meta-data/mac` | GET | `ENIRecord.MacAddress` | **DONE** | +| `/latest/meta-data/security-groups` | GET | `ENIRecord.SecurityGroupIds`, newline-separated | **DONE** | +| `/latest/meta-data/hostname`, `/local-hostname` | GET | Synthesised `ip-..compute.internal` | **DONE** | +| `/latest/meta-data/placement/availability-zone` | GET | `ENIRecord.AvailabilityZone` | **DONE** | +| `/latest/meta-data/placement/region` | GET | Derived from AZ (trailing letter stripped) | **DONE** | +| `/latest/meta-data/iam/info` | GET | `{InstanceProfileArn, InstanceProfileId}`; 404 if no profile | **DONE** | +| `/latest/meta-data/iam/security-credentials/` | GET | Role name(s) under the profile, one per line; empty body if none | **DONE** | +| `/latest/meta-data/iam/security-credentials/` | GET | STS `AssumeRoleForInstance` → ASIA-prefixed temporary credential JSON | **DONE** | +| `/latest/user-data` | GET | `vm.UserData`; 404 if none | **DONE** | +| `/latest/dynamic/instance-identity/document` | GET | Needs a per-cluster signing key | **NOT STARTED** (404; lands with EKS IRSA) | +| `/latest/meta-data/network/interfaces/...` | GET | Multi-ENI rendering | **NOT STARTED** (404) | +| `/latest/meta-data/tags/...` | GET | Instance-tag metadata | **NOT STARTED** (404) | + +--- + ## ELBv2 (Application & Network Load Balancer) -The data plane uses a system-managed LB VM running HAProxy, launched automatically during `create-load-balancer`. HAProxy config is pushed via NATS on listener/target changes. +The data plane uses a system-managed LB VM, launched automatically during `create-load-balancer`. Application Load Balancers run **HAProxy** (L7: rules, fixed-response, redirect, HTTP/HTTPS). Network Load Balancers run **nginx `stream`** (L4: TCP, UDP, TLS, TCP_UDP) — HAProxy cannot load-balance UDP. The agent selects the engine from the `Engine` field on the config-delivery response. ### ELBv2 — Load Balancers diff --git a/docs/administration/spinifex-admin-cli/README.md b/docs/administration/spinifex-admin-cli/README.md index 70944a37e..0a503a12a 100644 --- a/docs/administration/spinifex-admin-cli/README.md +++ b/docs/administration/spinifex-admin-cli/README.md @@ -82,6 +82,25 @@ spx admin images import --name debian-13-arm64 Catalog imports verify the image against the catalog-declared SHA-256/SHA-512 digest before extraction. Use `--file` to import operator-supplied media (verification skipped — operator is responsible for integrity), or `--force` to re-download after a checksum mismatch. +### EKS node image + +To run EKS, import the prebuilt node image from the catalog: + +```bash +spx admin images import --name spinifex-eks-node +``` + +This pulls the Alpine + K3s node AMI from `iso.mulgadc.com`, verifies its +checksum, and registers it tagged `spinifex:managed-by=eks`. `eks create-cluster` +and `eks create-nodegroup` resolve the boot AMI by that tag, so no further +configuration is needed. + +Operators publishing a freshly built node image to the mirror run the +**Publish EKS node AMI** GitHub Actions workflow (manual dispatch; uses the +`CF_R2_*` repo secrets) or, locally, `make publish-eks-node-image` (see +`scripts/publish-system-image.sh`; requires `R2_ENDPOINT` + R2-scoped +`AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`). + ## Cluster Shutdown diff --git a/docs/compute/launching-instances/README.md b/docs/compute/launching-instances/README.md index a1648629a..4d68b4af3 100644 --- a/docs/compute/launching-instances/README.md +++ b/docs/compute/launching-instances/README.md @@ -164,6 +164,16 @@ spx admin images import --name debian-13-x86_64 cloud-init needs time to configure the instance after boot. Wait 30-60 seconds and retry. +If the connection **times out** rather than being refused, the security group is likely blocking port 22. The default security group denies all inbound traffic (matching AWS), so SSH must be explicitly allowed: + +```bash +# Allow SSH from anywhere on the instance's security group +aws ec2 authorize-security-group-ingress \ + --group-id $SG_ID --protocol tcp --port 22 --cidr 0.0.0.0/0 +``` + +See [VPC Networking — Security Groups](/docs/vpc-networking#security-groups) for scoping rules to a trusted CIDR. + Verify the SSH key was specified correctly when launching: ```bash diff --git a/docs/compute/vpc-networking/README.md b/docs/compute/vpc-networking/README.md index 78b109a43..43bf16bba 100644 --- a/docs/compute/vpc-networking/README.md +++ b/docs/compute/vpc-networking/README.md @@ -48,8 +48,9 @@ NIC. Each host runs `ovn-controller` which programs OpenFlow rules on `br-int` ## Private vs Public Subnets -A subnet's behavior depends on two things: whether the VPC has an Internet -Gateway, and whether the subnet has `MapPublicIpOnLaunch` enabled. +A subnet's behavior depends on three things: whether the VPC has an Internet +Gateway, whether the subnet's route table has a default route to that IGW, and +whether the subnet has `MapPublicIpOnLaunch` enabled. ## Private Subnet (Default) @@ -61,9 +62,10 @@ internet or be reached from the WAN. Private subnet — instance hits router, no default route, packet dropped

-If the VPC has an IGW attached, private subnet instances CAN reach the internet -via the VPC router's SNAT rule (outbound only — they share the gateway IP). They -still cannot be reached from the WAN because they have no public IP. +Private subnet instances reach the internet only if their route table has a +default route to the IGW (shared SNAT, outbound only — they share the gateway +IP) or to a NAT gateway. With no default route, egress is dropped. Either way +they cannot be reached from the WAN because they have no public IP. ## Public Subnet @@ -77,8 +79,18 @@ managed by OVN — the instance OS only sees its private IP. **Requirements for a public subnet:** 1. VPC has an Internet Gateway attached -2. Subnet has `MapPublicIpOnLaunch = true` -3. External IP pool configured in `spinifex.toml` +2. A route table associated with the subnet has a `0.0.0.0/0` route to the IGW +3. Subnet has `MapPublicIpOnLaunch = true` +4. External IP pool configured in `spinifex.toml` + +Spinifex follows AWS route-table semantics: a subnet is only "public" if its +effective route table carries a default route to the IGW. A new VPC's main +route table has the local route only — Spinifex does **not** add the IGW route +for you. Without it, the subnet's egress is gated with a drop policy, so +instances cannot reach the internet (and inbound connections cannot complete +because return traffic is dropped) even with a public IP and an attached IGW. +Add the route explicitly — either to the main route table, or to a custom route +table associated with the subnet (see [Quick Start](#3-create-vpc-with-public-subnet)). ## Comparison @@ -86,7 +98,7 @@ managed by OVN — the instance OS only sees its private IP. | ------------------------ | --------------------------------- | ------------------------------ | | Private IP | Yes | Yes | | Public IP | No | Auto-assigned from pool | -| Outbound internet | Only if VPC has IGW (shared SNAT) | Yes (own public IP via SNAT) | +| Outbound internet | Only with a default route to IGW/NAT GW | Yes (own public IP via SNAT) | | Inbound from WAN | No | Yes (via 1:1 NAT to public IP) | | Instance sees public IP? | N/A | No — only sees private IP | | Elastic IP support | Only if explicitly associated | Yes | @@ -190,8 +202,7 @@ use `setup-ovn.sh --dhcp` to obtain one from the router. This is the router's DHCP — not Spinifex's internal OVN DHCP for VMs. **Use when:** VMs only need outbound access (apt update, pulling images). Edge -deployments behind ISP NAT. Single WAN IP available. Future: use with AWS-style -NAT Gateway for private subnet internet access. +deployments behind ISP NAT. Single WAN IP available. ```toml [network] @@ -935,17 +946,39 @@ spx admin init VPC=$(aws ec2 create-vpc --cidr-block 10.200.0.0/16 \ --query Vpc.VpcId --output text) -SUBNET=$(aws ec2 create-subnet --vpc-id $VPC \ - --cidr-block 10.200.1.0/24 \ - --query Subnet.SubnetId --output text) - IGW=$(aws ec2 create-internet-gateway \ --query InternetGateway.InternetGatewayId --output text) aws ec2 attach-internet-gateway \ --internet-gateway-id $IGW --vpc-id $VPC +SUBNET=$(aws ec2 create-subnet --vpc-id $VPC \ + --cidr-block 10.200.1.0/24 \ + --query Subnet.SubnetId --output text) + +# Route table — give the subnet a default route to the IGW. Spinifex does NOT +# add this automatically; without it the subnet's egress is dropped. +RT=$(aws ec2 create-route-table --vpc-id $VPC \ + --query RouteTable.RouteTableId --output text) + +aws ec2 create-route --route-table-id $RT \ + --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW + +aws ec2 associate-route-table --route-table-id $RT --subnet-id $SUBNET + +# Auto-assign a public IP to instances launched into the subnet aws ec2 modify-subnet-attribute \ --subnet-id $SUBNET --map-public-ip-on-launch + +# Allow SSH + ICMP on the VPC's default security group (blocks inbound by default) +SG=$(aws ec2 describe-security-groups \ + --filters Name=vpc-id,Values=$VPC \ + --query 'SecurityGroups[0].GroupId' --output text) + +aws ec2 authorize-security-group-ingress --group-id $SG \ + --protocol tcp --port 22 --cidr 0.0.0.0/0 + +aws ec2 authorize-security-group-ingress --group-id $SG \ + --protocol icmp --port -1 --cidr 0.0.0.0/0 ``` ## 4. Launch Instance @@ -1135,6 +1168,37 @@ ip route show sudo ovn-nbctl lr-nat-list vpc-$VPC ``` +### Instance Has Public IP But No Internet + +The instance shows a public IP in `describe-instances` but cannot reach the +internet, and inbound connections hang. The usual cause is a missing default +route: the subnet's effective route table has no `0.0.0.0/0` route to the IGW, +so Spinifex gates the subnet with a drop policy. + +```bash +# Find the route table that applies to the subnet and check its routes +aws ec2 describe-route-tables \ + --filters Name=association.subnet-id,Values=$SUBNET \ + --query 'RouteTables[0].Routes' +# Expect a route with DestinationCidrBlock 0.0.0.0/0 and a GatewayId of igw-... +``` + +If the subnet has no explicit association it falls back to the VPC's main route +table — check that one too, then add the route to whichever table applies: + +```bash +aws ec2 create-route --route-table-id $RT \ + --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW +``` + +On the host, the drop policy installed when a subnet lacks an IGW route appears +as a `Logical_Router_Policy` on the VPC router: + +```bash +sudo ovn-nbctl lr-policy-list vpc-$VPC +# A drop policy matching the subnet CIDR means the subnet is gated. +``` + ### Public IP Not Reachable from WAN Work through these checks in order — each eliminates a class of issues. diff --git a/docs/getting-started/setting-up-your-cluster/README.md b/docs/getting-started/setting-up-your-cluster/README.md index 20a372d22..a9d1edc44 100644 --- a/docs/getting-started/setting-up-your-cluster/README.md +++ b/docs/getting-started/setting-up-your-cluster/README.md @@ -34,7 +34,7 @@ resources: ## Overview This guide assumes Spinifex is already installed and running. If not, follow one of the installation guides first: -- [Single-Node Install)](/docs/install) +- [Single-Node Install](/docs/install) - [Multi-Node Install](/docs/install-multi-node) - [Source Install](/docs/install-source) @@ -50,7 +50,9 @@ export AWS_PROFILE=spinifex ## 1. Import an AMI -List available images and import one matching your architecture: +### Option A: Import a bundled image + +List the bundled images and import one matching your architecture: ```bash spx admin images list @@ -64,13 +66,11 @@ ubuntu-26.04-arm64 | ubuntu | 26.04 | arm64 | uefi ubuntu-26.04-x86_64 | ubuntu | 26.04 | x86_64 | uefi ``` -Import an image: - ```bash spx admin images import --name ubuntu-26.04-x86_64 ``` -Or import a local image file: +### Option B: Import a local image file ```bash spx admin images import --file ~/images/ubuntu-26.04.img --distro ubuntu --version 26.04 --arch x86_64 --boot-mode uefi @@ -79,7 +79,7 @@ spx admin images import --file ~/images/ubuntu-26.04.img --distro ubuntu --versi Verify the import and note the AMI ID: ```bash -aws ec2 describe-images +AMI_ID=$(aws ec2 describe-images --query 'Images[0].ImageId' --output text) ``` ## 2. Create an SSH Key @@ -115,8 +115,6 @@ aws ec2 describe-key-pairs ```bash VPC_ID=$(aws ec2 create-vpc --cidr-block 10.200.0.0/16 \ --query 'Vpc.VpcId' --output text) - -echo "VPC: $VPC_ID" ``` ### Create an Internet Gateway @@ -130,68 +128,95 @@ IGW_ID=$(aws ec2 create-internet-gateway \ aws ec2 attach-internet-gateway \ --internet-gateway-id $IGW_ID \ --vpc-id $VPC_ID - -echo "IGW: $IGW_ID (attached to $VPC_ID)" ``` -### Create a Public Subnet +### Create a Subnet -A public subnet auto-assigns a routable IP to each instance, making it directly reachable from your network. +Create the subnet your instances will launch into. The routing and public-IP steps below are what make it a *public* subnet. ```bash SUBNET_ID=$(aws ec2 create-subnet \ --vpc-id $VPC_ID \ --cidr-block 10.200.1.0/24 \ --query 'Subnet.SubnetId' --output text) +``` -# Enable auto-assign public IP +### Create a Route Table + +A subnet is only **public** if its route table has a default route to the Internet Gateway. Spinifex does **not** add this route automatically — a new VPC's route table only routes traffic within the VPC (matching AWS). Without a `0.0.0.0/0` route to the IGW, instances in the subnet cannot reach the internet (and inbound connections cannot complete) even with a public IP assigned. + +```bash +RT_ID=$(aws ec2 create-route-table \ + --vpc-id $VPC_ID \ + --query 'RouteTable.RouteTableId' --output text) + +# Default route to the internet via the IGW +aws ec2 create-route \ + --route-table-id $RT_ID \ + --destination-cidr-block 0.0.0.0/0 \ + --gateway-id $IGW_ID + +# Associate the route table with the subnet +aws ec2 associate-route-table \ + --route-table-id $RT_ID \ + --subnet-id $SUBNET_ID +``` + +### Enable Auto-Assign Public IP + +Give every instance launched into the subnet a routable public IP, making it directly reachable from your network. + +```bash aws ec2 modify-subnet-attribute \ --subnet-id $SUBNET_ID \ --map-public-ip-on-launch - -echo "Subnet: $SUBNET_ID (public)" ``` +### Allow SSH and ICMP + +Every VPC gets a default security group that **blocks inbound traffic from outside the group** (matching AWS). Instances launched without an explicit security group use this default, so you must authorize ingress before you can SSH or ping the instance. + +```bash +SG_ID=$(aws ec2 describe-security-groups \ + --filters Name=vpc-id,Values=$VPC_ID \ + --query 'SecurityGroups[0].GroupId' --output text) + +# Allow SSH from anywhere +aws ec2 authorize-security-group-ingress \ + --group-id $SG_ID \ + --protocol tcp --port 22 --cidr 0.0.0.0/0 + +# Allow ICMP (ping) from anywhere +aws ec2 authorize-security-group-ingress \ + --group-id $SG_ID \ + --protocol icmp --port -1 --cidr 0.0.0.0/0 +``` + +**Note:** `0.0.0.0/0` opens these ports to every source. For anything beyond a quick evaluation, scope the `--cidr` to a trusted range. Rule changes apply immediately — no instance restart needed. + ### Verify ```bash aws ec2 describe-vpcs --vpc-ids $VPC_ID aws ec2 describe-subnets --subnet-ids $SUBNET_ID +aws ec2 describe-route-tables --route-table-ids $RT_ID +aws ec2 describe-security-groups --group-ids $SG_ID ``` ## 4. Launch an Instance -Query available instance types for your hardware: +Launch an instance in the public subnet on a `t3.micro` (2 vCPU / 1 GiB). + +> **Note:** On an **arm64** host, use `t4g.micro` instead of `t3.micro`. ```bash -aws ec2 describe-instance-types -``` - -Launch an instance in the public subnet, note this will select an instance type with 2 VCPU and 1024MB of memory. - -> **Note:** Replace `AMI_NAME` with the previously imported image above with the `ami-` prefix. - -```bash -AMI_NAME="ami-ubuntu-26.04-x86_64" - -AMI_ID=$(aws ec2 describe-images \ - --filters "Name=name,Values=$AMI_NAME" \ - --query 'Images | sort_by(@, &CreationDate) | [-1].ImageId' \ - --output text) - -INSTANCE_TYPE=$(aws ec2 describe-instance-types \ - --query "sort_by(InstanceTypes[?VCpuInfo.DefaultVCpus==\`2\` && MemoryInfo.SizeInMiB>=\`1024\`], &MemoryInfo.SizeInMiB)[0].InstanceType" \ - --output text) - INSTANCE_ID=$(aws ec2 run-instances \ --image-id $AMI_ID \ - --instance-type $INSTANCE_TYPE \ + --instance-type t3.micro \ --key-name spinifex-key \ --subnet-id $SUBNET_ID \ --count 1 \ --query 'Instances[0].InstanceId' --output text) - -echo "Instance: $INSTANCE_ID" ``` Wait for the instance to reach `running` state: @@ -328,7 +353,36 @@ PRIVATE_SUBNET=$(aws ec2 create-subnet \ --query 'Subnet.SubnetId' --output text) ``` -Instances in private subnets get a private IP only. They can still reach the internet via SNAT through the VPC router, but are not directly reachable from your network. +Instances in a private subnet get a private IP only and are not reachable from your network. By default they also have **no internet access**: with no `0.0.0.0/0` route in the subnet's route table, Spinifex gates egress with a drop policy — matching AWS, where a private subnet has no route off the VPC. They can still reach other instances in the same VPC. + +To give a private subnet outbound-only internet access, deploy a NAT Gateway in a public subnet and point the private subnet's default route at it (the AWS pattern): + +```bash +# Allocate an Elastic IP for the NAT Gateway +NAT_EIP=$(aws ec2 allocate-address --query AllocationId --output text) + +# Create the NAT Gateway in the PUBLIC subnet (the one with the IGW route) +NATGW_ID=$(aws ec2 create-nat-gateway \ + --subnet-id $SUBNET_ID \ + --allocation-id $NAT_EIP \ + --query 'NatGateway.NatGatewayId' --output text) + +# Give the private subnet its own route table with a default route to the NAT Gateway +PRIVATE_RT=$(aws ec2 create-route-table \ + --vpc-id $VPC_ID \ + --query 'RouteTable.RouteTableId' --output text) + +aws ec2 create-route \ + --route-table-id $PRIVATE_RT \ + --destination-cidr-block 0.0.0.0/0 \ + --nat-gateway-id $NATGW_ID + +aws ec2 associate-route-table \ + --route-table-id $PRIVATE_RT \ + --subnet-id $PRIVATE_SUBNET +``` + +Instances in the private subnet now reach the internet outbound through the NAT Gateway's public IP, but remain unreachable from the WAN. ### Multiple Accounts @@ -348,7 +402,7 @@ journalctl -u spinifex-daemon -f aws ec2 describe-images ``` -### SSH Connection Refused +### SSH Connection Refused or Times Out cloud-init needs 30-60 seconds after boot. Check instance state: @@ -356,6 +410,20 @@ cloud-init needs 30-60 seconds after boot. Check instance state: aws ec2 describe-instances --instance-ids $INSTANCE_ID ``` +A connection that **times out** (rather than being refused) usually means the security group is blocking port 22. Confirm the default security group allows SSH ingress: + +```bash +aws ec2 describe-security-groups --group-ids $SG_ID \ + --query 'SecurityGroups[0].IpPermissions' +``` + +If there are no rules for TCP 22, authorize it (see [Allow SSH and ICMP](#allow-ssh-and-icmp)): + +```bash +aws ec2 authorize-security-group-ingress \ + --group-id $SG_ID --protocol tcp --port 22 --cidr 0.0.0.0/0 +``` + ### No Public IP Assigned Verify the subnet has `MapPublicIpOnLaunch` enabled: @@ -378,7 +446,23 @@ aws ec2 describe-internet-gateways ### Instance Has No Internet Access -Check the VPC router's NAT rules (from the host): +First confirm the subnet's route table has a default route to the IGW. Spinifex gates a subnet's egress with a drop policy when this route is missing, so an instance with a public IP still cannot reach the internet: + +```bash +aws ec2 describe-route-tables \ + --filters Name=association.subnet-id,Values=$SUBNET_ID \ + --query 'RouteTables[0].Routes' +# Expect a 0.0.0.0/0 route with a GatewayId of igw-... +``` + +If the route is missing, add it (see [Create a Route Table](#create-a-route-table)): + +```bash +aws ec2 create-route --route-table-id $RT_ID \ + --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID +``` + +If the route is present, check the VPC router's NAT rules (from the host): ```bash sudo ovn-nbctl lr-nat-list $(sudo ovn-nbctl lr-list | awk '{print $2}' | head -1) diff --git a/docs/install-vm-eval/README.md b/docs/install-vm-eval/README.md index bc584f868..b69e422c0 100644 --- a/docs/install-vm-eval/README.md +++ b/docs/install-vm-eval/README.md @@ -12,8 +12,6 @@ For this tutorial download UTM.app on Mac to simplify the VM installation. * UTM - [https://mac.getutm.app](https://mac.getutm.app) - - ## Download Debian ISO Next, download the Debian ISO (ARM64, Apple Silicon) @@ -28,7 +26,7 @@ Using the VM console, login using the `spinifex` user and your specified passwor For a minimal Debian install additional dependencies are required prior to the Spinifex installation: -``` +```bash su root apt update apt install sudo curl bridge-utils @@ -38,7 +36,7 @@ apt install sudo curl bridge-utils Enable sudo for `spinifex` user as the root user: -``` +```bash su root echo "spinifex ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/spinifex ``` diff --git a/docs/service-interfaces.yaml b/docs/service-interfaces.yaml index f92c6f276..af509c988 100644 --- a/docs/service-interfaces.yaml +++ b/docs/service-interfaces.yaml @@ -30,10 +30,12 @@ services: publishes: - "ec2.*" - "elbv2.*" + - "acm.*" - "iam.*" depends_on: - "ec2.*" - "elbv2.*" + - "acm.*" - "iam.*" spinifex_daemon: @@ -50,6 +52,7 @@ services: - spinifex/daemon - spinifex/handlers/ec2 - spinifex/handlers/elbv2 + - spinifex/handlers/acm - spinifex/handlers/iam subscribes: # EC2 instance lifecycle. @@ -95,6 +98,15 @@ services: - "elbv2.CreateListener" - "elbv2.DeleteListener" - "elbv2.DescribeListeners" + # ACM-alike certificate store (handlers/acm/service_nats.go topic map). + - "acm.ImportCertificate" + - "acm.DescribeCertificate" + - "acm.ListCertificates" + - "acm.DeleteCertificate" + - "elbv2.AddListenerCertificates" + - "elbv2.RemoveListenerCertificates" + - "elbv2.DescribeListenerCertificates" + - "elbv2.DescribeSSLPolicies" publishes: # → vpcd - "vpc.create" diff --git a/docs/terraform-workbooks/nginx-alb/README.md b/docs/terraform-workbooks/nginx-alb/README.md index 0eea9db55..c948d26f2 100644 --- a/docs/terraform-workbooks/nginx-alb/README.md +++ b/docs/terraform-workbooks/nginx-alb/README.md @@ -142,7 +142,6 @@ The ALB is internet-facing, but the DNS name Spinifex returns (`*.elb.spinifex.l ALB_IP=$(aws elbv2 describe-load-balancers --names nginx-alb \ --query 'LoadBalancers[0].AvailabilityZones[].LoadBalancerAddresses[].IpAddress' \ --output text) -echo "ALB public IP: $ALB_IP" ``` Then hit the ALB — successive requests should alternate between Server 1 and Server 2: @@ -163,8 +162,6 @@ TG_ARN=$(aws elbv2 describe-target-groups \ --query 'TargetGroups[0].TargetGroupArn' \ --output text) -echo "Target Group ARN: $TG_ARN" - aws elbv2 describe-target-health --target-group-arn $TG_ARN ``` diff --git a/go.mod b/go.mod index 855c644ca..32730e664 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/klauspost/cpuid/v2 v2.3.0 github.com/mulgadc/predastore v1.6.0 github.com/mulgadc/viperblock v1.6.0 - github.com/nats-io/nats-server/v2 v2.14.1 + github.com/nats-io/nats-server/v2 v2.14.2 github.com/nats-io/nats.go v1.52.0 github.com/ovn-kubernetes/libovsdb v0.8.1 github.com/pelletier/go-toml/v2 v2.3.1 @@ -101,8 +101,8 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nats-io/jwt/v2 v2.8.1 // indirect - github.com/nats-io/nkeys v0.4.15 // indirect + github.com/nats-io/jwt/v2 v2.8.2 // indirect + github.com/nats-io/nkeys v0.4.16 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index 35f6dbaef..a3ca31132 100644 --- a/go.sum +++ b/go.sum @@ -296,14 +296,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt/v2 v2.8.1 h1:V0xpGuD/N8Mi+fQNDynXohVvp7ZztevW5io8CUWlPmU= -github.com/nats-io/jwt/v2 v2.8.1/go.mod h1:nWnOEEiVMiKHQpnAy4eXlizVEtSfzacZ1Q43LIRavZg= -github.com/nats-io/nats-server/v2 v2.14.1 h1:wXs/a5fw9Hzm3CvuzLxGeIwpjPulSa7gMT3eSuhGkcg= -github.com/nats-io/nats-server/v2 v2.14.1/go.mod h1:4N17zLpuS7WMbG8T9gsE2B7z9hC9PraPyulVBfpK6nU= +github.com/nats-io/jwt/v2 v2.8.2 h1:XXRgB60MSTnqsRwejQurVDs/hcv2dkt+86GjI+I/bMc= +github.com/nats-io/jwt/v2 v2.8.2/go.mod h1:Ag/56sq9OblL4JgdYufDd16Egb17Kr/8WwwuO/forVc= +github.com/nats-io/nats-server/v2 v2.14.2 h1:Q7dRhCY03Y00rETFW3KV+KGaCIajlDfWgWUVgbMxyuk= +github.com/nats-io/nats-server/v2 v2.14.2/go.mod h1:lWpb1bSpRELZfRdlMkdz8E7lbXKKyNe8RIn0vvepIHs= github.com/nats-io/nats.go v1.52.0 h1:n3avV4VBsCgsdwh71TppsTwtv+QdPs7ntSKM8qJLGsc= github.com/nats-io/nats.go v1.52.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno= -github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4= -github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs= +github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg= +github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ovn-kubernetes/libovsdb v0.8.1 h1:M2J8bcJt5mXCom0HqzfEtuHkT80CTSQRcYG7acT8gf4= diff --git a/internal/eksgw/client.go b/internal/eksgw/client.go new file mode 100644 index 000000000..dd7441067 --- /dev/null +++ b/internal/eksgw/client.go @@ -0,0 +1,103 @@ +// Package eksgw is the on-VM SigV4 client EKS control-plane helpers use to +// reach the AWS gateway, instead of dialing core NATS. Both eks-gateway-publish +// (bootstrap + state relay) and eks-token-webhook (TokenReview relay) sign with +// the system (Predastore) credentials for service "eks" and POST over HTTPS; +// the gateway authorizes via IAM and relays to the host-side NATS/STS/KV. +package eksgw + +import ( + "bytes" + "crypto/sha256" + "crypto/tls" + "crypto/x509" + "encoding/hex" + "fmt" + "io" + "net/http" + "os" + "strings" + "time" + + "github.com/mulgadc/predastore/auth" + "github.com/mulgadc/spinifex/internal/tlsconfig" +) + +// Client posts SigV4-signed requests to the AWS gateway for the "eks" service. +type Client struct { + baseURL string + accessKey string + secretKey string + region string + httpClient *http.Client +} + +// New builds a client. caPath optionally pins the gateway TLS CA; when empty the +// client relies on the system trust store. region defaults to us-east-1 when +// empty (SigV4 requires a non-empty region). +func New(baseURL, caPath, accessKey, secretKey, region string) (*Client, error) { + if baseURL == "" { + return nil, fmt.Errorf("eksgw: baseURL is required") + } + if accessKey == "" || secretKey == "" { + return nil, fmt.Errorf("eksgw: access key and secret key are required") + } + if region == "" { + region = "us-east-1" + } + + tlsCfg := &tls.Config{ + MinVersion: tls.VersionTLS13, + CurvePreferences: tlsconfig.Curves, + } + if caPath != "" { + pem, err := os.ReadFile(caPath) + if err != nil { + return nil, fmt.Errorf("eksgw: read gateway CA %q: %w", caPath, err) + } + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(pem) { + return nil, fmt.Errorf("eksgw: gateway CA %q has no usable certificates", caPath) + } + tlsCfg.RootCAs = pool + } + + return &Client{ + baseURL: strings.TrimRight(baseURL, "/"), + accessKey: accessKey, + secretKey: secretKey, + region: region, + httpClient: &http.Client{ + Timeout: 10 * time.Second, + Transport: &http.Transport{TLSClientConfig: tlsCfg, MaxIdleConns: 2, IdleConnTimeout: 30 * time.Second}, + }, + }, nil +} + +// Post SigV4-signs and sends a single POST of body to path (e.g. +// "/clusters/alpha/token-review"). It returns the response body on a 2xx, or an +// error (including the gateway status + body) otherwise. No retry — callers that +// need it wrap Post in their own loop. +func (c *Client) Post(path string, body []byte) ([]byte, error) { + req, err := http.NewRequest(http.MethodPost, c.baseURL+path, bytes.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + req.Header.Set("Content-Type", "application/x-amz-json-1.1") + + sum := sha256.Sum256(body) + if err := auth.SignReq(req, c.accessKey, c.secretKey, hex.EncodeToString(sum[:]), "eks", c.region); err != nil { + return nil, fmt.Errorf("sign request: %w", err) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + respBody, _ := io.ReadAll(resp.Body) + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("gateway returned %d: %s", resp.StatusCode, strings.TrimSpace(string(respBody))) + } + return respBody, nil +} diff --git a/scripts/ansible/.gitignore b/scripts/ansible/.gitignore deleted file mode 100644 index 349f28175..000000000 --- a/scripts/ansible/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.creds/ -*.retry diff --git a/scripts/ansible/README.md b/scripts/ansible/README.md deleted file mode 100644 index 35e31b71f..000000000 --- a/scripts/ansible/README.md +++ /dev/null @@ -1,222 +0,0 @@ -# Ansible Dev Lifecycle (parallel to bash scripts) - -Experimental Ansible-based lifecycle for a single-node Spinifex dev host. -Lives alongside the existing `scripts/dev-*.sh` and `scripts/reset-dev-env.sh`; -neither set is removed. Decide which to keep after parity testing. - -Plan: `docs/development/improvements/ansible-dev-lifecycle.md` (mulga top-level). - -## Prerequisites - -Venv lives at sub-repo root (`spinifex/.venv/`) — one venv per sub-repo, -shared by any future python tooling in spinifex. Bootstrap from `spinifex/`: - -``` -sudo apt install python3.13-venv -python3 -m venv .venv -source .venv/bin/activate -pip install 'ansible>=9' -ansible-galaxy collection install -r scripts/ansible/requirements.yml -``` - -Then `cd scripts/ansible` to run playbooks — `ansible.cfg` resolves the -inventory and roles relative to CWD. - -## Playbooks - -| Playbook | Purpose | Bash parity | -|---|---|---| -| `playbooks/dev-preflight.yml` | Dependency + port check | `dev-setup.sh` §checks | -| `playbooks/dev-teardown.yml` | Wipe state only (no reinstall) | `reset-dev-env.sh` §teardown | -| `playbooks/dev-install.yml` | Build + install + init + smoketest on clean box | `dev-install.sh` | -| `playbooks/dev-reset.yml` | Capture settings → teardown → build → install → init → smoketest | `reset-dev-env.sh` (full) | -| `playbooks/dev-deploy.yml` | Rebuild + swap binaries/microvm artifacts + restart (no setup.sh, no smoketest) | `make deploy` | -| `playbooks/dev-status.yml` | Read-only health snapshot (units, ports, OVN/OVS, gateway, counts, drift) | none | -| `playbooks/dev-logs.yml` | Dump journald + `/var/log/spinifex/*` + status/networking/versions into a tarball | none | -| `playbooks/dev-snapshot.yml` | Quiesce spinifex+OVN+OVS, tar state paths to a named bundle, restart | none | -| `playbooks/dev-restore.yml` | Stop services, wipe + untar a named bundle, refresh CA, restart | none | -| `playbooks/dev-version.yml` | Build-vs-install drift report (spx, plugin, microvm) + sub-repo HEADs | none | -| `playbooks/dev-vpc.yml` | Idempotent dev VPC fixture (VPC + public subnet + IGW + default route); `-e dev_vpc_state=absent` tears down | partial overlap with `scripts/iac/examples/*` | - -Upcoming (not yet implemented): - -- `dev-mode-start.yml` / `dev-mode-stop.yml` — parity with `start-dev.sh` / `stop-dev.sh` - (lowest priority — deprecated bash scripts, CI-only callers). - -## Invocation - -``` -ansible-playbook playbooks/dev-preflight.yml -ansible-playbook playbooks/dev-teardown.yml -ansible-playbook playbooks/dev-install.yml -ansible-playbook playbooks/dev-reset.yml -ansible-playbook playbooks/dev-deploy.yml -ansible-playbook playbooks/dev-status.yml -ansible-playbook playbooks/dev-logs.yml -ansible-playbook playbooks/dev-snapshot.yml -e snapshot_name=before-merge -ansible-playbook playbooks/dev-restore.yml -e snapshot_name=before-merge -ansible-playbook playbooks/dev-version.yml -ansible-playbook playbooks/dev-vpc.yml -``` - -Or via `make` (from `spinifex/`): - -``` -make ansible-dev-preflight -make ansible-dev-teardown -make ansible-dev-install -make ansible-dev-reset -make ansible-dev-deploy -make ansible-dev-status -make ansible-dev-logs -make ansible-dev-snapshot ANSIBLE_EXTRA='-e snapshot_name=before-merge' -make ansible-dev-restore ANSIBLE_EXTRA='-e snapshot_name=before-merge' -make ansible-dev-version -make ansible-dev-vpc -make ansible-dev-vpc ANSIBLE_EXTRA='-e dev_vpc_state=absent' -``` - -### When to use which - -- First-time / clean box → `ansible-dev-install` -- Iterate on Go code, microvm initramfs, lb-agent → `ansible-dev-deploy` (fast) -- Changed systemd units, helper scripts, logrotate, setup.sh → `ansible-dev-reset` (slow, full rebuild) -- Need a clean slate without reinstall → `ansible-dev-teardown` -- "Is my dev box healthy?" → `ansible-dev-status` (read-only, never mutates) -- Filing a bug / capturing the state of a broken box → `ansible-dev-logs` (writes `/tmp/spinifex-logs--.tar.gz`; override `-e logs_since=10min` or `-e logs_include_dmesg=false`) -- Checkpoint before a risky op (branch switch, viperblock surgery, ovn restart cycle) → `ansible-dev-snapshot ANSIBLE_EXTRA='-e snapshot_name='`; roll back via `ansible-dev-restore` instead of a full `dev-reset`. Bundles live in `/var/lib/spinifex-snapshots/.tar.gz` (root-owned, survives `dev-teardown`). -- "Do I need to `ansible-dev-deploy`?" → `ansible-dev-version` (drift-only check; faster than `dev-status`) -- Want a known-good VPC/subnet for manual EC2 testing without writing tofu → `ansible-dev-vpc` (smoketest will then find and use it automatically). Tear down with `ANSIBLE_EXTRA='-e dev_vpc_state=absent'`. - -## Variable overrides - -Defaults in `vars/defaults.yml`. Override per run with `-e`: - -``` -ansible-playbook playbooks/dev-teardown.yml -e spinifex_wipe_ssh_keys=true -``` - -Useful overrides: - -| Var | Default | What it does | -|---|---|---| -| `spinifex_wipe_legacy_home` | `true` | Remove `$HOME/spinifex` (legacy dev-mode path) | -| `spinifex_wipe_ssh_keys` | `false` | Remove `~/.ssh/spinifex-key*` | -| `spinifex_wipe_aws_creds` | `false` | Remove spinifex profile from `~/.aws/credentials` | -| `teardown_process_wait_seconds` | `30` | Grace period for processes to exit | -| `spinifex_region` | `ap-southeast-2` | Region passed to `spx admin init` | -| `spinifex_az` | `{{ region }}a` | AZ passed to `spx admin init` | -| `spinifex_external_mode` | `pool` | External networking mode — `pool` or empty (NAT removed) | -| `spinifex_external_no_external` | `false` | `true` → emit `--no-external` and skip external networking | -| `spinifex_external_pool_start` | `""` | **Required** for `pool` mode — first IP of the static WAN pool | -| `spinifex_external_pool_end` | `""` | **Required** for `pool` mode — last IP of the static WAN pool | -| `spinifex_external_gateway` | `""` | WAN gateway (auto-detected when blank) | -| `spinifex_external_prefix_len` | `""` | WAN prefix length (auto-detected when blank) | -| `spinifex_external_gateway_ip` | `""` | OVN router external-IP override (`--gateway-ip`) | - -`pool` mode is mandatory unless you set `spinifex_external_no_external=true`. -`spx admin init` rejects the run without a `--external-pool` range, so the -init role asserts `spinifex_external_pool_start/_end` upfront. Either persist -them in `vars/defaults.yml`, or pass them at invocation: - -Direct `ansible-playbook` (use `-e` here — this is ansible's flag): - -``` -ansible-playbook playbooks/dev-install.yml \ - -e spinifex_external_pool_start=192.168.1.150 \ - -e spinifex_external_pool_end=192.168.1.250 -``` - -Via `make` (use `EXTRA_VARS=` — make eats its own `-e`): - -``` -make ansible-dev-install \ - EXTRA_VARS="spinifex_external_pool_start=192.168.1.150 spinifex_external_pool_end=192.168.1.250" -``` - -`dev-reset.yml` automatically captures `region`, `az`, `external_mode`, -pool range, gateway, prefix-len and `gateway_ip` from the existing -`/etc/spinifex/spinifex.toml` before teardown and replays them into -`init`. Legacy `external_mode = "nat"` is coerced to `"pool"` with a warn. -Use `-e` to override. - -## Helper scripts - -`scripts/aws-as-spinifex.sh` and `scripts/spx-as-spinifex.sh` wrap `aws` -and `spx` invocations in `sg spinifex -c '...'` so they run with the -`spinifex` supplementary group active. Both helpers preset -`AWS_PROFILE=spinifex`. All AWS-calling tasks in the roles use these -via `spinifex_aws_cli` / `spinifex_spx_cli` from `vars/defaults.yml`. - -Background: `/etc/spinifex/ca.pem` lives under `drwxr-x--- root:spinifex`, -so the AWS CLI needs `spinifex` group membership to read the CA bundle. -Ansible's `become_user` is a no-op when the target user equals the -calling user (no `sudo -u` invoked → no `initgroups(3)` → supplementary -groups inherited from the operator's shell). On a fresh login the -operator's shell would have `spinifex` (added by `setup.sh`); on shells -that predate the group add it does not. The helpers sidestep this -entirely. - -## Leak catalog - -`roles/teardown/README.md` tracks every known state source. When teardown -misses something, add it there and in `roles/teardown/tasks/main.yml`. - -## Multi-node cluster bootstrap - -`playbooks/cluster-bootstrap.yml` provisions a 2+ node Spinifex cluster -across a long-running tofu env (`env1`, `env2`, `bryn`, etc). Inventory -is generated dynamically from `scripts/tofu-cluster/envs/.tfvars` -via `inventory/tofu.py` — first node in `nodes = [...]` is the init host, -the rest are joins. - -```bash -make ansible-cluster-bootstrap ENV=env1 POOL=192.168.1.150-192.168.1.159 -``` - -`POOL` is optional; omit for DHCP fallback. Equivalent without make: - -```bash -cd scripts/ansible -CLUSTER_ENV=env1 ansible-playbook -i inventory/tofu.py \ - playbooks/cluster-bootstrap.yml \ - -e cluster_env=env1 \ - -e cluster_external_pool=192.168.1.150-192.168.1.159 -``` - -Plays sequence: - -1. **runner (localhost)** — `build` role produces `/tmp/spinifex-local.tar.gz`. -2. **all env hosts (parallel)** — copies tarball, stops any dev-layout - services (`~/spinifex/`), runs `preflight` + `install` roles. `setup.sh` - creates `/etc/spinifex`, the `spinifex` user/group, systemd units, and - installs `/usr/local/bin/spx`. -3. **init host only** — `cluster-init` role runs `spx admin init` in the - background, waits for the formation server, captures the join token. - CA is then copied into `/usr/local/share/ca-certificates/spinifex-ca.crt` - **before** `spinifex.target` starts (required — predastore enforces - strict TLS verify against the system trust store). -4. **join hosts (parallel)** — `cluster-join` role runs `spx admin join` - against the formation server, then installs CA, then starts - `spinifex.target`. -5. **all hosts** — polls `https://:4432/health` until `awsgw=ok`. -6. **init host only** — `cluster-post` role imports the SSH key + Ubuntu - AMI (both idempotent: skip if already present). - -Re-running the playbook against an already-bootstrapped env is safe — -`setup.sh`, `spx admin init --force`, CA copy, key/AMI import are all -idempotent. - -### Migration from `bootstrap.sh` - -`scripts/tofu-cluster/bootstrap.sh` ran services under `~/spinifex/` as -`tf-user` via `start-dev.sh` with no systemd supervision, and installed -the cluster CA *after* services started — predastore would crash with -`x509: certificate signed by unknown authority` on every first boot. -`cluster-bootstrap.yml` replaces that flow with production-layout -(`/etc/spinifex/` + systemd) and correct CA-before-services ordering. -The first run on an env previously bootstrapped by `bootstrap.sh` will -stop the dev-layout services, install the prod layout, and re-init the -cluster from scratch via `spx admin init --force`. - -`bootstrap.sh` stays in-tree for CI and pseudo-multinode envs. diff --git a/scripts/ansible/ansible.cfg b/scripts/ansible/ansible.cfg deleted file mode 100644 index fc4203126..000000000 --- a/scripts/ansible/ansible.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[defaults] -inventory = inventory/localhost.yml -roles_path = roles -stdout_callback = ansible.builtin.default -host_key_checking = False -retry_files_enabled = False -interpreter_python = auto_silent -forks = 10 - -[inventory] -# inventory/tofu.py is an executable script inventory used by -# cluster-bootstrap.yml — opted into via `-i inventory/tofu.py` on the -# command line; not default to keep dev-* playbooks pointing at localhost. -enable_plugins = host_list, script, auto, yaml, ini, toml - -[callback_default] -result_format = yaml diff --git a/scripts/ansible/inventory/localhost.yml b/scripts/ansible/inventory/localhost.yml deleted file mode 100644 index 1b6373943..000000000 --- a/scripts/ansible/inventory/localhost.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -all: - hosts: - localhost: - ansible_connection: local - ansible_python_interpreter: "{{ ansible_playbook_python }}" diff --git a/scripts/ansible/inventory/tofu.py b/scripts/ansible/inventory/tofu.py deleted file mode 100755 index 822091ba8..000000000 --- a/scripts/ansible/inventory/tofu.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python3 -"""Dynamic ansible inventory generator from tofu-cluster tfvars. - -Reads scripts/tofu-cluster/envs/.tfvars (HCL subset) and emits ansible -JSON inventory. First node in the tfvars `nodes = [...]` list is the init -host (forms the cluster); the rest are join hosts. - -Usage: - inventory/tofu.py --list --env env1 - inventory/tofu.py --host --env env1 - -Selecting the env: - CLUSTER_ENV=env1 inventory/tofu.py --list - inventory/tofu.py --list --env env1 -""" - -from __future__ import annotations - -import argparse -import json -import os -import re -import sys -from pathlib import Path - - -def _repo_root() -> Path: - here = Path(__file__).resolve() - for parent in here.parents: - if (parent / "go.work").exists() or (parent / ".gitmodules").exists(): - return parent - raise SystemExit(f"could not locate mulga repo root from {here}") - - -def _tfvars_path(env: str) -> Path: - root = _repo_root() - candidate = root / "scripts" / "tofu-cluster" / "envs" / f"{env}.tfvars" - if not candidate.exists(): - raise SystemExit(f"tfvars file not found: {candidate}") - return candidate - - -_STRING_RE = re.compile(r'^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*"([^"]*)"\s*$') -_NUMBER_RE = re.compile(r'^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*([0-9]+)\s*$') -_NODE_FIELD_RE = re.compile(r'(\w+)\s*=\s*"([^"]*)"') - - -def _parse_tfvars(path: Path) -> dict: - """Minimal tfvars parser — handles the subset used by envs/*.tfvars. - - Supports: - key = "string" - key = 12345 - nodes = [ - { name = "x", wan_ip = "1.2.3.4", ... }, - ... - ] - """ - out: dict = {"nodes": []} - text = path.read_text() - - # Strip line comments - text = re.sub(r"#.*", "", text) - - # Pull the nodes block out first, then process remaining lines. - nodes_match = re.search(r"nodes\s*=\s*\[(.+?)\]\s*$", text, re.DOTALL | re.MULTILINE) - if nodes_match: - nodes_body = nodes_match.group(1) - for entry in re.finditer(r"\{([^}]+)\}", nodes_body): - fields = dict(_NODE_FIELD_RE.findall(entry.group(1))) - if "name" in fields and "wan_ip" in fields: - out["nodes"].append(fields) - text = text.replace(nodes_match.group(0), "") - - for line in text.splitlines(): - if m := _STRING_RE.match(line): - out[m.group(1)] = m.group(2) - elif m := _NUMBER_RE.match(line): - out[m.group(1)] = int(m.group(2)) - - return out - - -def _resolve_ssh_key(value: str | None) -> str: - if not value: - return os.path.expanduser("~/.ssh/tf-user-ap-southeast-2") - return os.path.expanduser(value) - - -def _build_inventory(env: str) -> dict: - data = _parse_tfvars(_tfvars_path(env)) - nodes = data.get("nodes") or [] - if not nodes: - raise SystemExit(f"{env}.tfvars has no nodes") - - ssh_user = data.get("ssh_user", "tf-user") - ssh_key = _resolve_ssh_key(data.get("ssh_public_key_path", "").replace(".pub", "")) - - hostvars: dict[str, dict] = {} - all_hosts: list[str] = [] - init_hosts: list[str] = [] - join_hosts: list[str] = [] - - for idx, node in enumerate(nodes): - ip = node["wan_ip"] - role = "init" if idx == 0 else "join" - hostvars[ip] = { - "ansible_host": ip, - "ansible_user": ssh_user, - "ansible_ssh_private_key_file": ssh_key, - "ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR", - "spinifex_node_name": node["name"], - "spinifex_node_role": role, - "spinifex_node_index": idx, - "spinifex_wan_ip": ip, - } - all_hosts.append(ip) - (init_hosts if role == "init" else join_hosts).append(ip) - - # Group-level vars derived from tfvars (operator can override with -e). - cluster_init_host = init_hosts[0] - group_vars = { - "cluster_env": env, - "cluster_init_host": cluster_init_host, - "cluster_init_ip": cluster_init_host, - "spinifex_node_count": len(nodes), - "spinifex_region": data.get("region", "ap-southeast-2"), - "spinifex_az": data.get("az", "ap-southeast-2a"), - } - if cidr := data.get("env_cidr"): - group_vars["cluster_env_cidr"] = cidr - - inventory = { - "_meta": {"hostvars": hostvars}, - env: {"hosts": all_hosts, "vars": group_vars}, - "cluster": {"children": [env]}, - "init": {"hosts": init_hosts}, - "join": {"hosts": join_hosts}, - } - return inventory - - -def main() -> int: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--list", action="store_true", help="emit full inventory") - parser.add_argument("--host", help="emit hostvars for a single host (ignored — _meta carries them)") - parser.add_argument( - "--env", - default=os.environ.get("CLUSTER_ENV"), - help="tofu env name (matches scripts/tofu-cluster/envs/.tfvars); also via CLUSTER_ENV env var", - ) - args = parser.parse_args() - - if not args.env: - sys.stderr.write("error: --env required (or CLUSTER_ENV)\n") - return 64 - - if args.host: - json.dump({}, sys.stdout) - sys.stdout.write("\n") - return 0 - - inventory = _build_inventory(args.env) - json.dump(inventory, sys.stdout, indent=2) - sys.stdout.write("\n") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/ansible/playbooks/cluster-bootstrap.yml b/scripts/ansible/playbooks/cluster-bootstrap.yml deleted file mode 100644 index d6670c56a..000000000 --- a/scripts/ansible/playbooks/cluster-bootstrap.yml +++ /dev/null @@ -1,330 +0,0 @@ ---- -# cluster-bootstrap.yml — multi-node Spinifex cluster bootstrap against a -# tofu-provisioned env. Inventory comes from inventory/tofu.py which parses -# scripts/tofu-cluster/envs/.tfvars. -# -# Usage: -# cd spinifex/scripts/ansible -# ansible-playbook -i inventory/tofu.py playbooks/cluster-bootstrap.yml \ -# -e cluster_env=env1 -e cluster_external_pool=192.168.1.150-192.168.1.159 -# -# Or via the make wrapper: -# make ansible-cluster-bootstrap ENV=env1 POOL=192.168.1.150-192.168.1.159 -# -# Behaviour: -# - all hosts: preflight + install (setup.sh) — production layout -# - init host: spx admin init (backgrounded) + CA install + spinifex.target -# - join hosts: spx admin join + CA install + spinifex.target (parallel) -# - init host: import ssh key + ubuntu AMI (idempotent) - -- name: Cluster bootstrap — build artifact once on runner - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - tasks: - - name: assert cluster_env supplied - ansible.builtin.assert: - that: cluster_env | default('') | length > 0 - fail_msg: "Pass -e cluster_env= (matches scripts/tofu-cluster/envs/.tfvars)" - roles: - - build - -- name: Cluster bootstrap — install (all nodes, parallel) - hosts: "{{ cluster_env }}" - gather_facts: true - serial: 100% - vars: - install_run_ovn: false # we drive setup-ovn ourselves with role-aware args - cluster_bootstrap_purge: true # wipe stale single-node predastore.toml + raft data - vars_files: - - ../vars/defaults.yml - pre_tasks: - - name: copy build tarball to each node - ansible.builtin.copy: - src: "{{ build_tarball_path }}" - dest: "{{ build_tarball_path }}" - mode: "0644" - - - name: stop dev-layout services if running (migration from ~/spinifex/) - become: true - # Bracket trick on the first letter prevents the pattern literal in the - # shell command line from matching the running shell itself (pkill -f - # against literal patterns is otherwise self-killing — rc=-9). - # `spx [a]dmin` catches lingering formation servers from prior bootstrap - # attempts that own port 4432 and steal joins from the new init. - ansible.builtin.shell: - cmd: | - pkill -9 -f "[b]in/spx service" 2>/dev/null || true - pkill -9 -f "spx [a]dmin" 2>/dev/null || true - pkill -9 -f "spinifex-[u]i" 2>/dev/null || true - pkill -9 -f "[n]ats-server" 2>/dev/null || true - pkill -9 -f "[p]redastore" 2>/dev/null || true - pkill -9 -f "[v]iperblock" 2>/dev/null || true - sleep 2 - rm -rf "{{ ansible_env.HOME }}/spinifex" 2>/dev/null || true - exit 0 - changed_when: false - roles: - - install - -# Wipe OVN/OVS state before setup-ovn runs. Without this, re-bootstrapping a -# previously-built cluster leaves stale chassis rows in the SB DB, stale -# external_ids on Open_vSwitch, stale br-int flows + ports, stale -# systemd-networkd unit files, and stale ovs-monitor-ipsec strongSwan certs. -# Symptom: ovn-controller flows reference dead encap endpoints; IPsec SAs -# fail AUTHENTICATION_FAILED; new joins shadow ghosts under the same -# node-name. -- name: Cluster bootstrap — OVN/OVS state reset (all nodes, parallel) - hosts: "{{ cluster_env }}" - gather_facts: false - serial: 100% - vars_files: - - ../vars/defaults.yml - tasks: - - name: reset — stop OVN/OVS services - become: true - ansible.builtin.systemd: - name: "{{ item }}" - state: stopped - loop: - - ovn-controller - - ovn-central - - openvswitch-ipsec - - openvswitch-switch - failed_when: false - - - name: reset — delete OVN NB/SB databases - become: true - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - "{{ ovn_nb_db }}" - - "{{ ovn_sb_db }}" - - - name: reset — delete OVS conf.db + system-id (force fresh ovsdb) - become: true - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /etc/openvswitch/conf.db - - /etc/openvswitch/.conf.db.~lock~ - - /etc/openvswitch/system-id.conf - - - name: reset — drop spinifex-managed systemd-networkd unit files - become: true - ansible.builtin.shell: | - rm -f /etc/systemd/network/14-spinifex-*.netdev \ - /etc/systemd/network/15-spinifex-*.netdev \ - /etc/systemd/network/15-spinifex-*.network \ - /etc/systemd/network/16-spinifex-*.network - networkctl reload 2>/dev/null || true - changed_when: true - - - name: reset — enumerate stale spinifex veth/tap netdevs - ansible.builtin.shell: | - ip -o link show | awk -F': ' '{print $2}' | cut -d'@' -f1 | \ - grep -E '^(veth-wan-br|veth-wan-ovs|spx-ext-|spx-tap-|spx-)' || true - register: reset_links - changed_when: false - - - name: reset — delete stale netdevs - become: true - ansible.builtin.command: "ip link delete {{ item }}" - loop: "{{ reset_links.stdout_lines }}" - changed_when: true - failed_when: false - - - name: reset — clear ovs-monitor-ipsec strongSwan state - become: true - ansible.builtin.shell: | - rm -rf /var/lib/openvswitch/pki 2>/dev/null || true - rm -f /etc/ipsec.d/certs/ovs-* /etc/ipsec.d/private/ovs-* 2>/dev/null || true - rm -f /etc/ipsec.conf.d/ovs-* 2>/dev/null || true - changed_when: true - - - name: reset — start openvswitch-switch with fresh conf.db - become: true - ansible.builtin.systemd: - name: openvswitch-switch - state: started - daemon_reload: true - -- name: Cluster bootstrap — OVN central (init host only) - hosts: init - gather_facts: false - vars_files: - - ../vars/defaults.yml - tasks: - - name: setup-ovn --management on init host - become: true - ansible.builtin.command: - cmd: >- - /usr/local/share/spinifex/setup-ovn.sh - --management - --encap-ip={{ spinifex_wan_ip }} - --node-name={{ hostvars[inventory_hostname]['spinifex_node_name'] }} - changed_when: true - -- name: Cluster bootstrap — OVN chassis (join hosts, parallel) - hosts: join - gather_facts: false - vars_files: - - ../vars/defaults.yml - tasks: - - name: setup-ovn --ovn-remote on join hosts - become: true - ansible.builtin.command: - cmd: >- - /usr/local/share/spinifex/setup-ovn.sh - --ovn-remote=tcp:{{ cluster_init_host }}:6642 - --encap-ip={{ spinifex_wan_ip }} - --node-name={{ hostvars[inventory_hostname]['spinifex_node_name'] }} - changed_when: true - -- name: Cluster bootstrap — init host (start formation server) - hosts: init - gather_facts: false - vars_files: - - ../vars/defaults.yml - roles: - - cluster-init - -- name: Cluster bootstrap — join hosts run spx admin join (parallel) - hosts: join - gather_facts: false - serial: 100% - vars_files: - - ../vars/defaults.yml - roles: - - cluster-join - -- name: Cluster bootstrap — wait for init to finish writing multi-node configs - hosts: init - gather_facts: false - vars_files: - - ../vars/defaults.yml - tasks: - - name: wait for spx admin init async job to complete - become: true - ansible.builtin.async_status: - jid: "{{ cluster_init_async.ansible_job_id }}" - register: cluster_init_done - until: cluster_init_done.finished - retries: 60 - delay: 5 - - - name: dump init log on failure - ansible.builtin.command: tail -100 /tmp/spx-admin-init.log - register: cluster_init_log - changed_when: false - when: cluster_init_done.rc | default(1) != 0 - - - name: assert init exited cleanly - ansible.builtin.assert: - that: cluster_init_done.rc == 0 - fail_msg: | - spx admin init failed (rc={{ cluster_init_done.rc }}) - {{ cluster_init_log.stdout | default('(log not captured)') }} - - - name: confirm multi-node predastore.toml written - become: true - ansible.builtin.shell: | - if grep -q '127\.0\.0\.1' {{ spinifex_etc_dir }}/predastore/predastore.toml 2>/dev/null; then - echo "ERROR: predastore.toml still references loopback — multi-node config not written" - exit 1 - fi - grep -c '^\[\[nodes\]\]' {{ spinifex_etc_dir }}/predastore/predastore.toml 2>/dev/null || echo 0 - register: predastore_node_count - changed_when: false - -- name: Cluster bootstrap — install CA on all nodes (before service start) - hosts: "{{ cluster_env }}" - gather_facts: false - vars_files: - - ../vars/defaults.yml - tasks: - - name: install CA into system trust - become: true - ansible.builtin.copy: - src: "{{ spinifex_ca_src }}" - dest: "{{ spinifex_ca_trust_path }}" - mode: "0644" - remote_src: true - - - name: rebuild CA bundle - become: true - ansible.builtin.command: update-ca-certificates - changed_when: true - - # spinifex-ui reverse-proxies localhost:9999 (awsgw) and localhost:8443 - # (predastore) — hardcoded in spinifex/services/spinifexui/spinifexui.go. - # spx admin init binds those services to BindIP only, so the loopback - # connection fails (BadGateway in UI). Patch to wildcard bind; cert - # already includes 127.0.0.1 in IP SANs. - - name: patch awsgw bind to 0.0.0.0 (UI proxies via localhost) - become: true - ansible.builtin.replace: - path: "{{ spinifex_config_file }}" - regexp: '^(\s*host\s*=\s*")[^"]+(:9999")' - replace: '\g<1>0.0.0.0\g<2>' - - - name: patch predastore bind to 0.0.0.0 (UI proxies via localhost) - become: true - ansible.builtin.replace: - path: "{{ spinifex_config_file }}" - regexp: '^(\s*host\s*=\s*")[^"]+(:8443")' - replace: '\g<1>0.0.0.0\g<2>' - -- name: Cluster bootstrap — start services (init first, then joins) - hosts: init - gather_facts: false - vars_files: - - ../vars/defaults.yml - tasks: - - name: start spinifex.target on init - become: true - ansible.builtin.systemd: - name: spinifex.target - state: started - -- name: Cluster bootstrap — start services on join hosts - hosts: join - gather_facts: false - serial: 100% - vars_files: - - ../vars/defaults.yml - tasks: - - name: start spinifex.target on joiners - become: true - ansible.builtin.systemd: - name: spinifex.target - state: started - -- name: Cluster bootstrap — readiness check (all nodes) - hosts: "{{ cluster_env }}" - gather_facts: false - vars_files: - - ../vars/defaults.yml - tasks: - - name: wait for awsgw responsiveness - ansible.builtin.uri: - url: "https://{{ spinifex_wan_ip }}:{{ cluster_formation_port }}/health" - validate_certs: false - return_content: true - register: cluster_health - until: - - cluster_health.status == 200 - - cluster_health.json.service_health.awsgw == "ok" - retries: 30 - delay: 5 - -- name: Cluster bootstrap — post-bootstrap fixtures (init host only) - hosts: init - gather_facts: true - vars_files: - - ../vars/defaults.yml - roles: - - cluster-post diff --git a/scripts/ansible/playbooks/dev-deploy.yml b/scripts/ansible/playbooks/dev-deploy.yml deleted file mode 100644 index 123f46b29..000000000 --- a/scripts/ansible/playbooks/dev-deploy.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# In-place artifact swap: rebuilds + replaces binaries/microvm artifacts on -# an already-installed node, then restarts spinifex.target. Use when iterating -# on code without altering systemd units, users, dirs, or running the full -# smoketest. For first-time or unit/script changes use dev-install / dev-reset. -- name: Dev deploy — build + swap binaries + restart (no setup.sh, no smoketest) - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - - roles: - - build - - deploy diff --git a/scripts/ansible/playbooks/dev-install.yml b/scripts/ansible/playbooks/dev-install.yml deleted file mode 100644 index d7a358413..000000000 --- a/scripts/ansible/playbooks/dev-install.yml +++ /dev/null @@ -1,67 +0,0 @@ ---- -- name: Dev install — build, install, init, smoketest (idempotent) - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - - pre_tasks: - - name: preflight - ansible.builtin.include_role: - name: preflight - - - name: health — config file present? - ansible.builtin.stat: - path: "{{ spinifex_config_file }}" - register: health_config - - - name: health — spinifex.target active? - become: true - ansible.builtin.command: systemctl is-active spinifex.target - register: health_target - changed_when: false - failed_when: false - - - name: health — awsgw responds? - become: true - become_user: "{{ spinifex_invoking_user }}" - environment: - AWS_PROFILE: spinifex - ansible.builtin.command: aws ec2 describe-regions - register: health_gw - changed_when: false - failed_when: false - - - name: health — spx binary installed? - ansible.builtin.stat: - path: /usr/local/bin/spx - register: health_spx - - - name: health — set already_installed fact - ansible.builtin.set_fact: - spinifex_already_installed: >- - {{ health_config.stat.exists - and health_spx.stat.exists - and (health_target.stdout | trim) == 'active' - and health_gw.rc == 0 }} - - - name: health — summary - ansible.builtin.debug: - msg: | - Health: - spx binary: {{ 'present' if health_spx.stat.exists else 'missing' }} - config: {{ 'present' if health_config.stat.exists else 'missing' }} - target: {{ health_target.stdout | trim }} - awsgw: {{ 'ok' if health_gw.rc == 0 else 'not responding' }} - Already installed: {{ spinifex_already_installed }} - {% if spinifex_already_installed %} - → Skipping build/install/init. Smoketest only. - {% else %} - → Running full build → install → init → smoketest. - {% endif %} - - roles: - - { role: build, when: not (spinifex_already_installed | default(false)) } - - { role: install, when: not (spinifex_already_installed | default(false)) } - - { role: init, when: not (spinifex_already_installed | default(false)) } - - smoketest diff --git a/scripts/ansible/playbooks/dev-logs.yml b/scripts/ansible/playbooks/dev-logs.yml deleted file mode 100644 index 2461fd749..000000000 --- a/scripts/ansible/playbooks/dev-logs.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -# Capture spinifex + ovn/ovs + qemu/nbdkit logs from journald and -# /var/log/spinifex/ into a single tarball for bug reports. -# Tail mode is intentionally not provided — use `journalctl -u 'spinifex-*' -f` -# directly; ansible is the wrong tool for follow. -- name: Dev logs — dump journald + varlog + status into a bundle - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - vars: - logs_since: "1h" - logs_output_dir: "/tmp" - logs_include_dmesg: true - - roles: - - logs diff --git a/scripts/ansible/playbooks/dev-preflight.yml b/scripts/ansible/playbooks/dev-preflight.yml deleted file mode 100644 index 4e54d3ff6..000000000 --- a/scripts/ansible/playbooks/dev-preflight.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -- name: Dev preflight — validate host dependencies - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - roles: - - preflight diff --git a/scripts/ansible/playbooks/dev-reset.yml b/scripts/ansible/playbooks/dev-reset.yml deleted file mode 100644 index 82fa47d77..000000000 --- a/scripts/ansible/playbooks/dev-reset.yml +++ /dev/null @@ -1,72 +0,0 @@ ---- -- name: Dev reset — capture settings, wipe, rebuild, reinstall, re-init, smoketest - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - - pre_tasks: - - name: capture — check existing spinifex.toml - ansible.builtin.stat: - path: "{{ spinifex_config_file }}" - register: reset_config_stat - - - name: capture — slurp existing config - ansible.builtin.slurp: - src: "{{ spinifex_config_file }}" - register: reset_config_raw - when: reset_config_stat.stat.exists - - - name: capture — decode config - ansible.builtin.set_fact: - reset_config_txt: "{{ reset_config_raw.content | b64decode }}" - when: reset_config_stat.stat.exists - - # List-concat-and-take-first pattern: missing regex match falls back to current default. - # `gateway\s*=` deliberately does not match `gateway_ip` / `gateway_lrp_*`. - - name: capture — extract scalars - ansible.builtin.set_fact: - spinifex_region: "{{ (reset_config_txt | regex_findall('region\\s*=\\s*\"([^\"]+)\"') + [spinifex_region])[0] }}" - spinifex_az: "{{ (reset_config_txt | regex_findall('\\baz\\s*=\\s*\"([^\"]+)\"') + [spinifex_az])[0] }}" - spinifex_external_mode: "{{ (reset_config_txt | regex_findall('external_mode\\s*=\\s*\"([^\"]+)\"') + [spinifex_external_mode])[0] }}" - spinifex_external_pool_start: "{{ (reset_config_txt | regex_findall('range_start\\s*=\\s*\"([^\"]+)\"') + [spinifex_external_pool_start])[0] }}" - spinifex_external_pool_end: "{{ (reset_config_txt | regex_findall('range_end\\s*=\\s*\"([^\"]+)\"') + [spinifex_external_pool_end])[0] }}" - spinifex_external_gateway: "{{ (reset_config_txt | regex_findall('\\bgateway\\s*=\\s*\"([^\"]+)\"') + [spinifex_external_gateway])[0] }}" - spinifex_external_prefix_len: "{{ (reset_config_txt | regex_findall('prefix_len\\s*=\\s*([0-9]+)') + [spinifex_external_prefix_len | string])[0] }}" - spinifex_external_gateway_ip: "{{ (reset_config_txt | regex_findall('gateway_ip\\s*=\\s*\"([^\"]+)\"') + [spinifex_external_gateway_ip])[0] }}" - when: reset_config_stat.stat.exists - - # Legacy: external_mode = "nat" was removed from spx. Coerce to "pool" - # so old configs still reset cleanly; warn so the operator notices. - - name: capture — coerce legacy external_mode=nat to pool - ansible.builtin.set_fact: - spinifex_external_mode: "pool" - when: spinifex_external_mode == 'nat' - - - name: capture — warn on legacy nat coercion - ansible.builtin.debug: - msg: "⚠️ Captured external_mode=nat from legacy config; coerced to 'pool' (NAT mode removed from spx admin init)." - when: - - reset_config_stat.stat.exists - - (reset_config_txt | regex_findall('external_mode\\s*=\\s*\"nat\"')) | length > 0 - - - name: capture — summary - ansible.builtin.debug: - msg: | - {% if reset_config_stat.stat.exists %} - Captured settings from {{ spinifex_config_file }}: - region={{ spinifex_region }}, az={{ spinifex_az }} - external_mode={{ spinifex_external_mode }} - pool={{ spinifex_external_pool_start }}-{{ spinifex_external_pool_end }} - gateway={{ spinifex_external_gateway }}, prefix={{ spinifex_external_prefix_len }} - gateway_ip={{ spinifex_external_gateway_ip }} - {% else %} - No existing config; using defaults from vars/defaults.yml (or -e overrides). - {% endif %} - - roles: - - teardown - - build - - install - - init - - smoketest diff --git a/scripts/ansible/playbooks/dev-restore.yml b/scripts/ansible/playbooks/dev-restore.yml deleted file mode 100644 index 1742cd4ef..000000000 --- a/scripts/ansible/playbooks/dev-restore.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# Restore a dev-snapshot bundle: stop services, wipe the snapshot path -# set, untar the bundle, refresh CA trust, restart. -# -# Required: -e snapshot_name= -# Optional: -e snapshot_dir=/some/path -- name: Dev restore — wipe + untar snapshot + restart - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - - roles: - - restore diff --git a/scripts/ansible/playbooks/dev-snapshot.yml b/scripts/ansible/playbooks/dev-snapshot.yml deleted file mode 100644 index 04875e073..000000000 --- a/scripts/ansible/playbooks/dev-snapshot.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -# Quiesce spinifex + ovn + ovs, tar /etc/spinifex + /var/lib/spinifex + -# /var/lib/ovn + /etc/openvswitch/conf.db into a named bundle, restart. -# Use to checkpoint a working state before a risky operation (branch -# switch, viperblock surgery, ovn-controller restart cycle). Roll back -# via dev-restore without a full dev-reset. -# -# Required: -e snapshot_name= -# Optional: -e snapshot_dir=/some/path -- name: Dev snapshot — quiesce + tar state + restart - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - - roles: - - snapshot diff --git a/scripts/ansible/playbooks/dev-status.yml b/scripts/ansible/playbooks/dev-status.yml deleted file mode 100644 index 45a4e2d1e..000000000 --- a/scripts/ansible/playbooks/dev-status.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -# Read-only health snapshot for a Spinifex dev node. Never mutates state. -# Run any time the operator wants to know "is my box healthy?" — pairs with -# dev-preflight (run before install) as the post-install / running-system -# diagnostic. -- name: Dev status — health snapshot (read-only) - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - - roles: - - status diff --git a/scripts/ansible/playbooks/dev-teardown.yml b/scripts/ansible/playbooks/dev-teardown.yml deleted file mode 100644 index bff1c9798..000000000 --- a/scripts/ansible/playbooks/dev-teardown.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -- name: Dev teardown — wipe Spinifex state (no reinstall) - hosts: localhost - gather_facts: true - vars_files: - - ../vars/defaults.yml - roles: - - teardown diff --git a/scripts/ansible/playbooks/dev-version.yml b/scripts/ansible/playbooks/dev-version.yml deleted file mode 100644 index 8698af643..000000000 --- a/scripts/ansible/playbooks/dev-version.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -# Detailed build-vs-install drift report. Faster than dev-status (no AWS, -# OVN, or port probing). Answers "do I need to ansible-dev-deploy?". -- name: Dev version — build vs install drift (read-only) - hosts: localhost - gather_facts: false - vars_files: - - ../vars/defaults.yml - - roles: - - version diff --git a/scripts/ansible/playbooks/dev-vpc.yml b/scripts/ansible/playbooks/dev-vpc.yml deleted file mode 100644 index 3981dbb9b..000000000 --- a/scripts/ansible/playbooks/dev-vpc.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# Idempotent dev-mode VPC fixture: spinifex-dev-vpc + public subnet + -# IGW + default route. Hooks into smoketest's launch path (which finds -# the public subnet automatically). Re-runs are no-ops once converged. -# -# Default state: present. Tear down with -e dev_vpc_state=absent. -- name: Dev VPC — fixture VPC/subnet/IGW/route (idempotent) - hosts: localhost - gather_facts: false - vars_files: - - ../vars/defaults.yml - - roles: - - vpc diff --git a/scripts/ansible/requirements.yml b/scripts/ansible/requirements.yml deleted file mode 100644 index 3834074e0..000000000 --- a/scripts/ansible/requirements.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -collections: - - name: community.general - version: ">=8.0.0" - - name: ansible.posix - version: ">=1.5.0" diff --git a/scripts/ansible/roles/build/meta/main.yml b/scripts/ansible/roles/build/meta/main.yml deleted file mode 100644 index c03eba75c..000000000 --- a/scripts/ansible/roles/build/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: build - description: Build spinifex + viperblock-nbdkit, stage tarball for setup.sh consumption. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/build/tasks/main.yml b/scripts/ansible/roles/build/tasks/main.yml deleted file mode 100644 index 9096c62c0..000000000 --- a/scripts/ansible/roles/build/tasks/main.yml +++ /dev/null @@ -1,66 +0,0 @@ ---- -- name: build — spinifex (make build) - ansible.builtin.command: - cmd: make build - chdir: "{{ spinifex_root }}" - changed_when: true - -- name: build — viperblock nbdkit plugin (make go_build_nbd) - ansible.builtin.command: - cmd: make go_build_nbd - chdir: "{{ mulga_root }}/viperblock" - changed_when: true - -- name: build — detect microVM build script (present only on direct-boot branches) - ansible.builtin.stat: - path: "{{ spinifex_root }}/scripts/build-microvm-image.sh" - register: build_microvm_script - -- name: build — microVM kernel + initramfs (direct-boot LBs) - ansible.builtin.command: - cmd: make build-microvm-image - chdir: "{{ spinifex_root }}" - changed_when: true - when: build_microvm_script.stat.exists - -- name: build — verify build artifacts present - ansible.builtin.stat: - path: "{{ item }}" - register: build_artifacts - loop: - - "{{ spinifex_root }}/bin/spx" - - "{{ mulga_root }}/viperblock/lib/nbdkit-viperblock-plugin.so" - -- name: build — assert artifacts exist - ansible.builtin.assert: - that: item.stat.exists - fail_msg: "Missing build artifact: {{ item.item }}" - loop: "{{ build_artifacts.results }}" - loop_control: - label: "{{ item.item }}" - -- name: build — stage tarball contents (mirrors dev-install.sh) - ansible.builtin.shell: | - set -e - STAGING=$(mktemp -d) - trap 'rm -rf "$STAGING"' EXIT - cp "{{ spinifex_root }}/bin/spx" "$STAGING/spx" - cp "{{ mulga_root }}/viperblock/lib/nbdkit-viperblock-plugin.so" "$STAGING/" - cp "{{ spinifex_root }}/scripts/setup-ovn.sh" "$STAGING/" - mkdir -p "$STAGING/systemd" - cp "{{ spinifex_root }}/build/systemd/"* "$STAGING/systemd/" - mkdir -p "$STAGING/scripts" - cp "{{ spinifex_root }}/build/scripts/"* "$STAGING/scripts/" - cp "{{ spinifex_root }}/build/logrotate/spinifex" "$STAGING/logrotate-spinifex" - # microVM artifacts ship only when the branch built them. - if [ -f "{{ spinifex_root }}/build/microvm/vmlinuz" ] && [ -f "{{ spinifex_root }}/build/microvm/initramfs.cpio.gz" ]; then - mkdir -p "$STAGING/microvm" - cp "{{ spinifex_root }}/build/microvm/vmlinuz" "$STAGING/microvm/" - cp "{{ spinifex_root }}/build/microvm/initramfs.cpio.gz" "$STAGING/microvm/" - fi - tar czf "{{ build_tarball_path }}" -C "$STAGING" . - changed_when: true - -- name: build — summary - ansible.builtin.debug: - msg: "Built spx + nbdkit plugin{{ ' + microvm artifacts' if build_microvm_script.stat.exists else '' }}, tarball at {{ build_tarball_path }}" diff --git a/scripts/ansible/roles/cluster-init/meta/main.yml b/scripts/ansible/roles/cluster-init/meta/main.yml deleted file mode 100644 index b43eada69..000000000 --- a/scripts/ansible/roles/cluster-init/meta/main.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -galaxy_info: - role_name: cluster-init - description: | - Run `spx admin init` on the cluster's init host, backgrounded so the - formation server accepts joins from the cluster-join role. Installs - cluster CA into the system trust store BEFORE starting spinifex.target - so predastore's strict TLS verify is satisfied at first start. -dependencies: [] diff --git a/scripts/ansible/roles/cluster-init/tasks/main.yml b/scripts/ansible/roles/cluster-init/tasks/main.yml deleted file mode 100644 index 8f0725e35..000000000 --- a/scripts/ansible/roles/cluster-init/tasks/main.yml +++ /dev/null @@ -1,77 +0,0 @@ ---- -- name: cluster-init — assert this is the init host - ansible.builtin.assert: - that: spinifex_node_role == "init" - fail_msg: "cluster-init role must only run against hosts in the [init] group" - -# vars/defaults.yml sets spinifex_node_{name,count} for single-node dev use -# (precedence 11). Inventory hostvars (precedence 4) lose that fight, so -# we promote the inventory values via set_fact (precedence 15) before any -# task templates `{{ spinifex_node_count }}`. -- name: cluster-init — promote inventory node identity over defaults.yml - ansible.builtin.set_fact: - spinifex_node_name: "{{ hostvars[inventory_hostname]['spinifex_node_name'] }}" - spinifex_node_count: "{{ groups[cluster_env] | length }}" - -- name: cluster-init — assemble external network flags - ansible.builtin.set_fact: - cluster_external_flags: >- - {% if cluster_external_pool | length > 0 %}--external-mode=pool --external-pool={{ cluster_external_pool }}{% endif %} - -- name: cluster-init — run spx admin init (backgrounded, formation server waits for joins) - become: true - ansible.builtin.shell: | - set -o pipefail - spx admin init --force \ - --node {{ spinifex_node_name }} \ - --nodes {{ spinifex_node_count }} \ - --bind {{ spinifex_wan_ip }} \ - --cluster-bind {{ spinifex_wan_ip }} \ - --port {{ cluster_formation_port }} \ - --region {{ spinifex_region }} \ - --az {{ spinifex_az }} \ - {{ cluster_external_flags }} \ - > /tmp/spx-admin-init.log 2>&1 - async: 600 - poll: 0 - register: cluster_init_async - -- name: cluster-init — brief pause so init can fail fast if misconfigured - ansible.builtin.pause: - seconds: 5 - -- name: cluster-init — check if init exited prematurely (sanity) - ansible.builtin.shell: | - if pgrep -f 'spx admin init' >/dev/null; then - echo "running" - else - tail -50 /tmp/spx-admin-init.log 2>/dev/null - exit 1 - fi - register: cluster_init_alive - changed_when: false - failed_when: cluster_init_alive.rc != 0 - -- name: cluster-init — wait for formation server health - ansible.builtin.uri: - url: "https://{{ spinifex_wan_ip }}:{{ cluster_formation_port }}/formation/health" - validate_certs: false - status_code: 200 - register: cluster_init_health - until: cluster_init_health.status == 200 - retries: "{{ cluster_formation_timeout // 2 }}" - delay: 2 - -- name: cluster-init — fetch join token (delegated read for joiners) - become: true - ansible.builtin.slurp: - src: "{{ cluster_join_token_path }}" - register: cluster_join_token_raw - -- name: cluster-init — register join token as host fact - ansible.builtin.set_fact: - cluster_join_token: "{{ cluster_join_token_raw.content | b64decode | trim }}" - -- name: cluster-init — join token captured; waiting for joins handled by later play - ansible.builtin.debug: - msg: "Formation server up on {{ spinifex_wan_ip }}:{{ cluster_formation_port }}, {{ spinifex_node_count }} nodes expected" diff --git a/scripts/ansible/roles/cluster-join/meta/main.yml b/scripts/ansible/roles/cluster-join/meta/main.yml deleted file mode 100644 index bf45870de..000000000 --- a/scripts/ansible/roles/cluster-join/meta/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -galaxy_info: - role_name: cluster-join - description: | - Run `spx admin join` against the cluster init host's formation server. - Installs cluster CA + starts spinifex.target after join completes - (CA-before-services ordering avoids predastore TLS-verify crashes). -dependencies: [] diff --git a/scripts/ansible/roles/cluster-join/tasks/main.yml b/scripts/ansible/roles/cluster-join/tasks/main.yml deleted file mode 100644 index 2a3c77592..000000000 --- a/scripts/ansible/roles/cluster-join/tasks/main.yml +++ /dev/null @@ -1,39 +0,0 @@ ---- -- name: cluster-join — assert this is a join host - ansible.builtin.assert: - that: spinifex_node_role == "join" - fail_msg: "cluster-join role must only run against hosts in the [join] group" - -# Promote inventory hostvars over vars/defaults.yml (see cluster-init). -- name: cluster-join — promote inventory node identity over defaults.yml - ansible.builtin.set_fact: - spinifex_node_name: "{{ hostvars[inventory_hostname]['spinifex_node_name'] }}" - -- name: cluster-join — pull join token from init host - ansible.builtin.set_fact: - cluster_join_token: "{{ hostvars[cluster_init_host]['cluster_join_token'] }}" - -- name: cluster-join — assert token present - ansible.builtin.assert: - that: cluster_join_token | length > 0 - fail_msg: "join token missing — init host did not export cluster_join_token" - -- name: cluster-join — run spx admin join (retry up to 5 times) - become: true - ansible.builtin.command: - cmd: >- - spx admin join - --node {{ spinifex_node_name }} - --bind {{ spinifex_wan_ip }} - --cluster-bind {{ spinifex_wan_ip }} - --host {{ cluster_init_host }}:{{ cluster_formation_port }} - --token {{ cluster_join_token }} - --region {{ spinifex_region }} - --az {{ spinifex_az }} - register: cluster_join_result - retries: 5 - delay: 3 - until: cluster_join_result.rc == 0 - -# CA install + service start handled by cluster-bootstrap.yml plays that -# run after all joins complete and init has written multi-node configs. diff --git a/scripts/ansible/roles/cluster-post/meta/main.yml b/scripts/ansible/roles/cluster-post/meta/main.yml deleted file mode 100644 index bcc7079f0..000000000 --- a/scripts/ansible/roles/cluster-post/meta/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -galaxy_info: - role_name: cluster-post - description: | - Post-bootstrap idempotent fixtures on the init host: import SSH key, - import Ubuntu AMI (dual-version detect 24.04/26.04). Skip when - artifacts already exist. -dependencies: [] diff --git a/scripts/ansible/roles/cluster-post/tasks/main.yml b/scripts/ansible/roles/cluster-post/tasks/main.yml deleted file mode 100644 index 973f25d9e..000000000 --- a/scripts/ansible/roles/cluster-post/tasks/main.yml +++ /dev/null @@ -1,205 +0,0 @@ ---- -- name: cluster-post — locate ssh public key for import - ansible.builtin.find: - paths: "{{ ansible_env.HOME }}/.ssh" - patterns: - - spinifex-key.pub - - tf-user-*.pub - file_type: file - register: cluster_post_ssh_pubkey - -- name: cluster-post — fail when no public key found - ansible.builtin.assert: - that: cluster_post_ssh_pubkey.files | length > 0 - fail_msg: "no public key in ~/.ssh — needed for spinifex-key import" - -- name: cluster-post — set pubkey path fact - ansible.builtin.set_fact: - cluster_post_pubkey_path: "{{ cluster_post_ssh_pubkey.files[0].path }}" - -- name: cluster-post — check whether spinifex-key already imported - ansible.builtin.command: - cmd: "{{ spinifex_aws_cli }} ec2 describe-key-pairs --key-names spinifex-key" - register: cluster_post_key_check - changed_when: false - failed_when: false - -- name: cluster-post — import spinifex-key (if absent) - ansible.builtin.command: - cmd: >- - {{ spinifex_aws_cli }} ec2 import-key-pair - --key-name spinifex-key - --public-key-material fileb://{{ cluster_post_pubkey_path }} - register: cluster_post_key_import - retries: 10 - delay: 6 - until: cluster_post_key_import.rc == 0 - when: cluster_post_key_check.rc != 0 - -- name: cluster-post — locate ubuntu cloud image (26.04 preferred, 24.04 fallback) - ansible.builtin.stat: - path: "{{ item }}" - register: cluster_post_ubuntu_stat - loop: - - "{{ ansible_env.HOME }}/images/ubuntu-26.04.img" - - "{{ ansible_env.HOME }}/images/ubuntu-24.04.img" - -- name: cluster-post — pick first available image - ansible.builtin.set_fact: - cluster_post_ubuntu_image: "{{ (cluster_post_ubuntu_stat.results | selectattr('stat.exists') | map(attribute='item') | list | first) | default('') }}" - -- name: cluster-post — fail when no ubuntu image present - ansible.builtin.assert: - that: cluster_post_ubuntu_image | length > 0 - fail_msg: "no ubuntu-{24,26}.04.img in ~/images/ — gold image must stage it" - -- name: cluster-post — derive ubuntu version from image filename - ansible.builtin.set_fact: - cluster_post_ubuntu_version: "{{ cluster_post_ubuntu_image | regex_search('ubuntu-([0-9]+\\.[0-9]+)\\.img', '\\1') | first }}" - -- name: cluster-post — check whether ubuntu AMI already imported - ansible.builtin.command: - cmd: >- - {{ spinifex_aws_cli }} ec2 describe-images - --owners self - --filters Name=name,Values=ubuntu-{{ cluster_post_ubuntu_version }} - register: cluster_post_ami_check - changed_when: false - failed_when: false - -- name: cluster-post — import ubuntu AMI (if absent) - ansible.builtin.command: - cmd: >- - {{ spinifex_spx_cli }} admin images import - --file {{ cluster_post_ubuntu_image }} - --arch x86_64 - --distro ubuntu - --version {{ cluster_post_ubuntu_version }} - --boot-mode uefi - when: - - cluster_post_ami_check.rc != 0 - or "ubuntu-" + cluster_post_ubuntu_version not in cluster_post_ami_check.stdout - -- name: cluster-post — slurp final admin creds from ~/.aws/credentials - # Source of truth: SetupAWSCredentials writes the FINAL admin keys here. - # Init log shows obsolete keys (multi-node path silently regenerates them - # after the initial print — see cmd/spinifex/cmd/admin.go writeBootstrapFiles - # called twice in the multi-node flow). - ansible.builtin.slurp: - src: "{{ ansible_env.HOME }}/.aws/credentials" - register: cluster_post_aws_creds_raw - -- name: cluster-post — extract admin access/secret keys from spinifex profile - ansible.builtin.set_fact: - cluster_post_admin_access_key: >- - {{ (cluster_post_aws_creds_raw.content | b64decode - | regex_search('\[spinifex\][^\[]*aws_access_key_id\s*=\s*(\S+)', '\1') - | first) | default('') }} - cluster_post_admin_secret_key: >- - {{ (cluster_post_aws_creds_raw.content | b64decode - | regex_search('\[spinifex\][^\[]*aws_secret_access_key\s*=\s*(\S+)', '\1') - | first) | default('') }} - -- name: cluster-post — assert creds parsed - ansible.builtin.assert: - that: - - cluster_post_admin_access_key | length > 0 - - cluster_post_admin_secret_key | length > 0 - fail_msg: "could not parse admin keys from {{ ansible_env.HOME }}/.aws/credentials [spinifex] profile" - -- name: cluster-post — ensure local .creds dir exists - delegate_to: localhost - become: false - ansible.builtin.file: - path: "{{ playbook_dir }}/../.creds" - state: directory - mode: "0700" - -- name: cluster-post — write creds to operator workstation - delegate_to: localhost - become: false - ansible.builtin.copy: - dest: "{{ playbook_dir }}/../.creds/{{ cluster_env }}.txt" - mode: "0600" - content: | - # Spinifex cluster admin credentials — {{ cluster_env }} - # Generated: {{ ansible_facts['date_time']['iso8601'] }} - # Init host: {{ cluster_init_host }} - # - # UI: https://{{ cluster_init_host }}:3000 - # AWS API: https://{{ cluster_init_host }}:4432 - # Region: {{ spinifex_region }} - # - # Login (admin user / AWS profile spinifex): - AWS_ACCESS_KEY_ID={{ cluster_post_admin_access_key }} - AWS_SECRET_ACCESS_KEY={{ cluster_post_admin_secret_key }} - -# Per-env AWS profile in operator's ~/.aws/credentials so tofu / aws CLI can -# target this cluster via AWS_PROFILE=spinifex-{{ cluster_env }}. blockinfile -# marker keeps re-runs idempotent — replaces the block in place, never -# duplicates. Does not touch the user's existing [spinifex] profile (single- -# node dev). Tofu side: provider "aws" { profile = "spinifex-env1" ... }. -- name: cluster-post — ensure local ~/.aws dir exists - delegate_to: localhost - become: false - ansible.builtin.file: - path: "{{ lookup('env', 'HOME') }}/.aws" - state: directory - mode: "0700" - -- name: cluster-post — ensure local ~/.aws/credentials exists - delegate_to: localhost - become: false - ansible.builtin.copy: - dest: "{{ lookup('env', 'HOME') }}/.aws/credentials" - content: "" - force: false - mode: "0600" - -- name: cluster-post — upsert spinifex-{{ cluster_env }} profile in local ~/.aws/credentials - delegate_to: localhost - become: false - ansible.builtin.blockinfile: - path: "{{ lookup('env', 'HOME') }}/.aws/credentials" - marker: "# {mark} SPINIFEX-{{ cluster_env | upper }} MANAGED BLOCK" - block: | - [spinifex-{{ cluster_env }}] - aws_access_key_id = {{ cluster_post_admin_access_key }} - aws_secret_access_key = {{ cluster_post_admin_secret_key }} - region = {{ spinifex_region }} - ca_bundle = {{ playbook_dir }}/../.creds/{{ cluster_env }}-ca.pem - endpoint_url = https://{{ cluster_init_host }}:4432 - -- name: cluster-post — slurp cluster CA from init host - ansible.builtin.slurp: - src: "{{ spinifex_ca_src }}" - register: cluster_post_ca_raw - become: true - -- name: cluster-post — write CA pem to operator workstation - delegate_to: localhost - become: false - ansible.builtin.copy: - dest: "{{ playbook_dir }}/../.creds/{{ cluster_env }}-ca.pem" - mode: "0644" - content: "{{ cluster_post_ca_raw.content | b64decode }}" - -- name: cluster-post — resolve absolute paths for summary - delegate_to: localhost - become: false - ansible.builtin.command: "readlink -f {{ playbook_dir }}/../.creds" - register: cluster_post_creds_abs - changed_when: false - -- name: cluster-post — summary - ansible.builtin.debug: - msg: | - Post-bootstrap done: - spinifex-key: {{ 'imported now' if (cluster_post_key_import.changed | default(false)) else 'already present' }} - ubuntu AMI: ubuntu-{{ cluster_post_ubuntu_version }} - admin creds: {{ cluster_post_creds_abs.stdout }}/{{ cluster_env }}.txt - CA cert: {{ cluster_post_creds_abs.stdout }}/{{ cluster_env }}-ca.pem - AWS profile: spinifex-{{ cluster_env }} (in ~/.aws/credentials) - UI URL: https://{{ cluster_init_host }}:3000 - Access Key: {{ cluster_post_admin_access_key }} - Tofu usage: AWS_PROFILE=spinifex-{{ cluster_env }} tofu plan diff --git a/scripts/ansible/roles/deploy/meta/main.yml b/scripts/ansible/roles/deploy/meta/main.yml deleted file mode 100644 index 8e4aa0695..000000000 --- a/scripts/ansible/roles/deploy/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: deploy - description: In-place binary + microvm artifact swap for a running Spinifex node (parity with `make deploy`). - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/deploy/tasks/main.yml b/scripts/ansible/roles/deploy/tasks/main.yml deleted file mode 100644 index 1153b7faf..000000000 --- a/scripts/ansible/roles/deploy/tasks/main.yml +++ /dev/null @@ -1,144 +0,0 @@ ---- -# In-place artifact swap for an already-installed spinifex node. Mirrors -# `make deploy`: replace binaries + microvm artifacts, restart services. -# Does NOT run setup.sh (no user/dir/systemd-unit changes) or smoketest. - -- name: deploy — fail fast if spinifex isn't already installed - ansible.builtin.stat: - path: "{{ spinifex_config_file }}" - register: deploy_installed_stat - failed_when: not deploy_installed_stat.stat.exists - -- name: deploy — assert config present - ansible.builtin.assert: - that: deploy_installed_stat.stat.exists - fail_msg: | - {{ spinifex_config_file }} missing — this playbook only updates an - existing install. Run ansible-dev-install or ansible-dev-reset first. - -- name: deploy — verify build artifacts present - ansible.builtin.stat: - path: "{{ item }}" - register: deploy_artifacts - loop: - - "{{ spinifex_root }}/bin/spx" - - "{{ mulga_root }}/viperblock/lib/nbdkit-viperblock-plugin.so" - -- name: deploy — assert artifacts exist after build role - ansible.builtin.assert: - that: item.stat.exists - fail_msg: "Build artifact missing: {{ item.item }} — build role should have produced it" - loop: "{{ deploy_artifacts.results }}" - loop_control: - label: "{{ item.item }}" - -- name: deploy — detect microvm artifacts (direct-boot branches only) - ansible.builtin.stat: - path: "{{ spinifex_root }}/build/microvm/{{ item }}" - register: deploy_microvm_stat - loop: - - vmlinuz - - initramfs.cpio.gz - -- name: deploy — set microvm presence fact - ansible.builtin.set_fact: - deploy_microvm_present: "{{ deploy_microvm_stat.results | map(attribute='stat.exists') | min }}" - -- name: deploy — detect nbdkit plugin directory - ansible.builtin.shell: | - set -e - nbdkit --dump-config | awk -F= '/^plugindir=/{print $2; exit}' - register: deploy_plugindir_cmd - changed_when: false - -- name: deploy — set plugin path fact - ansible.builtin.set_fact: - deploy_plugindir: "{{ deploy_plugindir_cmd.stdout | trim }}" - -- name: deploy — assert plugindir resolved - ansible.builtin.assert: - that: deploy_plugindir | length > 0 - fail_msg: "Could not detect nbdkit plugin directory via `nbdkit --dump-config`" - -- name: deploy — stop spinifex.target before binary swap - become: true - ansible.builtin.systemd: - name: spinifex.target - state: stopped - failed_when: false - -- name: deploy — install spx binary - become: true - ansible.builtin.copy: - src: "{{ spinifex_root }}/bin/spx" - dest: /usr/local/bin/spx - mode: "0755" - owner: root - group: root - -- name: deploy — install viperblock nbdkit plugin - become: true - ansible.builtin.copy: - src: "{{ mulga_root }}/viperblock/lib/nbdkit-viperblock-plugin.so" - dest: "{{ deploy_plugindir }}/nbdkit-viperblock-plugin.so" - mode: "0755" - owner: root - group: root - -- name: deploy — ensure microvm dir - become: true - ansible.builtin.file: - path: /usr/share/spinifex/microvm - state: directory - mode: "0755" - owner: root - group: root - when: deploy_microvm_present - -- name: deploy — install microvm kernel - become: true - ansible.builtin.copy: - src: "{{ spinifex_root }}/build/microvm/vmlinuz" - dest: /usr/share/spinifex/microvm/vmlinuz - mode: "0644" - owner: root - group: root - when: deploy_microvm_present - -- name: deploy — install microvm initramfs - become: true - ansible.builtin.copy: - src: "{{ spinifex_root }}/build/microvm/initramfs.cpio.gz" - dest: /usr/share/spinifex/microvm/initramfs.cpio.gz - mode: "0644" - owner: root - group: root - when: deploy_microvm_present - -- name: deploy — daemon-reload (covers any unit changes shipped via setup.sh later) - become: true - ansible.builtin.systemd: - daemon_reload: true - -- name: deploy — start spinifex.target - become: true - ansible.builtin.systemd: - name: spinifex.target - state: started - -- name: deploy — wait for awsgw to respond - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-regions" - register: deploy_awsgw - retries: 15 - delay: 2 - until: deploy_awsgw.rc == 0 - changed_when: false - -- name: deploy — summary - ansible.builtin.debug: - msg: | - Deployed: - spx → /usr/local/bin/spx - nbdkit plugin → {{ deploy_plugindir }}/nbdkit-viperblock-plugin.so - microvm → {{ '/usr/share/spinifex/microvm/{vmlinuz,initramfs.cpio.gz}' if deploy_microvm_present else 'skipped (not built on this branch)' }} - spinifex.target restarted; awsgw responding. diff --git a/scripts/ansible/roles/init/meta/main.yml b/scripts/ansible/roles/init/meta/main.yml deleted file mode 100644 index ab1f3ffd0..000000000 --- a/scripts/ansible/roles/init/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: init - description: Run spx admin init, install CA trust, start services, import LB image. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/init/tasks/main.yml b/scripts/ansible/roles/init/tasks/main.yml deleted file mode 100644 index 62f49e5fb..000000000 --- a/scripts/ansible/roles/init/tasks/main.yml +++ /dev/null @@ -1,90 +0,0 @@ ---- -- name: init — base admin-init flags - ansible.builtin.set_fact: - init_base_flags: >- - --force - --region {{ spinifex_region }} - --az {{ spinifex_az }} - --node {{ spinifex_node_name }} - --nodes {{ spinifex_node_count }} - -- name: init — no-external opt-out - ansible.builtin.set_fact: - init_external_flags: "--no-external" - when: spinifex_external_no_external | bool - -- name: init — assert pool range supplied when mode=pool - ansible.builtin.assert: - that: - - spinifex_external_pool_start | length > 0 - - spinifex_external_pool_end | length > 0 - fail_msg: | - spx admin init requires --external-pool when --external-mode=pool. - Set spinifex_external_pool_start / spinifex_external_pool_end in - scripts/ansible/vars/defaults.yml, or pass: - -e spinifex_external_pool_start=192.168.1.150 - -e spinifex_external_pool_end=192.168.1.250 - Pass -e spinifex_external_no_external=true to skip external networking. - when: - - not (spinifex_external_no_external | bool) - - spinifex_external_mode == 'pool' - -- name: init — pool-mode external flags - ansible.builtin.set_fact: - init_external_flags: >- - --external-mode=pool - --external-pool={{ spinifex_external_pool_start }}-{{ spinifex_external_pool_end }} - {% if spinifex_external_gateway | length > 0 %}--external-gateway={{ spinifex_external_gateway }}{% endif %} - {% if spinifex_external_prefix_len | string | length > 0 %}--external-prefix-len={{ spinifex_external_prefix_len }}{% endif %} - {% if spinifex_external_gateway_ip | length > 0 %}--gateway-ip={{ spinifex_external_gateway_ip }}{% endif %} - when: - - not (spinifex_external_no_external | bool) - - spinifex_external_mode == 'pool' - -- name: init — assemble full flag string - ansible.builtin.set_fact: - init_all_flags: "{{ init_base_flags }} {{ init_external_flags | default('') }}" - -- name: init — run spx admin init - become: true - ansible.builtin.command: "spx admin init {{ init_all_flags }}" - changed_when: true - -- name: init — copy CA to system trust - become: true - ansible.builtin.copy: - src: "{{ spinifex_ca_src }}" - dest: "{{ spinifex_ca_trust_path }}" - mode: "0644" - remote_src: true - -- name: init — rebuild CA bundle (not --fresh — preserve other CAs) - become: true - ansible.builtin.command: update-ca-certificates - changed_when: true - -- name: init — start spinifex.target - become: true - ansible.builtin.systemd: - name: spinifex.target - state: started - -# systemctl start returns as soon as units activate; awsgw still wires up -# to NATS + daemons after that. Bash start-dev.sh has internal wait loops; -# systemd path does not. Poll a benign call until gateway is fully ready. -- name: init — wait for AWS gateway readiness - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-regions" - register: init_gw_ready - until: init_gw_ready.rc == 0 - retries: 30 - delay: 2 - changed_when: false - -- name: init — summary - ansible.builtin.debug: - msg: | - Init complete: - region={{ spinifex_region }}, az={{ spinifex_az }}, node={{ spinifex_node_name }} - external_mode={{ 'disabled' if (spinifex_external_no_external | bool) else spinifex_external_mode }} - spinifex.target: started - CA: {{ spinifex_ca_trust_path }} diff --git a/scripts/ansible/roles/install/meta/main.yml b/scripts/ansible/roles/install/meta/main.yml deleted file mode 100644 index 958623e0d..000000000 --- a/scripts/ansible/roles/install/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: install - description: Invoke setup.sh and setup-ovn.sh to install Spinifex into production paths. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/install/tasks/main.yml b/scripts/ansible/roles/install/tasks/main.yml deleted file mode 100644 index aba7e19e0..000000000 --- a/scripts/ansible/roles/install/tasks/main.yml +++ /dev/null @@ -1,79 +0,0 @@ ---- -- name: install — assert tarball exists - ansible.builtin.stat: - path: "{{ build_tarball_path }}" - register: install_tarball - -- name: install — fail if tarball missing - ansible.builtin.assert: - that: install_tarball.stat.exists - fail_msg: "Tarball {{ build_tarball_path }} missing. Run the build role first." - -- name: install — stop spinifex.target before reinstall - become: true - ansible.builtin.systemd: - name: spinifex.target - state: stopped - failed_when: false - -- name: install — reset failed state (spinifex-* scope) - become: true - ansible.builtin.command: systemctl reset-failed 'spinifex-*' - changed_when: false - failed_when: false - -- name: install — scrub stale pid/log files from prior dev-mode installs - become: true - ansible.builtin.shell: | - for dir in {{ spinifex_data_dir }} {{ spinifex_log_dir }} {{ spinifex_etc_dir }}; do - if [ -d "$dir" ]; then - find "$dir" -name '*.pid' -delete 2>/dev/null || true - find "$dir" -name '*.log' -delete 2>/dev/null || true - find "$dir" -name '*.log.*' -delete 2>/dev/null || true - fi - done - changed_when: false - -- name: install — remove legacy /var/lib/spinifex/config - become: true - ansible.builtin.file: - path: "{{ spinifex_data_dir }}/config" - state: absent - -- name: install — purge stale cluster state (configs + raft + NATS KV) on bootstrap - become: true - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - "{{ spinifex_etc_dir }}/predastore/predastore.toml" - - "{{ spinifex_etc_dir }}/spinifex.toml" - - "{{ spinifex_data_dir }}/predastore" - - "{{ spinifex_data_dir }}/nats" - - "{{ spinifex_data_dir }}/awsgw" - when: cluster_bootstrap_purge | default(false) | bool - -- name: install — run setup.sh (creates users, dirs, systemd units) - become: true - ansible.builtin.command: - cmd: "bash {{ spinifex_root }}/scripts/setup.sh" - environment: - INSTALL_SPINIFEX_TARBALL: "{{ build_tarball_path }}" - INSTALL_SPINIFEX_SKIP_NEWGRP: "1" - changed_when: true - -- name: install — cleanup tarball - ansible.builtin.file: - path: "{{ build_tarball_path }}" - state: absent - -- name: install — run setup-ovn.sh --management - become: true - ansible.builtin.command: - cmd: "/usr/local/share/spinifex/setup-ovn.sh --management --node-name={{ spinifex_node_name }}" - changed_when: true - when: install_run_ovn | default(true) | bool - -- name: install — summary - ansible.builtin.debug: - msg: "Install complete — setup.sh + setup-ovn.sh ran. Services NOT yet started (init role handles that)." diff --git a/scripts/ansible/roles/logs/meta/main.yml b/scripts/ansible/roles/logs/meta/main.yml deleted file mode 100644 index 72b3aaa47..000000000 --- a/scripts/ansible/roles/logs/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: logs - description: Dump spinifex + ovn + system logs into a tarball for bug reports. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/logs/tasks/main.yml b/scripts/ansible/roles/logs/tasks/main.yml deleted file mode 100644 index 38cea5572..000000000 --- a/scripts/ansible/roles/logs/tasks/main.yml +++ /dev/null @@ -1,153 +0,0 @@ ---- -# Collect a self-contained log bundle for bug reports. NEVER mutates running -# state — only reads journalctl + /var/log/spinifex/* + status output, then -# tars into logs_output_dir. Errors on missing inputs are non-fatal so a -# half-broken node still produces a bundle. - -- name: logs — resolve staging directory - ansible.builtin.set_fact: - logs_stage_dir: "/tmp/spinifex-logs-{{ ansible_facts['hostname'] }}-{{ ansible_facts['date_time']['iso8601_basic_short'] }}" - logs_bundle_path: "{{ logs_output_dir }}/spinifex-logs-{{ ansible_facts['hostname'] }}-{{ ansible_facts['date_time']['iso8601_basic_short'] }}.tar.gz" - -- name: logs — create staging tree - ansible.builtin.file: - path: "{{ logs_stage_dir }}/{{ item }}" - state: directory - mode: "0755" - loop: - - journal - - varlog - - status - - kernel - -- name: logs — capture journalctl for spinifex-* units - become: true - ansible.builtin.shell: | - journalctl --no-pager --output=short-iso \ - -u 'spinifex-*' --since {{ logs_since | quote }} \ - > {{ logs_stage_dir }}/journal/spinifex.log 2>&1 || true - changed_when: true - -- name: logs — capture journalctl for ovn-* + openvswitch-switch - become: true - ansible.builtin.shell: | - journalctl --no-pager --output=short-iso \ - -u 'ovn-*' -u openvswitch-switch \ - --since {{ logs_since | quote }} \ - > {{ logs_stage_dir }}/journal/ovn-ovs.log 2>&1 || true - changed_when: true - -- name: logs — capture journalctl for nbdkit + qemu (best-effort match) - become: true - ansible.builtin.shell: | - journalctl --no-pager --output=short-iso \ - --since {{ logs_since | quote }} \ - _COMM=qemu-system-x86_64 + _COMM=qemu-system-aarch64 + _COMM=nbdkit \ - > {{ logs_stage_dir }}/journal/qemu-nbdkit.log 2>&1 || true - changed_when: true - -- name: logs — copy /var/log/spinifex/* - become: true - ansible.builtin.shell: | - if [ -d {{ spinifex_log_dir | quote }} ]; then - cp -a {{ spinifex_log_dir | quote }}/. {{ logs_stage_dir | quote }}/varlog/ 2>/dev/null || true - fi - changed_when: true - -- name: logs — systemctl status snapshot (spinifex-*) - become: true - ansible.builtin.shell: | - systemctl status 'spinifex-*' --no-pager --all \ - > {{ logs_stage_dir }}/status/spinifex-units.txt 2>&1 || true - systemctl status ovn-controller ovn-central openvswitch-switch \ - --no-pager > {{ logs_stage_dir }}/status/ovn-ovs-units.txt 2>&1 || true - systemctl list-units 'spinifex-*' --all --no-legend --plain \ - > {{ logs_stage_dir }}/status/spinifex-list.txt 2>&1 || true - changed_when: true - -- name: logs — networking snapshot (ip, ss, ovs) - become: true - ansible.builtin.shell: | - ip -d -s link show > {{ logs_stage_dir }}/status/ip-link.txt 2>&1 || true - ip addr show > {{ logs_stage_dir }}/status/ip-addr.txt 2>&1 || true - ip route show table all > {{ logs_stage_dir }}/status/ip-route.txt 2>&1 || true - ss -tlnp > {{ logs_stage_dir }}/status/ss-tlnp.txt 2>&1 || true - if command -v ovs-vsctl >/dev/null 2>&1; then - ovs-vsctl show > {{ logs_stage_dir }}/status/ovs-show.txt 2>&1 || true - ovs-vsctl list-br > {{ logs_stage_dir }}/status/ovs-br.txt 2>&1 || true - fi - if command -v ovn-nbctl >/dev/null 2>&1; then - ovn-nbctl show > {{ logs_stage_dir }}/status/ovn-nb.txt 2>&1 || true - ovn-sbctl show > {{ logs_stage_dir }}/status/ovn-sb.txt 2>&1 || true - fi - changed_when: true - -- name: logs — kernel ring buffer - become: true - ansible.builtin.shell: | - dmesg --time-format=iso > {{ logs_stage_dir }}/kernel/dmesg.txt 2>&1 || true - changed_when: true - when: logs_include_dmesg | bool - -- name: logs — capture spx + plugin versions - become: true - ansible.builtin.shell: | - { - echo '## spx version' - /usr/local/bin/spx version 2>&1 | grep -vE '^[0-9]{4}/' || true - echo - echo '## nbdkit plugin sha256' - pdir=$(nbdkit --dump-config 2>/dev/null | awk -F= '/^plugindir=/{print $2; exit}') - [ -n "$pdir" ] && sha256sum "$pdir/nbdkit-viperblock-plugin.so" 2>/dev/null || true - echo - echo '## microvm artifacts' - sha256sum /usr/share/spinifex/microvm/* 2>/dev/null || true - echo - echo '## kernel + os' - uname -a - cat /etc/os-release 2>/dev/null || true - } > {{ logs_stage_dir }}/status/versions.txt 2>&1 || true - changed_when: true - -- name: logs — create bundle tarball - become: true - ansible.builtin.shell: | - tar -C {{ logs_stage_dir | dirname | quote }} \ - -czf {{ logs_bundle_path | quote }} \ - {{ logs_stage_dir | basename | quote }} - changed_when: true - -- name: logs — make bundle owned by invoking user - become: true - ansible.builtin.file: - path: "{{ logs_bundle_path }}" - owner: "{{ spinifex_invoking_user }}" - group: "{{ spinifex_invoking_user }}" - mode: "0640" - -- name: logs — remove staging tree - become: true - ansible.builtin.file: - path: "{{ logs_stage_dir }}" - state: absent - -- name: logs — bundle size - ansible.builtin.stat: - path: "{{ logs_bundle_path }}" - register: logs_bundle_stat - -- name: logs — summary - ansible.builtin.debug: - msg: | - Log bundle written: - path : {{ logs_bundle_path }} - size : {{ (logs_bundle_stat.stat.size | int / 1024) | round(1) }} KiB - since : {{ logs_since }} - Contents: - journal/ spinifex-*, ovn-*+openvswitch-switch, qemu/nbdkit - varlog/ copy of {{ spinifex_log_dir }} - status/ systemctl status, ip/ss/ovs/ovn show, versions - kernel/ dmesg{{ '' if logs_include_dmesg else ' (skipped via -e logs_include_dmesg=false)' }} - - Attach to bug report. For live tail use: - journalctl -u 'spinifex-*' -f diff --git a/scripts/ansible/roles/preflight/meta/main.yml b/scripts/ansible/roles/preflight/meta/main.yml deleted file mode 100644 index 192dbf8c5..000000000 --- a/scripts/ansible/roles/preflight/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: preflight - description: Validate dev-host dependencies for Spinifex (parity with dev-setup.sh). - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/preflight/tasks/main.yml b/scripts/ansible/roles/preflight/tasks/main.yml deleted file mode 100644 index a73d705ca..000000000 --- a/scripts/ansible/roles/preflight/tasks/main.yml +++ /dev/null @@ -1,98 +0,0 @@ ---- -- name: Check required commands - ansible.builtin.command: "which {{ item }}" - register: preflight_required - loop: "{{ spinifex_required_commands }}" - changed_when: false - failed_when: preflight_required.rc != 0 - loop_control: - label: "{{ item }}" - -- name: Check optional commands - ansible.builtin.command: "which {{ item }}" - register: preflight_optional - loop: "{{ spinifex_optional_commands }}" - changed_when: false - failed_when: false - loop_control: - label: "{{ item }}" - -- name: Warn on missing optional commands - ansible.builtin.debug: - msg: "WARN: optional command '{{ item.item }}' not found" - loop: "{{ preflight_optional.results }}" - when: item.rc != 0 - loop_control: - label: "{{ item.item }}" - -- name: Check OVN/OVS commands - ansible.builtin.command: "which {{ item }}" - register: preflight_ovn - loop: "{{ spinifex_ovn_commands }}" - changed_when: false - failed_when: false - loop_control: - label: "{{ item }}" - -- name: Warn on missing OVN/OVS tools - ansible.builtin.debug: - msg: "WARN: OVN/OVS tool '{{ item.item }}' not found (required for VPC networking). Install: sudo apt-get install ovn-central ovn-host openvswitch-switch" - loop: "{{ preflight_ovn.results }}" - when: item.rc != 0 - loop_control: - label: "{{ item.item }}" - -- name: Record OVN tool availability - ansible.builtin.set_fact: - preflight_ovs_present: "{{ (preflight_ovn.results | selectattr('item','eq','ovs-vsctl') | selectattr('rc','eq',0) | list | length) > 0 }}" - -- name: Check br-int bridge - ansible.builtin.command: ovs-vsctl br-exists br-int - register: preflight_brint - changed_when: false - failed_when: false - when: preflight_ovs_present - -- name: Warn if br-int missing - ansible.builtin.debug: - msg: "WARN: br-int missing. Run scripts/setup-ovn.sh --management to initialise OVN networking." - when: - - preflight_ovs_present - - preflight_brint.rc | default(0) != 0 - -- name: Check sibling component directories - ansible.builtin.stat: - path: "{{ playbook_dir }}/../../../../{{ item }}/go.mod" - register: preflight_components - loop: "{{ spinifex_components }}" - loop_control: - label: "{{ item }}" - -- name: Warn on missing component checkouts - ansible.builtin.debug: - msg: "WARN: component '{{ item.item }}' not found at expected sibling path (no go.mod). Run scripts/clone-deps.sh." - loop: "{{ preflight_components.results }}" - when: not item.stat.exists - loop_control: - label: "{{ item.item }}" - -- name: Check port availability - ansible.builtin.shell: "ss -tlnH 'sport = :{{ item.port }}' | head -1" - register: preflight_ports - loop: "{{ spinifex_ports }}" - changed_when: false - failed_when: false - loop_control: - label: "{{ item.service }} ({{ item.port }})" - -- name: Warn on occupied ports - ansible.builtin.debug: - msg: "WARN: port {{ item.item.port }} occupied (needed for {{ item.item.service }})" - loop: "{{ preflight_ports.results }}" - when: item.stdout | length > 0 - loop_control: - label: "{{ item.item.service }}" - -- name: Preflight summary - ansible.builtin.debug: - msg: "Preflight passed. Required: {{ spinifex_required_commands | join(', ') }}. Warnings above (if any) are non-fatal." diff --git a/scripts/ansible/roles/restore/meta/main.yml b/scripts/ansible/roles/restore/meta/main.yml deleted file mode 100644 index 30285dc4d..000000000 --- a/scripts/ansible/roles/restore/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: restore - description: Restore a spinifex+ovn+ovs snapshot bundle, refresh CA trust, restart. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/restore/tasks/main.yml b/scripts/ansible/roles/restore/tasks/main.yml deleted file mode 100644 index 862e39a4c..000000000 --- a/scripts/ansible/roles/restore/tasks/main.yml +++ /dev/null @@ -1,140 +0,0 @@ ---- -# Inverse of the snapshot role: stop services, wipe the same path set, -# untar the named bundle, refresh CA trust (because /etc/spinifex/ca.pem -# came from the snapshot but the system trust store didn't), restart. - -- name: restore — require snapshot_name - ansible.builtin.assert: - that: - - snapshot_name is defined - - snapshot_name | length > 0 - - snapshot_name is match('^[A-Za-z0-9._-]+$') - fail_msg: "snapshot_name required (alphanumeric/._-): -e snapshot_name=" - -- name: restore — resolve bundle path - ansible.builtin.set_fact: - snapshot_bundle_path: "{{ snapshot_dir }}/{{ snapshot_name }}.tar.gz" - -- name: restore — assert bundle exists - become: true - ansible.builtin.stat: - path: "{{ snapshot_bundle_path }}" - register: restore_bundle_stat - -- name: restore — fail if missing - ansible.builtin.assert: - that: restore_bundle_stat.stat.exists - fail_msg: | - Snapshot bundle missing: {{ snapshot_bundle_path }}. - Available bundles: - ls -lh {{ snapshot_dir }}/*.tar.gz 2>/dev/null - -- name: restore — stop services in dependency order - become: true - ansible.builtin.systemd: - name: "{{ item }}" - state: stopped - loop: "{{ snapshot_services_stop_order }}" - failed_when: false - -- name: restore — kill lingering QEMU VMs - become: true - ansible.builtin.command: "pkill -x {{ item }}" - loop: "{{ spinifex_qemu_processes }}" - changed_when: false - failed_when: false - -- name: restore — wait for QEMU exit - become: true - ansible.builtin.shell: | - set -e - for i in $(seq 1 {{ teardown_process_wait_seconds }}); do - alive=0 - for proc in {{ spinifex_qemu_processes | join(' ') }}; do - if pgrep -x "$proc" >/dev/null 2>&1; then alive=1; break; fi - done - if [ "$alive" = "0" ]; then exit 0; fi - sleep 1 - done - exit 1 - changed_when: false - -- name: restore — wipe target paths (only those the snapshot will replace) - become: true - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: "{{ snapshot_paths }}" - -- name: restore — recreate parent dirs that tar will refill - become: true - ansible.builtin.file: - path: "{{ item }}" - state: directory - owner: root - group: root - mode: "0755" - loop: - - "{{ ovn_db_dir }}" - - /etc/openvswitch - -- name: restore — extract bundle (preserve permissions/ACLs) - become: true - ansible.builtin.command: - cmd: >- - tar --acls --xattrs -xzpf {{ snapshot_bundle_path | quote }} - -C / - changed_when: true - -- name: restore — refresh spinifex CA in system trust - become: true - ansible.builtin.stat: - path: "{{ spinifex_ca_src }}" - register: restore_ca_stat - -- name: restore — copy CA back to trust path - become: true - ansible.builtin.copy: - src: "{{ spinifex_ca_src }}" - dest: "{{ spinifex_ca_trust_path }}" - mode: "0644" - remote_src: true - when: restore_ca_stat.stat.exists - -- name: restore — rebuild CA bundle - become: true - ansible.builtin.command: update-ca-certificates - changed_when: true - when: restore_ca_stat.stat.exists - -- name: restore — daemon-reload (covers any unit file delta in the snapshot) - become: true - ansible.builtin.systemd: - daemon_reload: true - -- name: restore — restart services in reverse order - become: true - ansible.builtin.systemd: - name: "{{ item }}" - state: started - loop: "{{ snapshot_services_stop_order | reverse | list }}" - failed_when: false - -- name: restore — wait for AWS gateway readiness - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-regions" - register: restore_gw_ready - retries: 30 - delay: 2 - until: restore_gw_ready.rc == 0 - changed_when: false - failed_when: false - -- name: restore — summary - ansible.builtin.debug: - msg: | - Restore complete: - snapshot : {{ snapshot_name }} - bundle : {{ snapshot_bundle_path }} - paths : {{ snapshot_paths | join(', ') }} - CA : {{ 'refreshed' if restore_ca_stat.stat.exists else 'not in bundle — system trust unchanged' }} - gateway : {{ 'ready' if restore_gw_ready.rc == 0 else 'NOT ready after restart — investigate' }} diff --git a/scripts/ansible/roles/smoketest/meta/main.yml b/scripts/ansible/roles/smoketest/meta/main.yml deleted file mode 100644 index cf03cf808..000000000 --- a/scripts/ansible/roles/smoketest/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: smoketest - description: Import SSH key, import Ubuntu AMI, launch one smoke-test instance. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/smoketest/tasks/main.yml b/scripts/ansible/roles/smoketest/tasks/main.yml deleted file mode 100644 index d944bc36a..000000000 --- a/scripts/ansible/roles/smoketest/tasks/main.yml +++ /dev/null @@ -1,197 +0,0 @@ ---- -- name: smoketest — resolve invoking user's home - ansible.builtin.getent: - database: passwd - key: "{{ spinifex_invoking_user }}" - -- name: smoketest — set user home fact - ansible.builtin.set_fact: - smoketest_user_home: "{{ ansible_facts['getent_passwd'][spinifex_invoking_user][4] }}" - -- name: smoketest — generate SSH key if absent - become: true - become_user: "{{ spinifex_invoking_user }}" - ansible.builtin.command: - cmd: ssh-keygen -t ed25519 -f {{ smoketest_user_home }}/.ssh/spinifex-key -N "" - creates: "{{ smoketest_user_home }}/.ssh/spinifex-key" - -- name: smoketest — import SSH key to spinifex - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 import-key-pair - --key-name spinifex-key - --public-key-material fileb://{{ smoketest_user_home }}/.ssh/spinifex-key.pub - register: smoketest_keypair - until: >- - smoketest_keypair.rc == 0 or - 'InvalidKeyPair.Duplicate' in (smoketest_keypair.stderr | default('')) - retries: 10 - delay: 3 - changed_when: "'InvalidKeyPair.Duplicate' not in (smoketest_keypair.stderr | default(''))" - failed_when: - - smoketest_keypair.rc != 0 - - "'InvalidKeyPair.Duplicate' not in (smoketest_keypair.stderr | default(''))" - -- name: smoketest — detect architecture - ansible.builtin.set_fact: - smoketest_arch: "{{ 'arm64' if ansible_facts['architecture'] in ['aarch64','arm64'] else 'x86_64' }}" - -- name: smoketest — resolve local image path - ansible.builtin.set_fact: - smoketest_local_image_path: "{{ smoketest_local_image | expanduser }}" - vars: - smoketest_local_image: "{{ spinifex_smoketest_local_image | replace('~', smoketest_user_home) }}" - -- name: smoketest — check for local image - ansible.builtin.stat: - path: "{{ smoketest_local_image_path }}" - register: smoketest_local_img - -- name: smoketest — import AMI from local file - ansible.builtin.command: >- - {{ spinifex_spx_cli }} admin images import - --file {{ smoketest_local_image_path }} - --distro {{ spinifex_smoketest_distro }} - --version {{ spinifex_smoketest_version }} - --arch {{ smoketest_arch }} - --boot-mode uefi - register: smoketest_import_local - until: >- - smoketest_import_local.rc == 0 or - 'already exists' in (smoketest_import_local.stderr | default('')) - retries: 5 - delay: 3 - changed_when: smoketest_import_local.rc == 0 - failed_when: - - smoketest_import_local.rc != 0 - - "'already exists' not in (smoketest_import_local.stderr | default(''))" - when: smoketest_local_img.stat.exists - -- name: smoketest — import AMI by name (fallback) - ansible.builtin.command: >- - {{ spinifex_spx_cli }} admin images import - --name {{ spinifex_smoketest_distro }}-{{ spinifex_smoketest_version }}-{{ smoketest_arch }} - register: smoketest_import_remote - until: >- - smoketest_import_remote.rc == 0 or - 'already exists' in (smoketest_import_remote.stderr | default('')) - retries: 5 - delay: 3 - changed_when: smoketest_import_remote.rc == 0 - failed_when: - - smoketest_import_remote.rc != 0 - - "'already exists' not in (smoketest_import_remote.stderr | default(''))" - when: not smoketest_local_img.stat.exists - -- name: smoketest — detect CPU vendor for instance type choice - ansible.builtin.command: grep -q AuthenticAMD /proc/cpuinfo - register: smoketest_amd - changed_when: false - failed_when: false - -- name: smoketest — pick instance type - ansible.builtin.set_fact: - smoketest_instance_type: "{{ 't3a.small' if smoketest_amd.rc == 0 else 't3.small' }}" - -- name: smoketest — find AMI ID - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-images --query Images[0].ImageId --output text" - register: smoketest_ami - until: smoketest_ami.rc == 0 - retries: 10 - delay: 3 - changed_when: false - -- name: smoketest — find public subnet (best-effort) - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-subnets - --filters Name=map-public-ip-on-launch,Values=true - --query Subnets[0].SubnetId - --output text - register: smoketest_subnet_public - changed_when: false - failed_when: false - -- name: smoketest — fallback to first subnet (best-effort) - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-subnets --query Subnets[0].SubnetId --output text" - register: smoketest_subnet_any - changed_when: false - failed_when: false - when: - - smoketest_subnet_public.rc != 0 or smoketest_subnet_public.stdout in ['', 'None'] - -- name: smoketest — resolve subnet id (empty string if none) - ansible.builtin.set_fact: - smoketest_subnet_id: >- - {%- if smoketest_subnet_public.rc == 0 and smoketest_subnet_public.stdout not in ['', 'None'] -%} - {{ smoketest_subnet_public.stdout }} - {%- elif (smoketest_subnet_any.rc | default(1)) == 0 and (smoketest_subnet_any.stdout | default('')) not in ['', 'None'] -%} - {{ smoketest_subnet_any.stdout }} - {%- else -%}{%- endif -%} - -- name: smoketest — skip launch if no subnet (fresh install with no user VPC) - ansible.builtin.debug: - msg: | - No subnet found — skipping instance launch. - Services are up, CA is trusted, keypair imported, AMI imported. - Create a VPC+subnet via tofu examples (or `aws ec2 create-vpc` / - `create-subnet`) then re-run if you want a live instance. - when: smoketest_subnet_id | trim == '' - -- name: smoketest — launch instance - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 run-instances - --image-id {{ smoketest_ami.stdout }} - --instance-type {{ smoketest_instance_type }} - --key-name spinifex-key - --subnet-id {{ smoketest_subnet_id | trim }} - --count 1 - --query Instances[0].InstanceId - --output text - register: smoketest_instance - until: smoketest_instance.rc == 0 - retries: 5 - delay: 3 - changed_when: true - when: smoketest_subnet_id | trim != '' - -- name: smoketest — wait for instance running (proves launch path works) - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-instances - --instance-ids {{ smoketest_instance.stdout }} - --query Reservations[0].Instances[0].State.Name - --output text - register: smoketest_instance_state - until: smoketest_instance_state.stdout | trim == 'running' - retries: 30 - delay: 2 - changed_when: false - failed_when: false - when: - - smoketest_subnet_id | trim != '' - - smoketest_instance.stdout is defined - - smoketest_instance.stdout | trim != '' - -- name: smoketest — terminate instance (cleanup) - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 terminate-instances - --instance-ids {{ smoketest_instance.stdout }} - register: smoketest_terminate - until: smoketest_terminate.rc == 0 - retries: 5 - delay: 3 - changed_when: true - failed_when: false - when: - - smoketest_subnet_id | trim != '' - - smoketest_instance.stdout is defined - - smoketest_instance.stdout | trim != '' - -- name: smoketest — summary - ansible.builtin.debug: - msg: | - Smoketest complete: - AMI: {{ smoketest_ami.stdout }} - Subnet: {{ smoketest_subnet_id | trim | default('(none — launch skipped)', true) }} - Instance type: {{ smoketest_instance_type }} - Instance ID: {{ smoketest_instance.stdout | default('(skipped)', true) }} - Final state: {{ smoketest_instance_state.stdout | default('(skipped)', true) | trim }} - Terminated: {{ 'yes' if smoketest_terminate.rc | default(1) == 0 else 'no (launch skipped or terminate failed)' }} diff --git a/scripts/ansible/roles/snapshot/meta/main.yml b/scripts/ansible/roles/snapshot/meta/main.yml deleted file mode 100644 index 2c415c4a1..000000000 --- a/scripts/ansible/roles/snapshot/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: snapshot - description: Quiesce spinifex + ovn + ovs, tar state paths, restart. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/snapshot/tasks/main.yml b/scripts/ansible/roles/snapshot/tasks/main.yml deleted file mode 100644 index 5458c6542..000000000 --- a/scripts/ansible/roles/snapshot/tasks/main.yml +++ /dev/null @@ -1,154 +0,0 @@ ---- -# Snapshot a quiesced spinifex node: stop spinifex.target → ovn → ovs in order, -# tar /etc/spinifex + /var/lib/spinifex + /var/lib/ovn + /etc/openvswitch/conf.db, -# restart in reverse. Lets devs checkpoint a working state before risky ops -# (branch switch, viperblock surgery, ovn-controller restart cycle) and roll -# back via dev-restore without a full ~10-min dev-reset. -# -# Caveat: /var/lib/spinifex contains viperblock volumes — snapshot size scales -# with VM disks. Default gzip; switch to a faster compressor by hand if needed. - -- name: snapshot — require snapshot_name - ansible.builtin.assert: - that: - - snapshot_name is defined - - snapshot_name | length > 0 - - snapshot_name is match('^[A-Za-z0-9._-]+$') - fail_msg: "snapshot_name required (alphanumeric/._-): -e snapshot_name=" - -- name: snapshot — resolve bundle path - ansible.builtin.set_fact: - snapshot_bundle_path: "{{ snapshot_dir }}/{{ snapshot_name }}.tar.gz" - -- name: snapshot — refuse to overwrite existing bundle - ansible.builtin.stat: - path: "{{ snapshot_bundle_path }}" - register: snapshot_existing - -- name: snapshot — assert not already present - ansible.builtin.assert: - that: not snapshot_existing.stat.exists - fail_msg: | - Snapshot {{ snapshot_bundle_path }} already exists. Pick another - name, or `sudo rm` the existing bundle first. - -- name: snapshot — ensure snapshot dir exists - become: true - ansible.builtin.file: - path: "{{ snapshot_dir }}" - state: directory - owner: root - group: root - mode: "0750" - -- name: snapshot — stop services in dependency order - become: true - ansible.builtin.systemd: - name: "{{ item }}" - state: stopped - loop: "{{ snapshot_services_stop_order }}" - failed_when: false - -- name: snapshot — kill lingering QEMU VMs - become: true - ansible.builtin.command: "pkill -x {{ item }}" - loop: "{{ spinifex_qemu_processes }}" - changed_when: false - failed_when: false - -- name: snapshot — wait for QEMU exit (up to {{ teardown_process_wait_seconds }}s) - become: true - ansible.builtin.shell: | - set -e - for i in $(seq 1 {{ teardown_process_wait_seconds }}); do - alive=0 - for proc in {{ spinifex_qemu_processes | join(' ') }}; do - if pgrep -x "$proc" >/dev/null 2>&1; then alive=1; break; fi - done - if [ "$alive" = "0" ]; then exit 0; fi - sleep 1 - done - echo "QEMU still running after {{ teardown_process_wait_seconds }}s" >&2 - exit 1 - changed_when: false - -- name: snapshot — verify which paths actually exist (skip missing silently) - ansible.builtin.stat: - path: "{{ item }}" - register: snapshot_path_stats - loop: "{{ snapshot_paths }}" - loop_control: - label: "{{ item }}" - -- name: snapshot — assemble list of present paths - ansible.builtin.set_fact: - snapshot_present_paths: >- - {{ snapshot_path_stats.results - | selectattr('stat.exists') | map(attribute='item') | list }} - -- name: snapshot — assert at least one path present - ansible.builtin.assert: - that: snapshot_present_paths | length > 0 - fail_msg: | - None of the snapshot paths exist: - {{ snapshot_paths | join(', ') }} - Has spinifex ever been installed on this node? - -- name: snapshot — create bundle (tar czf with absolute paths) - become: true - ansible.builtin.command: - cmd: >- - tar --acls --xattrs -czpf {{ snapshot_bundle_path | quote }} - {{ snapshot_present_paths | map('quote') | join(' ') }} - changed_when: true - -- name: snapshot — record manifest inside the bundle dir for traceability - become: true - ansible.builtin.copy: - dest: "{{ snapshot_dir }}/{{ snapshot_name }}.manifest" - mode: "0640" - content: | - snapshot_name: {{ snapshot_name }} - created_at: {{ ansible_facts['date_time']['iso8601'] }} - hostname: {{ ansible_facts['hostname'] }} - paths: - {% for p in snapshot_present_paths %} - {{ p }} - {% endfor %} - services_quiesced: - {% for s in snapshot_services_stop_order %} - {{ s }} - {% endfor %} - -- name: snapshot — restart services in reverse order - become: true - ansible.builtin.systemd: - name: "{{ item }}" - state: started - loop: "{{ snapshot_services_stop_order | reverse | list }}" - failed_when: false - -- name: snapshot — wait for AWS gateway readiness - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-regions" - register: snapshot_gw_ready - retries: 30 - delay: 2 - until: snapshot_gw_ready.rc == 0 - changed_when: false - failed_when: false - -- name: snapshot — bundle size - become: true - ansible.builtin.stat: - path: "{{ snapshot_bundle_path }}" - register: snapshot_bundle_stat - -- name: snapshot — summary - ansible.builtin.debug: - msg: | - Snapshot complete: - name : {{ snapshot_name }} - path : {{ snapshot_bundle_path }} - size : {{ (snapshot_bundle_stat.stat.size | int / 1024 / 1024) | round(1) }} MiB - paths : {{ snapshot_present_paths | join(', ') }} - gateway : {{ 'ready' if snapshot_gw_ready.rc == 0 else 'NOT ready after restart — investigate' }} - - Restore with: ansible-playbook playbooks/dev-restore.yml -e snapshot_name={{ snapshot_name }} diff --git a/scripts/ansible/roles/status/meta/main.yml b/scripts/ansible/roles/status/meta/main.yml deleted file mode 100644 index a3abdbf04..000000000 --- a/scripts/ansible/roles/status/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: status - description: Read-only health snapshot for a Spinifex dev node (doctor). - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/status/tasks/main.yml b/scripts/ansible/roles/status/tasks/main.yml deleted file mode 100644 index 2e37b03ea..000000000 --- a/scripts/ansible/roles/status/tasks/main.yml +++ /dev/null @@ -1,259 +0,0 @@ ---- -# Read-only health snapshot. NEVER mutates state. Every probe must use -# `changed_when: false` and `failed_when: false` so the role always reaches -# the summary task even when the node is half-broken — surfacing brokenness -# is the whole point. - -- name: status — spinifex.target activation - ansible.builtin.command: systemctl is-active spinifex.target - register: status_target - changed_when: false - failed_when: false - -- name: status — list spinifex-* units (active + failed) - ansible.builtin.shell: | - systemctl list-units 'spinifex-*' --all --no-legend --plain \ - --output=json 2>/dev/null || \ - systemctl list-units 'spinifex-*' --all --no-legend --plain - register: status_units - changed_when: false - failed_when: false - -- name: status — failed spinifex-* units (one line per) - ansible.builtin.shell: | - systemctl list-units 'spinifex-*' --state=failed --no-legend --plain \ - | awk '{print $1}' - register: status_failed_units - changed_when: false - failed_when: false - -- name: status — port listeners (which process holds each spinifex port) - ansible.builtin.shell: | - ss -tlnpH 'sport = :{{ item.port }}' | head -1 - register: status_ports - loop: "{{ spinifex_ports }}" - changed_when: false - failed_when: false - loop_control: - label: "{{ item.service }} ({{ item.port }})" - -- name: status — OVS tooling present? - ansible.builtin.command: which ovs-vsctl - register: status_ovs_which - changed_when: false - failed_when: false - -- name: status — OVS bridges - become: true - ansible.builtin.command: ovs-vsctl list-br - register: status_ovs_brs - changed_when: false - failed_when: false - when: status_ovs_which.rc == 0 - -- name: status — br-int exists? - become: true - ansible.builtin.command: ovs-vsctl br-exists br-int - register: status_brint - changed_when: false - failed_when: false - when: status_ovs_which.rc == 0 - -- name: status — ovn-controller active? - ansible.builtin.command: systemctl is-active ovn-controller - register: status_ovn_ctrl - changed_when: false - failed_when: false - -- name: status — ovn-central active? - ansible.builtin.command: systemctl is-active ovn-central - register: status_ovn_central - changed_when: false - failed_when: false - -- name: status — OVN NB/SB DB files present? - become: true - ansible.builtin.stat: - path: "{{ item }}" - register: status_ovn_dbs - loop: - - "{{ ovn_nb_db }}" - - "{{ ovn_sb_db }}" - loop_control: - label: "{{ item }}" - -- name: status — spinifex.toml present? - become: true - ansible.builtin.stat: - path: "{{ spinifex_config_file }}" - register: status_config - -- name: status — CA trust present? - become: true - ansible.builtin.stat: - path: "{{ spinifex_ca_trust_path }}" - register: status_ca - -- name: status — AWS gateway round-trip (describe-regions) - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-regions --output text" - register: status_awsgw - changed_when: false - failed_when: false - -- name: status — installed spx binary - become: true - ansible.builtin.stat: - path: /usr/local/bin/spx - checksum_algorithm: sha256 - register: status_spx_installed - -- name: status — built spx binary - ansible.builtin.stat: - path: "{{ spinifex_root }}/bin/spx" - checksum_algorithm: sha256 - register: status_spx_built - -- name: status — spx version (installed) - ansible.builtin.shell: | - /usr/local/bin/spx version 2>/dev/null | grep -vE '^[0-9]{4}/' | head -1 - register: status_spx_version - changed_when: false - failed_when: false - when: status_spx_installed.stat.exists - -- name: status — installed nbdkit plugin - become: true - ansible.builtin.shell: | - set -e - pdir=$(nbdkit --dump-config | awk -F= '/^plugindir=/{print $2; exit}') - [ -f "$pdir/nbdkit-viperblock-plugin.so" ] && \ - sha256sum "$pdir/nbdkit-viperblock-plugin.so" | awk '{print $1}' - register: status_plugin_installed_sha - changed_when: false - failed_when: false - -- name: status — built nbdkit plugin - ansible.builtin.stat: - path: "{{ mulga_root }}/viperblock/lib/nbdkit-viperblock-plugin.so" - checksum_algorithm: sha256 - register: status_plugin_built - -- name: status — microvm artifacts (built) - ansible.builtin.stat: - path: "{{ spinifex_root }}/build/microvm/{{ item }}" - checksum_algorithm: sha256 - register: status_microvm_built - loop: - - vmlinuz - - initramfs.cpio.gz - -- name: status — microvm artifacts (installed) - become: true - ansible.builtin.stat: - path: /usr/share/spinifex/microvm/{{ item }} - checksum_algorithm: sha256 - register: status_microvm_installed - loop: - - vmlinuz - - initramfs.cpio.gz - -- name: status — running QEMU VMs - ansible.builtin.shell: pgrep -fa 'qemu-system-' | wc -l - register: status_qemu_count - changed_when: false - failed_when: false - -- name: status — count AMIs - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-images --query length(Images) --output text" - register: status_ami_count - changed_when: false - failed_when: false - -- name: status — count keypairs - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-key-pairs --query length(KeyPairs) --output text" - register: status_key_count - changed_when: false - failed_when: false - -- name: status — count volumes - ansible.builtin.command: "{{ spinifex_aws_cli }} ec2 describe-volumes --query length(Volumes) --output text" - register: status_vol_count - changed_when: false - failed_when: false - -- name: status — count running instances - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-instances - --filters Name=instance-state-name,Values=running - --query length(Reservations[].Instances[]) --output text - register: status_instance_count - changed_when: false - failed_when: false - -- name: status — compute drift facts - ansible.builtin.set_fact: - status_spx_drift: >- - {{ status_spx_installed.stat.exists and status_spx_built.stat.exists and - (status_spx_installed.stat.checksum | default('')) != - (status_spx_built.stat.checksum | default('')) }} - status_plugin_drift: >- - {{ status_plugin_built.stat.exists and - (status_plugin_installed_sha.stdout | default('') | trim) != - (status_plugin_built.stat.checksum | default('')) }} - status_microvm_built_present: "{{ status_microvm_built.results - | map(attribute='stat.exists') | min }}" - -- name: status — render port lines - ansible.builtin.set_fact: - status_port_lines: >- - {{ status_port_lines | default([]) + - [('%-24s' | format(item.item.service ~ ' (' ~ item.item.port ~ ')')) - ~ ': ' - ~ ('listening' if (item.stdout | length) > 0 else 'free')] }} - loop: "{{ status_ports.results }}" - loop_control: - label: "{{ item.item.service }}" - -- name: status — assemble summary - ansible.builtin.set_fact: - status_summary_lines: >- - {{ - [ - '── Spinifex dev-node status ──────────────────────────────────────────────', - 'target : ' ~ (status_target.stdout | trim), - 'failed units : ' ~ (status_failed_units.stdout_lines | length) - ~ ((' → ' ~ (status_failed_units.stdout_lines | join(', '))) if (status_failed_units.stdout_lines | length) > 0 else ''), - '── Ports ──' - ] - + status_port_lines - + [ - '── Networking ──', - 'ovs-vsctl : ' ~ ('present' if status_ovs_which.rc == 0 else 'MISSING'), - 'ovs bridges : ' ~ ((status_ovs_brs.stdout_lines | default([]) | join(', ')) or '(none)'), - 'br-int : ' ~ ('present' if (status_brint.rc | default(1)) == 0 else 'MISSING'), - 'ovn-controller : ' ~ (status_ovn_ctrl.stdout | trim), - 'ovn-central : ' ~ (status_ovn_central.stdout | trim), - 'ovn NB DB : ' ~ ('present' if status_ovn_dbs.results[0].stat.exists else 'absent'), - 'ovn SB DB : ' ~ ('present' if status_ovn_dbs.results[1].stat.exists else 'absent'), - '── Config / trust ──', - 'spinifex.toml : ' ~ ('present' if status_config.stat.exists else 'MISSING') ~ ' (' ~ spinifex_config_file ~ ')', - 'CA trust : ' ~ ('present' if status_ca.stat.exists else 'MISSING') ~ ' (' ~ spinifex_ca_trust_path ~ ')', - '── AWS gateway ──', - 'describe-regions : ' ~ ('OK' if status_awsgw.rc == 0 else 'FAIL (rc=' ~ status_awsgw.rc ~ ')'), - 'AMIs : ' ~ (status_ami_count.stdout | default('?') | trim), - 'keypairs : ' ~ (status_key_count.stdout | default('?') | trim), - 'volumes : ' ~ (status_vol_count.stdout | default('?') | trim), - 'running instances: ' ~ (status_instance_count.stdout | default('?') | trim), - 'running QEMU : ' ~ (status_qemu_count.stdout | default('?') | trim), - '── Versions / drift ──', - 'spx (installed) : ' ~ ((status_spx_version.stdout | default('') | trim) or '(not installed)'), - 'spx drift : ' ~ ('YES (built ≠ installed) — run ansible-dev-deploy' if status_spx_drift else 'no'), - 'plugin drift : ' ~ ('YES (built ≠ installed) — run ansible-dev-deploy' if status_plugin_drift else 'no'), - 'microvm built : ' ~ ('present' if status_microvm_built_present else 'not built on this branch'), - '──────────────────────────────────────────────────────────────────────────' - ] - }} - -- name: status — summary - ansible.builtin.debug: - msg: "{{ status_summary_lines }}" diff --git a/scripts/ansible/roles/teardown/README.md b/scripts/ansible/roles/teardown/README.md deleted file mode 100644 index dd2f1a46f..000000000 --- a/scripts/ansible/roles/teardown/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# teardown — Leak Catalog - -Every task in this role corresponds to a known state source that survived a -previous reset. When the operator reports "reset didn't clean X", add a new -task and a new row here citing the symptom. - -## Initial catalog (from reset-dev-env.sh + reset-dev-env.md target) - -| State source | Cleaned by | Symptom if leaked | -|---|---|---| -| `spinifex.target` active | `systemctl stop` | Services keep running, file locks held | -| `spinifex-*` units in failed state | `systemctl reset-failed 'spinifex-*'` | `systemctl status` shows red units, next start refuses | -| Spinifex processes (spx, nats, predastore, etc.) | `pkill` pattern + wait loop | Stale listeners on 4222/8443/9999, start fails with "address in use" | -| QEMU VMs | `pkill qemu-system-*` + wait loop | Zombie VMs hold tap interfaces and viperblock state | -| `/etc/spinifex/` | `file: state=absent` | Stale TOML, stale CA, service users can't read fresh config | -| `/var/lib/spinifex/` | `file: state=absent` | Stale WAL, stale volumes, viperblock mounts fail | -| `/var/log/spinifex/` | `file: state=absent` | Logrotate confusion, permission drift after user changes | -| `/run/spinifex/` | `file: state=absent` | Stale sockets, pidfiles from prior systemd run | -| `$HOME/spinifex/` (legacy) | `file: state=absent` | Old dev-mode state shadowing new install | -| OVS bridges (all) | `ovs-vsctl del-br` loop | `br-int` populated with stale ports → `ovn-controller` commit loop | -| OVS `external_ids` | `ovs-vsctl clear Open_vSwitch .` | Stale chassis entries in SBDB | -| `/var/lib/ovn/ovnnb_db.db`, `ovnsb_db.db` | `file: state=absent` | "OVNSB commit failed, force recompute" loop after reset | -| `veth-wan-br` / `veth-wan-ovs` | `ip link delete` | Pair left dangling after setup-ovn.sh re-run | -| `spx-ext-*` macvlan interfaces | `ip link delete` loop | Stale macvlan on WAN NIC, packet duplication | -| Spinifex CA in trust store | remove + `update-ca-certificates` | TLS clients trust old CA, new cert fails verification | - -## Not cleaned by default (opt-in) - -| State source | Flag | Why opt-in | -|---|---|---| -| `~/.ssh/spinifex-key*` | `-e spinifex_wipe_ssh_keys=true` | Operator may want key to persist across resets | -| `~/.aws/credentials` spinifex profile | `-e spinifex_wipe_aws_creds=true` | `spx admin init --force` rewrites this block anyway; wipe only on demand | - -## Known traps - -- **`pgrep -f` self-match.** A literal pattern that matches the pattern - string itself causes `pgrep -f` to match our own shell. Mitigation: - bracket trick (`[b]in/spx` matches `bin/spx` but not `[b]in/spx`). -- **Log followers / ansible wrapper false positives.** `journalctl -f - _SYSTEMD_UNIT=spinifex-*` and ansible's own module shell matches the - service-name patterns in their cmdlines. Mitigation: - `spinifex_process_exclude` filter drops `journalctl|ansible|/bin/sh|pgrep|pkill|tail -[fF]`. -- **SIGTERM survivors.** `spx service spinifex start` (and potentially - other service-supervisor processes) ignore or delay SIGTERM. Mitigation: - kill task escalates SIGTERM → SIGKILL after 5s. - -## Adding a new entry - -1. Add a task to `tasks/main.yml` in the right section (services / state-dirs / - ovn / ovs / netlink / trust / user-owned). -2. Prefix the task `name:` with the state class (e.g. `netlink:`, `ovn:`). -3. Add a row to the catalog above — symptom is what the operator described. -4. If the leak class is general (e.g. "any interface matching spx-*"), - generalise the task; do not pattern-match on a single instance name. diff --git a/scripts/ansible/roles/teardown/meta/main.yml b/scripts/ansible/roles/teardown/meta/main.yml deleted file mode 100644 index f87f28d85..000000000 --- a/scripts/ansible/roles/teardown/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: teardown - description: Wipe Spinifex dev state. Central leak-enumeration surface — every state location cleaned here is a known leak source. See README.md for catalog. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/teardown/tasks/main.yml b/scripts/ansible/roles/teardown/tasks/main.yml deleted file mode 100644 index f3fcea4d6..000000000 --- a/scripts/ansible/roles/teardown/tasks/main.yml +++ /dev/null @@ -1,242 +0,0 @@ ---- -- name: guard — detect node count in spinifex.toml - ansible.builtin.shell: | - set -e - if [ -f {{ spinifex_config_file | quote }} ]; then - grep -cE '^\[nodes\.[^.]+\]' {{ spinifex_config_file | quote }} || echo 0 - else - echo 0 - fi - register: teardown_node_count - changed_when: false - -- name: guard — refuse to run on multi-node cluster - ansible.builtin.assert: - that: teardown_node_count.stdout | int <= 1 - fail_msg: >- - Multi-node cluster detected ({{ teardown_node_count.stdout }} nodes in - {{ spinifex_config_file }}). Teardown supports single-node only; - use 'spx admin cluster shutdown' per node. - -- name: services — stop spinifex.target - become: true - ansible.builtin.systemd: - name: spinifex.target - state: stopped - failed_when: false - -- name: services — reset failed state (spinifex-* scope only) - become: true - ansible.builtin.command: systemctl reset-failed 'spinifex-*' - changed_when: false - failed_when: false - -- name: processes — kill spinifex processes (SIGTERM, escalate to SIGKILL after 5s) - become: true - ansible.builtin.shell: | - pids=$(pgrep -af "{{ spinifex_process_pattern }}" \ - | grep -vE "{{ spinifex_process_exclude }}" \ - | awk '{print $1}') - if [ -n "$pids" ]; then - kill $pids 2>/dev/null || true - sleep 5 - pids=$(pgrep -af "{{ spinifex_process_pattern }}" \ - | grep -vE "{{ spinifex_process_exclude }}" \ - | awk '{print $1}') - if [ -n "$pids" ]; then - kill -9 $pids 2>/dev/null || true - fi - fi - changed_when: false - failed_when: false - -- name: processes — wait for exit (up to {{ teardown_process_wait_seconds }}s) - become: true - ansible.builtin.shell: | - for i in $(seq 1 {{ teardown_process_wait_seconds }}); do - pids=$(pgrep -af "{{ spinifex_process_pattern }}" \ - | grep -vE "{{ spinifex_process_exclude }}" \ - | awk '{print $1}') - if [ -z "$pids" ]; then - exit 0 - fi - sleep 1 - done - echo "still running after {{ teardown_process_wait_seconds }}s:" >&2 - pgrep -af "{{ spinifex_process_pattern }}" \ - | grep -vE "{{ spinifex_process_exclude }}" >&2 || true - exit 1 - changed_when: false - -- name: qemu — kill lingering VMs - become: true - ansible.builtin.command: "pkill -x {{ item }}" - loop: "{{ spinifex_qemu_processes }}" - changed_when: false - failed_when: false - -- name: qemu — wait for exit (up to {{ teardown_process_wait_seconds }}s) - become: true - ansible.builtin.shell: | - set -e - for i in $(seq 1 {{ teardown_process_wait_seconds }}); do - alive=0 - for proc in {{ spinifex_qemu_processes | join(' ') }}; do - if pgrep -x "$proc" >/dev/null 2>&1; then alive=1; break; fi - done - if [ "$alive" = "0" ]; then exit 0; fi - sleep 1 - done - echo "QEMU still running after {{ teardown_process_wait_seconds }}s" >&2 - exit 1 - changed_when: false - -- name: state-dirs — remove production dirs - become: true - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - "{{ spinifex_etc_dir }}" - - "{{ spinifex_data_dir }}" - - "{{ spinifex_log_dir }}" - - "{{ spinifex_run_dir }}" - -- name: state-dirs — remove legacy $HOME/spinifex (opt-in) - become: true - become_user: "{{ spinifex_invoking_user }}" - ansible.builtin.file: - path: "{{ spinifex_legacy_home_dir }}" - state: absent - when: spinifex_wipe_legacy_home | bool - -- name: ovs — detect tooling - ansible.builtin.command: which ovs-vsctl - register: teardown_ovs_which - changed_when: false - failed_when: false - -- name: ovs — ensure openvswitch-switch is running for cleanup - become: true - ansible.builtin.systemd: - name: openvswitch-switch - state: started - when: teardown_ovs_which.rc == 0 - failed_when: false - -- name: ovs — list bridges - become: true - ansible.builtin.command: ovs-vsctl list-br - register: teardown_ovs_brs - changed_when: false - failed_when: false - when: teardown_ovs_which.rc == 0 - -- name: ovs — delete all bridges - become: true - ansible.builtin.command: "ovs-vsctl --if-exists del-br {{ item }}" - loop: "{{ teardown_ovs_brs.stdout_lines | default([]) }}" - changed_when: true - when: teardown_ovs_which.rc == 0 - -- name: ovs — clear external_ids - become: true - ansible.builtin.command: ovs-vsctl --if-exists clear Open_vSwitch . external_ids - changed_when: true - failed_when: false - when: teardown_ovs_which.rc == 0 - -- name: ovs — stop openvswitch-switch after cleanup - become: true - ansible.builtin.systemd: - name: openvswitch-switch - state: stopped - when: teardown_ovs_which.rc == 0 - failed_when: false - -- name: ovn — stop ovn-central - become: true - ansible.builtin.systemd: - name: ovn-central - state: stopped - failed_when: false - -- name: ovn — stop ovn-controller - become: true - ansible.builtin.systemd: - name: ovn-controller - state: stopped - failed_when: false - -- name: ovn — delete NB/SB DB files - become: true - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - "{{ ovn_nb_db }}" - - "{{ ovn_sb_db }}" - -- name: netlink — enumerate spinifex-managed veth/tap - ansible.builtin.shell: | - ip -o link show | awk -F': ' '{print $2}' | cut -d'@' -f1 | \ - grep -E '^(veth-wan-br|veth-wan-ovs|spx-ext-|spx-tap-|spx-)' || true - register: teardown_links - changed_when: false - -- name: netlink — delete enumerated interfaces - become: true - ansible.builtin.command: "ip link delete {{ item }}" - loop: "{{ teardown_links.stdout_lines }}" - changed_when: true - failed_when: false - -- name: trust — remove spinifex CA from system store - become: true - ansible.builtin.file: - path: "{{ spinifex_ca_trust_path }}" - state: absent - register: teardown_ca_removed - -- name: trust — rebuild CA bundle (without --fresh, preserve other CAs) - become: true - ansible.builtin.command: update-ca-certificates - changed_when: true - when: teardown_ca_removed.changed - -- name: user-owned — remove spinifex profile from AWS credentials (opt-in) - become: true - become_user: "{{ spinifex_invoking_user }}" - ansible.builtin.blockinfile: - path: "~/.aws/credentials" - marker: "# {mark} SPINIFEX MANAGED BLOCK" - block: "" - state: absent - when: spinifex_wipe_aws_creds | bool - failed_when: false - -- name: user-owned — remove spinifex SSH key (opt-in) - become: true - become_user: "{{ spinifex_invoking_user }}" - ansible.builtin.file: - path: "~/.ssh/{{ item }}" - state: absent - loop: - - spinifex-key - - spinifex-key.pub - when: spinifex_wipe_ssh_keys | bool - -- name: summary - ansible.builtin.debug: - msg: | - Teardown complete. Catalog: roles/teardown/README.md - systemd : spinifex.target stopped, spinifex-* failed cleared - procs : {{ spinifex_process_pattern }} - qemu : {{ spinifex_qemu_processes | join(', ') }} - dirs : {{ spinifex_etc_dir }}, {{ spinifex_data_dir }}, {{ spinifex_log_dir }}, {{ spinifex_run_dir }}{{ ', ' ~ spinifex_legacy_home_dir if spinifex_wipe_legacy_home else '' }} - ovs : all bridges deleted, external_ids cleared - ovn : NB/SB DB files removed - netlink : {{ teardown_links.stdout_lines | length }} interface(s) removed - trust : {{ spinifex_ca_trust_path }} removed{% if spinifex_wipe_aws_creds %} - aws : credentials block removed{% endif %}{% if spinifex_wipe_ssh_keys %} - ssh : spinifex-key pair removed{% endif %} diff --git a/scripts/ansible/roles/version/meta/main.yml b/scripts/ansible/roles/version/meta/main.yml deleted file mode 100644 index 780fdbf08..000000000 --- a/scripts/ansible/roles/version/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: version - description: Detailed build-vs-install drift report for spinifex artifacts. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/version/tasks/main.yml b/scripts/ansible/roles/version/tasks/main.yml deleted file mode 100644 index 3c794c816..000000000 --- a/scripts/ansible/roles/version/tasks/main.yml +++ /dev/null @@ -1,161 +0,0 @@ ---- -# Detailed build-vs-install drift report for the four artifacts that -# `ansible-dev-deploy` swaps: spx, the nbdkit viperblock plugin, and the -# two microvm artifacts (kernel + initramfs). Pure read-only; faster -# than dev-status (no AWS / OVN / port probing). - -- name: version — built spx - ansible.builtin.stat: - path: "{{ spinifex_root }}/bin/spx" - checksum_algorithm: sha256 - register: ver_spx_built - -- name: version — installed spx - become: true - ansible.builtin.stat: - path: /usr/local/bin/spx - checksum_algorithm: sha256 - register: ver_spx_installed - -- name: version — installed spx version string - ansible.builtin.shell: | - /usr/local/bin/spx version 2>/dev/null | grep -vE '^[0-9]{4}/' | head -1 - register: ver_spx_version - changed_when: false - failed_when: false - when: ver_spx_installed.stat.exists - -- name: version — built plugin - ansible.builtin.stat: - path: "{{ mulga_root }}/viperblock/lib/nbdkit-viperblock-plugin.so" - checksum_algorithm: sha256 - register: ver_plugin_built - -- name: version — nbdkit plugin dir - ansible.builtin.shell: | - nbdkit --dump-config 2>/dev/null | awk -F= '/^plugindir=/{print $2; exit}' - register: ver_plugindir - changed_when: false - failed_when: false - -- name: version — installed plugin - become: true - ansible.builtin.stat: - path: "{{ ver_plugindir.stdout | trim }}/nbdkit-viperblock-plugin.so" - checksum_algorithm: sha256 - register: ver_plugin_installed - when: ver_plugindir.stdout | trim | length > 0 - -- name: version — built microvm artifacts - ansible.builtin.stat: - path: "{{ spinifex_root }}/build/microvm/{{ item }}" - checksum_algorithm: sha256 - register: ver_microvm_built - loop: - - vmlinuz - - initramfs.cpio.gz - -- name: version — installed microvm artifacts - become: true - ansible.builtin.stat: - path: /usr/share/spinifex/microvm/{{ item }} - checksum_algorithm: sha256 - register: ver_microvm_installed - loop: - - vmlinuz - - initramfs.cpio.gz - -- name: version — sub-repo HEAD short-rev (spinifex) - ansible.builtin.command: git -C {{ spinifex_root | quote }} rev-parse --short HEAD - register: ver_spinifex_head - changed_when: false - failed_when: false - -- name: version — sub-repo HEAD short-rev (viperblock) - ansible.builtin.command: git -C {{ mulga_root }}/viperblock rev-parse --short HEAD - register: ver_viperblock_head - changed_when: false - failed_when: false - -- name: version — sub-repo HEAD short-rev (predastore) - ansible.builtin.command: git -C {{ mulga_root }}/predastore rev-parse --short HEAD - register: ver_predastore_head - changed_when: false - failed_when: false - -- name: version — assemble per-artifact rows - ansible.builtin.set_fact: - ver_artifact_rows: >- - {{ - [ - ('%-18s built=%s installed=%s drift=%s' | format( - 'spx', - (ver_spx_built.stat.checksum | default('-') | truncate(13, true, '')) - if ver_spx_built.stat.exists else 'MISSING', - (ver_spx_installed.stat.checksum | default('-') | truncate(13, true, '')) - if ver_spx_installed.stat.exists else 'MISSING', - ('YES' if ver_spx_built.stat.exists and ver_spx_installed.stat.exists - and ver_spx_built.stat.checksum != ver_spx_installed.stat.checksum - else 'no') - )), - ('%-18s built=%s installed=%s drift=%s' | format( - 'plugin (nbdkit)', - (ver_plugin_built.stat.checksum | default('-') | truncate(13, true, '')) - if ver_plugin_built.stat.exists else 'MISSING', - (ver_plugin_installed.stat.checksum | default('-') | truncate(13, true, '')) - if (ver_plugin_installed.stat | default({})).get('exists', false) else 'MISSING', - ('YES' if ver_plugin_built.stat.exists - and (ver_plugin_installed.stat | default({})).get('exists', false) - and ver_plugin_built.stat.checksum != ver_plugin_installed.stat.checksum - else 'no') - )), - ('%-18s built=%s installed=%s drift=%s' | format( - 'microvm vmlinuz', - (ver_microvm_built.results[0].stat.checksum | default('-') | truncate(13, true, '')) - if ver_microvm_built.results[0].stat.exists else 'MISSING', - (ver_microvm_installed.results[0].stat.checksum | default('-') | truncate(13, true, '')) - if ver_microvm_installed.results[0].stat.exists else 'MISSING', - ('YES' if ver_microvm_built.results[0].stat.exists - and ver_microvm_installed.results[0].stat.exists - and ver_microvm_built.results[0].stat.checksum - != ver_microvm_installed.results[0].stat.checksum - else 'no') - )), - ('%-18s built=%s installed=%s drift=%s' | format( - 'microvm initramfs', - (ver_microvm_built.results[1].stat.checksum | default('-') | truncate(13, true, '')) - if ver_microvm_built.results[1].stat.exists else 'MISSING', - (ver_microvm_installed.results[1].stat.checksum | default('-') | truncate(13, true, '')) - if ver_microvm_installed.results[1].stat.exists else 'MISSING', - ('YES' if ver_microvm_built.results[1].stat.exists - and ver_microvm_installed.results[1].stat.exists - and ver_microvm_built.results[1].stat.checksum - != ver_microvm_installed.results[1].stat.checksum - else 'no') - )) - ] - }} - -- name: version — compute any-drift flag - ansible.builtin.set_fact: - ver_any_drift: "{{ 'YES' in (ver_artifact_rows | join(' ')) }}" - -- name: version — summary - ansible.builtin.debug: - msg: >- - {{ - [ - '── Spinifex build vs install ─────────────────────────────────────────────', - 'spx (installed): ' ~ ((ver_spx_version.stdout | default('') | trim) or '(not installed)') - ] - + ver_artifact_rows - + [ - '── Sub-repo HEADs ──', - 'spinifex : ' ~ ((ver_spinifex_head.stdout | default('') | trim) or '(not a git repo)'), - 'viperblock : ' ~ ((ver_viperblock_head.stdout | default('') | trim) or '(missing)'), - 'predastore : ' ~ ((ver_predastore_head.stdout | default('') | trim) or '(missing)'), - '──────────────────────────────────────────────────────────────────────────', - ('DRIFT detected — run: make ansible-dev-deploy' if ver_any_drift - else 'No drift; built = installed for all artifacts.') - ] - }} diff --git a/scripts/ansible/roles/vpc/meta/main.yml b/scripts/ansible/roles/vpc/meta/main.yml deleted file mode 100644 index cc90f9d83..000000000 --- a/scripts/ansible/roles/vpc/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -galaxy_info: - role_name: vpc - description: Idempotent dev-mode VPC + public subnet + IGW + default route fixture. - license: BSD-3-Clause - min_ansible_version: "2.14" -dependencies: [] diff --git a/scripts/ansible/roles/vpc/tasks/absent.yml b/scripts/ansible/roles/vpc/tasks/absent.yml deleted file mode 100644 index 2f72e21fd..000000000 --- a/scripts/ansible/roles/vpc/tasks/absent.yml +++ /dev/null @@ -1,128 +0,0 @@ ---- -# Tear down the dev VPC fixture. Deletes in reverse dependency order: -# route-table associations, route table, subnet, IGW (detach + delete), -# VPC. Each step is best-effort so a partially-converged fixture still -# converges to "gone" on re-run. - -- name: vpc — find VPC by Name tag - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-vpcs - --filters Name=tag:Name,Values={{ dev_vpc_name }} - --query Vpcs[0].VpcId --output text - register: absent_vpc - changed_when: false - -- name: vpc — nothing to do - ansible.builtin.debug: - msg: "VPC tagged Name={{ dev_vpc_name }} not found; nothing to delete." - when: absent_vpc.stdout | trim in ['', 'None'] - -- name: vpc — short-circuit when no VPC - ansible.builtin.meta: end_play - when: absent_vpc.stdout | trim in ['', 'None'] - -- name: vpc — set VPC id fact - ansible.builtin.set_fact: - absent_vpc_id: "{{ absent_vpc.stdout | trim }}" - -- name: vpc — find subnet - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-subnets - --filters Name=vpc-id,Values={{ absent_vpc_id }} - Name=tag:Name,Values={{ dev_vpc_name }}-public - --query Subnets[0].SubnetId --output text - register: absent_subnet - changed_when: false - -# Enumerate ALL non-main route tables in the VPC (main RT can't be -# deleted; it goes when the VPC does). Catches orphans from earlier -# runs that landed before the create-tags workaround. -- name: vpc — find all non-main route tables in VPC - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-route-tables - --filters Name=vpc-id,Values={{ absent_vpc_id }} - --query RouteTables[?!(Associations[?Main==`true`])].RouteTableId - --output text - register: absent_rt_list - changed_when: false - -- name: vpc — split RT id list - ansible.builtin.set_fact: - absent_rt_ids: "{{ absent_rt_list.stdout.split() | reject('equalto', '') | list }}" - -- name: vpc — find all subnet/RT associations to disassociate - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-route-tables - --route-table-ids {{ item }} - --query RouteTables[0].Associations[?SubnetId!=null].RouteTableAssociationId - --output text - register: absent_assocs - loop: "{{ absent_rt_ids }}" - changed_when: false - failed_when: false - -- name: vpc — disassociate subnets from route tables - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 disassociate-route-table - --association-id {{ item }} - loop: "{{ absent_assocs.results - | map(attribute='stdout') - | map('default', '') - | map('split') - | flatten - | reject('equalto', '') - | list }}" - changed_when: true - failed_when: false - -- name: vpc — delete all non-main route tables - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 delete-route-table - --route-table-id {{ item }} - loop: "{{ absent_rt_ids }}" - changed_when: true - failed_when: false - -- name: vpc — delete subnet - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 delete-subnet - --subnet-id {{ absent_subnet.stdout | trim }} - when: absent_subnet.stdout | trim not in ['', 'None'] - changed_when: true - failed_when: false - -- name: vpc — find IGW - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-internet-gateways - --filters Name=attachment.vpc-id,Values={{ absent_vpc_id }} - --query InternetGateways[0].InternetGatewayId --output text - register: absent_igw - changed_when: false - -- name: vpc — detach IGW - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 detach-internet-gateway - --internet-gateway-id {{ absent_igw.stdout | trim }} - --vpc-id {{ absent_vpc_id }} - when: absent_igw.stdout | trim not in ['', 'None'] - changed_when: true - failed_when: false - -- name: vpc — delete IGW - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 delete-internet-gateway - --internet-gateway-id {{ absent_igw.stdout | trim }} - when: absent_igw.stdout | trim not in ['', 'None'] - changed_when: true - failed_when: false - -- name: vpc — delete VPC - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 delete-vpc - --vpc-id {{ absent_vpc_id }} - changed_when: true - failed_when: false - -- name: vpc — summary (absent) - ansible.builtin.debug: - msg: "Dev VPC {{ dev_vpc_name }} (was {{ absent_vpc_id }}) torn down." diff --git a/scripts/ansible/roles/vpc/tasks/main.yml b/scripts/ansible/roles/vpc/tasks/main.yml deleted file mode 100644 index 5258f3bd4..000000000 --- a/scripts/ansible/roles/vpc/tasks/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -- name: vpc — dispatch by state - ansible.builtin.include_tasks: "{{ dev_vpc_state }}.yml" diff --git a/scripts/ansible/roles/vpc/tasks/present.yml b/scripts/ansible/roles/vpc/tasks/present.yml deleted file mode 100644 index 275c7def9..000000000 --- a/scripts/ansible/roles/vpc/tasks/present.yml +++ /dev/null @@ -1,209 +0,0 @@ ---- -# Idempotent create. Every step describes-then-creates: re-running on an -# already-converged VPC produces zero changes. Tag everything with -# Name={{ dev_vpc_name }} so a single tag query finds the whole graph. - -- name: vpc — find existing VPC by Name tag - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-vpcs - --filters Name=tag:Name,Values={{ dev_vpc_name }} - --query Vpcs[0].VpcId --output text - register: vpc_existing - changed_when: false - -- name: vpc — create VPC if missing - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 create-vpc - --cidr-block {{ dev_vpc_cidr }} - --tag-specifications - ResourceType=vpc,Tags=[{Key=Name,Value={{ dev_vpc_name }}}] - --query Vpc.VpcId --output text - register: vpc_created - when: vpc_existing.stdout | trim in ['', 'None'] - changed_when: true - -- name: vpc — set VPC id fact - ansible.builtin.set_fact: - dev_vpc_id: >- - {{ vpc_created.stdout | default('') | trim - if (vpc_existing.stdout | trim in ['', 'None']) - else vpc_existing.stdout | trim }} - -- name: vpc — assert VPC id resolved - ansible.builtin.assert: - that: dev_vpc_id | length > 0 - fail_msg: "VPC id could not be resolved after describe/create" - -- name: vpc — find existing IGW attached to this VPC - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-internet-gateways - --filters Name=attachment.vpc-id,Values={{ dev_vpc_id }} - --query InternetGateways[0].InternetGatewayId --output text - register: vpc_igw_existing - changed_when: false - -- name: vpc — create IGW if missing - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 create-internet-gateway - --tag-specifications - ResourceType=internet-gateway,Tags=[{Key=Name,Value={{ dev_vpc_name }}-igw}] - --query InternetGateway.InternetGatewayId --output text - register: vpc_igw_created - when: vpc_igw_existing.stdout | trim in ['', 'None'] - changed_when: true - -- name: vpc — set IGW id fact - ansible.builtin.set_fact: - dev_vpc_igw_id: >- - {{ vpc_igw_created.stdout | default('') | trim - if (vpc_igw_existing.stdout | trim in ['', 'None']) - else vpc_igw_existing.stdout | trim }} - -- name: vpc — attach IGW to VPC if needed - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 attach-internet-gateway - --internet-gateway-id {{ dev_vpc_igw_id }} - --vpc-id {{ dev_vpc_id }} - register: vpc_igw_attach - when: vpc_igw_existing.stdout | trim in ['', 'None'] - changed_when: true - failed_when: - - vpc_igw_attach.rc | default(0) != 0 - - "'Resource.AlreadyAssociated' not in (vpc_igw_attach.stderr | default(''))" - -- name: vpc — find existing subnet by Name tag - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-subnets - --filters Name=vpc-id,Values={{ dev_vpc_id }} - Name=tag:Name,Values={{ dev_vpc_name }}-public - --query Subnets[0].SubnetId --output text - register: vpc_subnet_existing - changed_when: false - -- name: vpc — create subnet if missing - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 create-subnet - --vpc-id {{ dev_vpc_id }} - --cidr-block {{ dev_vpc_subnet_cidr }} - --availability-zone {{ dev_vpc_subnet_az }} - --tag-specifications - ResourceType=subnet,Tags=[{Key=Name,Value={{ dev_vpc_name }}-public}] - --query Subnet.SubnetId --output text - register: vpc_subnet_created - when: vpc_subnet_existing.stdout | trim in ['', 'None'] - changed_when: true - -- name: vpc — set subnet id fact - ansible.builtin.set_fact: - dev_vpc_subnet_id: >- - {{ vpc_subnet_created.stdout | default('') | trim - if (vpc_subnet_existing.stdout | trim in ['', 'None']) - else vpc_subnet_existing.stdout | trim }} - -- name: vpc — ensure subnet has map-public-ip-on-launch - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 modify-subnet-attribute - --subnet-id {{ dev_vpc_subnet_id }} - --map-public-ip-on-launch - register: vpc_subnet_mapip - changed_when: vpc_subnet_existing.stdout | trim in ['', 'None'] - failed_when: false - -# Try tag-based lookup first. If the gateway didn't tag a previously -# created RT (spinifex gateway silently drops --tag-specifications on -# create-route-table AND no-ops create-tags for route tables — see -# mulga-siv-94; works correctly for subnets), fall back to "RT -# associated with our subnet" which doesn't need tags. -- name: vpc — find existing route table by Name tag - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-route-tables - --filters Name=vpc-id,Values={{ dev_vpc_id }} - Name=tag:Name,Values={{ dev_vpc_name }}-public - --query RouteTables[0].RouteTableId --output text - register: vpc_rt_existing - changed_when: false - -- name: vpc — fallback lookup by subnet association - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 describe-route-tables - --filters Name=association.subnet-id,Values={{ dev_vpc_subnet_id }} - --query RouteTables[0].RouteTableId --output text - register: vpc_rt_byassoc - changed_when: false - when: vpc_rt_existing.stdout | trim in ['', 'None'] - -- name: vpc — create route table if neither lookup found one - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 create-route-table - --vpc-id {{ dev_vpc_id }} - --query RouteTable.RouteTableId --output text - register: vpc_rt_created - when: - - vpc_rt_existing.stdout | trim in ['', 'None'] - - (vpc_rt_byassoc.stdout | default('') | trim) in ['', 'None'] - changed_when: true - -- name: vpc — set route-table id fact - ansible.builtin.set_fact: - dev_vpc_rt_id: >- - {%- if (vpc_rt_existing.stdout | trim) not in ['', 'None'] -%} - {{ vpc_rt_existing.stdout | trim }} - {%- elif (vpc_rt_byassoc.stdout | default('') | trim) not in ['', 'None'] -%} - {{ vpc_rt_byassoc.stdout | default('') | trim }} - {%- else -%} - {{ vpc_rt_created.stdout | default('') | trim }} - {%- endif -%} - -- name: vpc — assert route-table id resolved - ansible.builtin.assert: - that: dev_vpc_rt_id | length > 0 - fail_msg: "Route table id could not be resolved after describe/create" - -# Spinifex gateway dropped --tag-specifications on the create call; -# apply via create-tags so re-runs can find the RT via tag filter. -- name: vpc — tag route table (workaround for create-tag-specifications gap) - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 create-tags - --resources {{ dev_vpc_rt_id }} - --tags Key=Name,Value={{ dev_vpc_name }}-public - changed_when: false - failed_when: false - -- name: vpc — add default route via IGW (ignore RouteAlreadyExists) - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 create-route - --route-table-id {{ dev_vpc_rt_id }} - --destination-cidr-block 0.0.0.0/0 - --gateway-id {{ dev_vpc_igw_id }} - register: vpc_route_add - changed_when: - - vpc_route_add.rc == 0 - - "'RouteAlreadyExists' not in (vpc_route_add.stderr | default(''))" - failed_when: - - vpc_route_add.rc != 0 - - "'RouteAlreadyExists' not in (vpc_route_add.stderr | default(''))" - -- name: vpc — associate subnet with route table (ignore already-associated) - ansible.builtin.command: >- - {{ spinifex_aws_cli }} ec2 associate-route-table - --route-table-id {{ dev_vpc_rt_id }} - --subnet-id {{ dev_vpc_subnet_id }} - register: vpc_assoc - changed_when: - - vpc_assoc.rc == 0 - - "'Resource.AlreadyAssociated' not in (vpc_assoc.stderr | default(''))" - failed_when: - - vpc_assoc.rc != 0 - - "'Resource.AlreadyAssociated' not in (vpc_assoc.stderr | default(''))" - -- name: vpc — summary (present) - ansible.builtin.debug: - msg: | - Dev VPC ready: - VPC : {{ dev_vpc_id }} ({{ dev_vpc_cidr }}) - IGW : {{ dev_vpc_igw_id }} - Subnet : {{ dev_vpc_subnet_id }} ({{ dev_vpc_subnet_cidr }} @ {{ dev_vpc_subnet_az }}, public) - Route table : {{ dev_vpc_rt_id }} - - Smoketest now finds this subnet via its existing - Name=map-public-ip-on-launch,Values=true filter. diff --git a/scripts/ansible/scripts/aws-as-spinifex.sh b/scripts/ansible/scripts/aws-as-spinifex.sh deleted file mode 100755 index 59b0fffe2..000000000 --- a/scripts/ansible/scripts/aws-as-spinifex.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# Run `aws` CLI inside a shell that has the `spinifex` supplementary group -# active, with AWS_PROFILE=spinifex preset. Works around mulga-siv-92: -# ansible's `become` is a no-op when the target user equals the calling -# user, so `sudo -u` is never invoked and supplementary groups are not -# re-initialised. Without the `spinifex` group, /etc/spinifex/ca.pem (the -# AWS endpoint CA bundle) is unreadable and aws fails with SSL Errno 13. -# -# Usage from ansible tasks: -# ansible.builtin.command: -# cmd: "{{ playbook_dir }}/../scripts/aws-as-spinifex.sh ec2 describe-regions" -# -# All args are forwarded to `aws`. Quoting is preserved via printf %q so -# things like `--query 'length(Images)'` reach aws intact. -set -euo pipefail - -if [[ $# -eq 0 ]]; then - echo "usage: $0 " >&2 - exit 64 -fi - -cmd="AWS_PROFILE=spinifex aws" -for arg in "$@"; do - cmd+=" $(printf '%q' "$arg")" -done - -exec sg spinifex -c "$cmd" diff --git a/scripts/ansible/scripts/spx-as-spinifex.sh b/scripts/ansible/scripts/spx-as-spinifex.sh deleted file mode 100755 index 8d8ab3538..000000000 --- a/scripts/ansible/scripts/spx-as-spinifex.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -# Run `spx` inside a shell that has the `spinifex` supplementary group -# active. Works around mulga-siv-92 (see aws-as-spinifex.sh for details). -# All args are forwarded to /usr/local/bin/spx via printf %q so flag -# values with spaces or shell metachars survive intact. -set -euo pipefail - -if [[ $# -eq 0 ]]; then - echo "usage: $0 " >&2 - exit 64 -fi - -cmd="AWS_PROFILE=spinifex /usr/local/bin/spx" -for arg in "$@"; do - cmd+=" $(printf '%q' "$arg")" -done - -exec sg spinifex -c "$cmd" diff --git a/scripts/ansible/vars/defaults.yml b/scripts/ansible/vars/defaults.yml deleted file mode 100644 index c4270dd2d..000000000 --- a/scripts/ansible/vars/defaults.yml +++ /dev/null @@ -1,137 +0,0 @@ ---- -# Production layout (per spinifex/scripts/setup.sh). -spinifex_etc_dir: /etc/spinifex -spinifex_data_dir: /var/lib/spinifex -spinifex_log_dir: /var/log/spinifex -spinifex_run_dir: /run/spinifex -spinifex_config_file: "{{ spinifex_etc_dir }}/spinifex.toml" -spinifex_ca_trust_path: /usr/local/share/ca-certificates/spinifex-ca.crt -spinifex_ca_src: "{{ spinifex_etc_dir }}/ca.pem" - -# Legacy $HOME layout (still present on older dev boxes). -spinifex_legacy_home_dir: "{{ lookup('env','HOME') }}/spinifex" - -# OVN state. -ovn_db_dir: /var/lib/ovn -ovn_nb_db: "{{ ovn_db_dir }}/ovnnb_db.db" -ovn_sb_db: "{{ ovn_db_dir }}/ovnsb_db.db" - -# Region / init. -spinifex_region: ap-southeast-2 -spinifex_az: "{{ spinifex_region }}a" -spinifex_node_name: node1 -spinifex_node_count: 1 - -# External network. -# spx admin init contract (as of network-layer-redesign): -# external_mode : "pool" or "" (NAT path removed) -# external_pool : MANDATORY when mode=pool — set start/end below or pass -e. -# external_gw : auto-detected from default route when blank. -# external_prefix: auto-detected from WAN when blank. -# external_gw_ip : OVN router external-IP override (was nat_gateway_ip). -# no_external : true → emit --no-external (skip external networking). -spinifex_external_mode: pool -spinifex_external_no_external: false -spinifex_external_pool_start: "" -spinifex_external_pool_end: "" -spinifex_external_gateway: "" -spinifex_external_prefix_len: "" -spinifex_external_gateway_ip: "" - -# Preflight checks. -spinifex_ports: - - { port: 4222, service: NATS } - - { port: 8443, service: Predastore } - - { port: 9999, service: "Spinifex Gateway" } - -spinifex_required_commands: - - go - - make - - nbdkit -spinifex_optional_commands: - - air -spinifex_ovn_commands: - - ovs-vsctl - - ovn-controller - - ovn-nbctl - -spinifex_components: - - viperblock - - predastore - -# Process patterns for teardown. -# Bracket trick ([b]in/spx etc.) prevents the pattern literal from matching -# our own pgrep/shell cmdline. Exclude filter drops benign log followers and -# ansible's own wrappers that also mention these service names. -spinifex_process_pattern: "([b]in/spx|spinifex-[u]i|nats-[s]erver|[p]redastore|[v]iperblock|[v]pcd)" -spinifex_process_exclude: "(journalctl|ansible|/bin/sh|pgrep|pkill|tail -[fF])" -spinifex_qemu_processes: - - qemu-system-x86_64 - - qemu-system-aarch64 - -# Invoking user (when run under sudo). -spinifex_invoking_user: "{{ lookup('env','SUDO_USER') | default(ansible_facts['user_id'], true) }}" - -# Opt-in wipes. -spinifex_wipe_ssh_keys: false -spinifex_wipe_aws_creds: false -spinifex_wipe_legacy_home: true - -# Timeouts. -teardown_process_wait_seconds: 30 - -# Path resolution — playbook_dir is scripts/ansible/playbooks/ at runtime. -spinifex_root: "{{ lookup('pipe', 'readlink -f ' ~ playbook_dir ~ '/../../..') }}" -mulga_root: "{{ lookup('pipe', 'readlink -f ' ~ playbook_dir ~ '/../../../..') }}" - -# Wrappers around `aws` and `spx` that re-enter a shell with the -# `spinifex` supplementary group active. Needed because ansible's -# `become_user` is a no-op when target user equals current user, so -# supplementary groups are never re-initialised and /etc/spinifex/ca.pem -# (drwxr-x--- root:spinifex) is unreadable. -spinifex_aws_cli: "{{ playbook_dir }}/../scripts/aws-as-spinifex.sh" -spinifex_spx_cli: "{{ playbook_dir }}/../scripts/spx-as-spinifex.sh" - -# dev-vpc fixture. Deterministic VPC + public subnet + IGW + default -# route. Idempotent: re-running detects existing resources by Name tag. -# Used by smoketest's launch path (which looks for any -# map-public-ip-on-launch=true subnet). -dev_vpc_name: spinifex-dev-vpc -dev_vpc_cidr: 10.99.0.0/16 -dev_vpc_subnet_cidr: 10.99.1.0/24 -dev_vpc_subnet_az: "{{ spinifex_az }}" -dev_vpc_state: present # present | absent - -# Build output. -build_tarball_path: /tmp/spinifex-local.tar.gz - -# Snapshot / restore. Snapshots quiesce the full spinifex+ovn+ovs stack, -# tar these paths, then restart services. Restore wipes + untars + restarts. -# Default snapshot dir is root-owned so it survives `dev-teardown` -# (teardown only removes the four spinifex_*_dir paths above). -snapshot_dir: /var/lib/spinifex-snapshots -snapshot_paths: - - "{{ spinifex_etc_dir }}" - - "{{ spinifex_data_dir }}" - - "{{ ovn_db_dir }}" - - /etc/openvswitch/conf.db -# Order matters: stop spinifex first (depends on OVN/OVS), then OVN, then OVS. -# Restart in reverse. -snapshot_services_stop_order: - - spinifex.target - - ovn-controller - - ovn-central - - openvswitch-switch - -# Smoketest image — parameterised so tofu examples can share the same base. -spinifex_smoketest_distro: debian -spinifex_smoketest_version: "13" -# Local image file path (if present, imported directly; otherwise fall back to --name). -spinifex_smoketest_local_image: "~/images/{{ spinifex_smoketest_distro }}-{{ spinifex_smoketest_version }}.img" - -# Multi-node cluster bootstrap (cluster-bootstrap.yml). -# cluster_env, cluster_init_host, spinifex_node_count come from inventory/tofu.py. -cluster_external_pool: "" # "-" or empty for DHCP fallback -cluster_join_token_path: /etc/spinifex/join-token -cluster_formation_port: 4432 -cluster_formation_timeout: 60 # seconds (uri retries = value // 2) diff --git a/scripts/build-microvm-image.sh b/scripts/build-microvm-image.sh index 5804cdf1e..dba6ffaed 100755 --- a/scripts/build-microvm-image.sh +++ b/scripts/build-microvm-image.sh @@ -19,14 +19,21 @@ INITTAB="$BUILD_DIR/inittab" ALPINE_VERSION="${ALPINE_VERSION:-3.20}" ALPINE_MIRROR="${ALPINE_MIRROR:-https://dl-cdn.alpinelinux.org/alpine}" -ALPINE_PACKAGES="busybox linux-virt haproxy iproute2 ca-certificates" +# haproxy: ALB (L7) data plane. nginx + nginx-mod-stream: NLB (L4) data plane — +# the `stream` module (a separate Alpine package) load-balances TCP/UDP/TLS, +# which haproxy cannot do for UDP. +ALPINE_PACKAGES="busybox linux-virt haproxy nginx nginx-mod-stream iproute2 ca-certificates" echo "[build-microvm-image] repo root: $REPO_ROOT" echo "[build-microvm-image] build dir: $BUILD_DIR" mkdir -p "$BUILD_DIR" # --- Verify non-negotiable tools --- -for tool in cpio gzip find; do +# fakeroot is mandatory: device nodes are written into the cpio archive inside a +# faked-root environment so no real device node is ever created on the host +# filesystem (which previously required sudo mknod and risked clobbering host +# /dev/null on cleanup). +for tool in cpio gzip find fakeroot; do if ! command -v "$tool" >/dev/null 2>&1; then echo "ERROR: required tool not found: $tool" >&2 exit 1 @@ -35,6 +42,13 @@ done # --- Create chroot --- CHROOT_DIR=$(mktemp -d) +# Guard: every destructive op below targets "$CHROOT_DIR/...". An empty or +# unexpected value would aim rm -rf at the host (e.g. /dev). Refuse anything that +# is not a freshly-created temp dir. +case "${CHROOT_DIR:?mktemp -d returned empty}" in + /tmp/*|/var/tmp/*|"${TMPDIR:-/nonexistent}"/*) ;; + *) echo "ERROR: refusing to use unexpected CHROOT_DIR: $CHROOT_DIR" >&2; exit 1 ;; +esac CONTAINER_TOOL="" CONTAINER_CID="" cleanup() { @@ -93,16 +107,14 @@ else $CONTAINER_TOOL export "$CONTAINER_CID" | tar -x -C "$CHROOT_DIR" fi -# --- Fix /dev device nodes --- +# --- /dev: empty mountpoint only --- # tar extraction without root silently creates 0-byte regular files for device -# nodes (mknod requires CAP_MKNOD). The kernel opens /dev/console to wire -# init's stdio to the serial console; a regular file there causes all init -# output to be silently discarded. Recreate the two nodes needed before -# devtmpfs mounts (which provides the rest at runtime). -rm -rf "$CHROOT_DIR/dev" +# nodes. Drop them and leave an empty /dev mountpoint. The two static nodes the +# kernel needs (/dev/console to wire init's stdio, /dev/null) are written into +# the cpio archive under fakeroot at pack time — NOT created on the host. init +# mounts devtmpfs over /dev as its first action for everything else at runtime. +rm -rf "${CHROOT_DIR:?}/dev" mkdir "$CHROOT_DIR/dev" -sudo mknod -m 600 "$CHROOT_DIR/dev/console" c 5 1 -sudo mknod -m 666 "$CHROOT_DIR/dev/null" c 1 3 # --- Place init script --- echo "[build-microvm-image] installing init.sh as /init..." @@ -161,6 +173,21 @@ else echo "[build-microvm-image] fw_cfg: module found in lib/modules" fi +# --- Assert nginx + stream module (NLB L4 data plane) --- +# The lb-agent runs `nginx -c` with a config that `load_module`s the stream +# module; if either the binary or the .so is missing the agent never enters +# nginx mode, never probes, and NLB targets stay at 0/N healthy. Fail the build +# loudly here rather than discover it as an opaque e2e health timeout. +if [ ! -x "$CHROOT_DIR/usr/sbin/nginx" ] && [ ! -x "$CHROOT_DIR/usr/bin/nginx" ]; then + echo "ERROR: nginx binary missing from image — NLB data plane will not start" >&2 + exit 1 +fi +if ! find "$CHROOT_DIR/usr/lib/nginx/modules" -name "ngx_stream_module.so" 2>/dev/null | grep -q .; then + echo "ERROR: ngx_stream_module.so missing from image — NLB stream config fails to load" >&2 + exit 1 +fi +echo "[build-microvm-image] nginx: binary + stream module present" + # --- Strip kernel modules (Phase 2 keep-list approach) --- # Only modules needed by a virtio-mmio microvm guest are kept: # virtio*.ko* — virtio bus, net, blk, rng, console, and helpers @@ -197,8 +224,34 @@ for kver_dir in "$CHROOT_DIR/lib/modules"/*/; do done) rm -rf "$tmp_keep" - # Regenerate module dependency map from the surviving modules. - depmod -b "$CHROOT_DIR" "$kver" 2>/dev/null || true + # Decompress kept modules to plain .ko. Alpine ships .ko.gz, but a host + # kmod that lacks gzip support (e.g. Debian trixie's kmod 34) silently + # produces an EMPTY modules.dep from compressed modules — the guest's + # modprobe then no-ops and qemu_fw_cfg never loads. Plain .ko sidesteps the + # host-kmod dependency entirely and the guest needs no decompressor either. + find "$kernel_dir" -name "*.ko.gz" -exec gunzip -f {} + + + # Regenerate the module dependency map when a host depmod is available. A + # populated modules.dep lets the guest init's load_mod() resolve modules via + # modprobe; without it, load_mod() falls back to insmod-by-path (see + # build/microvm/init.sh), so modules.dep is an optimisation, not a boot + # requirement. depmod (kmod) is absent on some self-hosted CI runners and + # cannot be apt-installed there — skip rather than hard-fail. When depmod + # IS present, still assert a non-empty result so the trixie-kmod-can't-read + # -compressed-modules bug (empty modules.dep from .ko.gz) fails loudly. + if command -v depmod >/dev/null 2>&1; then + if ! depmod -b "$CHROOT_DIR" "$kver"; then + echo "ERROR: depmod failed for $kver" >&2 + exit 1 + fi + if [ ! -s "$kver_dir/modules.dep" ]; then + echo "ERROR: ${kver_dir}modules.dep missing or empty after depmod" >&2 + exit 1 + fi + else + echo "[build-microvm-image] depmod not found; skipping modules.dep" \ + "(guest init load_mod() will insmod modules by path)" >&2 + fi done # --- Strip package-manager artifacts (not needed at runtime) --- @@ -238,10 +291,15 @@ rm -rf "$CHROOT_DIR/boot" # --- Build initramfs --- INITRAMFS_OUT="$BUILD_DIR/initramfs.cpio.gz" echo "[build-microvm-image] building initramfs: $INITRAMFS_OUT" -( - cd "$CHROOT_DIR" - find . | cpio --quiet -o -H newc | gzip -9 > "$INITRAMFS_OUT" -) +# Create the static device nodes and pack the archive inside a single fakeroot +# session: mknod/cpio see faked device entries and write real char-device +# records into the cpio, while the host filesystem never gets an actual node. +fakeroot sh -c ' + cd "$1" || exit 1 + mknod -m 600 dev/console c 5 1 + mknod -m 666 dev/null c 1 3 + find . | cpio --quiet -o -H newc | gzip -9 > "$2" +' _ "$CHROOT_DIR" "$INITRAMFS_OUT" # --- Log artifact sizes --- echo "" diff --git a/scripts/build-system-image.sh b/scripts/build-system-image.sh index eaff2b855..6da9c732d 100755 --- a/scripts/build-system-image.sh +++ b/scripts/build-system-image.sh @@ -3,17 +3,28 @@ set -euo pipefail # build-system-image.sh — Build a minimal system AMI from a manifest # -# Supports Alpine Linux and Ubuntu 24.04 as base distros. -# Creates a pre-baked image with custom packages, binaries, and setup scripts -# installed, ready for import as a Spinifex AMI. +# Supports Alpine Linux and Ubuntu 24.04+ as base distros. Creates a pre-baked +# image with custom packages, binaries, and setup scripts installed, ready for +# import as a Spinifex AMI. # -# Requirements: qemu-nbd, qemu-img, sudo (for mount/chroot), curl +# Customization runs entirely inside the libguestfs appliance (its own kernel + +# userspace), so the build touches NO host block device, NO host mount, and +# needs NO sudo. This deliberately replaces the previous qemu-nbd + `sudo mount` +# + chroot + /dev bind-mount flow, whose leaked binds could be traversed by a +# later `rm -rf` and wipe the host's /dev (it wedged CI runners). libguestfs +# isolates all of that in a throwaway VM. +# +# Requirements: virt-customize, guestfish, virt-resize (libguestfs-tools), +# qemu-img, curl # Usage: ./scripts/build-system-image.sh [--import] # --import Also import the image as an AMI via spx admin images import SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +# No libvirt on CI runners — drive qemu directly. Harmless when libvirt exists. +export LIBGUESTFS_BACKEND="${LIBGUESTFS_BACKEND:-direct}" + usage() { echo "Usage: $0 [--import]" echo "" @@ -60,8 +71,6 @@ done # Derived paths BUILD_DIR="/tmp/${IMAGE_NAME}-image-build" -NBD_DEV="/dev/nbd0" -MOUNT_DIR="${BUILD_DIR}/mnt" case "$DISTRO" in alpine) @@ -74,7 +83,6 @@ case "$DISTRO" in OUTPUT_IMAGE="${BUILD_DIR}/${IMAGE_NAME}-alpine.qcow2" OUTPUT_RAW="${BUILD_DIR}/${IMAGE_NAME}-alpine.raw" DISTRO_VERSION="${ALPINE_VERSION}" - ROOT_PART="${NBD_DEV}" BOOT_MODE="bios" ;; ubuntu) @@ -87,7 +95,6 @@ case "$DISTRO" in OUTPUT_IMAGE="${BUILD_DIR}/${IMAGE_NAME}-ubuntu.qcow2" OUTPUT_RAW="${BUILD_DIR}/${IMAGE_NAME}-ubuntu.raw" DISTRO_VERSION="${UBUNTU_VERSION}" - ROOT_PART="${NBD_DEV}p1" BOOT_MODE="uefi" ;; *) @@ -96,19 +103,20 @@ case "$DISTRO" in ;; esac -cleanup() { - echo "Cleaning up..." - # Ubuntu chroot has bind mounts that must be released first - sudo umount "${MOUNT_DIR}/dev/pts" 2>/dev/null || true - sudo umount "${MOUNT_DIR}/dev" 2>/dev/null || true - sudo umount "${MOUNT_DIR}/sys" 2>/dev/null || true - sudo umount "${MOUNT_DIR}/proc" 2>/dev/null || true - sudo umount "${MOUNT_DIR}" 2>/dev/null || true - sudo qemu-nbd --disconnect "${NBD_DEV}" 2>/dev/null || true - exec 9>&- 2>/dev/null || true # release nbd lock if held - echo "Done." +# import_ami registers the built raw image as a Spinifex AMI. Shared by the +# fresh-build and cached-build paths. +import_ami() { + echo "Importing as AMI..." + rm -f "$OUTPUT_IMAGE" + local args=(--file "$OUTPUT_RAW" --distro "${DISTRO}" --version "${DISTRO_VERSION}" --arch x86_64 --boot-mode "${BOOT_MODE}") + if [[ -n "${AMI_NAME:-}" ]]; then + args+=(--ami-name "$AMI_NAME") + fi + if [[ -n "${SYSTEM_TAG:-}" ]]; then + args+=(--tag "$SYSTEM_TAG") + fi + spx admin images import "${args[@]}" } -trap cleanup EXIT # In quiet mode, redirect build output to /dev/null (import output still shown) if [[ "$QUIET" == true ]]; then @@ -116,7 +124,7 @@ if [[ "$QUIET" == true ]]; then exec 1>/dev/null # suppress build output fi -echo "=== System Image Builder ===" +echo "=== System Image Builder (libguestfs) ===" echo "Image: ${IMAGE_NAME} — ${IMAGE_DESCRIPTION:-}" echo "Distro: ${DISTRO} ${DISTRO_VERSION}" echo "Size: ${IMAGE_SIZE}" @@ -124,18 +132,18 @@ echo "Build: ${BUILD_DIR}" echo "" # Step 0: Check prerequisites -if ! command -v qemu-nbd &>/dev/null; then - echo "ERROR: qemu-nbd not found. Install qemu-utils." +for tool in virt-customize qemu-img; do + if ! command -v "$tool" &>/dev/null; then + echo "ERROR: $tool not found. Install libguestfs-tools + qemu-utils." + exit 1 + fi +done +if [[ "$DISTRO" == "alpine" ]] && ! command -v guestfish &>/dev/null; then + echo "ERROR: guestfish not found. Install libguestfs-tools." exit 1 fi - -if ! command -v qemu-img &>/dev/null; then - echo "ERROR: qemu-img not found. Install qemu-utils." - exit 1 -fi - -if [[ "$DISTRO" == "ubuntu" ]] && ! command -v parted &>/dev/null && ! [[ -x /usr/sbin/parted ]]; then - echo "ERROR: parted not found. Install parted." +if [[ "$DISTRO" == "ubuntu" ]] && ! command -v virt-resize &>/dev/null; then + echo "ERROR: virt-resize not found. Install libguestfs-tools." exit 1 fi @@ -166,11 +174,11 @@ if [[ -n "${INSTALL_BINARIES:-}" ]]; then done fi -# Serialize the entire image build with flock — concurrent builds on the same -# host (e.g. CI single-node + multi-node jobs) share /dev/nbd0 and BUILD_DIR. -NBD_LOCK="/tmp/build-system-image.lock" +# Serialize the build with flock — concurrent builds on the same host (e.g. CI +# single-node + multi-node jobs) share BUILD_DIR + the cached raw image. +BUILD_LOCK="/tmp/build-system-image.lock" echo "Acquiring build lock..." -exec 9>"$NBD_LOCK" +exec 9>"$BUILD_LOCK" flock 9 echo "Lock acquired" @@ -189,18 +197,12 @@ if [[ -f "$OUTPUT_RAW" ]] && [[ $(( $(date +%s) - $(stat -c %Y "$OUTPUT_RAW") )) echo " raw: $OUTPUT_RAW ($(du -h "$OUTPUT_RAW" | cut -f1))" if [[ "$DO_IMPORT" == true ]]; then - echo "Importing as AMI..." - rm -f "$OUTPUT_IMAGE" - IMPORT_ARGS=(--file "$OUTPUT_RAW" --distro "${DISTRO}" --version "${DISTRO_VERSION}" --arch x86_64 --boot-mode "${BOOT_MODE}") - if [[ -n "${SYSTEM_TAG:-}" ]]; then - IMPORT_ARGS+=(--tag "$SYSTEM_TAG") - fi - spx admin images import "${IMPORT_ARGS[@]}" + import_ami fi exit 0 fi -mkdir -p "$BUILD_DIR" "$MOUNT_DIR" +mkdir -p "$BUILD_DIR" # Step 1: Download base cloud image if [[ -f "${BUILD_DIR}/${SOURCE_IMAGE}" ]]; then @@ -225,126 +227,66 @@ rm -f "$OUTPUT_IMAGE" echo "Copying image for customization..." cp "${BUILD_DIR}/${SOURCE_IMAGE}" "$OUTPUT_IMAGE" -# Resize the image to provide room for packages -qemu-img resize "$OUTPUT_IMAGE" "$IMAGE_SIZE" - -# Step 3: Connect via qemu-nbd -echo "Connecting image via qemu-nbd..." -sudo modprobe nbd max_part=4 2>/dev/null || true -if [[ ! -e "${NBD_DEV}" ]]; then - echo "ERROR: ${NBD_DEV} does not exist. Is the nbd kernel module loaded? Try: sudo modprobe nbd" - exit 1 +# Step 3: Resize to provide room for packages. Both paths grow the root +# filesystem entirely inside the libguestfs appliance — no host loop/nbd device. +echo "Resizing image to ${IMAGE_SIZE}..." +if [[ "$DISTRO" == "alpine" ]]; then + # Alpine cloud images have ext4 directly on the whole disk (no partition + # table); grow the disk file then expand the fs from inside the appliance. + qemu-img resize "$OUTPUT_IMAGE" "$IMAGE_SIZE" + guestfish --rw -a "$OUTPUT_IMAGE" run \ + : e2fsck /dev/sda forceall:true \ + : resize2fs /dev/sda +else + # Ubuntu images use a GPT partition table with the root fs on /dev/sda1; + # virt-resize expands the partition + fs into a fresh IMAGE_SIZE disk. + RESIZED="${OUTPUT_IMAGE}.resized" + rm -f "$RESIZED" + qemu-img create -f qcow2 "$RESIZED" "$IMAGE_SIZE" + virt-resize --expand /dev/sda1 "$OUTPUT_IMAGE" "$RESIZED" + mv -f "$RESIZED" "$OUTPUT_IMAGE" fi -sudo qemu-nbd --disconnect "${NBD_DEV}" 2>/dev/null || true -sudo qemu-nbd --connect="${NBD_DEV}" "$OUTPUT_IMAGE" -# Wait for the nbd device to be ready -for i in $(seq 1 10); do - if sudo blockdev --getsize64 "${NBD_DEV}" &>/dev/null; then - break - fi - sleep 1 -done -sleep 1 - -# Ubuntu images have a partition table; wait for partition devices then resize -# the partition to fill the expanded image before resizing the filesystem. +# Step 4: Assemble the virt-customize operation list. virt-customize runs the +# operations in the order they appear on the command line, so this mirrors the +# old chroot sequence: packages → binaries → files → setup → services → clean. +CUST=(virt-customize -a "$OUTPUT_IMAGE" --network) if [[ "$DISTRO" == "ubuntu" ]]; then - for i in $(seq 1 10); do - if [[ -e "${ROOT_PART}" ]]; then break; fi - sleep 1 - done - echo "Resizing partition..." - # After qemu-img resize the GPT backup header sits at the old end of disk. - # Relocate it to the new end (sfdisk --relocate is util-linux built-in, - # no extra packages needed), then re-read the partition table before parted - # runs — without this, parted sees stale GPT geometry and fails with - # "Unable to satisfy all constraints on the partition." - sudo sgdisk --move-second-header "${NBD_DEV}" - sudo partprobe "${NBD_DEV}" 2>/dev/null || true - sleep 1 - sudo parted --script "${NBD_DEV}" resizepart 1 100% + # DKMS driver bakes (GPU images) need real resources in the appliance. + CUST+=(--memsize 6144 --smp 4) +else + CUST+=(--memsize 2048) fi -# Alpine cloud images have ext4 directly on the block device (no partition table). -echo "Checking filesystem..." -sudo e2fsck -f -y "${ROOT_PART}" || { - ec=$? - if [[ $ec -gt 1 ]]; then - echo "ERROR: e2fsck failed with exit code $ec on ${ROOT_PART}" - exit 1 - fi -} - -echo "Resizing filesystem..." -if ! sudo resize2fs "${ROOT_PART}"; then - echo "ERROR: resize2fs failed on ${ROOT_PART}" - exit 1 -fi - -# Step 4: Mount and customize -echo "Mounting root filesystem..." -sudo mount "${ROOT_PART}" "$MOUNT_DIR" - -# Set up resolv.conf for DNS inside chroot. -# Ubuntu cloud images symlink /etc/resolv.conf → /run/systemd/resolve/stub-resolv.conf -# which doesn't exist yet. Remove the symlink and write a real file. -sudo rm -f "${MOUNT_DIR}/etc/resolv.conf" -sudo cp /etc/resolv.conf "${MOUNT_DIR}/etc/resolv.conf" - -# Ubuntu chroot requires /proc /sys /dev bind mounts for systemd and DKMS -if [[ "$DISTRO" == "ubuntu" ]]; then - sudo mount --bind /proc "${MOUNT_DIR}/proc" - sudo mount --bind /sys "${MOUNT_DIR}/sys" - sudo mount --bind /dev "${MOUNT_DIR}/dev" - sudo mount --bind /dev/pts "${MOUNT_DIR}/dev/pts" -fi - -# Install packages +# Packages if [[ "$DISTRO" == "alpine" ]] && [[ -n "${APK_PACKAGES:-}" ]]; then - echo "Installing packages: ${APK_PACKAGES}..." - sudo chroot "$MOUNT_DIR" /bin/sh -c " -set -e -# Enable community repo -sed -i 's|^#\(.*community\)|\1|' /etc/apk/repositories 2>/dev/null || true - -# Ensure community repo is present -if ! grep -q community /etc/apk/repositories; then - MIRROR=\$(grep main /etc/apk/repositories | head -1 | sed 's|/main|/community|') - echo \"\$MIRROR\" >> /etc/apk/repositories -fi - -apk update -apk add ${APK_PACKAGES} -" + echo "Will install packages: ${APK_PACKAGES}" + CUST+=(--run-command 'sed -i "s|^#\(.*community\)|\1|" /etc/apk/repositories 2>/dev/null || true') + CUST+=(--run-command 'grep -q community /etc/apk/repositories || grep main /etc/apk/repositories | head -1 | sed "s|/main|/community|" >> /etc/apk/repositories') + CUST+=(--run-command 'apk update') + CUST+=(--run-command "apk add ${APK_PACKAGES}") elif [[ "$DISTRO" == "ubuntu" ]] && [[ -n "${APT_PACKAGES:-}" ]]; then - echo "Installing packages: ${APT_PACKAGES}..." - sudo chroot "$MOUNT_DIR" /bin/bash -c " -export DEBIAN_FRONTEND=noninteractive -apt-get update -apt-get install -y --no-install-recommends ${APT_PACKAGES} -" + echo "Will install packages: ${APT_PACKAGES}" + CUST+=(--run-command 'export DEBIAN_FRONTEND=noninteractive; apt-get update') + CUST+=(--run-command "export DEBIAN_FRONTEND=noninteractive; apt-get install -y --no-install-recommends ${APT_PACKAGES}") fi -# Step 5: Copy binaries into the image (before setup script, which may reference them) +# Binaries (mode 0755). --mkdir is idempotent (mkdir -p semantics). if [[ -n "${INSTALL_BINARIES:-}" ]]; then - echo "Installing binaries..." IFS=' ' read -ra BINARY_PAIRS <<< "$INSTALL_BINARIES" for pair in "${BINARY_PAIRS[@]}"; do src="${pair%%:*}" dst="${pair#*:}" src_path="${PROJECT_DIR}/${src}" - echo " ${src} -> ${dst}" - sudo cp "$src_path" "${MOUNT_DIR}${dst}" - sudo chmod 755 "${MOUNT_DIR}${dst}" + CUST+=(--mkdir "$(dirname "$dst")") + CUST+=(--upload "${src_path}:${dst}") + CUST+=(--chmod "0755:${dst}") done fi -# Copy auxiliary files (systemd units, OpenRC initd scripts, cron entries, configs). -# Same src:dst pair grammar as INSTALL_BINARIES; mode 0644, parent dirs auto-created. -# Optional — manifests without INSTALL_FILES (e.g. existing GPU images) skip this step. +# Auxiliary files (systemd units, OpenRC initd scripts, cron entries, configs). +# Mode 0644; setup scripts may chmod role-specific files to 0755 afterwards. if [[ -n "${INSTALL_FILES:-}" ]]; then - echo "Installing files..." IFS=' ' read -ra FILE_PAIRS <<< "$INSTALL_FILES" for pair in "${FILE_PAIRS[@]}"; do src="${pair%%:*}" @@ -354,85 +296,45 @@ if [[ -n "${INSTALL_FILES:-}" ]]; then echo "ERROR: INSTALL_FILES source not found: $src_path" exit 1 fi - echo " ${src} -> ${dst}" - sudo mkdir -p "${MOUNT_DIR}$(dirname "$dst")" - sudo cp "$src_path" "${MOUNT_DIR}${dst}" - sudo chmod 0644 "${MOUNT_DIR}${dst}" + CUST+=(--mkdir "$(dirname "$dst")") + CUST+=(--upload "${src_path}:${dst}") + CUST+=(--chmod "0644:${dst}") done fi -# Run custom setup script inside chroot (after binaries are installed) +# Custom setup script (uploaded + executed + removed by --run, inside the guest). if [[ -n "${SETUP_SCRIPT:-}" ]]; then setup_path="${PROJECT_DIR}/${SETUP_SCRIPT}" if [[ ! -f "$setup_path" ]]; then echo "ERROR: Setup script not found: $setup_path" exit 1 fi - echo "Running setup script: ${SETUP_SCRIPT}..." - sudo cp "$setup_path" "${MOUNT_DIR}/tmp/setup.sh" - sudo chmod 755 "${MOUNT_DIR}/tmp/setup.sh" - if [[ "$DISTRO" == "ubuntu" ]]; then - if [[ ! -x "${MOUNT_DIR}/bin/bash" ]]; then - echo "ERROR: /bin/bash not found or not executable in chroot" - exit 1 - fi - sudo chroot "$MOUNT_DIR" /bin/bash -x /tmp/setup.sh - else - sudo chroot "$MOUNT_DIR" /tmp/setup.sh - fi - sudo rm -f "${MOUNT_DIR}/tmp/setup.sh" + CUST+=(--run "$setup_path") fi -# Enable services (runs after INSTALL_FILES + SETUP_SCRIPT so init scripts the -# manifest references are already on disk). +# Enable services (after files + setup so referenced init scripts exist). if [[ -n "${ENABLE_SERVICES:-}" ]]; then - echo "Enabling services: ${ENABLE_SERVICES}..." IFS=' ' read -ra SERVICES <<< "$ENABLE_SERVICES" for svc in "${SERVICES[@]}"; do if [[ "$DISTRO" == "alpine" ]]; then - if ! sudo chroot "$MOUNT_DIR" /bin/sh -c "rc-update add ${svc} default"; then - echo "ERROR: Failed to enable service '${svc}' — does it exist in the image?" - exit 1 - fi + CUST+=(--run-command "rc-update add ${svc} default") else - if ! sudo chroot "$MOUNT_DIR" /bin/bash -c "systemctl enable ${svc}"; then - echo "ERROR: Failed to enable service '${svc}'" - exit 1 - fi + CUST+=(--run-command "systemctl enable ${svc}") fi done fi -# Step 6: Clean up and unmount -echo "Cleaning up image..." +# Final cleanup of package caches + tmp. if [[ "$DISTRO" == "alpine" ]]; then - sudo chroot "$MOUNT_DIR" /bin/sh -c ' -apk cache clean 2>/dev/null || true -rm -rf /var/cache/apk/* /tmp/* -' + CUST+=(--run-command 'apk cache clean 2>/dev/null || true; rm -rf /var/cache/apk/* /tmp/* 2>/dev/null || true') else - sudo chroot "$MOUNT_DIR" /bin/bash -c ' -export DEBIAN_FRONTEND=noninteractive -apt-get clean -rm -rf /var/lib/apt/lists/* /tmp/* -' + CUST+=(--run-command 'export DEBIAN_FRONTEND=noninteractive; apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* 2>/dev/null || true') fi -# Restore the systemd-resolved symlink (Ubuntu default); cloud-init sets DNS on boot. -sudo rm -f "${MOUNT_DIR}/etc/resolv.conf" -sudo ln -sf /run/systemd/resolve/stub-resolv.conf "${MOUNT_DIR}/etc/resolv.conf" +echo "Customizing image (libguestfs appliance)..." +"${CUST[@]}" -echo "Unmounting..." -if [[ "$DISTRO" == "ubuntu" ]]; then - sudo umount "${MOUNT_DIR}/dev/pts" - sudo umount "${MOUNT_DIR}/dev" - sudo umount "${MOUNT_DIR}/sys" - sudo umount "${MOUNT_DIR}/proc" -fi -sudo umount "$MOUNT_DIR" -sudo qemu-nbd --disconnect "${NBD_DEV}" - -# Step 7: Convert to raw for import +# Step 5: Convert to raw for import echo "Converting to raw format..." qemu-img convert -f qcow2 -O raw "$OUTPUT_IMAGE" "$OUTPUT_RAW" @@ -449,23 +351,14 @@ echo " raw: $OUTPUT_RAW ($(du -h "$OUTPUT_RAW" | cut -f1))" echo "" if [[ "$DO_IMPORT" == true ]]; then - echo "Importing as AMI..." - rm -f "$OUTPUT_IMAGE" - IMPORT_ARGS=(--file "$OUTPUT_RAW" --distro "${DISTRO}" --version "${DISTRO_VERSION}" --arch x86_64 --boot-mode "${BOOT_MODE}") - if [[ -n "${AMI_NAME:-}" ]]; then - IMPORT_ARGS+=(--name "$AMI_NAME") - fi - if [[ -n "${SYSTEM_TAG:-}" ]]; then - IMPORT_ARGS+=(--tag "$SYSTEM_TAG") - fi - spx admin images import "${IMPORT_ARGS[@]}" + import_ami else echo "To import as AMI, run:" echo " spx admin images import \\" echo " --file $OUTPUT_RAW \\" NAME_HINT="" if [[ -n "${AMI_NAME:-}" ]]; then - NAME_HINT=" \\\n --name ${AMI_NAME}" + NAME_HINT=" \\\n --ami-name ${AMI_NAME}" fi if [[ -n "${SYSTEM_TAG:-}" ]]; then echo -e " --distro ${DISTRO} --version ${DISTRO_VERSION} --arch x86_64 --boot-mode ${BOOT_MODE}${NAME_HINT} \\" diff --git a/scripts/dev-install.sh b/scripts/dev-install.sh index f3f221534..e3a86969a 100755 --- a/scripts/dev-install.sh +++ b/scripts/dev-install.sh @@ -90,6 +90,17 @@ fi echo "=== Building and installing microVM artifacts ===" cd "$PROJECT_ROOT" && make build-lb-agent install-microvm +# Unified EKS node AMI (server + agent; role chosen at first boot). Heavy (full +# Alpine + K3s build, needs sudo + network), so it is opt-in: set +# DEV_INSTALL_EKS=1 to build + register it. Without it, `aws eks create-cluster` +# fails AMI resolution until the operator runs `make import-eks-node-image`. +if [ "${DEV_INSTALL_EKS:-0}" = "1" ]; then + echo "=== Building + importing eks-node AMI ===" + cd "$PROJECT_ROOT" && make import-eks-node-image +else + echo "=== Skipping eks-node AMI (set DEV_INSTALL_EKS=1 to enable, or run 'make import-eks-node-image') ===" +fi + echo "=== Done ===" echo "Services: sudo systemctl status spinifex.target" echo "Logs: journalctl -u 'spinifex-*' -f" diff --git a/scripts/images/eks-agent/manifest.conf b/scripts/images/eks-agent/manifest.conf deleted file mode 100644 index fc8b5c7bb..000000000 --- a/scripts/images/eks-agent/manifest.conf +++ /dev/null @@ -1,25 +0,0 @@ -IMAGE_NAME="eks-agent" -IMAGE_DESCRIPTION="EKS K3s agent VM — Alpine + K3s v1.32.5+k3s1 (worker node)" -DISTRO="alpine" -ALPINE_VERSION="3.21.7" - -# 2G for the worker — kubelet + containerd + node images. Customer workload -# storage attaches via separate EBS volumes (managed by viperblock); this is -# the OS root only. -IMAGE_SIZE="2G" - -APK_PACKAGES="ca-certificates curl iptables ip6tables conntrack-tools cgroup-tools openrc" - -# K3s binary downloaded by setup.sh (same pinned version + SHA path as -# eks-server) to keep server / agent in lock-step. - -INSTALL_FILES="\ -scripts/images/eks-agent/k3s-agent.initd:/etc/init.d/k3s-agent \ -scripts/images/eks-agent/mulga-eks-state-report.sh:/etc/periodic/15min/mulga-eks-state-report" - -ENABLE_SERVICES="crond k3s-agent" - -SETUP_SCRIPT="scripts/images/eks-agent/setup.sh" - -AMI_NAME="spinifex-eks-agent" -SYSTEM_TAG="spinifex:managed-by=eks" diff --git a/scripts/images/eks-agent/mulga-eks-state-report.sh b/scripts/images/eks-agent/mulga-eks-state-report.sh deleted file mode 100644 index bb382bf38..000000000 --- a/scripts/images/eks-agent/mulga-eks-state-report.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -set -eu - -# mulga-eks-state-report (agent variant) — periodic NATS publish of agent -# liveness signal. Subject: eks.state.{accountID}.{clusterName}.agent.{nodeName} -# Payload: JSON { kubelet: "ok"|"fail", ts: } -# -# The agent has no API server to query for its own status; we shell-check the -# local kubelet socket via crictl, and fall back to a process check. - -ENVFILE=/etc/spinifex-eks/state-report.env -[ -f "${ENVFILE}" ] || { logger -t mulga-eks-state-report "${ENVFILE} missing"; exit 0; } -# shellcheck disable=SC1090 -. "${ENVFILE}" - -: "${SPINIFEX_NATS_URL:?}" -: "${EKS_ACCOUNT_ID:?}" -: "${EKS_CLUSTER_NAME:?}" -: "${EKS_NODE_NAME:?}" - -if pgrep -f 'k3s agent' >/dev/null 2>&1; then - KUBELET=ok -else - KUBELET=fail -fi - -TS=$(date +%s) -PAYLOAD=$(printf '{"kubelet":"%s","ts":%s}' "${KUBELET}" "${TS}") -SUBJ="eks.state.${EKS_ACCOUNT_ID}.${EKS_CLUSTER_NAME}.agent.${EKS_NODE_NAME}" - -NATS_ARGS="-s ${SPINIFEX_NATS_URL}" -if [ -n "${SPINIFEX_NATS_CREDS_FILE:-}" ] && [ -f "${SPINIFEX_NATS_CREDS_FILE}" ]; then - NATS_ARGS="${NATS_ARGS} --creds ${SPINIFEX_NATS_CREDS_FILE}" -fi - -# shellcheck disable=SC2086 -printf '%s' "${PAYLOAD}" | nats ${NATS_ARGS} pub "${SUBJ}" --stdin 2>&1 \ - | logger -t mulga-eks-state-report diff --git a/scripts/images/eks-agent/setup.sh b/scripts/images/eks-agent/setup.sh deleted file mode 100644 index b1ad1897a..000000000 --- a/scripts/images/eks-agent/setup.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -set -eu - -# setup.sh — chroot customisation for the eks-agent AMI. -# Pulls the same K3s release as eks-server (lock-step versioning per Q12) and -# wires the k3s-agent OpenRC service. - -K3S_VERSION="v1.32.5+k3s1" -K3S_URL_BASE="https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}" - -echo "[eks-agent-setup] fetching k3s checksums ${K3S_VERSION}" -curl -fsSL -o /tmp/k3s-checksums.txt "${K3S_URL_BASE}/sha256sum-amd64.txt" -K3S_SHA256=$(awk '/[ \t]k3s$/{print $1; exit}' /tmp/k3s-checksums.txt) -if [ -z "${K3S_SHA256}" ]; then - echo "[eks-agent-setup] could not parse k3s sha256 from upstream checksums" - cat /tmp/k3s-checksums.txt - exit 1 -fi - -echo "[eks-agent-setup] downloading k3s ${K3S_VERSION} (sha256=${K3S_SHA256})" -curl -fsSL -o /usr/local/bin/k3s "${K3S_URL_BASE}/k3s" -echo "${K3S_SHA256} /usr/local/bin/k3s" > /tmp/k3s.sha256 -if ! sha256sum -c /tmp/k3s.sha256; then - echo "[eks-agent-setup] k3s SHA256 mismatch — refusing to bake AMI" - exit 1 -fi -chmod 0755 /usr/local/bin/k3s -ln -sf /usr/local/bin/k3s /usr/local/bin/kubectl -ln -sf /usr/local/bin/k3s /usr/local/bin/crictl -ln -sf /usr/local/bin/k3s /usr/local/bin/ctr - -# nats-cli — see eks-server setup.sh for rationale. -NATS_CLI_VERSION="0.4.0" -NATS_CLI_URL="https://github.com/nats-io/natscli/releases/download/v${NATS_CLI_VERSION}/nats-${NATS_CLI_VERSION}-linux-amd64.zip" -echo "[eks-agent-setup] downloading nats-cli ${NATS_CLI_VERSION}" -curl -fsSL -o /tmp/nats-cli.zip "${NATS_CLI_URL}" -apk add --no-cache unzip -unzip -q -d /tmp/nats-cli /tmp/nats-cli.zip -install -m 0755 /tmp/nats-cli/nats-${NATS_CLI_VERSION}-linux-amd64/nats /usr/local/bin/nats -rm -rf /tmp/nats-cli /tmp/nats-cli.zip -apk del --no-cache unzip - -chmod 0755 /etc/init.d/k3s-agent -chmod 0755 /etc/periodic/15min/mulga-eks-state-report - -mkdir -p /etc/spinifex-eks -echo "[eks-agent-setup] done" diff --git a/scripts/images/eks-node/eks-node-role.initd b/scripts/images/eks-node/eks-node-role.initd new file mode 100755 index 000000000..8d737672e --- /dev/null +++ b/scripts/images/eks-node/eks-node-role.initd @@ -0,0 +1,27 @@ +#!/sbin/openrc-run +# eks-node-role — one-shot first-boot service that selects the node's role +# (server vs agent) from the cloud-init env and enables + starts the matching +# K3s services. The unified eks-node AMI bakes every role's services but leaves +# them out of the default runlevel; this selector adds the correct ones and then +# removes itself so it runs exactly once. + +description="EKS node role selector (server vs agent)" +# Backgrounded: the selector calls `rc-service k3s start`, which blocks on k3s' +# start-stop-daemon --wait window; backgrounding keeps it off the runlevel's +# critical path. command_background is also required for output_log/error_log. +command="/usr/local/sbin/eks-node-role" +command_background="yes" +pidfile="/run/eks-node-role.pid" +# Route to the serial console so the role decision + any failure is visible in +# the orchestrator's host-side console capture — the VM has no operator login. +output_log="/dev/console" +error_log="/dev/console" + +depend() { + # Needs the same substrate the role services do, since it starts them. + need net cgroups + # cloud-init write_files delivers /etc/spinifex-eks/first-boot.env (the role + # source) in the cloud-config/cloud-final stages. Order after them so the + # selector never races ahead and reads a missing env file on a fast boot. + after firewall cloud-config cloud-final +} diff --git a/scripts/images/eks-node/eks-node-role.sh b/scripts/images/eks-node/eks-node-role.sh new file mode 100755 index 000000000..22948623f --- /dev/null +++ b/scripts/images/eks-node/eks-node-role.sh @@ -0,0 +1,71 @@ +#!/bin/sh +set -eu + +# eks-node-role — first-boot role selector for the unified eks-node AMI. +# +# The same image boots either as a K3s control-plane server or a worker agent; +# the role is chosen per-instance at first boot from SPINIFEX_K3S_ROLE in the +# cloud-init env file. This script enables + starts the matching OpenRC services +# (none are in the default runlevel at bake time), records the resolved role to +# ROLE_FILE so later boots and the etcd-snapshot cron can branch without +# re-parsing, then removes itself from the runlevel so it runs exactly once. +# +# server → eks-token-webhook, k3s (server), k3s-first-boot (bootstrap publish) +# agent → k3s-agent +# +# Paths are overridable via env so the selection logic is unit-testable (bats) +# without root or a real /etc. + +ROLE_FILE="${EKS_NODE_ROLE_FILE:-/etc/spinifex-eks/role}" +ENVFILE="${EKS_NODE_ENVFILE:-/etc/spinifex-eks/first-boot.env}" +AGENT_ENVFILE="${EKS_NODE_AGENT_ENVFILE:-/etc/spinifex-eks/agent.env}" +LOGTAG="eks-node-role" + +log() { echo "[${LOGTAG}] $*"; } +die() { log "ERROR: $*"; exit 1; } + +# Already resolved on a prior boot — the role services are in the runlevel and +# this service should have been pulled, but guard anyway so a stray re-run is a +# no-op rather than a double-enable. +if [ -f "${ROLE_FILE}" ]; then + log "role already resolved ($(cat "${ROLE_FILE}")); nothing to do" + exit 0 +fi + +# Resolve the role. Server seeds SPINIFEX_K3S_ROLE in first-boot.env; a worker +# seed that predates the explicit var is inferred from the presence of agent.env +# (K3S_URL/K3S_TOKEN live there). +ROLE="" +if [ -f "${ENVFILE}" ]; then + ROLE=$(. "${ENVFILE}"; printf '%s' "${SPINIFEX_K3S_ROLE:-}") +fi +if [ -z "${ROLE}" ] && [ -f "${AGENT_ENVFILE}" ]; then + ROLE="agent" +fi + +case "${ROLE}" in + server) + log "configuring server role" + rc-update add eks-token-webhook default + rc-update add k3s default + rc-update add k3s-first-boot default + rc-update add mulga-eks-state-report default + rc-service eks-token-webhook start + rc-service k3s start + rc-service k3s-first-boot start + rc-service mulga-eks-state-report start + ;; + agent) + log "configuring agent role" + rc-update add k3s-agent default + rc-service k3s-agent start + ;; + *) + die "SPINIFEX_K3S_ROLE missing or invalid: '${ROLE}'" + ;; +esac + +mkdir -p "$(dirname "${ROLE_FILE}")" +printf '%s\n' "${ROLE}" > "${ROLE_FILE}" +rc-update del eks-node-role default 2>/dev/null || true +log "role '${ROLE}' configured" diff --git a/scripts/images/eks-node/eks-node-role_test.sh b/scripts/images/eks-node/eks-node-role_test.sh new file mode 100755 index 000000000..ac6d78f33 --- /dev/null +++ b/scripts/images/eks-node/eks-node-role_test.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# Self-contained POSIX test for eks-node-role.sh role selection. No bats/root +# needed: rc-update / rc-service are stubbed on PATH and record their calls; the +# script's file paths are redirected into a temp dir via its EKS_NODE_* env knobs. +# +# Run: sh scripts/images/eks-node/eks-node-role_test.sh +set -eu + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +SELECTOR="${SCRIPT_DIR}/eks-node-role.sh" +WORK=$(mktemp -d) +trap 'rm -rf "${WORK}"' EXIT + +# Stub rc-update / rc-service: append "cmd args" to ${CALLS} when invoked. +STUBBIN="${WORK}/bin" +mkdir -p "${STUBBIN}" +for tool in rc-update rc-service; do + cat > "${STUBBIN}/${tool}" <> "\${CALLS}" +exit 0 +EOF + chmod +x "${STUBBIN}/${tool}" +done + +PASS=0 +FAIL=0 +check() { # check + if [ "$2" = "$3" ]; then + PASS=$((PASS + 1)) + else + FAIL=$((FAIL + 1)) + echo "FAIL: $1" + echo " expected: [$2]" + echo " actual: [$3]" + fi +} + +# run_case : resets a per-case dir, exports the knobs; caller seeds env +# files then invokes the selector. Sets CASE (dir), CALLS (log), ROLE_FILE. +run_case() { + CASE="${WORK}/$1" + mkdir -p "${CASE}" + CALLS="${CASE}/calls" + : > "${CALLS}" + ROLE_FILE="${CASE}/role" + export CALLS + export EKS_NODE_ROLE_FILE="${ROLE_FILE}" + export EKS_NODE_ENVFILE="${CASE}/first-boot.env" + export EKS_NODE_AGENT_ENVFILE="${CASE}/agent.env" +} + +# Run the selector, capturing its exit code in ${rc} without tripping set -e. +invoke() { rc=0; PATH="${STUBBIN}:${PATH}" sh "${SELECTOR}" >/dev/null 2>&1 || rc=$?; } + +# --- Case 1: explicit server role --- +run_case server +printf 'SPINIFEX_K3S_ROLE=server\nEKS_CLUSTER_NAME=alpha\n' > "${EKS_NODE_ENVFILE}" +invoke +check "server: exit 0" 0 "${rc}" +check "server: role file" "server" "$(cat "${ROLE_FILE}")" +check "server: enables webhook" "yes" "$(grep -q 'rc-update add eks-token-webhook default' "${CALLS}" && echo yes || echo no)" +check "server: enables k3s" "yes" "$(grep -q 'rc-update add k3s default' "${CALLS}" && echo yes || echo no)" +check "server: enables k3s-first-boot" "yes" "$(grep -q 'rc-update add k3s-first-boot default' "${CALLS}" && echo yes || echo no)" +check "server: starts k3s" "yes" "$(grep -q 'rc-service k3s start' "${CALLS}" && echo yes || echo no)" +check "server: no k3s-agent" "no" "$(grep -q 'k3s-agent' "${CALLS}" && echo yes || echo no)" +check "server: self-disables" "yes" "$(grep -q 'rc-update del eks-node-role default' "${CALLS}" && echo yes || echo no)" + +# --- Case 2: explicit agent role --- +run_case agent_explicit +printf 'SPINIFEX_K3S_ROLE=agent\n' > "${EKS_NODE_ENVFILE}" +invoke +check "agent: exit 0" 0 "${rc}" +check "agent: role file" "agent" "$(cat "${ROLE_FILE}")" +check "agent: enables k3s-agent" "yes" "$(grep -q 'rc-update add k3s-agent default' "${CALLS}" && echo yes || echo no)" +check "agent: no server services" "no" "$(grep -qE 'eks-token-webhook|k3s-first-boot|add k3s default' "${CALLS}" && echo yes || echo no)" + +# --- Case 3: agent inferred from agent.env (no explicit role) --- +run_case agent_inferred +printf 'K3S_URL=https://nlb:443\nK3S_TOKEN=abc\n' > "${EKS_NODE_AGENT_ENVFILE}" +invoke +check "infer: exit 0" 0 "${rc}" +check "infer: role file" "agent" "$(cat "${ROLE_FILE}")" +check "infer: enables k3s-agent" "yes" "$(grep -q 'rc-update add k3s-agent default' "${CALLS}" && echo yes || echo no)" + +# --- Case 4: missing role + no env files → fail, no role file, no services --- +run_case missing +invoke +check "missing: nonzero exit" "1" "${rc}" +check "missing: no role file" "absent" "$([ -f "${ROLE_FILE}" ] && echo present || echo absent)" +check "missing: no rc calls" "" "$(cat "${CALLS}")" + +# --- Case 5: already resolved (role file present) → no-op, exit 0 --- +run_case resolved +printf 'SPINIFEX_K3S_ROLE=server\n' > "${EKS_NODE_ENVFILE}" +printf 'server\n' > "${ROLE_FILE}" +invoke +check "resolved: exit 0" 0 "${rc}" +check "resolved: no rc calls" "" "$(cat "${CALLS}")" + +echo "---" +echo "PASS=${PASS} FAIL=${FAIL}" +[ "${FAIL}" -eq 0 ] diff --git a/scripts/images/eks-node/eks-token-webhook.initd b/scripts/images/eks-node/eks-token-webhook.initd new file mode 100755 index 000000000..6b8260b28 --- /dev/null +++ b/scripts/images/eks-node/eks-token-webhook.initd @@ -0,0 +1,41 @@ +#!/sbin/openrc-run +# eks-token-webhook — K8s TokenReview authenticator for IAM-backed kubectl. +# Binds 127.0.0.1:8443 only; kube-apiserver is the sole client (loopback path). +# Relays `aws eks get-token` bearer tokens through the AWS gateway broker (SigV4 +# HTTPS), which host-side verifies via Mulga STS + resolves the principal to a +# TokenReview via the cluster AccessEntry KV — the VM never speaks core NATS. +# Ordered `before k3s` so its apiserver kubeconfig exists before k3s reads +# --authentication-token-webhook-config-file. + +description="EKS Token Review webhook (Mulga)" +command="/usr/local/bin/eks-token-webhook" +command_args="--addr=127.0.0.1:8443" +command_background="yes" +pidfile="/run/eks-token-webhook.pid" +# Headless appliance: no SSH. Route the webhook's stdout/stderr to the serial +# console so its startup + per-request decisions land in the host-captured +# console log (the only off-VM window into webhook health). +output_log="/dev/console" +error_log="/dev/console" + +# Gateway endpoint + SigV4 creds + cluster identity come from the cloud-init- +# seeded first-boot env. Sourced here and exported so start-stop-daemon hands +# them to the binary, which reads EKS_GATEWAY_* / EKS_ACCESS_KEY / EKS_SECRET_KEY +# / EKS_REGION / EKS_ACCOUNT_ID / EKS_CLUSTER_NAME from its environment. +if [ -f /etc/spinifex-eks/first-boot.env ]; then + # shellcheck disable=SC1091 + . /etc/spinifex-eks/first-boot.env + export EKS_GATEWAY_URL EKS_GATEWAY_CA EKS_ACCESS_KEY EKS_SECRET_KEY EKS_REGION EKS_ACCOUNT_ID EKS_CLUSTER_NAME +fi + +depend() { + need net + before k3s +} + +start_pre() { + if [ ! -f /etc/spinifex-eks/first-boot.env ]; then + eerror "/etc/spinifex-eks/first-boot.env not present — cloud-init did not seed gateway creds" + return 1 + fi +} diff --git a/scripts/images/eks-agent/k3s-agent.initd b/scripts/images/eks-node/k3s-agent.initd old mode 100644 new mode 100755 similarity index 100% rename from scripts/images/eks-agent/k3s-agent.initd rename to scripts/images/eks-node/k3s-agent.initd diff --git a/scripts/images/eks-node/k3s-first-boot.initd b/scripts/images/eks-node/k3s-first-boot.initd new file mode 100755 index 000000000..2e6fa6da7 --- /dev/null +++ b/scripts/images/eks-node/k3s-first-boot.initd @@ -0,0 +1,30 @@ +#!/sbin/openrc-run +# k3s-first-boot — one-shot OpenRC service that runs the first-boot publisher +# after K3s is up. Self-disables by removing itself from the default runlevel +# once the sentinel file is gone. + +description="K3s first-boot publisher (node-token + admin kubeconfig)" +# Backgrounded: the publisher waits up to 5min for K3s /healthz before it can +# read the node-token + kubeconfig, so it must not block the default runlevel. +# command_background is also required for output_log/error_log — start-stop-daemon +# rejects --stdout/--stderr without --background ("Failed to start k3s-first-boot"). +command="/usr/local/sbin/k3s-first-boot" +command_background="yes" +pidfile="/run/k3s-first-boot.pid" +# Route to the serial console so the bootstrap publisher's progress (healthz +# wait, token/kubeconfig publish, errors) is visible in the orchestrator's +# host-side console capture — the VM has no operator login. Low volume. +output_log="/dev/console" +error_log="/dev/console" + +depend() { + need k3s + after k3s +} + +start_pre() { + if [ ! -f /var/lib/spinifex-eks/first-boot.pending ]; then + einfo "first-boot already complete; nothing to do" + return 0 + fi +} diff --git a/scripts/images/eks-node/k3s-first-boot.sh b/scripts/images/eks-node/k3s-first-boot.sh new file mode 100755 index 000000000..72166df9f --- /dev/null +++ b/scripts/images/eks-node/k3s-first-boot.sh @@ -0,0 +1,139 @@ +#!/bin/sh +set -eu + +# k3s-first-boot — runs once after the K3s server reaches a healthy state. +# Reads the bootstrap node-token and admin kubeconfig that K3s writes at +# server startup, rewrites the kubeconfig server address to the cluster's +# NLB endpoint (so workers and external kubectl can use it), and publishes +# the bootstrap artifacts to the host via the AWS gateway (SigV4-signed HTTPS +# POST — the eks-gateway-publish helper) for the spinifex cluster reconciler +# to consume into KV. The gateway relays onto the eks.bus.* NATS subjects; the +# VM never speaks NATS directly. +# +# Required env (from cloud-init user-data /etc/spinifex-eks/first-boot.env): +# EKS_GATEWAY_URL https://{mgmt-host}:9999 (AWS gateway) +# EKS_GATEWAY_CA /etc/spinifex-eks/gateway-ca.pem (TLS CA bundle) +# EKS_ACCESS_KEY system SigV4 access key id +# EKS_SECRET_KEY system SigV4 secret access key +# EKS_REGION SigV4 signing region +# EKS_ACCOUNT_ID +# EKS_CLUSTER_NAME +# EKS_NLB_ENDPOINT https://{cluster}.{accountID}.eks.{region}.{suffix} +# +# Idempotent: a sentinel file at /var/lib/spinifex-eks/first-boot.pending gates +# execution. On success the sentinel is removed and the OpenRC service is +# pulled from the default runlevel so it does not retry on subsequent boots. + +SENTINEL=/var/lib/spinifex-eks/first-boot.pending +ENVFILE=/etc/spinifex-eks/first-boot.env +LOGTAG="k3s-first-boot" + +log() { echo "[${LOGTAG}] $*"; } +die() { log "ERROR: $*"; exit 1; } + +if [ ! -f "${SENTINEL}" ]; then + log "sentinel missing — first boot already complete" + exit 0 +fi + +if [ ! -f "${ENVFILE}" ]; then + die "${ENVFILE} not found — cloud-init did not seed first-boot env" +fi + +# set -a so the sourced KEY=value lines are exported: the eks-gateway-publish +# child reads its config from the environment (EKS_ACCOUNT_ID etc.). A bare +# source leaves them unexported and the helper exits "--account-id is required". +set -a +# shellcheck disable=SC1090 +. "${ENVFILE}" +set +a + +for v in EKS_GATEWAY_URL EKS_ACCESS_KEY EKS_SECRET_KEY EKS_ACCOUNT_ID EKS_CLUSTER_NAME EKS_NLB_ENDPOINT; do + eval "val=\${$v:-}" + [ -n "${val}" ] || die "env ${v} not set" +done + +# Paths K3s writes during server startup: the bootstrap node-token and the +# admin kubeconfig (used both to gate readiness and to publish downstream). +TOKEN_FILE=/var/lib/rancher/k3s/server/node-token +KUBECONFIG_FILE=/etc/rancher/k3s/k3s.yaml + +# 1. Wait for the apiserver to be ready to serve, gating on /readyz. K3s runs +# the apiserver with anonymous-auth=false, so an anonymous probe returns 401, +# never "ok" — the probe must be authenticated. Use the node's admin +# kubeconfig (kubectl get --raw), which k3s writes early in startup. kubectl +# exits 0 only when /readyz returns 200; on a failing sub-check it exits +# non-zero, so the body "ok" check plus exit status gates correctly. Every +# 30s the failing /readyz checks are logged so a stuck boot is diagnosable +# from the captured serial console. +log "waiting for K3s apiserver readiness (/readyz) on 127.0.0.1:6443" +i=0 +ready=0 +while [ "${i}" -lt 300 ]; do + if [ -r "${KUBECONFIG_FILE}" ] && \ + [ "$(kubectl --kubeconfig "${KUBECONFIG_FILE}" get --raw='/readyz' 2>/dev/null)" = "ok" ]; then + log "K3s apiserver ready after ${i}s" + ready=1 + break + fi + if [ $((i % 30)) -eq 0 ]; then + log "apiserver not ready after ${i}s:" + if [ -r "${KUBECONFIG_FILE}" ]; then + kubectl --kubeconfig "${KUBECONFIG_FILE}" get --raw='/readyz?verbose' 2>&1 \ + | grep -E '^\[-\]|failed$' | head -20 | while IFS= read -r l; do log " ${l}"; done + else + log " (admin kubeconfig ${KUBECONFIG_FILE} not written yet)" + fi + fi + i=$((i + 5)) + sleep 5 +done +if [ "${ready}" -ne 1 ]; then + log "K3s apiserver not ready within 5 minutes; last /readyz body:" + kubectl --kubeconfig "${KUBECONFIG_FILE}" get --raw='/readyz?verbose' 2>&1 \ + | head -40 | while IFS= read -r l; do log " ${l}"; done + log "last 40 lines of /var/log/k3s.log follow:" + tail -n 40 /var/log/k3s.log 2>/dev/null || log "(no /var/log/k3s.log)" + die "K3s apiserver not ready within 5 minutes" +fi + +# 2. Read the four bootstrap artifacts K3s wrote at server startup. +[ -r "${TOKEN_FILE}" ] || die "${TOKEN_FILE} unreadable" +[ -r "${KUBECONFIG_FILE}" ] || die "${KUBECONFIG_FILE} unreadable" + +NODE_TOKEN=$(cat "${TOKEN_FILE}") +# K3s ships kubeconfig with server: https://127.0.0.1:6443 — rewrite to the +# NLB endpoint so it works from outside the control plane VM. +KUBECONFIG_REWRITTEN=$(sed "s|server: https://127\.0\.0\.1:6443|server: ${EKS_NLB_ENDPOINT}|" "${KUBECONFIG_FILE}") +# The cluster CA the daemon records (base64 PEM) is exactly the +# certificate-authority-data the admin kubeconfig already embeds. +CA_B64=$(awk '/certificate-authority-data:/ {print $2; exit}' "${KUBECONFIG_FILE}") +[ -n "${CA_B64}" ] || die "no certificate-authority-data in ${KUBECONFIG_FILE}" +# The OIDC JWKS the apiserver serves from the signing key cloud-init seeded. The +# daemon cross-checks its kid/kty against the controller-generated keypair. +JWKS=$(kubectl --kubeconfig "${KUBECONFIG_FILE}" get --raw='/openid/v1/jwks' 2>/dev/null) +[ -n "${JWKS}" ] || die "apiserver returned empty /openid/v1/jwks" + +# 3. Publish the four bootstrap messages through the AWS gateway. Each is a +# BootstrapEnvelope JSON document (handlers/eks/nats_bootstrap.go); jq encodes +# the values so embedded newlines/quotes in the kubeconfig and JWKS stay valid +# JSON. eks-gateway-publish SigV4-signs the POST and retries until the gateway +# acks, then the gateway relays onto eks.bus.{account}.{cluster}.{kind}. It +# reads EKS_GATEWAY_URL/EKS_GATEWAY_CA/EKS_ACCESS_KEY/EKS_SECRET_KEY/EKS_REGION/ +# EKS_ACCOUNT_ID/EKS_CLUSTER_NAME from the env file already sourced above. + +# publish_envelope : reads the envelope JSON from stdin. +publish_envelope() { + log "publishing $1 via gateway" + eks-gateway-publish -channel bootstrap -kind "$1" +} + +jq -n --arg t "${NODE_TOKEN}" '{token: $t}' | publish_envelope k3s-bootstrap-token +jq -n --arg k "${KUBECONFIG_REWRITTEN}" '{adminKubeconfig: $k}' | publish_envelope k3s-admin-kubeconfig +jq -n --arg j "${JWKS}" '{jwks: $j}' | publish_envelope k3s-oidc-jwks +jq -n --arg c "${CA_B64}" '{certificateAuthority: $c}' | publish_envelope k3s-ca + +# 4. Self-disable. Remove sentinel, pull from runlevel. +rm -f "${SENTINEL}" +rc-update del k3s-first-boot default 2>/dev/null || true +log "first boot complete" diff --git a/scripts/images/eks-node/k3s.initd b/scripts/images/eks-node/k3s.initd new file mode 100755 index 000000000..b4c0f54ae --- /dev/null +++ b/scripts/images/eks-node/k3s.initd @@ -0,0 +1,65 @@ +#!/sbin/openrc-run +# K3s server control plane. The per-cluster config.yaml sets cluster-init, so +# k3s uses the embedded etcd datastore (required for multi-server HA). Data +# lives at /var/lib/rancher/k3s; subsequent starts reuse it. + +description="K3s Kubernetes server (Mulga EKS control plane)" +command="/usr/local/bin/k3s" +command_args="server --config /etc/rancher/k3s/config.yaml" +pidfile="/run/k3s.pid" + +# Supervise the apiserver. A transient k3s crash must self-heal: without a +# supervisor a single exit leaves the control plane permanently dead, the NLB +# target never recovers, and the cluster wedges ACTIVE-but-unreachable. +supervisor="supervise-daemon" +respawn_delay=5 +respawn_max=0 +output_log="/var/log/k3s.log" +error_log="/var/log/k3s.log" + +depend() { + need net cgroups + after firewall +} + +start_pre() { + # config.yaml is written by k3s-first-boot from the skeleton + cloud-init + # data. Refuse to start if neither exists. + if [ ! -f /etc/rancher/k3s/config.yaml ]; then + if [ -f /etc/rancher/k3s/config.yaml.skel ]; then + cp /etc/rancher/k3s/config.yaml.skel /etc/rancher/k3s/config.yaml + else + eerror "no /etc/rancher/k3s/config.yaml and no .skel to derive from" + return 1 + fi + fi + + # Deferred built-in ingress: when this cluster wants traefik + # (EKS_DEFER_TRAEFIK=1) but the control plane has not yet stabilised (no + # traefik-enabled sentinel), stage a deploy-controller .skip marker so k3s + # writes traefik.yaml/traefik-crd.yaml but does NOT apply them. Staged here, + # pre-start, so the marker exists before the deploy controller first scans — + # racing it post-start could apply traefik (and crash etcd) anyway. + # mulga-eks-state-report removes the marker once the apiserver is healthy. + if [ -f /etc/spinifex-eks/first-boot.env ]; then + defer=$(. /etc/spinifex-eks/first-boot.env 2>/dev/null; echo "${EKS_DEFER_TRAEFIK:-0}") + if [ "${defer}" = "1" ] && [ ! -f /var/lib/spinifex-eks/traefik-enabled ]; then + mkdir -p /var/lib/rancher/k3s/server/manifests + : > /var/lib/rancher/k3s/server/manifests/traefik.yaml.skip + : > /var/lib/rancher/k3s/server/manifests/traefik-crd.yaml.skip + fi + fi + + # The apiserver is configured with --authentication-token-webhook-config-file + # pointing at the kubeconfig eks-token-webhook writes at startup. `before k3s` + # orders the webhook's start but it backgrounds, so wait (bounded) for the + # kubeconfig to land before letting the apiserver load a missing file. + i=0 + while [ ! -f /etc/spinifex-eks/token-webhook.kubeconfig ] && [ "${i}" -lt 30 ]; do + sleep 1 + i=$((i + 1)) + done + if [ ! -f /etc/spinifex-eks/token-webhook.kubeconfig ]; then + ewarn "eks-token-webhook kubeconfig absent after ${i}s; IAM token auth will be unavailable until the webhook starts" + fi +} diff --git a/scripts/images/eks-node/manifest.conf b/scripts/images/eks-node/manifest.conf new file mode 100644 index 000000000..317270b3c --- /dev/null +++ b/scripts/images/eks-node/manifest.conf @@ -0,0 +1,47 @@ +IMAGE_NAME="eks-node" +IMAGE_DESCRIPTION="Unified EKS K3s node VM — Alpine + K3s v1.32.5+k3s1 + eks-token-webhook; role (server|agent) chosen at first boot" +DISTRO="alpine" +ALPINE_VERSION="3.21.7" + +# 4G — server superset (K3s server binary + embedded-etcd datastore working set under +# /var/lib/rancher/k3s on the root disk). Agents use the same image; the ~10MB +# of unused server binary + webhook is dwarfed by the headroom for node images. +IMAGE_SIZE="4G" + +# Server superset of both package sets (etcd-ctl is ~5MB, not worth branching). +# K3s pulls cgroups + iptables; jq builds the bootstrap-message JSON envelopes; +# etcd-client used by the snapshot script (server role only). The control plane +# reaches the host via the AWS gateway (eks-gateway-publish), not a NATS CLI. +APK_PACKAGES="ca-certificates curl jq iptables ip6tables conntrack-tools cgroup-tools etcd-ctl openrc" + +# eks-token-webhook binary always installed (agents never start it). +# eks-gateway-publish relays the bootstrap envelopes + state reports to the AWS +# gateway over SigV4 HTTPS (server role only). The k3s binary is downloaded + +# SHA256-verified by setup.sh (network in chroot). +BUILD_COMMANDS="CGO_ENABLED=0 GOFIPS140=v1.0.0 go build -o bin/eks-token-webhook ./cmd/eks-token-webhook && CGO_ENABLED=0 GOFIPS140=v1.0.0 go build -o bin/eks-gateway-publish ./cmd/eks-gateway-publish" + +INSTALL_BINARIES="bin/eks-token-webhook:/usr/local/bin/eks-token-webhook bin/eks-gateway-publish:/usr/local/bin/eks-gateway-publish" + +# All role services ship; none but the selector are enabled at bake time. +# eks-node-role picks server vs agent at first boot and enables the rest. +INSTALL_FILES="\ +scripts/images/eks-node/eks-node-role.initd:/etc/init.d/eks-node-role \ +scripts/images/eks-node/eks-node-role.sh:/usr/local/sbin/eks-node-role \ +scripts/images/eks-node/k3s.initd:/etc/init.d/k3s \ +scripts/images/eks-node/k3s-agent.initd:/etc/init.d/k3s-agent \ +scripts/images/eks-node/eks-token-webhook.initd:/etc/init.d/eks-token-webhook \ +scripts/images/eks-node/k3s-first-boot.initd:/etc/init.d/k3s-first-boot \ +scripts/images/eks-node/k3s-first-boot.sh:/usr/local/sbin/k3s-first-boot \ +scripts/images/eks-node/mulga-eks-state-report.initd:/etc/init.d/mulga-eks-state-report \ +scripts/images/eks-node/mulga-eks-state-report.sh:/usr/local/sbin/mulga-eks-state-report \ +scripts/images/eks-node/mulga-eks-etcd-snapshot.sh:/etc/periodic/daily/mulga-eks-etcd-snapshot" + +# Only crond (etcd snapshot) + the role selector at bake. The selector adds the +# role-specific services (k3s/eks-token-webhook/k3s-first-boot/state-report or +# k3s-agent) to the default runlevel on first boot. +ENABLE_SERVICES="crond eks-node-role" + +SETUP_SCRIPT="scripts/images/eks-node/setup.sh" + +AMI_NAME="spinifex-eks-node" +SYSTEM_TAG="spinifex:managed-by=eks" diff --git a/scripts/images/eks-server/mulga-eks-etcd-snapshot.sh b/scripts/images/eks-node/mulga-eks-etcd-snapshot.sh old mode 100644 new mode 100755 similarity index 90% rename from scripts/images/eks-server/mulga-eks-etcd-snapshot.sh rename to scripts/images/eks-node/mulga-eks-etcd-snapshot.sh index 3798b258f..782a4b2ae --- a/scripts/images/eks-server/mulga-eks-etcd-snapshot.sh +++ b/scripts/images/eks-node/mulga-eks-etcd-snapshot.sh @@ -12,6 +12,12 @@ set -eu # Retention: managed via S3 lifecycle policy on the bucket; this script does # not prune. +# Worker nodes share the unified eks-node AMI but have no etcd/SQLite datastore +# to snapshot. The role file is written once by eks-node-role at first boot. +if [ "$(cat /etc/spinifex-eks/role 2>/dev/null)" = "agent" ]; then + exit 0 +fi + ENVFILE=/etc/spinifex-eks/etcd-snapshot.env [ -f "${ENVFILE}" ] || { logger -t mulga-eks-etcd-snapshot "${ENVFILE} missing"; exit 0; } # shellcheck disable=SC1090 diff --git a/scripts/images/eks-node/mulga-eks-state-report.initd b/scripts/images/eks-node/mulga-eks-state-report.initd new file mode 100644 index 000000000..34b26fb4c --- /dev/null +++ b/scripts/images/eks-node/mulga-eks-state-report.initd @@ -0,0 +1,29 @@ +#!/sbin/openrc-run +# mulga-eks-state-report — server-role service that publishes K3s health + +# node_count to the management NATS bus every STATE_REPORT_INTERVAL seconds. The +# spinifex cluster reconciler consumes this to gate CREATING → ACTIVE and to +# track nodegroup join/scale, since the apiserver (bound to the VPC node-ip) is +# unreachable from the host. Enabled on the server role by eks-node-role.sh. + +description="Mulga EKS control-plane state reporter" +command="/usr/local/sbin/mulga-eks-state-report" +command_background="yes" +pidfile="/run/mulga-eks-state-report.pid" +output_log="/var/log/mulga-eks-state-report.log" +error_log="/var/log/mulga-eks-state-report.log" + +# Cadence the report loop sleeps between publishes. 30s is well under the +# reconciler's 90s stale window, so a missed tick does not flap health. +export STATE_REPORT_INTERVAL="${STATE_REPORT_INTERVAL:-30}" + +depend() { + need net + after k3s +} + +start_pre() { + if [ ! -f /etc/spinifex-eks/first-boot.env ]; then + eerror "/etc/spinifex-eks/first-boot.env not present — cloud-init did not seed NATS creds" + return 1 + fi +} diff --git a/scripts/images/eks-node/mulga-eks-state-report.sh b/scripts/images/eks-node/mulga-eks-state-report.sh new file mode 100755 index 000000000..ba737e017 --- /dev/null +++ b/scripts/images/eks-node/mulga-eks-state-report.sh @@ -0,0 +1,92 @@ +#!/bin/sh +set -eu + +# mulga-eks-state-report — publishes K3s server health to the spinifex cluster +# reconciler through the AWS gateway. The apiserver binds the VPC node-ip, +# unreachable from the host-side daemon, so the daemon cannot probe /healthz +# directly — this report carries {healthz, node_count}. eks-gateway-publish +# SigV4-signs the POST to the gateway, which relays it onto the management NATS +# bus the daemon shares; the VM never speaks NATS directly. +# +# Subject (gateway-side): eks.state.{accountID}.{clusterName}.server +# Payload: JSON { healthz: "ok"|"fail", node_count: N, ts: } +# +# Reads gateway creds + cluster identity from the cloud-init-seeded first-boot +# env (same vars k3s-first-boot.sh publishes its bootstrap artifacts with). Run +# as a loop by the mulga-eks-state-report OpenRC service when +# STATE_REPORT_INTERVAL is set; one-shot otherwise (e.g. a manual invocation). + +ENVFILE=/etc/spinifex-eks/first-boot.env +[ -f "${ENVFILE}" ] || { logger -t mulga-eks-state-report "${ENVFILE} missing — exiting"; exit 0; } +# set -a so the sourced KEY=value lines are exported to the eks-gateway-publish +# child (it reads EKS_ACCOUNT_ID etc. from its environment); a bare source +# leaves them unexported and the helper exits "--account-id is required". +set -a +# shellcheck disable=SC1090 +. "${ENVFILE}" +set +a + +: "${EKS_GATEWAY_URL:?}" +: "${EKS_ACCESS_KEY:?}" +: "${EKS_SECRET_KEY:?}" +: "${EKS_ACCOUNT_ID:?}" +: "${EKS_CLUSTER_NAME:?}" + +KUBECONFIG=${KUBECONFIG:-/etc/rancher/k3s/k3s.yaml} +export KUBECONFIG + +# --- Deferred built-in ingress enable ------------------------------------- +# traefik is deferred at boot (its helm-install Job overloads the embedded etcd +# during bootstrap and can crash the control plane). k3s writes traefik.yaml but +# k3s.initd pre-stages a .yaml.skip marker the deploy controller honours, +# so traefik is not applied. Once the apiserver has been healthy for +# TRAEFIK_ENABLE_AFTER consecutive reports — a crash resets the streak, so this +# only fires post-stabilisation — remove the skip markers so the deploy +# controller installs traefik with etcd headroom. Gated on EKS_DEFER_TRAEFIK=1 +# (set only for clusters that want built-in ingress) and run once via a sentinel. +TRAEFIK_ENABLE_AFTER=${TRAEFIK_ENABLE_AFTER:-4} +TRAEFIK_ENABLED_SENTINEL=/var/lib/spinifex-eks/traefik-enabled +K3S_MANIFESTS=/var/lib/rancher/k3s/server/manifests +healthy_streak=0 + +maybe_enable_traefik() { + [ "${EKS_DEFER_TRAEFIK:-0}" = "1" ] || return 0 + [ -f "${TRAEFIK_ENABLED_SENTINEL}" ] && return 0 + if [ "${1}" = "ok" ]; then + healthy_streak=$((healthy_streak + 1)) + else + healthy_streak=0 + return 0 + fi + [ "${healthy_streak}" -ge "${TRAEFIK_ENABLE_AFTER}" ] || return 0 + rm -f "${K3S_MANIFESTS}/traefik.yaml.skip" "${K3S_MANIFESTS}/traefik-crd.yaml.skip" + : > "${TRAEFIK_ENABLED_SENTINEL}" + logger -t mulga-eks-state-report \ + "enabled deferred built-in ingress (traefik) after ${healthy_streak} healthy reports" +} + +publish_report() { + if curl -sk --max-time 3 https://127.0.0.1:6443/healthz | grep -q '^ok$'; then + health=ok + # Count Ready nodes only — k3s retains terminated workers as NotReady + # until manually pruned, so a raw node count never drops on scale-down. + node_count=$(kubectl get nodes --no-headers 2>/dev/null | awk '$2=="Ready"' | wc -l | tr -d ' ') + else + health=fail + node_count=0 + fi + payload=$(printf '{"healthz":"%s","node_count":%s,"ts":%s}' "${health}" "${node_count}" "$(date +%s)") + printf '%s' "${payload}" | eks-gateway-publish -channel state 2>&1 \ + | logger -t mulga-eks-state-report +} + +# STATE_REPORT_INTERVAL set → run forever (service mode); unset → one-shot. +if [ -n "${STATE_REPORT_INTERVAL:-}" ] && [ "${STATE_REPORT_INTERVAL}" -gt 0 ] 2>/dev/null; then + while true; do + publish_report + maybe_enable_traefik "${health}" + sleep "${STATE_REPORT_INTERVAL}" + done +else + publish_report +fi diff --git a/scripts/images/eks-node/setup.sh b/scripts/images/eks-node/setup.sh new file mode 100755 index 000000000..2e0896acb --- /dev/null +++ b/scripts/images/eks-node/setup.sh @@ -0,0 +1,96 @@ +#!/bin/sh +set -eu + +# setup.sh — guest customisation for the unified eks-node AMI (server + agent). +# +# Runs inside the libguestfs appliance (via virt-customize --run) under +# build-system-image.sh after packages and binaries are installed. Downloads the +# pinned K3s binary, verifies its SHA256, drops it into /usr/local/bin, sets +# executable bits on all role init scripts + cron entries, and writes the K3s +# server config skeleton. The role (server vs agent) is selected per-instance at +# first boot by eks-node-role. +# +# Network access inside the appliance is provided by virt-customize --network. +# curl is in APK_PACKAGES. + +K3S_VERSION="v1.32.5+k3s1" +K3S_URL_BASE="https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}" + +# Pull the upstream signed checksums file and pin the amd64 line. A tampered +# release replacing both files would still produce a self-consistent download, +# so the SHA file URL is the trust anchor — anyone forging a Mulga AMI build +# would need to compromise the k3s-io GitHub release artefacts. +echo "[eks-node-setup] fetching k3s checksums ${K3S_VERSION}" +curl -fsSL -o /tmp/k3s-checksums.txt "${K3S_URL_BASE}/sha256sum-amd64.txt" +K3S_SHA256=$(awk '/[ \t]k3s$/{print $1; exit}' /tmp/k3s-checksums.txt) +if [ -z "${K3S_SHA256}" ]; then + echo "[eks-node-setup] could not parse k3s sha256 from upstream checksums" + cat /tmp/k3s-checksums.txt + exit 1 +fi +echo "[eks-node-setup] downloading k3s ${K3S_VERSION} (sha256=${K3S_SHA256})" +curl -fsSL -o /usr/local/bin/k3s "${K3S_URL_BASE}/k3s" +echo "${K3S_SHA256} /usr/local/bin/k3s" > /tmp/k3s.sha256 +if ! sha256sum -c /tmp/k3s.sha256; then + echo "[eks-node-setup] k3s SHA256 mismatch — refusing to bake AMI" + exit 1 +fi +chmod 0755 /usr/local/bin/k3s +ln -sf /usr/local/bin/k3s /usr/local/bin/kubectl +ln -sf /usr/local/bin/k3s /usr/local/bin/crictl +ln -sf /usr/local/bin/k3s /usr/local/bin/ctr + +# Init scripts ship as 0644 from INSTALL_FILES; OpenRC requires 0755. Every +# role's services are baked; the selector enables the right ones at first boot. +chmod 0755 /etc/init.d/eks-node-role /etc/init.d/k3s /etc/init.d/k3s-agent \ + /etc/init.d/eks-token-webhook /etc/init.d/k3s-first-boot /etc/init.d/mulga-eks-state-report +chmod 0755 /usr/local/sbin/eks-node-role /usr/local/sbin/k3s-first-boot \ + /usr/local/sbin/mulga-eks-state-report +chmod 0755 /etc/periodic/daily/mulga-eks-etcd-snapshot + +# K3s server config — skeleton; cloud-init / first-boot fills in the +# per-cluster fields (cluster-cidr, service-cidr, token-file, etc). Agents +# ignore this file (they run `k3s agent`). +# IAM token-auth is wired here via kube-apiserver-arg: the eks-token-webhook +# service (ordered `before k3s`) writes its kubeconfig to +# /etc/spinifex-eks/token-webhook.kubeconfig before the apiserver reads it. +# This only affects bearer-token requests; anonymous and client-cert paths +# (the first-boot /readyz probe uses the admin kubeconfig) are unaffected. +mkdir -p /etc/rancher/k3s +cat > /etc/rancher/k3s/config.yaml.skel <<'EOF' +# Populated at first boot by cloud-init user-data via k3s-first-boot.sh. +# Fields documented at https://docs.k3s.io/installation/configuration. +# cluster-init selects the embedded etcd datastore. cloud-init write_files +# normally overrides this skeleton; kept consistent as the boot fallback. +cluster-init: true +disable: + - traefik + - servicelb + - local-storage +kube-apiserver-arg: + - "authentication-token-webhook-config-file=/etc/spinifex-eks/token-webhook.kubeconfig" + - "authentication-token-webhook-cache-ttl=5m" +EOF + +# Shared state dir for both roles (agent.env / role marker live here). +mkdir -p /etc/spinifex-eks + +# Sentinel file marker — k3s-first-boot (server role) self-disables after first +# success by checking for this path. Initial state is "not yet run". +touch /var/lib/spinifex-eks/first-boot.pending 2>/dev/null || { + mkdir -p /var/lib/spinifex-eks + touch /var/lib/spinifex-eks/first-boot.pending +} + +# Bind /dev/console to the serial port so userspace boot output — OpenRC +# service starts, cloud-init, role selection, and k3s-first-boot diagnostics — +# reaches ttyS0, which the orchestrator captures to a host-side log. The stock +# Alpine cloud image lists `console=tty0` LAST in default_kernel_opts; Linux +# makes the last console= the controlling /dev/console, so userspace logs to +# the framebuffer and the serial capture sees only kernel dmesg. Reorder so +# ttyS0 is last in both the generator config and the rendered extlinux.conf. +sed -i \ + 's|console=ttyS0,115200n8 console=ttyAMA0,115200n8 console=tty0|console=tty0 console=ttyAMA0,115200n8 console=ttyS0,115200n8|' \ + /etc/update-extlinux.conf /boot/extlinux.conf + +echo "[eks-node-setup] done" diff --git a/scripts/images/eks-server/eks-token-webhook.initd b/scripts/images/eks-server/eks-token-webhook.initd deleted file mode 100644 index 9f18d2147..000000000 --- a/scripts/images/eks-server/eks-token-webhook.initd +++ /dev/null @@ -1,17 +0,0 @@ -#!/sbin/openrc-run -# eks-token-webhook — K8s TokenReview authenticator for IAM-backed kubectl. -# Binds 127.0.0.1:8443 only; kube-apiserver is the sole client (loopback path). -# Stub binary returns 503 on every request until cs-eks-6c lands. - -description="EKS Token Review webhook (Mulga)" -command="/usr/local/bin/eks-token-webhook" -command_args="--addr=127.0.0.1:8443" -command_background="yes" -pidfile="/run/eks-token-webhook.pid" -output_log="/var/log/eks-token-webhook.log" -error_log="/var/log/eks-token-webhook.log" - -depend() { - need net - before k3s -} diff --git a/scripts/images/eks-server/k3s-first-boot.initd b/scripts/images/eks-server/k3s-first-boot.initd deleted file mode 100644 index ab7029a06..000000000 --- a/scripts/images/eks-server/k3s-first-boot.initd +++ /dev/null @@ -1,23 +0,0 @@ -#!/sbin/openrc-run -# k3s-first-boot — one-shot OpenRC service that runs the first-boot publisher -# after K3s is up. Self-disables by removing itself from the default runlevel -# once the sentinel file is gone. - -description="K3s first-boot publisher (node-token + admin kubeconfig)" -command="/usr/local/sbin/k3s-first-boot" -command_background="no" -pidfile="/run/k3s-first-boot.pid" -output_log="/var/log/k3s-first-boot.log" -error_log="/var/log/k3s-first-boot.log" - -depend() { - need k3s - after k3s -} - -start_pre() { - if [ ! -f /var/lib/spinifex-eks/first-boot.pending ]; then - einfo "first-boot already complete; nothing to do" - return 0 - fi -} diff --git a/scripts/images/eks-server/k3s-first-boot.sh b/scripts/images/eks-server/k3s-first-boot.sh deleted file mode 100644 index bd871fefc..000000000 --- a/scripts/images/eks-server/k3s-first-boot.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/sh -set -eu - -# k3s-first-boot — runs once after the K3s server reaches a healthy state. -# Reads the bootstrap node-token and admin kubeconfig that K3s writes during -# `--cluster-init`, rewrites the kubeconfig server address to the cluster's -# NLB endpoint (so workers and external kubectl can use it), and publishes -# both as one-shot NATS messages for the spinifex cluster reconciler to -# consume into KV. -# -# Required env (from cloud-init user-data /etc/spinifex-eks/first-boot.env): -# SPINIFEX_NATS_URL nats://... -# SPINIFEX_NATS_CREDS_FILE /etc/spinifex-eks/nats.creds -# EKS_ACCOUNT_ID -# EKS_CLUSTER_NAME -# EKS_NLB_ENDPOINT https://{cluster}.{accountID}.eks.{region}.{suffix} -# -# Idempotent: a sentinel file at /var/lib/spinifex-eks/first-boot.pending gates -# execution. On success the sentinel is removed and the OpenRC service is -# pulled from the default runlevel so it does not retry on subsequent boots. - -SENTINEL=/var/lib/spinifex-eks/first-boot.pending -ENVFILE=/etc/spinifex-eks/first-boot.env -LOGTAG="k3s-first-boot" - -log() { echo "[${LOGTAG}] $*"; } -die() { log "ERROR: $*"; exit 1; } - -if [ ! -f "${SENTINEL}" ]; then - log "sentinel missing — first boot already complete" - exit 0 -fi - -if [ ! -f "${ENVFILE}" ]; then - die "${ENVFILE} not found — cloud-init did not seed first-boot env" -fi - -# shellcheck disable=SC1090 -. "${ENVFILE}" - -for v in SPINIFEX_NATS_URL EKS_ACCOUNT_ID EKS_CLUSTER_NAME EKS_NLB_ENDPOINT; do - eval "val=\${$v:-}" - [ -n "${val}" ] || die "env ${v} not set" -done - -# 1. Wait for K3s /healthz. K3s self-signs initially; --insecure-skip is fine -# here, we are on loopback. -log "waiting for K3s /healthz on 127.0.0.1:6443" -i=0 -while [ "${i}" -lt 300 ]; do - if curl -sk --max-time 2 https://127.0.0.1:6443/healthz | grep -q '^ok$'; then - log "K3s healthz ok after ${i}s" - break - fi - i=$((i + 2)) - sleep 2 -done -[ "${i}" -lt 300 ] || die "K3s did not become healthy within 5 minutes" - -# 2. Read node-token + admin kubeconfig. -TOKEN_FILE=/var/lib/rancher/k3s/server/node-token -KUBECONFIG_FILE=/etc/rancher/k3s/k3s.yaml -[ -r "${TOKEN_FILE}" ] || die "${TOKEN_FILE} unreadable" -[ -r "${KUBECONFIG_FILE}" ] || die "${KUBECONFIG_FILE} unreadable" - -NODE_TOKEN=$(cat "${TOKEN_FILE}") -# K3s ships kubeconfig with server: https://127.0.0.1:6443 — rewrite to the -# NLB endpoint so it works from outside the control plane VM. -KUBECONFIG_REWRITTEN=$(sed "s|server: https://127\.0\.0\.1:6443|server: ${EKS_NLB_ENDPOINT}|" "${KUBECONFIG_FILE}") - -# 3. Publish one-shot NATS messages. Subjects per eks-v1.md Q14 + Q15. -TOKEN_SUBJ="eks.bus.${EKS_ACCOUNT_ID}.${EKS_CLUSTER_NAME}.k3s-bootstrap-token" -KUBE_SUBJ="eks.bus.${EKS_ACCOUNT_ID}.${EKS_CLUSTER_NAME}.k3s-admin-kubeconfig" - -NATS_ARGS="-s ${SPINIFEX_NATS_URL}" -if [ -n "${SPINIFEX_NATS_CREDS_FILE:-}" ] && [ -f "${SPINIFEX_NATS_CREDS_FILE}" ]; then - NATS_ARGS="${NATS_ARGS} --creds ${SPINIFEX_NATS_CREDS_FILE}" -fi - -log "publishing node-token to ${TOKEN_SUBJ}" -# shellcheck disable=SC2086 -printf '%s' "${NODE_TOKEN}" | nats ${NATS_ARGS} pub "${TOKEN_SUBJ}" --stdin - -log "publishing admin kubeconfig to ${KUBE_SUBJ}" -# shellcheck disable=SC2086 -printf '%s' "${KUBECONFIG_REWRITTEN}" | nats ${NATS_ARGS} pub "${KUBE_SUBJ}" --stdin - -# 4. Self-disable. Remove sentinel, pull from runlevel. -rm -f "${SENTINEL}" -rc-update del k3s-first-boot default 2>/dev/null || true -log "first boot complete" diff --git a/scripts/images/eks-server/k3s.initd b/scripts/images/eks-server/k3s.initd deleted file mode 100644 index ca69f2269..000000000 --- a/scripts/images/eks-server/k3s.initd +++ /dev/null @@ -1,36 +0,0 @@ -#!/sbin/openrc-run -# K3s server control plane. v1 = single-node embedded etcd (eks-v1.md Q6). -# --cluster-init bootstraps a new etcd quorum on first start; subsequent -# starts pick up the existing data dir at /var/lib/rancher/k3s. v2 HA grows -# the cluster by launching additional eks-server VMs with --server pointing -# at the existing NLB endpoint (Q17) — no AMI change required. - -description="K3s Kubernetes server (Mulga EKS control plane)" -command="/usr/local/bin/k3s" -command_args="server --config /etc/rancher/k3s/config.yaml" -command_background="yes" -pidfile="/run/k3s.pid" -output_log="/var/log/k3s.log" -error_log="/var/log/k3s.log" - -# Generous start timeout — K3s bootstraps containerd, runs etcd cluster-init, -# pulls system images. First boot can take 2-3 minutes on cold cache. -start_stop_daemon_args="--wait 5000" - -depend() { - need net cgroups - after firewall -} - -start_pre() { - # config.yaml is written by k3s-first-boot from the skeleton + cloud-init - # data. Refuse to start if neither exists. - if [ ! -f /etc/rancher/k3s/config.yaml ]; then - if [ -f /etc/rancher/k3s/config.yaml.skel ]; then - cp /etc/rancher/k3s/config.yaml.skel /etc/rancher/k3s/config.yaml - else - eerror "no /etc/rancher/k3s/config.yaml and no .skel to derive from" - return 1 - fi - fi -} diff --git a/scripts/images/eks-server/manifest.conf b/scripts/images/eks-server/manifest.conf deleted file mode 100644 index aca0fab02..000000000 --- a/scripts/images/eks-server/manifest.conf +++ /dev/null @@ -1,34 +0,0 @@ -IMAGE_NAME="eks-server" -IMAGE_DESCRIPTION="EKS K3s server VM — Alpine + K3s v1.32.5+k3s1 + eks-token-webhook + state-report + etcd-snapshot" -DISTRO="alpine" -ALPINE_VERSION="3.21.7" - -# 4G fits Alpine + K3s server binary (~80MB) + etcd snapshot working set. -# K3s embedded etcd lives on the data volume the orchestrator attaches at -# LaunchSystemInstance time; root disk only carries the image + logs. -IMAGE_SIZE="4G" - -# Packages required at runtime (K3s pulls cgroups + iptables; nats-cli used by -# first-boot publisher + state-report; etcd-client used by snapshot script). -APK_PACKAGES="ca-certificates curl iptables ip6tables conntrack-tools cgroup-tools etcd-ctl openrc" - -# k3s binary downloaded by setup.sh (network access inside chroot); SHA256 -# verified at install time per spec Q12. -BUILD_COMMANDS="CGO_ENABLED=0 go build -o bin/eks-token-webhook ./cmd/eks-token-webhook" - -INSTALL_BINARIES="bin/eks-token-webhook:/usr/local/bin/eks-token-webhook" - -INSTALL_FILES="\ -scripts/images/eks-server/k3s.initd:/etc/init.d/k3s \ -scripts/images/eks-server/eks-token-webhook.initd:/etc/init.d/eks-token-webhook \ -scripts/images/eks-server/k3s-first-boot.initd:/etc/init.d/k3s-first-boot \ -scripts/images/eks-server/k3s-first-boot.sh:/usr/local/sbin/k3s-first-boot \ -scripts/images/eks-server/mulga-eks-state-report.sh:/etc/periodic/15min/mulga-eks-state-report \ -scripts/images/eks-server/mulga-eks-etcd-snapshot.sh:/etc/periodic/daily/mulga-eks-etcd-snapshot" - -ENABLE_SERVICES="crond k3s eks-token-webhook k3s-first-boot" - -SETUP_SCRIPT="scripts/images/eks-server/setup.sh" - -AMI_NAME="spinifex-eks-server" -SYSTEM_TAG="spinifex:managed-by=eks" diff --git a/scripts/images/eks-server/mulga-eks-state-report.sh b/scripts/images/eks-server/mulga-eks-state-report.sh deleted file mode 100644 index 1427e7e43..000000000 --- a/scripts/images/eks-server/mulga-eks-state-report.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -set -eu - -# mulga-eks-state-report — periodic NATS publish of K3s server health. -# Invoked every 15 minutes via /etc/periodic/15min (Alpine crond). One-shot -# best-effort; failures log but do not retry — the spinifex reconciler also -# does its own NLB healthcheck so this is supplementary signal. -# -# Subject: eks.state.{accountID}.{clusterName}.server -# Payload: JSON { healthz: "ok"|"fail", node_count: N, ts: } - -ENVFILE=/etc/spinifex-eks/state-report.env -[ -f "${ENVFILE}" ] || { logger -t mulga-eks-state-report "${ENVFILE} missing — exiting"; exit 0; } -# shellcheck disable=SC1090 -. "${ENVFILE}" - -: "${SPINIFEX_NATS_URL:?}" -: "${EKS_ACCOUNT_ID:?}" -: "${EKS_CLUSTER_NAME:?}" - -KUBECONFIG=${KUBECONFIG:-/etc/rancher/k3s/k3s.yaml} -export KUBECONFIG - -if curl -sk --max-time 3 https://127.0.0.1:6443/healthz | grep -q '^ok$'; then - HEALTH=ok -else - HEALTH=fail -fi - -if [ "${HEALTH}" = "ok" ]; then - NODE_COUNT=$(kubectl get nodes --no-headers 2>/dev/null | wc -l | tr -d ' ') -else - NODE_COUNT=0 -fi - -TS=$(date +%s) -PAYLOAD=$(printf '{"healthz":"%s","node_count":%s,"ts":%s}' "${HEALTH}" "${NODE_COUNT}" "${TS}") -SUBJ="eks.state.${EKS_ACCOUNT_ID}.${EKS_CLUSTER_NAME}.server" - -NATS_ARGS="-s ${SPINIFEX_NATS_URL}" -if [ -n "${SPINIFEX_NATS_CREDS_FILE:-}" ] && [ -f "${SPINIFEX_NATS_CREDS_FILE}" ]; then - NATS_ARGS="${NATS_ARGS} --creds ${SPINIFEX_NATS_CREDS_FILE}" -fi - -# shellcheck disable=SC2086 -printf '%s' "${PAYLOAD}" | nats ${NATS_ARGS} pub "${SUBJ}" --stdin 2>&1 \ - | logger -t mulga-eks-state-report diff --git a/scripts/images/eks-server/setup.sh b/scripts/images/eks-server/setup.sh deleted file mode 100644 index 5803b0a3b..000000000 --- a/scripts/images/eks-server/setup.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/sh -set -eu - -# setup.sh — chroot customisation for the eks-server AMI. -# -# Runs inside an Alpine 3.21 chroot under build-system-image.sh after packages -# and binaries are installed. Downloads the pinned K3s binary, verifies its -# SHA256, drops it into /usr/local/bin, sets executable bits on init scripts + -# cron entries, and writes the K3s server config skeleton. -# -# Network access inside the chroot is provided by the host's /etc/resolv.conf -# (build-system-image.sh copies it in). curl is in APK_PACKAGES. - -K3S_VERSION="v1.32.5+k3s1" -K3S_URL_BASE="https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}" - -# Pull the upstream signed checksums file and pin the amd64 line. A tampered -# release replacing both files would still produce a self-consistent download, -# so the SHA file URL is the trust anchor — anyone forging a Mulga AMI build -# would need to compromise the k3s-io GitHub release artefacts. -echo "[eks-server-setup] fetching k3s checksums ${K3S_VERSION}" -curl -fsSL -o /tmp/k3s-checksums.txt "${K3S_URL_BASE}/sha256sum-amd64.txt" -K3S_SHA256=$(awk '/[ \t]k3s$/{print $1; exit}' /tmp/k3s-checksums.txt) -if [ -z "${K3S_SHA256}" ]; then - echo "[eks-server-setup] could not parse k3s sha256 from upstream checksums" - cat /tmp/k3s-checksums.txt - exit 1 -fi -echo "[eks-server-setup] downloading k3s ${K3S_VERSION} (sha256=${K3S_SHA256})" -curl -fsSL -o /usr/local/bin/k3s "${K3S_URL_BASE}/k3s" -echo "${K3S_SHA256} /usr/local/bin/k3s" > /tmp/k3s.sha256 -if ! sha256sum -c /tmp/k3s.sha256; then - echo "[eks-server-setup] k3s SHA256 mismatch — refusing to bake AMI" - exit 1 -fi -chmod 0755 /usr/local/bin/k3s -ln -sf /usr/local/bin/k3s /usr/local/bin/kubectl -ln -sf /usr/local/bin/k3s /usr/local/bin/crictl -ln -sf /usr/local/bin/k3s /usr/local/bin/ctr - -# nats-cli is not in Alpine main/community — pull the upstream release binary. -# Used by k3s-first-boot.sh (one-shot publishes) and mulga-eks-state-report.sh -# (periodic publishes). -NATS_CLI_VERSION="0.4.0" -NATS_CLI_URL="https://github.com/nats-io/natscli/releases/download/v${NATS_CLI_VERSION}/nats-${NATS_CLI_VERSION}-linux-amd64.zip" -echo "[eks-server-setup] downloading nats-cli ${NATS_CLI_VERSION}" -curl -fsSL -o /tmp/nats-cli.zip "${NATS_CLI_URL}" -apk add --no-cache unzip -unzip -q -d /tmp/nats-cli /tmp/nats-cli.zip -install -m 0755 /tmp/nats-cli/nats-${NATS_CLI_VERSION}-linux-amd64/nats /usr/local/bin/nats -rm -rf /tmp/nats-cli /tmp/nats-cli.zip -apk del --no-cache unzip - -# Init scripts ship as 0644 from INSTALL_FILES; OpenRC requires 0755. -chmod 0755 /etc/init.d/k3s /etc/init.d/eks-token-webhook /etc/init.d/k3s-first-boot -chmod 0755 /usr/local/sbin/k3s-first-boot -chmod 0755 /etc/periodic/15min/mulga-eks-state-report -chmod 0755 /etc/periodic/daily/mulga-eks-etcd-snapshot - -# K3s server config — empty skeleton; cloud-init / first-boot fills in the -# per-cluster fields (cluster-cidr, service-cidr, token-file, etc). -# Webhook-token-auth is NOT wired in the skeleton: enabling it before the -# eks-token-webhook binary is the real implementation (currently a 503 stub, -# replaced by cs-eks-6c) would block /healthz from anonymous callers. The -# Sprint 6c orchestrator drops a webhook-config.yaml + restarts k3s once -# the binary is the real one. -mkdir -p /etc/rancher/k3s -cat > /etc/rancher/k3s/config.yaml.skel <<'EOF' -# Populated at first boot by cloud-init user-data via k3s-first-boot.sh. -# Fields documented at https://docs.k3s.io/installation/configuration. -cluster-init: true -disable: - - traefik - - servicelb - - local-storage -EOF - -# Sentinel file marker — k3s-first-boot self-disables after first success by -# checking for this path. Initial state is "not yet run". -touch /var/lib/spinifex-eks/first-boot.pending 2>/dev/null || { - mkdir -p /var/lib/spinifex-eks - touch /var/lib/spinifex-eks/first-boot.pending -} - -echo "[eks-server-setup] done" diff --git a/scripts/publish-system-image.sh b/scripts/publish-system-image.sh new file mode 100755 index 000000000..3a5c05df1 --- /dev/null +++ b/scripts/publish-system-image.sh @@ -0,0 +1,147 @@ +#!/bin/bash +set -euo pipefail + +# publish-system-image.sh — Publish a built system AMI to Cloudflare R2 +# +# Converts the raw image produced by build-system-image.sh into a compressed +# qcow2, writes a .sha256 sidecar (coreutils format, matched by the spx catalog +# checksum verifier), and uploads both to the R2 bucket that backs +# iso.mulgadc.com/system-ami/. The matching catalog entry in +# spinifex/spinifex/utils/images.go then lets operators run: +# +# spx admin images import --name +# +# Object name is -.qcow2 — this MUST match the +# basename of the catalog entry's URL. +# +# Requirements: qemu-img, sha256sum, aws (CLI; R2 speaks the S3 API) +# Usage: ./scripts/publish-system-image.sh [--build] [--dry-run] +# --build Run build-system-image.sh first (otherwise reuse the cached raw) +# --dry-run Print the convert/checksum/upload steps without uploading +# +# Env (required unless --dry-run): +# R2_ENDPOINT https://.r2.cloudflarestorage.com +# R2_BUCKET defaults to "spinifex-iso" (backs iso.mulgadc.com) +# AWS_ACCESS_KEY_ID R2 token access key +# AWS_SECRET_ACCESS_KEY R2 token secret + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +usage() { + echo "Usage: $0 [--build] [--dry-run]" + exit 1 +} + +if [[ $# -lt 1 || "$1" == "-h" || "$1" == "--help" ]]; then + usage +fi + +MANIFEST="$1" +shift + +if [[ ! -f "$MANIFEST" ]]; then + echo "ERROR: Manifest not found: $MANIFEST" + exit 1 +fi + +DO_BUILD=false +DRY_RUN=false +for arg in "$@"; do + case "$arg" in + --build) DO_BUILD=true ;; + --dry-run) DRY_RUN=true ;; + *) echo "ERROR: unknown flag: $arg"; usage ;; + esac +done + +# shellcheck source=/dev/null +source "$MANIFEST" + +DISTRO="${DISTRO:-alpine}" +ARCH="x86_64" # build-system-image.sh builds x86_64 only today +R2_BUCKET="${R2_BUCKET:-spinifex-iso}" # iso.mulgadc.com is served from this bucket + +if [[ -z "${IMAGE_NAME:-}" ]]; then + echo "ERROR: Manifest missing required field: IMAGE_NAME" + exit 1 +fi + +# Mirror build-system-image.sh's derived paths so we reuse its cached raw. +BUILD_DIR="/tmp/${IMAGE_NAME}-image-build" +RAW_IMAGE="${BUILD_DIR}/${IMAGE_NAME}-${DISTRO}.raw" + +OBJECT_BASE="${AMI_NAME:-$IMAGE_NAME}-${ARCH}" +QCOW2_OUT="${BUILD_DIR}/${OBJECT_BASE}.qcow2" +SHA_OUT="${QCOW2_OUT}.sha256" +OBJECT_QCOW2="${OBJECT_BASE}.qcow2" +OBJECT_SHA="${OBJECT_BASE}.qcow2.sha256" + +for tool in qemu-img sha256sum; do + if ! command -v "$tool" &>/dev/null; then + echo "ERROR: $tool not found" + exit 1 + fi +done +if [[ "$DRY_RUN" == false ]] && ! command -v aws &>/dev/null; then + echo "ERROR: aws CLI not found (required for R2 upload; use --dry-run to skip)" + exit 1 +fi + +if [[ "$DO_BUILD" == true ]]; then + echo "=== Building ${IMAGE_NAME} ===" + "${SCRIPT_DIR}/build-system-image.sh" "$MANIFEST" +fi + +if [[ ! -f "$RAW_IMAGE" ]]; then + echo "ERROR: Raw image not found: $RAW_IMAGE" + echo " Run with --build, or 'make build-system-image IMAGE=...' first." + exit 1 +fi + +if [[ "$DRY_RUN" == false ]]; then + for var in R2_ENDPOINT AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY; do + if [[ -z "${!var:-}" ]]; then + echo "ERROR: $var must be set (or use --dry-run)" + exit 1 + fi + done +fi + +echo "=== Publish System Image ===" +echo " Manifest: ${MANIFEST}" +echo " Raw: ${RAW_IMAGE}" +echo " Object: s3://${R2_BUCKET}/system-ami/${OBJECT_QCOW2}" +echo "" + +run() { + echo "+ $*" + if [[ "$DRY_RUN" == false ]]; then + "$@" + fi +} + +# Compressed qcow2 keeps the transfer small (consistent with the GPU images). +run qemu-img convert -c -f raw -O qcow2 "$RAW_IMAGE" "$QCOW2_OUT" + +# Sidecar in coreutils format: " ". The spx catalog verifier +# matches the entry by the downloaded file's basename, so write the basename +# the catalog URL resolves to, not the local build path. +if [[ "$DRY_RUN" == false ]]; then + ( cd "$(dirname "$QCOW2_OUT")" && sha256sum "$(basename "$QCOW2_OUT")" > "$(basename "$SHA_OUT")" ) + echo " sha256: $(cut -d' ' -f1 "$SHA_OUT")" +else + echo "+ sha256sum ${OBJECT_QCOW2} > ${OBJECT_SHA}" +fi + +S3_PREFIX="s3://${R2_BUCKET}/system-ami" +run aws s3 cp --endpoint-url "${R2_ENDPOINT:-}" "$QCOW2_OUT" "${S3_PREFIX}/${OBJECT_QCOW2}" +run aws s3 cp --endpoint-url "${R2_ENDPOINT:-}" "$SHA_OUT" "${S3_PREFIX}/${OBJECT_SHA}" + +echo "" +if [[ "$DRY_RUN" == true ]]; then + echo "=== Dry run complete (no upload) ===" +else + echo "=== Published ===" + echo " https://iso.mulgadc.com/system-ami/${OBJECT_QCOW2}" + echo " https://iso.mulgadc.com/system-ami/${OBJECT_SHA}" +fi diff --git a/spinifex/admin/admin.go b/spinifex/admin/admin.go index 5c04716c8..ef507de09 100644 --- a/spinifex/admin/admin.go +++ b/spinifex/admin/admin.go @@ -409,8 +409,28 @@ func UpdateAWSINIFile(path, section string, values map[string]string) error { sec.Key(key).SetValue(value) } - // Save with proper permissions - return cfg.SaveTo(path) + // Write atomically with 0600. ini.SaveTo uses os.Create (0666 & ~umask, + // typically 0644), which leaves ~/.aws/credentials world-readable. Render + // to a sibling temp file (os.CreateTemp is 0600) and rename into place so + // the secrets are never exposed, even briefly. + dir := filepath.Dir(path) + tmp, err := os.CreateTemp(dir, ".aws-ini-*.tmp") + if err != nil { + return fmt.Errorf("failed to create temp INI file: %w", err) + } + tmpName := tmp.Name() + defer os.Remove(tmpName) // no-op once the rename succeeds + if _, err := cfg.WriteTo(tmp); err != nil { + tmp.Close() + return fmt.Errorf("failed to write INI file: %w", err) + } + if err := tmp.Close(); err != nil { + return fmt.Errorf("failed to close INI file: %w", err) + } + if err := os.Rename(tmpName, path); err != nil { + return fmt.Errorf("failed to rename INI file into place: %w", err) + } + return nil } // generateAWSAccessKey generates an AWS-style access key diff --git a/spinifex/awserrors/awserrors.go b/spinifex/awserrors/awserrors.go index 999e98450..554bac72c 100644 --- a/spinifex/awserrors/awserrors.go +++ b/spinifex/awserrors/awserrors.go @@ -1,5 +1,7 @@ package awserrors +import "strings" + type ErrorMessage struct { HTTPCode int Message string @@ -219,222 +221,232 @@ var ( ErrorInvalidOptionConflict = "InvalidOption.Conflict" ErrorInvalidPaginationToken = "InvalidPaginationToken" ErrorInvalidParameter = "InvalidParameter" - ErrorInvalidParameterCombination = "InvalidParameterCombination" - ErrorInvalidParameterDependency = "InvalidParameterDependency" - ErrorInvalidParameterValue = "InvalidParameterValue" - ErrorInvalidPermissionDuplicate = "InvalidPermission.Duplicate" - ErrorInvalidPermissionMalformed = "InvalidPermission.Malformed" - ErrorInvalidPermissionNotFound = "InvalidPermission.NotFound" - ErrorInvalidPlacementGroupDuplicate = "InvalidPlacementGroup.Duplicate" - ErrorInvalidPlacementGroupInUse = "InvalidPlacementGroup.InUse" - ErrorInvalidPlacementGroupUnknown = "InvalidPlacementGroup.Unknown" - ErrorInvalidPlacementGroupIdMalformed = "InvalidPlacementGroupId.Malformed" - ErrorInvalidPolicyDocument = "InvalidPolicyDocument" - ErrorInvalidPrefixListIDNotFound = "InvalidPrefixListID.NotFound" - ErrorInvalidPrefixListIdMalformed = "InvalidPrefixListId.Malformed" - ErrorInvalidProductInfo = "InvalidProductInfo" - ErrorInvalidPurchaseTokenExpired = "InvalidPurchaseToken.Expired" - ErrorInvalidPurchaseTokenMalformed = "InvalidPurchaseToken.Malformed" - ErrorInvalidQuantity = "InvalidQuantity" - ErrorInvalidQueryParameter = "InvalidQueryParameter" - ErrorInvalidRamDiskIdMalformed = "InvalidRamDiskId.Malformed" - ErrorInvalidRegion = "InvalidRegion" - ErrorInvalidRequest = "InvalidRequest" - ErrorInvalidReservationIDMalformed = "InvalidReservationID.Malformed" - ErrorInvalidReservationIDNotFound = "InvalidReservationID.NotFound" - ErrorInvalidReservedInstancesId = "InvalidReservedInstancesId" - ErrorInvalidReservedInstancesOfferingId = "InvalidReservedInstancesOfferingId" - ErrorInvalidResourceConfigurationArnMalformed = "InvalidResourceConfigurationArn.Malformed" - ErrorInvalidResourceConfigurationArnNotFound = "InvalidResourceConfigurationArn.NotFound" - ErrorInvalidResourceTypeUnknown = "InvalidResourceType.Unknown" - ErrorInvalidRouteInvalidState = "InvalidRoute.InvalidState" - ErrorInvalidRouteMalformed = "InvalidRoute.Malformed" - ErrorInvalidRouteNotFound = "InvalidRoute.NotFound" - ErrorInvalidRouteTableIDNotFound = "InvalidRouteTableID.NotFound" - ErrorInvalidRouteTableIdMalformed = "InvalidRouteTableId.Malformed" - ErrorInvalidScheduledInstance = "InvalidScheduledInstance" - ErrorInvalidSecurityRequestHasExpired = "InvalidSecurity.RequestHasExpired" - ErrorInvalidSecurityGroupIDNotFound = "InvalidSecurityGroupID.NotFound" - ErrorInvalidSecurityGroupIdMalformed = "InvalidSecurityGroupId.Malformed" - ErrorInvalidSecurityGroupRuleIdMalformed = "InvalidSecurityGroupRuleId.Malformed" - ErrorInvalidSecurityGroupRuleIdNotFound = "InvalidSecurityGroupRuleId.NotFound" - ErrorInvalidServiceName = "InvalidServiceName" - ErrorInvalidSnapshotInUse = "InvalidSnapshot.InUse" - ErrorInvalidSnapshotNotFound = "InvalidSnapshot.NotFound" - ErrorInvalidSnapshotIDMalformed = "InvalidSnapshotID.Malformed" - ErrorInvalidSpotDatafeedNotFound = "InvalidSpotDatafeed.NotFound" - ErrorInvalidSpotFleetRequestConfig = "InvalidSpotFleetRequestConfig" - ErrorInvalidSpotFleetRequestIdMalformed = "InvalidSpotFleetRequestId.Malformed" - ErrorInvalidSpotFleetRequestIdNotFound = "InvalidSpotFleetRequestId.NotFound" - ErrorInvalidSpotInstanceRequestIDMalformed = "InvalidSpotInstanceRequestID.Malformed" - ErrorInvalidSpotInstanceRequestIDNotFound = "InvalidSpotInstanceRequestID.NotFound" - ErrorInvalidState = "InvalidState" - ErrorInvalidStateTransition = "InvalidStateTransition" - ErrorInvalidSubnet = "InvalidSubnet" - ErrorInvalidSubnetConflict = "InvalidSubnet.Conflict" - ErrorInvalidSubnetRange = "InvalidSubnet.Range" - ErrorInvalidSubnetIDMalformed = "InvalidSubnetID.Malformed" - ErrorInvalidSubnetIDNotFound = "InvalidSubnetID.NotFound" - ErrorInvalidTagKeyMalformed = "InvalidTagKey.Malformed" - ErrorInvalidTargetArnUnknown = "InvalidTargetArn.Unknown" - ErrorInvalidTargetException = "InvalidTargetException" - ErrorInvalidTenancy = "InvalidTenancy" - ErrorInvalidTime = "InvalidTime" - ErrorInvalidTrafficMirrorFilterNotFound = "InvalidTrafficMirrorFilterNotFound" - ErrorInvalidTrafficMirrorFilterRuleNotFound = "InvalidTrafficMirrorFilterRuleNotFound" - ErrorInvalidTrafficMirrorSessionNotFound = "InvalidTrafficMirrorSessionNotFound" - ErrorInvalidTrafficMirrorTargetNoFound = "InvalidTrafficMirrorTargetNoFound" - ErrorInvalidUserIDMalformed = "InvalidUserID.Malformed" - ErrorInvalidVolumeNotFound = "InvalidVolume.NotFound" - ErrorInvalidVolumeZoneMismatch = "InvalidVolume.ZoneMismatch" - ErrorInvalidVolumeIDDuplicate = "InvalidVolumeID.Duplicate" - ErrorInvalidVolumeIDMalformed = "InvalidVolumeID.Malformed" - ErrorInvalidVolumeIDZoneMismatch = "InvalidVolumeID.ZoneMismatch" - ErrorInvalidVpcEndpointNotFound = "InvalidVpcEndpoint.NotFound" - ErrorInvalidVpcEndpointIdMalformed = "InvalidVpcEndpointId.Malformed" - ErrorInvalidVpcEndpointIdNotFound = "InvalidVpcEndpointId.NotFound" - ErrorInvalidVpcEndpointServiceNotFound = "InvalidVpcEndpointService.NotFound" - ErrorInvalidVpcEndpointServiceIdNotFound = "InvalidVpcEndpointServiceId.NotFound" - ErrorInvalidVpcEndpointServiceIdIdMalformed = "InvalidVpcEndpointServiceIdId.Malformed" - ErrorInvalidVpcEndpointType = "InvalidVpcEndpointType" - ErrorInvalidVpcIDMalformed = "InvalidVpcID.Malformed" - ErrorInvalidVpcIDNotFound = "InvalidVpcID.NotFound" - ErrorInvalidVpcPeeringConnectionIDNotFound = "InvalidVpcPeeringConnectionID.NotFound" - ErrorInvalidVpcPeeringConnectionIdMalformed = "InvalidVpcPeeringConnectionId.Malformed" - ErrorInvalidVpcPeeringConnectionStateDnsHostnamesDisabled = "InvalidVpcPeeringConnectionState.DnsHostnamesDisabled" - ErrorInvalidVpcRange = "InvalidVpcRange" - ErrorInvalidVpcState = "InvalidVpcState" - ErrorInvalidVpnConnectionInvalidState = "InvalidVpnConnection.InvalidState" - ErrorInvalidVpnConnectionInvalidType = "InvalidVpnConnection.InvalidType" - ErrorInvalidVpnConnectionID = "InvalidVpnConnectionID" - ErrorInvalidVpnConnectionIDNotFound = "InvalidVpnConnectionID.NotFound" - ErrorInvalidVpnGatewayAttachmentNotFound = "InvalidVpnGatewayAttachment.NotFound" - ErrorInvalidVpnGatewayIDNotFound = "InvalidVpnGatewayID.NotFound" - ErrorInvalidVpnGatewayState = "InvalidVpnGatewayState" - ErrorInvalidZoneNotFound = "InvalidZone.NotFound" - ErrorKeyPairLimitExceeded = "KeyPairLimitExceeded" - ErrorLegacySecurityGroup = "LegacySecurityGroup" - ErrorLimitPriceExceeded = "LimitPriceExceeded" - ErrorLogDestinationNotFound = "LogDestinationNotFound" - ErrorLogDestinationPermissionIssue = "LogDestinationPermissionIssue" - ErrorMalformedQueryString = "MalformedQueryString" - ErrorMaxConfigLimitExceededException = "MaxConfigLimitExceededException" - ErrorMaxIOPSLimitExceeded = "MaxIOPSLimitExceeded" - ErrorMaxScheduledInstanceCapacityExceeded = "MaxScheduledInstanceCapacityExceeded" - ErrorMaxSpotFleetRequestCountExceeded = "MaxSpotFleetRequestCountExceeded" - ErrorMaxSpotInstanceCountExceeded = "MaxSpotInstanceCountExceeded" - ErrorMaxTemplateLimitExceeded = "MaxTemplateLimitExceeded" - ErrorMaxTemplateVersionLimitExceeded = "MaxTemplateVersionLimitExceeded" - ErrorMissingAction = "MissingAction" - ErrorMissingAuthenticationToken = "MissingAuthenticationToken" - ErrorMissingInput = "MissingInput" - ErrorMissingParameter = "MissingParameter" - ErrorNatGatewayLimitExceeded = "NatGatewayLimitExceeded" - ErrorNatGatewayMalformed = "NatGatewayMalformed" - ErrorNatGatewayNotFound = "NatGatewayNotFound" - ErrorNetworkAclEntryAlreadyExists = "NetworkAclEntryAlreadyExists" - ErrorNetworkAclEntryLimitExceeded = "NetworkAclEntryLimitExceeded" - ErrorNetworkAclLimitExceeded = "NetworkAclLimitExceeded" - ErrorNetworkInterfaceLimitExceeded = "NetworkInterfaceLimitExceeded" - ErrorNetworkInterfaceNotSupported = "NetworkInterfaceNotSupported" - ErrorNetworkLoadBalancerNotFoundException = "NetworkLoadBalancerNotFoundException" - ErrorNlbInUseByTrafficMirrorTargetException = "NlbInUseByTrafficMirrorTargetException" - ErrorNoSuchVersion = "NoSuchVersion" - ErrorNonEBSInstance = "NonEBSInstance" - ErrorNotExportable = "NotExportable" - ErrorNotImplemented = "NotImplemented" - ErrorOperationNotPermitted = "OperationNotPermitted" - ErrorOptInRequired = "OptInRequired" - ErrorOutstandingVpcPeeringConnectionLimitExceeded = "OutstandingVpcPeeringConnectionLimitExceeded" - ErrorPackedPolicyTooLarge = "PackedPolicyTooLarge" - ErrorPendingSnapshotLimitExceeded = "PendingSnapshotLimitExceeded" - ErrorPendingVerification = "PendingVerification" - ErrorPendingVpcPeeringConnectionLimitExceeded = "PendingVpcPeeringConnectionLimitExceeded" - ErrorPlacementGroupLimitExceeded = "PlacementGroupLimitExceeded" - ErrorPrivateIpAddressLimitExceeded = "PrivateIpAddressLimitExceeded" - ErrorRegionDisabled = "RegionDisabledException" - ErrorRequestEntityTooLarge = "RequestEntityTooLarge" - ErrorRequestExpired = "RequestExpired" - ErrorRequestLimitExceeded = "RequestLimitExceeded" - ErrorRequestResourceCountExceeded = "RequestResourceCountExceeded" - ErrorReservationCapacityExceeded = "ReservationCapacityExceeded" - ErrorReservedInstancesCountExceeded = "ReservedInstancesCountExceeded" - ErrorReservedInstancesLimitExceeded = "ReservedInstancesLimitExceeded" - ErrorReservedInstancesUnavailable = "ReservedInstancesUnavailable" - ErrorResourceAlreadyAssigned = "Resource.AlreadyAssigned" - ErrorResourceAlreadyAssociated = "Resource.AlreadyAssociated" - ErrorResourceCountExceeded = "ResourceCountExceeded" - ErrorResourceCountLimitExceeded = "ResourceCountLimitExceeded" - ErrorResourceLimitExceeded = "ResourceLimitExceeded" - ErrorRetryableError = "RetryableError" - ErrorRouteAlreadyExists = "RouteAlreadyExists" - ErrorRouteLimitExceeded = "RouteLimitExceeded" - ErrorRouteTableLimitExceeded = "RouteTableLimitExceeded" - ErrorRulesPerSecurityGroupLimitExceeded = "RulesPerSecurityGroupLimitExceeded" - ErrorScheduledInstanceLimitExceeded = "ScheduledInstanceLimitExceeded" - ErrorScheduledInstanceParameterMismatch = "ScheduledInstanceParameterMismatch" - ErrorScheduledInstanceSlotNotOpen = "ScheduledInstanceSlotNotOpen" - ErrorScheduledInstanceSlotUnavailable = "ScheduledInstanceSlotUnavailable" - ErrorSecurityGroupLimitExceeded = "SecurityGroupLimitExceeded" - ErrorSecurityGroupsPerInstanceLimitExceeded = "SecurityGroupsPerInstanceLimitExceeded" - ErrorSecurityGroupsPerInterfaceLimitExceeded = "SecurityGroupsPerInterfaceLimitExceeded" - ErrorSerialConsoleSessionUnavailable = "SerialConsoleSessionUnavailable" - ErrorServerInternal = "ServerInternal" - ErrorServiceUnavailable = "ServiceUnavailable" - ErrorSignatureDoesNotMatch = "SignatureDoesNotMatch" - ErrorSnapshotCopyUnsupportedInterRegion = "SnapshotCopyUnsupported.InterRegion" - ErrorSnapshotCreationPerVolumeRateExceeded = "SnapshotCreationPerVolumeRateExceeded" - ErrorSnapshotLimitExceeded = "SnapshotLimitExceeded" - ErrorSpotMaxPriceTooLow = "SpotMaxPriceTooLow" - ErrorSubnetLimitExceeded = "SubnetLimitExceeded" - ErrorTagLimitExceeded = "TagLimitExceeded" - ErrorTagPolicyViolation = "TagPolicyViolation" - ErrorThrottling = "Throttling" - ErrorTargetCapacityLimitExceededException = "TargetCapacityLimitExceededException" - ErrorTrafficMirrorFilterInUse = "TrafficMirrorFilterInUse" - ErrorTrafficMirrorFilterLimitExceeded = "TrafficMirrorFilterLimitExceeded" - ErrorTrafficMirrorFilterRuleAlreadyExists = "TrafficMirrorFilterRuleAlreadyExists" - ErrorTrafficMirrorFilterRuleLimitExceeded = "TrafficMirrorFilterRuleLimitExceeded" - ErrorTrafficMirrorSessionLimitExceeded = "TrafficMirrorSessionLimitExceeded" - ErrorTrafficMirrorSessionsPerInterfaceLimitExceeded = "TrafficMirrorSessionsPerInterfaceLimitExceeded" - ErrorTrafficMirrorSessionsPerTargetLimitExceeded = "TrafficMirrorSessionsPerTargetLimitExceeded" - ErrorTrafficMirrorSourcesPerTargetLimitExceeded = "TrafficMirrorSourcesPerTargetLimitExceeded" - ErrorTrafficMirrorTargetInUseException = "TrafficMirrorTargetInUseException" - ErrorTrafficMirrorTargetLimitExceeded = "TrafficMirrorTargetLimitExceeded" - ErrorUnauthorizedOperation = "UnauthorizedOperation" - ErrorUnavailable = "Unavailable" - ErrorUnavailableHostRequirements = "UnavailableHostRequirements" - ErrorUnfulfillableCapacity = "UnfulfillableCapacity" - ErrorUnknownParameter = "UnknownParameter" - ErrorUnknownPrincipalTypeUnsupported = "UnknownPrincipalType.Unsupported" - ErrorUnknownVolumeType = "UnknownVolumeType" - ErrorUnsupported = "Unsupported" - ErrorUnsupportedException = "UnsupportedException" - ErrorUnsupportedHibernationConfiguration = "UnsupportedHibernationConfiguration" - ErrorUnsupportedHostConfiguration = "UnsupportedHostConfiguration" - ErrorUnsupportedInstanceAttribute = "UnsupportedInstanceAttribute" - ErrorUnsupportedInstanceTypeOnHost = "UnsupportedInstanceTypeOnHost" - ErrorUnsupportedOperation = "UnsupportedOperation" - ErrorUnsupportedProtocol = "UnsupportedProtocol" - ErrorUnsupportedTenancy = "UnsupportedTenancy" - ErrorUpdateLimitExceeded = "UpdateLimitExceeded" - ErrorVPCIdNotSpecified = "VPCIdNotSpecified" - ErrorVPCResourceNotSpecified = "VPCResourceNotSpecified" - ErrorValidationError = "ValidationError" - ErrorVcpuLimitExceeded = "VcpuLimitExceeded" - ErrorVolumeIOPSLimit = "VolumeIOPSLimit" - ErrorVolumeInUse = "VolumeInUse" - ErrorVolumeLimitExceeded = "VolumeLimitExceeded" - ErrorVolumeModificationSizeLimitExceeded = "VolumeModificationSizeLimitExceeded" - ErrorVolumeTypeNotAvailableInZone = "VolumeTypeNotAvailableInZone" - ErrorVpcEndpointLimitExceeded = "VpcEndpointLimitExceeded" - ErrorVpcLimitExceeded = "VpcLimitExceeded" - ErrorVpcPeeringConnectionAlreadyExists = "VpcPeeringConnectionAlreadyExists" - ErrorVpcPeeringConnectionsPerVpcLimitExceeded = "VpcPeeringConnectionsPerVpcLimitExceeded" - ErrorVpnConnectionLimitExceeded = "VpnConnectionLimitExceeded" - ErrorVpnGatewayAttachmentLimitExceeded = "VpnGatewayAttachmentLimitExceeded" - ErrorVpnGatewayLimitExceeded = "VpnGatewayLimitExceeded" - ErrorZonesMismatched = "ZonesMismatched" + // ACM (AWS JSON 1.1). Suffix-less; the JSON error envelope appends + // "Exception" on the wire (InvalidArnException), matching aws-sdk-go acm + // ErrCode* values. Not-found reuses the shared ErrorResourceNotFound. + ErrorACMInvalidArn = "InvalidArn" + ErrorInvalidParameterCombination = "InvalidParameterCombination" + ErrorInvalidParameterDependency = "InvalidParameterDependency" + ErrorInvalidParameterValue = "InvalidParameterValue" + ErrorInvalidPermissionDuplicate = "InvalidPermission.Duplicate" + ErrorInvalidPermissionMalformed = "InvalidPermission.Malformed" + ErrorInvalidPermissionNotFound = "InvalidPermission.NotFound" + ErrorInvalidPlacementGroupDuplicate = "InvalidPlacementGroup.Duplicate" + ErrorInvalidPlacementGroupInUse = "InvalidPlacementGroup.InUse" + ErrorInvalidPlacementGroupUnknown = "InvalidPlacementGroup.Unknown" + ErrorInvalidPlacementGroupIdMalformed = "InvalidPlacementGroupId.Malformed" + ErrorInvalidPolicyDocument = "InvalidPolicyDocument" + ErrorInvalidPrefixListIDNotFound = "InvalidPrefixListID.NotFound" + ErrorInvalidPrefixListIdMalformed = "InvalidPrefixListId.Malformed" + ErrorInvalidProductInfo = "InvalidProductInfo" + ErrorInvalidPurchaseTokenExpired = "InvalidPurchaseToken.Expired" + ErrorInvalidPurchaseTokenMalformed = "InvalidPurchaseToken.Malformed" + ErrorInvalidQuantity = "InvalidQuantity" + ErrorInvalidQueryParameter = "InvalidQueryParameter" + ErrorInvalidRamDiskIdMalformed = "InvalidRamDiskId.Malformed" + ErrorInvalidRegion = "InvalidRegion" + ErrorInvalidRequest = "InvalidRequest" + ErrorInvalidReservationIDMalformed = "InvalidReservationID.Malformed" + ErrorInvalidReservationIDNotFound = "InvalidReservationID.NotFound" + ErrorInvalidReservedInstancesId = "InvalidReservedInstancesId" + ErrorInvalidReservedInstancesOfferingId = "InvalidReservedInstancesOfferingId" + ErrorInvalidResourceConfigurationArnMalformed = "InvalidResourceConfigurationArn.Malformed" + ErrorInvalidResourceConfigurationArnNotFound = "InvalidResourceConfigurationArn.NotFound" + ErrorInvalidResourceTypeUnknown = "InvalidResourceType.Unknown" + ErrorInvalidRouteInvalidState = "InvalidRoute.InvalidState" + ErrorInvalidRouteMalformed = "InvalidRoute.Malformed" + ErrorInvalidRouteNotFound = "InvalidRoute.NotFound" + ErrorInvalidRouteTableIDNotFound = "InvalidRouteTableID.NotFound" + ErrorInvalidRouteTableIdMalformed = "InvalidRouteTableId.Malformed" + ErrorInvalidScheduledInstance = "InvalidScheduledInstance" + ErrorInvalidSecurityRequestHasExpired = "InvalidSecurity.RequestHasExpired" + ErrorInvalidSecurityGroupIDNotFound = "InvalidSecurityGroupID.NotFound" + ErrorInvalidSecurityGroupIdMalformed = "InvalidSecurityGroupId.Malformed" + ErrorInvalidSecurityGroupRuleIdMalformed = "InvalidSecurityGroupRuleId.Malformed" + ErrorInvalidSecurityGroupRuleIdNotFound = "InvalidSecurityGroupRuleId.NotFound" + ErrorInvalidServiceName = "InvalidServiceName" + ErrorInvalidSnapshotInUse = "InvalidSnapshot.InUse" + ErrorInvalidSnapshotNotFound = "InvalidSnapshot.NotFound" + ErrorInvalidSnapshotIDMalformed = "InvalidSnapshotID.Malformed" + ErrorInvalidSpotDatafeedNotFound = "InvalidSpotDatafeed.NotFound" + ErrorInvalidSpotFleetRequestConfig = "InvalidSpotFleetRequestConfig" + ErrorInvalidSpotFleetRequestIdMalformed = "InvalidSpotFleetRequestId.Malformed" + ErrorInvalidSpotFleetRequestIdNotFound = "InvalidSpotFleetRequestId.NotFound" + ErrorInvalidSpotInstanceRequestIDMalformed = "InvalidSpotInstanceRequestID.Malformed" + ErrorInvalidSpotInstanceRequestIDNotFound = "InvalidSpotInstanceRequestID.NotFound" + ErrorInvalidState = "InvalidState" + ErrorInvalidStateTransition = "InvalidStateTransition" + ErrorInvalidSubnet = "InvalidSubnet" + ErrorInvalidSubnetConflict = "InvalidSubnet.Conflict" + ErrorInvalidSubnetRange = "InvalidSubnet.Range" + ErrorInvalidSubnetIDMalformed = "InvalidSubnetID.Malformed" + ErrorInvalidSubnetIDNotFound = "InvalidSubnetID.NotFound" + ErrorInvalidTagKeyMalformed = "InvalidTagKey.Malformed" + ErrorInvalidTargetArnUnknown = "InvalidTargetArn.Unknown" + ErrorInvalidTargetException = "InvalidTargetException" + ErrorInvalidTenancy = "InvalidTenancy" + ErrorInvalidTime = "InvalidTime" + ErrorInvalidTrafficMirrorFilterNotFound = "InvalidTrafficMirrorFilterNotFound" + ErrorInvalidTrafficMirrorFilterRuleNotFound = "InvalidTrafficMirrorFilterRuleNotFound" + ErrorInvalidTrafficMirrorSessionNotFound = "InvalidTrafficMirrorSessionNotFound" + ErrorInvalidTrafficMirrorTargetNoFound = "InvalidTrafficMirrorTargetNoFound" + ErrorInvalidUserIDMalformed = "InvalidUserID.Malformed" + ErrorInvalidVolumeNotFound = "InvalidVolume.NotFound" + ErrorInvalidVolumeZoneMismatch = "InvalidVolume.ZoneMismatch" + ErrorInvalidVolumeIDDuplicate = "InvalidVolumeID.Duplicate" + ErrorInvalidVolumeIDMalformed = "InvalidVolumeID.Malformed" + ErrorInvalidVolumeIDZoneMismatch = "InvalidVolumeID.ZoneMismatch" + ErrorInvalidVpcEndpointNotFound = "InvalidVpcEndpoint.NotFound" + ErrorInvalidVpcEndpointIdMalformed = "InvalidVpcEndpointId.Malformed" + ErrorInvalidVpcEndpointIdNotFound = "InvalidVpcEndpointId.NotFound" + ErrorInvalidVpcEndpointServiceNotFound = "InvalidVpcEndpointService.NotFound" + ErrorInvalidVpcEndpointServiceIdNotFound = "InvalidVpcEndpointServiceId.NotFound" + ErrorInvalidVpcEndpointServiceIdIdMalformed = "InvalidVpcEndpointServiceIdId.Malformed" + ErrorInvalidVpcEndpointType = "InvalidVpcEndpointType" + ErrorInvalidVpcIDMalformed = "InvalidVpcID.Malformed" + ErrorInvalidVpcIDNotFound = "InvalidVpcID.NotFound" + ErrorInvalidVpcPeeringConnectionIDNotFound = "InvalidVpcPeeringConnectionID.NotFound" + ErrorInvalidVpcPeeringConnectionIdMalformed = "InvalidVpcPeeringConnectionId.Malformed" + ErrorInvalidVpcPeeringConnectionStateDnsHostnamesDisabled = "InvalidVpcPeeringConnectionState.DnsHostnamesDisabled" + ErrorInvalidVpcRange = "InvalidVpcRange" + ErrorInvalidVpcState = "InvalidVpcState" + ErrorInvalidVpnConnectionInvalidState = "InvalidVpnConnection.InvalidState" + ErrorInvalidVpnConnectionInvalidType = "InvalidVpnConnection.InvalidType" + ErrorInvalidVpnConnectionID = "InvalidVpnConnectionID" + ErrorInvalidVpnConnectionIDNotFound = "InvalidVpnConnectionID.NotFound" + ErrorInvalidVpnGatewayAttachmentNotFound = "InvalidVpnGatewayAttachment.NotFound" + ErrorInvalidVpnGatewayIDNotFound = "InvalidVpnGatewayID.NotFound" + ErrorInvalidVpnGatewayState = "InvalidVpnGatewayState" + ErrorInvalidZoneNotFound = "InvalidZone.NotFound" + ErrorKeyPairLimitExceeded = "KeyPairLimitExceeded" + ErrorLegacySecurityGroup = "LegacySecurityGroup" + ErrorLimitPriceExceeded = "LimitPriceExceeded" + ErrorLogDestinationNotFound = "LogDestinationNotFound" + ErrorLogDestinationPermissionIssue = "LogDestinationPermissionIssue" + ErrorMalformedQueryString = "MalformedQueryString" + ErrorMaxConfigLimitExceededException = "MaxConfigLimitExceededException" + ErrorMaxIOPSLimitExceeded = "MaxIOPSLimitExceeded" + ErrorMaxScheduledInstanceCapacityExceeded = "MaxScheduledInstanceCapacityExceeded" + ErrorMaxSpotFleetRequestCountExceeded = "MaxSpotFleetRequestCountExceeded" + ErrorMaxSpotInstanceCountExceeded = "MaxSpotInstanceCountExceeded" + ErrorMaxTemplateLimitExceeded = "MaxTemplateLimitExceeded" + ErrorMaxTemplateVersionLimitExceeded = "MaxTemplateVersionLimitExceeded" + ErrorMissingAction = "MissingAction" + ErrorMissingAuthenticationToken = "MissingAuthenticationToken" + ErrorMissingInput = "MissingInput" + ErrorMissingParameter = "MissingParameter" + ErrorNatGatewayLimitExceeded = "NatGatewayLimitExceeded" + ErrorNatGatewayMalformed = "NatGatewayMalformed" + ErrorNatGatewayNotFound = "NatGatewayNotFound" + ErrorNetworkAclEntryAlreadyExists = "NetworkAclEntryAlreadyExists" + ErrorNetworkAclEntryLimitExceeded = "NetworkAclEntryLimitExceeded" + ErrorNetworkAclLimitExceeded = "NetworkAclLimitExceeded" + ErrorNetworkInterfaceLimitExceeded = "NetworkInterfaceLimitExceeded" + ErrorNetworkInterfaceNotSupported = "NetworkInterfaceNotSupported" + ErrorNetworkLoadBalancerNotFoundException = "NetworkLoadBalancerNotFoundException" + ErrorNlbInUseByTrafficMirrorTargetException = "NlbInUseByTrafficMirrorTargetException" + ErrorNoSuchVersion = "NoSuchVersion" + ErrorNonEBSInstance = "NonEBSInstance" + ErrorNotExportable = "NotExportable" + ErrorNotImplemented = "NotImplemented" + ErrorOperationNotPermitted = "OperationNotPermitted" + ErrorOptInRequired = "OptInRequired" + ErrorOutstandingVpcPeeringConnectionLimitExceeded = "OutstandingVpcPeeringConnectionLimitExceeded" + ErrorPackedPolicyTooLarge = "PackedPolicyTooLarge" + ErrorPendingSnapshotLimitExceeded = "PendingSnapshotLimitExceeded" + ErrorPendingVerification = "PendingVerification" + ErrorPendingVpcPeeringConnectionLimitExceeded = "PendingVpcPeeringConnectionLimitExceeded" + ErrorPlacementGroupLimitExceeded = "PlacementGroupLimitExceeded" + ErrorPrivateIpAddressLimitExceeded = "PrivateIpAddressLimitExceeded" + ErrorRegionDisabled = "RegionDisabledException" + ErrorRequestEntityTooLarge = "RequestEntityTooLarge" + ErrorRequestExpired = "RequestExpired" + ErrorRequestLimitExceeded = "RequestLimitExceeded" + ErrorRequestResourceCountExceeded = "RequestResourceCountExceeded" + ErrorReservationCapacityExceeded = "ReservationCapacityExceeded" + ErrorReservedInstancesCountExceeded = "ReservedInstancesCountExceeded" + ErrorReservedInstancesLimitExceeded = "ReservedInstancesLimitExceeded" + ErrorReservedInstancesUnavailable = "ReservedInstancesUnavailable" + ErrorResourceAlreadyAssigned = "Resource.AlreadyAssigned" + ErrorResourceAlreadyAssociated = "Resource.AlreadyAssociated" + ErrorResourceCountExceeded = "ResourceCountExceeded" + ErrorResourceCountLimitExceeded = "ResourceCountLimitExceeded" + ErrorResourceLimitExceeded = "ResourceLimitExceeded" + // ErrorResourceNotFound ("ResourceNotFound") is the ACM not-found code. EKS + // must use ErrorEKSResourceNotFound ("ResourceNotFoundException", below) — + // the restjson code the aws eks client expects. Do not cross-wire the two. + ErrorResourceNotFound = "ResourceNotFound" + ErrorEKSResourceInUse = "ResourceInUseException" + ErrorEKSResourceNotFound = "ResourceNotFoundException" + ErrorRetryableError = "RetryableError" + ErrorRouteAlreadyExists = "RouteAlreadyExists" + ErrorRouteLimitExceeded = "RouteLimitExceeded" + ErrorRouteTableLimitExceeded = "RouteTableLimitExceeded" + ErrorRulesPerSecurityGroupLimitExceeded = "RulesPerSecurityGroupLimitExceeded" + ErrorScheduledInstanceLimitExceeded = "ScheduledInstanceLimitExceeded" + ErrorScheduledInstanceParameterMismatch = "ScheduledInstanceParameterMismatch" + ErrorScheduledInstanceSlotNotOpen = "ScheduledInstanceSlotNotOpen" + ErrorScheduledInstanceSlotUnavailable = "ScheduledInstanceSlotUnavailable" + ErrorSecurityGroupLimitExceeded = "SecurityGroupLimitExceeded" + ErrorSecurityGroupsPerInstanceLimitExceeded = "SecurityGroupsPerInstanceLimitExceeded" + ErrorSecurityGroupsPerInterfaceLimitExceeded = "SecurityGroupsPerInterfaceLimitExceeded" + ErrorSerialConsoleSessionUnavailable = "SerialConsoleSessionUnavailable" + ErrorServerInternal = "ServerInternal" + ErrorServiceUnavailable = "ServiceUnavailable" + ErrorSignatureDoesNotMatch = "SignatureDoesNotMatch" + ErrorSnapshotCopyUnsupportedInterRegion = "SnapshotCopyUnsupported.InterRegion" + ErrorSnapshotCreationPerVolumeRateExceeded = "SnapshotCreationPerVolumeRateExceeded" + ErrorSnapshotLimitExceeded = "SnapshotLimitExceeded" + ErrorSpotMaxPriceTooLow = "SpotMaxPriceTooLow" + ErrorSubnetLimitExceeded = "SubnetLimitExceeded" + ErrorTagLimitExceeded = "TagLimitExceeded" + ErrorTagPolicyViolation = "TagPolicyViolation" + ErrorThrottling = "Throttling" + ErrorTargetCapacityLimitExceededException = "TargetCapacityLimitExceededException" + ErrorTrafficMirrorFilterInUse = "TrafficMirrorFilterInUse" + ErrorTrafficMirrorFilterLimitExceeded = "TrafficMirrorFilterLimitExceeded" + ErrorTrafficMirrorFilterRuleAlreadyExists = "TrafficMirrorFilterRuleAlreadyExists" + ErrorTrafficMirrorFilterRuleLimitExceeded = "TrafficMirrorFilterRuleLimitExceeded" + ErrorTrafficMirrorSessionLimitExceeded = "TrafficMirrorSessionLimitExceeded" + ErrorTrafficMirrorSessionsPerInterfaceLimitExceeded = "TrafficMirrorSessionsPerInterfaceLimitExceeded" + ErrorTrafficMirrorSessionsPerTargetLimitExceeded = "TrafficMirrorSessionsPerTargetLimitExceeded" + ErrorTrafficMirrorSourcesPerTargetLimitExceeded = "TrafficMirrorSourcesPerTargetLimitExceeded" + ErrorTrafficMirrorTargetInUseException = "TrafficMirrorTargetInUseException" + ErrorTrafficMirrorTargetLimitExceeded = "TrafficMirrorTargetLimitExceeded" + ErrorUnauthorizedOperation = "UnauthorizedOperation" + ErrorUnavailable = "Unavailable" + ErrorUnavailableHostRequirements = "UnavailableHostRequirements" + ErrorUnfulfillableCapacity = "UnfulfillableCapacity" + ErrorUnknownParameter = "UnknownParameter" + ErrorUnknownPrincipalTypeUnsupported = "UnknownPrincipalType.Unsupported" + ErrorUnknownVolumeType = "UnknownVolumeType" + ErrorUnsupported = "Unsupported" + ErrorUnsupportedException = "UnsupportedException" + ErrorUnsupportedHibernationConfiguration = "UnsupportedHibernationConfiguration" + ErrorUnsupportedHostConfiguration = "UnsupportedHostConfiguration" + ErrorUnsupportedInstanceAttribute = "UnsupportedInstanceAttribute" + ErrorUnsupportedInstanceTypeOnHost = "UnsupportedInstanceTypeOnHost" + ErrorUnsupportedOperation = "UnsupportedOperation" + ErrorUnsupportedProtocol = "UnsupportedProtocol" + ErrorUnsupportedTenancy = "UnsupportedTenancy" + ErrorUpdateLimitExceeded = "UpdateLimitExceeded" + ErrorVPCIdNotSpecified = "VPCIdNotSpecified" + ErrorVPCResourceNotSpecified = "VPCResourceNotSpecified" + ErrorValidationError = "ValidationError" + ErrorVcpuLimitExceeded = "VcpuLimitExceeded" + ErrorVolumeIOPSLimit = "VolumeIOPSLimit" + ErrorVolumeInUse = "VolumeInUse" + ErrorVolumeLimitExceeded = "VolumeLimitExceeded" + ErrorVolumeModificationSizeLimitExceeded = "VolumeModificationSizeLimitExceeded" + ErrorVolumeTypeNotAvailableInZone = "VolumeTypeNotAvailableInZone" + ErrorVpcEndpointLimitExceeded = "VpcEndpointLimitExceeded" + ErrorVpcLimitExceeded = "VpcLimitExceeded" + ErrorVpcPeeringConnectionAlreadyExists = "VpcPeeringConnectionAlreadyExists" + ErrorVpcPeeringConnectionsPerVpcLimitExceeded = "VpcPeeringConnectionsPerVpcLimitExceeded" + ErrorVpnConnectionLimitExceeded = "VpnConnectionLimitExceeded" + ErrorVpnGatewayAttachmentLimitExceeded = "VpnGatewayAttachmentLimitExceeded" + ErrorVpnGatewayLimitExceeded = "VpnGatewayLimitExceeded" + ErrorZonesMismatched = "ZonesMismatched" // IAM-specific error codes ErrorIAMNoSuchEntity = "NoSuchEntity" @@ -469,6 +481,8 @@ var ( ErrorELBv2TooManyActions = "TooManyActions" ErrorELBv2InvalidRulePriority = "InvalidRulePriority" ErrorELBv2IncompatibleProtocols = "IncompatibleProtocols" + ErrorELBv2CertificateNotFound = "CertificateNotFound" + ErrorELBv2SSLPolicyNotFound = "SSLPolicyNotFound" ) // ValidErrorCode returns the error code if it exists in ErrorLookup, @@ -481,6 +495,15 @@ func ValidErrorCode(code string) string { return ErrorServerInternal } +// IsErrorCode reports whether err carries the named AWS error code. Service +// handlers return these codes as plain strings (errors.New(code)), commonly +// wrapped with %w by orchestration callers, so the match is a substring test +// against err.Error() rather than equality. Use it to classify a delete error +// as idempotent (already-gone) or retryable (still-in-use) in teardown paths. +func IsErrorCode(err error, code string) bool { + return err != nil && strings.Contains(err.Error(), code) +} + var ErrorLookup = map[string]ErrorMessage{ ErrorAccountDisabled: {HTTPCode: 400, Message: "The functionality you have requested has been administratively disabled for this account."}, ErrorActiveVpcPeeringConnectionPerVpcLimitExceeded: {HTTPCode: 400, Message: "You've reached the limit on the number of active VPC peering connections you can have for the specified VPC."}, @@ -682,6 +705,7 @@ var ErrorLookup = map[string]ErrorMessage{ ErrorInvalidMaxResults: {HTTPCode: 400, Message: "The specified value for MaxResults is not valid."}, ErrorInvalidNatGatewayIDNotFound: {HTTPCode: 404, Message: "The specified NAT gateway ID does not exist. Ensure that you specify the AWS Region in which the NAT gateway is located, if it's not in the default Region."}, ErrorInvalidNetworkAclEntryNotFound: {HTTPCode: 404, Message: "The specified network ACL entry does not exist."}, + ErrorACMInvalidArn: {HTTPCode: 400, Message: "The requested Amazon Resource Name (ARN) does not refer to an existing resource."}, ErrorInvalidNetworkAclIDNotFound: {HTTPCode: 404, Message: "The specified network ACL does not exist. Ensure that you specify the AWS Region in which the network ACL is located, if it's not in the default Region."}, ErrorInvalidNetworkAclIdMalformed: {HTTPCode: 400, Message: "The specified network ACL ID is malformed. Ensure that you provide the ID in the form acl-xxxxxxxxxxxxxxxxx."}, ErrorInvalidNetworkInterfaceInUse: {HTTPCode: 409, Message: "The specified interface is currently in use and cannot be deleted or attached to another instance. Ensure that you have detached the network interface first. If a network interface is in use, you may also receive the InvalidParameterValue error."}, @@ -842,6 +866,9 @@ var ErrorLookup = map[string]ErrorMessage{ ErrorResourceCountExceeded: {HTTPCode: 400, Message: "You have exceeded the number of resources allowed for this request; for example, if you try to launch more instances than AWS allows in a single request. This limit is separate from your individual resource limit. If you get this error, break up your request into smaller requests; for example, if you are launching 15 instances, try launching 5 instances in 3 separate requests."}, ErrorResourceCountLimitExceeded: {HTTPCode: 400, Message: "You have exceeded a resource limit for creating routes."}, ErrorResourceLimitExceeded: {HTTPCode: 400, Message: "You have exceeded an Amazon EC2 resource limit. For example, you might have too many snapshot copies in progress."}, + ErrorResourceNotFound: {HTTPCode: 404, Message: "The specified resource was not found."}, + ErrorEKSResourceInUse: {HTTPCode: 409, Message: "A cluster with this name already exists in this account and Region. Delete the existing cluster, or choose a different name."}, + ErrorEKSResourceNotFound: {HTTPCode: 404, Message: "The specified cluster could not be found. You can view your available clusters with ListClusters. Amazon EKS clusters are Region specific."}, ErrorRetryableError: {HTTPCode: 400, Message: "A request submitted by an AWS service on your behalf could not be completed. The requesting service might automatically retry the request."}, ErrorRouteAlreadyExists: {HTTPCode: 409, Message: "A route for the specified CIDR block already exists in this route table."}, ErrorRouteLimitExceeded: {HTTPCode: 400, Message: "You've reached the limit on the number of routes that you can add to a route table."}, @@ -937,11 +964,13 @@ var ErrorLookup = map[string]ErrorMessage{ ErrorELBv2InvalidScheme: {HTTPCode: 400, Message: "The specified scheme is not valid. Specify 'internet-facing' or 'internal'."}, ErrorELBv2SubnetNotFound: {HTTPCode: 400, Message: "The specified subnet does not exist."}, ErrorELBv2AvailabilityZoneNotSupported: {HTTPCode: 400, Message: "The specified Availability Zone is not supported."}, - ErrorELBv2InvalidConfigurationRequest: {HTTPCode: 400, Message: "Security groups are not supported for load balancers with type 'network'."}, + ErrorELBv2InvalidConfigurationRequest: {HTTPCode: 400, Message: "The requested configuration change is not valid."}, ErrorELBv2RuleNotFound: {HTTPCode: 400, Message: "One or more rules not found."}, ErrorELBv2PriorityInUse: {HTTPCode: 400, Message: "The specified priority is in use."}, ErrorELBv2TooManyRules: {HTTPCode: 400, Message: "You've reached the limit on the number of rules per load balancer."}, ErrorELBv2TooManyActions: {HTTPCode: 400, Message: "You've reached the limit on the number of actions per rule."}, ErrorELBv2InvalidRulePriority: {HTTPCode: 400, Message: "The specified rule priority is not valid. Priority must be between 1 and 50000."}, ErrorELBv2IncompatibleProtocols: {HTTPCode: 400, Message: "The listener protocol is incompatible with the target group protocol."}, + ErrorELBv2CertificateNotFound: {HTTPCode: 400, Message: "One or more specified certificates do not exist."}, + ErrorELBv2SSLPolicyNotFound: {HTTPCode: 400, Message: "The specified SSL policy does not exist."}, } diff --git a/spinifex/daemon/cpuinfo_test.go b/spinifex/daemon/cpuinfo_test.go new file mode 100644 index 000000000..5aeb55cf2 --- /dev/null +++ b/spinifex/daemon/cpuinfo_test.go @@ -0,0 +1,61 @@ +package daemon + +import "testing" + +func TestParsePhysicalCores(t *testing.T) { + tests := []struct { + name string + cpuinfo string + want int + wantOK bool + }{ + { + name: "hyperthreaded single socket: 2 cores / 4 threads", + cpuinfo: "processor\t: 0\nphysical id\t: 0\ncore id\t: 0\n\n" + + "processor\t: 1\nphysical id\t: 0\ncore id\t: 1\n\n" + + "processor\t: 2\nphysical id\t: 0\ncore id\t: 0\n\n" + + "processor\t: 3\nphysical id\t: 0\ncore id\t: 1\n", + want: 2, + wantOK: true, + }, + { + name: "hybrid: P-cores share core ids across HT, E-cores distinct", + // 2 P-cores (core id 0,1) each 2 threads + 2 E-cores (core id 16,17). + cpuinfo: "processor\t: 0\nphysical id\t: 0\ncore id\t: 0\n\n" + + "processor\t: 1\nphysical id\t: 0\ncore id\t: 0\n\n" + + "processor\t: 2\nphysical id\t: 0\ncore id\t: 1\n\n" + + "processor\t: 3\nphysical id\t: 0\ncore id\t: 1\n\n" + + "processor\t: 4\nphysical id\t: 0\ncore id\t: 16\n\n" + + "processor\t: 5\nphysical id\t: 0\ncore id\t: 17\n", + want: 4, + wantOK: true, + }, + { + name: "dual socket: 2 sockets x 1 core, same core id", + cpuinfo: "processor\t: 0\nphysical id\t: 0\ncore id\t: 0\n\n" + + "processor\t: 1\nphysical id\t: 1\ncore id\t: 0\n", + want: 2, + wantOK: true, + }, + { + name: "no topology fields (e.g. ARM/container) falls back", + cpuinfo: "processor\t: 0\nvendor_id\t: GenuineIntel\n\nprocessor\t: 1\nvendor_id\t: GenuineIntel\n", + want: 0, + wantOK: false, + }, + { + name: "empty", + cpuinfo: "", + want: 0, + wantOK: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, ok := parsePhysicalCores([]byte(tt.cpuinfo)) + if got != tt.want || ok != tt.wantOK { + t.Fatalf("parsePhysicalCores() = (%d, %v), want (%d, %v)", got, ok, tt.want, tt.wantOK) + } + }) + } +} diff --git a/spinifex/daemon/daemon.go b/spinifex/daemon/daemon.go index 5ffe47b71..fa8081b1a 100644 --- a/spinifex/daemon/daemon.go +++ b/spinifex/daemon/daemon.go @@ -33,6 +33,7 @@ import ( "github.com/mulgadc/spinifex/spinifex/awserrors" "github.com/mulgadc/spinifex/spinifex/config" "github.com/mulgadc/spinifex/spinifex/gpu" + handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" handlers_ec2_account "github.com/mulgadc/spinifex/spinifex/handlers/ec2/account" handlers_ec2_eigw "github.com/mulgadc/spinifex/spinifex/handlers/ec2/eigw" handlers_ec2_eip "github.com/mulgadc/spinifex/spinifex/handlers/ec2/eip" @@ -49,6 +50,7 @@ import ( handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + handlers_imds "github.com/mulgadc/spinifex/spinifex/handlers/imds" "github.com/mulgadc/spinifex/spinifex/instancetypes" "github.com/mulgadc/spinifex/spinifex/network/external/dhcp" "github.com/mulgadc/spinifex/spinifex/network/host" @@ -83,9 +85,9 @@ type EBS struct { // when full, it unsubscribes so NATS routes requests to other nodes. type ResourceManager struct { mu sync.RWMutex - // hostVCPU / hostMemGB are the raw figures reported by the host - // (runtime.NumCPU, /proc/meminfo). Schedulable capacity for guest VMs - // is host - reserved - allocated. + // hostVCPU / hostMemGB are the host figures guests schedule against: + // physical cores (physicalCoreCount, not SMT threads) and /proc/meminfo. + // Schedulable capacity for guest VMs is host - reserved - allocated. hostVCPU int hostMemGB float64 // reservedVCPU / reservedMem are held back from guest scheduling for @@ -132,6 +134,7 @@ type Daemon struct { eipService *handlers_ec2_eip.EIPServiceImpl elbv2Service *handlers_elbv2.ELBv2ServiceImpl eksService *handlers_eks.EKSServiceImpl + acmService *handlers_acm.ACMServiceImpl routeTableService *handlers_ec2_routetable.RouteTableServiceImpl natGatewayService *handlers_ec2_natgw.NatGatewayServiceImpl externalIPAM *handlers_ec2_vpc.ExternalIPAM @@ -400,6 +403,69 @@ func getSystemMemory() (float64, error) { } } +// physicalCoreCount returns the number of physical CPU cores. Guest vCPUs are +// scheduled 1:1 against physical cores rather than SMT threads: +// runtime.NumCPU() counts logical threads, so on an SMT or hybrid host (e.g. +// 12 cores / 16 threads) it would let guest vCPUs pack past the physical core +// count and thrash them during the boot spike. Parses /proc/cpuinfo, counting +// distinct (physical id, core id) pairs. Falls back to runtime.NumCPU() on +// non-Linux hosts or when the topology fields are absent (some VMs/containers +// omit them). +func physicalCoreCount() int { + logical := runtime.NumCPU() + if runtime.GOOS != "linux" { + return logical + } + data, err := os.ReadFile("/proc/cpuinfo") + if err != nil { + slog.Warn("physical core detection failed, scheduling against logical CPUs", + "err", err, "logicalCPU", logical) + return logical + } + if n, ok := parsePhysicalCores(data); ok { + return n + } + return logical +} + +// parsePhysicalCores counts distinct (physical id, core id) pairs in +// /proc/cpuinfo contents — i.e. physical cores, with SMT siblings collapsed. +// Returns ok=false when the topology fields are absent (some VMs/containers +// omit them) so the caller can fall back to the logical CPU count. +func parsePhysicalCores(data []byte) (int, bool) { + cores := make(map[string]struct{}) + var phys, core string + sawCore := false + flush := func() { + if core != "" { + cores[phys+":"+core] = struct{}{} + } + phys, core = "", "" + } + for line := range strings.SplitSeq(string(data), "\n") { + if strings.TrimSpace(line) == "" { + flush() + continue + } + key, val, ok := strings.Cut(line, ":") + if !ok { + continue + } + switch strings.TrimSpace(key) { + case "physical id": + phys = strings.TrimSpace(val) + case "core id": + core = strings.TrimSpace(val) + sawCore = true + } + } + flush() + if !sawCore || len(cores) == 0 { + return 0, false + } + return len(cores), true +} + // NewResourceManager creates a new resource manager with system capabilities. // Returns an error if system memory cannot be detected, since an incorrect // default would either under-provision (large servers) or over-commit (small devices). @@ -409,8 +475,11 @@ func getSystemMemory() (float64, error) { // gpuModels is the list of recognised GPU models present on the host; pass nil if // GPU passthrough is disabled or no GPUs were found. func NewResourceManager(gpuModels []instancetypes.GPUModel, migProfiles []instancetypes.MIGProfileSpec, gpuMgr *gpu.Manager) (*ResourceManager, error) { - // Get system CPU cores - numCPU := runtime.NumCPU() + // Schedule guest vCPUs against physical cores, not SMT threads (see + // physicalCoreCount), so a hyperthreaded/hybrid host can't be packed past + // what it can actually run. SPINIFEX_HOST_VCPU overrides detection for + // hosts that misreport topology. + hostVCPU := resolveHostVCPU(os.Getenv, physicalCoreCount()) // Get system memory (in GB) totalMemGB, err := getSystemMemory() @@ -419,10 +488,10 @@ func NewResourceManager(gpuModels []instancetypes.GPUModel, migProfiles []instan } reserve := resolveHostReserve(os.Getenv) - reservedVCPU, reservedMem, err := applyHostReserve(reserve, numCPU, totalMemGB) + reservedVCPU, reservedMem, err := applyHostReserve(reserve, hostVCPU, totalMemGB) if err != nil { slog.Error("host below minimum reserve — daemon refuses to start", - "err", err, "hostVCPU", numCPU, "hostMemGB", totalMemGB, + "err", err, "hostVCPU", hostVCPU, "hostMemGB", totalMemGB, "reserveVCPU", reserve.vCPU, "reserveMemGB", reserve.memGB) return nil, fmt.Errorf("validate host reserve: %w", err) } @@ -441,13 +510,13 @@ func NewResourceManager(gpuModels []instancetypes.GPUModel, migProfiles []instan } slog.Info("System resources detected", - "hostVCPU", numCPU, "hostMemGB", totalMemGB, + "hostVCPU", hostVCPU, "logicalCPU", runtime.NumCPU(), "hostMemGB", totalMemGB, "reservedVCPU", reservedVCPU, "reservedMemGB", reservedMem, - "schedulableVCPU", numCPU-reservedVCPU, "schedulableMemGB", totalMemGB-reservedMem, + "schedulableVCPU", hostVCPU-reservedVCPU, "schedulableMemGB", totalMemGB-reservedMem, "instanceTypes", len(instanceTypes)) return &ResourceManager{ - hostVCPU: numCPU, + hostVCPU: hostVCPU, hostMemGB: totalMemGB, reservedVCPU: reservedVCPU, reservedMem: reservedMem, @@ -840,12 +909,21 @@ func (d *Daemon) subscribeAll() error { natsSub{"elbv2.DescribeRules", d.handleELBv2DescribeRules, "spinifex-workers"}, natsSub{"elbv2.SetRulePriorities", d.handleELBv2SetRulePriorities, "spinifex-workers"}, natsSub{"elbv2.DescribeTags", d.handleELBv2DescribeTags, "spinifex-workers"}, + natsSub{"elbv2.AddTags", d.handleELBv2AddTags, "spinifex-workers"}, + natsSub{"elbv2.RemoveTags", d.handleELBv2RemoveTags, "spinifex-workers"}, natsSub{"elbv2.LBAgentHeartbeat", d.handleELBv2LBAgentHeartbeat, "spinifex-workers"}, natsSub{"elbv2.GetLBConfig", d.handleELBv2GetLBConfig, "spinifex-workers"}, natsSub{"elbv2.ModifyTargetGroupAttributes", d.handleELBv2ModifyTargetGroupAttributes, "spinifex-workers"}, natsSub{"elbv2.DescribeTargetGroupAttributes", d.handleELBv2DescribeTargetGroupAttributes, "spinifex-workers"}, natsSub{"elbv2.ModifyLoadBalancerAttributes", d.handleELBv2ModifyLoadBalancerAttributes, "spinifex-workers"}, natsSub{"elbv2.DescribeLoadBalancerAttributes", d.handleELBv2DescribeLoadBalancerAttributes, "spinifex-workers"}, + natsSub{"elbv2.SetSecurityGroups", d.handleELBv2SetSecurityGroups, "spinifex-workers"}, + natsSub{"elbv2.SetIpAddressType", d.handleELBv2SetIpAddressType, "spinifex-workers"}, + natsSub{"elbv2.SetSubnets", d.handleELBv2SetSubnets, "spinifex-workers"}, + natsSub{"elbv2.AddListenerCertificates", d.handleELBv2AddListenerCertificates, "spinifex-workers"}, + natsSub{"elbv2.RemoveListenerCertificates", d.handleELBv2RemoveListenerCertificates, "spinifex-workers"}, + natsSub{"elbv2.DescribeListenerCertificates", d.handleELBv2DescribeListenerCertificates, "spinifex-workers"}, + natsSub{"elbv2.DescribeSSLPolicies", d.handleELBv2DescribeSSLPolicies, "spinifex-workers"}, ) } @@ -891,6 +969,16 @@ func (d *Daemon) subscribeAll() error { ) } + // ACM gateway → daemon subscriptions (minimal certificate store). + if d.acmService != nil { + subs = append(subs, + natsSub{"acm.ImportCertificate", d.handleACMImportCertificate, "spinifex-workers"}, + natsSub{"acm.DescribeCertificate", d.handleACMDescribeCertificate, "spinifex-workers"}, + natsSub{"acm.ListCertificates", d.handleACMListCertificates, "spinifex-workers"}, + natsSub{"acm.DeleteCertificate", d.handleACMDeleteCertificate, "spinifex-workers"}, + ) + } + // EIP operations require external IPAM (pool mode). Only subscribe when available; // without a subscriber the gateway returns a NATS timeout → clean error to the client. if d.eipService != nil { @@ -1186,6 +1274,16 @@ func (d *Daemon) startCluster() error { return fmt.Errorf("failed to initialize VPC service: %w", err) } + // Wire the eni-by-vpc-ip reverse index so the ENI controller keeps the + // IMDS source-IP→ENI lookup in sync on Create/DeleteNetworkInterface. + if vpcJS, jsErr := d.natsConn.JetStream(); jsErr != nil { + slog.Warn("Failed to get JetStream for eni-by-ip index", "err", jsErr) + } else if eniByIPKV, kvErr := handlers_imds.InitENIByIPBucket(vpcJS, 1); kvErr != nil { + slog.Warn("Failed to init eni-by-ip index bucket", "err", kvErr) + } else { + d.vpcService.SetENIByIPIndex(handlers_ec2_vpc.NewENIByIPIndex(eniByIPKV)) + } + d.routeTableService, err = initServiceWithRetry("RouteTable service", func() (*handlers_ec2_routetable.RouteTableServiceImpl, error) { return handlers_ec2_routetable.NewRouteTableServiceImplWithNATS(d.config, d.natsConn) }) @@ -1328,12 +1426,23 @@ func (d *Daemon) startCluster() error { } d.eksService, err = initServiceWithRetry("EKS service", func() (*handlers_eks.EKSServiceImpl, error) { - return handlers_eks.NewEKSServiceImplWithNATS(d.config, d.natsConn) + return handlers_eks.NewEKSServiceImpl(d.buildEKSServiceDeps()) }) if err != nil { return fmt.Errorf("failed to initialize EKS service: %w", err) } + d.acmService, err = initServiceWithRetry("ACM service", func() (*handlers_acm.ACMServiceImpl, error) { + return handlers_acm.NewACMServiceImplWithNATS(d.config, d.natsConn) + }) + if err != nil { + return fmt.Errorf("failed to initialize ACM service: %w", err) + } + + if err := d.eksService.SpawnRegisteredReconcilers(); err != nil { + slog.Warn("EKS: SpawnRegisteredReconcilers failed", "err", err) + } + // Ensure default VPC exists for system and admin accounts // (matches AWS: every account has a default VPC with IGW + default SG) if d.vpcService != nil { @@ -1927,6 +2036,11 @@ func (d *Daemon) setupShutdown() { d.elbv2Service.Close() } + // Stop EKS per-cluster reconciler + bootstrap goroutines. + if d.eksService != nil { + d.eksService.Shutdown() + } + // Final cleanup for _, sub := range d.natsSubscriptions { // Unsubscribe from each subscription @@ -2210,26 +2324,15 @@ func (d *Daemon) wireLBAgentConfig() { } // Resolve gateway URL — the address LB VMs use to reach the AWS gateway. - // Precedence: - // 1. br-mgmt present + AWSGW on a dedicated IP distinct from AdvertiseIP - // (multi-node: AWSGW on a mgmt-only IP, VPC path can't reach it) → - // gateway URL is the AWSGW bind IP and lb-agent gets a bootcmd host - // route via br-mgmt. - // 2. AdvertiseIP set (single-node, or multi-node where AWSGW binds to - // the advertised IP) → AdvertiseIP. VMs reach it via VPC → external - // (OVN's own dnat_and_snat SNATs their reply back to the ALB EIP). - // Critically, we do NOT add the mgmt host route here: when host IPs - // on the WAN share the advertiseIP, the /32 route would steal the - // return path for host-initiated ALB connections — replies would - // egress via mgmt with the VM's 10.x source IP, bypass OVN's SNAT, - // and arrive at the host with a source that doesn't match the open - // TCP socket (the client dialed the EIP, not the VM IP). - // 3. br-mgmt present + AWSGW on 0.0.0.0 → br-mgmt IP (both LB flavours - // reach the daemon via mgmt). - // 4. DevNetworking shim → 10.0.2.2. - // 5. AWSGW bound to specific IP (no br-mgmt, no advertise) → that IP. - // 6. Else: error and skip assignment — no silent empty URL. - var gatewayHost string + // Host selection is centralized in resolveGatewayHost so the OIDC issuer + // host and EKS NATS URL come from the same source (see M7). The only + // LB-specific extra here is the multi-node mgmt host route: when the host + // resolves to a dedicated AWSGW bind IP reachable only over br-mgmt, the + // lb-agent needs a bootcmd /32 route via br-mgmt. We deliberately do NOT + // add that route when the host is AdvertiseIP — WAN host IPs may share the + // advertiseIP and a /32 would steal the return path for host-initiated ALB + // connections (reply egresses mgmt with the VM's 10.x source, bypassing + // OVN's SNAT, mismatching the open TCP socket dialed against the EIP). awsgwBindIP := "" if d.config.AWSGW.Host != "" { if h, _, splitErr := net.SplitHostPort(d.config.AWSGW.Host); splitErr == nil { @@ -2239,25 +2342,13 @@ func (d *Daemon) wireLBAgentConfig() { advertiseIP := d.config.AdvertiseIP - switch { - case d.mgmtBridgeIP != "" && awsgwBindIP != "" && awsgwBindIP != "0.0.0.0" && - !net.ParseIP(awsgwBindIP).IsLoopback() && awsgwBindIP != advertiseIP: - // Multi-node: AWSGW on a dedicated mgmt IP. VMs can't reach it via - // VPC → external, so add a bootcmd host route via br-mgmt. - gatewayHost = awsgwBindIP + gatewayHost := d.resolveGatewayHost() + + // Multi-node mgmt-dedicated AWSGW: host resolved to the bind IP over + // br-mgmt (case 1 in resolveGatewayHost). Loopback / no-mgmt / advertiseIP + // paths can't satisfy all three guards, so this matches only that case. + if gatewayHost != "" && gatewayHost == awsgwBindIP && d.mgmtBridgeIP != "" && awsgwBindIP != advertiseIP { d.mgmtRouteVia = awsgwBindIP - case advertiseIP != "" && advertiseIP != "0.0.0.0": - // Single-node, or multi-node where AWSGW binds to AdvertiseIP: VMs - // reach AWSGW via the normal VPC → external path. No mgmt host route. - gatewayHost = advertiseIP - case d.mgmtBridgeIP != "": - // br-mgmt present + AWSGW on 0.0.0.0 and no advertiseIP — br-mgmt IP - // is the only reachable address. - gatewayHost = d.mgmtBridgeIP - case d.config.Daemon.DevNetworking: - gatewayHost = "10.0.2.2" - case awsgwBindIP != "" && awsgwBindIP != "0.0.0.0": - gatewayHost = awsgwBindIP } // Extract port from AWSGW host config (e.g. "0.0.0.0:9999" → "9999"). diff --git a/spinifex/daemon/daemon_handlers.go b/spinifex/daemon/daemon_handlers.go index 2dd71a721..62be46a9d 100644 --- a/spinifex/daemon/daemon_handlers.go +++ b/spinifex/daemon/daemon_handlers.go @@ -58,6 +58,28 @@ func handleNATSRequest[I any, O any](msg *nats.Msg, serviceFn func(*I, string) ( respondWithJSON(msg, output) } +// handleNATSRequestWithPrincipal is handleNATSRequest for service methods that +// also need the caller's IAM principal ARN (X-Principal-ARN header) — e.g. EKS +// CreateCluster, which mints the bootstrap-creator-admin AccessEntry for the +// caller. +func handleNATSRequestWithPrincipal[I any, O any](msg *nats.Msg, serviceFn func(*I, string, string) (*O, error)) { + accountID := utils.AccountIDFromMsg(msg) + principalARN := utils.PrincipalARNFromMsg(msg) + input := new(I) + if errResp := utils.UnmarshalJsonPayload(input, msg.Data); errResp != nil { + if err := msg.Respond(errResp); err != nil { + slog.Error("Failed to respond to NATS request", "err", err) + } + return + } + output, err := serviceFn(input, accountID, principalARN) + if err != nil { + respondWithError(msg, awserrors.ValidErrorCode(err.Error())) + return + } + respondWithJSON(msg, output) +} + // handleEC2Events processes incoming EC2 instance events (start, stop, terminate, attach-volume) func (d *Daemon) handleEC2Events(msg *nats.Msg) { var command types.EC2InstanceCommand diff --git a/spinifex/daemon/daemon_handlers_acm.go b/spinifex/daemon/daemon_handlers_acm.go new file mode 100644 index 000000000..cc6fdb526 --- /dev/null +++ b/spinifex/daemon/daemon_handlers_acm.go @@ -0,0 +1,19 @@ +package daemon + +import "github.com/nats-io/nats.go" + +func (d *Daemon) handleACMImportCertificate(msg *nats.Msg) { + handleNATSRequest(msg, d.acmService.ImportCertificate) +} + +func (d *Daemon) handleACMDescribeCertificate(msg *nats.Msg) { + handleNATSRequest(msg, d.acmService.DescribeCertificate) +} + +func (d *Daemon) handleACMListCertificates(msg *nats.Msg) { + handleNATSRequest(msg, d.acmService.ListCertificates) +} + +func (d *Daemon) handleACMDeleteCertificate(msg *nats.Msg) { + handleNATSRequest(msg, d.acmService.DeleteCertificate) +} diff --git a/spinifex/daemon/daemon_handlers_eks.go b/spinifex/daemon/daemon_handlers_eks.go index 12147a168..ada154367 100644 --- a/spinifex/daemon/daemon_handlers_eks.go +++ b/spinifex/daemon/daemon_handlers_eks.go @@ -5,7 +5,7 @@ import "github.com/nats-io/nats.go" // --- Cluster --- func (d *Daemon) handleEKSCreateCluster(msg *nats.Msg) { - handleNATSRequest(msg, d.eksService.CreateCluster) + handleNATSRequestWithPrincipal(msg, d.eksService.CreateCluster) } func (d *Daemon) handleEKSDescribeCluster(msg *nats.Msg) { diff --git a/spinifex/daemon/daemon_handlers_eks_test.go b/spinifex/daemon/daemon_handlers_eks_test.go index 3a2265546..ab453e289 100644 --- a/spinifex/daemon/daemon_handlers_eks_test.go +++ b/spinifex/daemon/daemon_handlers_eks_test.go @@ -26,8 +26,10 @@ func setupEKSDaemon(t *testing.T) (*Daemon, *nats.Conn) { } // requestEKS publishes a request on the given subject and returns the response -// payload. Every EKS handler in this branch returns NotImplemented, so the -// payload always decodes to that error code. +// payload. Proves a responder is wired for the subject and dispatched to the +// service. The expected Code per case is asserted by the caller; the empty `{}` +// body steers implemented handlers into a deterministic validation/dispatch +// error so the wiring is provable without a full orchestration graph. func requestEKS(t *testing.T, nc *nats.Conn, subject string) []byte { t.Helper() msg := nats.NewMsg(subject) @@ -38,59 +40,72 @@ func requestEKS(t *testing.T, nc *nats.Conn, subject string) []byte { return resp.Data } -func assertNotImpl(t *testing.T, payload []byte) { +// assertErrorCode decodes the error envelope and asserts the AWS error Code. +func assertErrorCode(t *testing.T, payload []byte, want string) { t.Helper() var env struct { Code *string `json:"Code"` } require.NoError(t, json.Unmarshal(payload, &env), "decode payload %q", payload) require.NotNil(t, env.Code, "payload %q has no Code field", payload) - assert.Equal(t, awserrors.ErrorNotImplemented, *env.Code) + assert.Equal(t, want, *env.Code) } func TestDaemonHandleEKS_AllHandlersDispatchToService(t *testing.T) { d, nc := setupEKSDaemon(t) + // wantCode is the error Code an empty-body request must elicit, proving the + // subject dispatched to its service method. Access-entry handlers are + // implemented (Sprint 6c): an empty body fails input/cluster validation with + // InvalidParameterValue. ListAccessPolicies returns the static catalogue with + // no error, so wantCode is "" (success — assert a non-error payload). + notImpl := awserrors.ErrorNotImplemented + invalid := awserrors.ErrorInvalidParameterValue + // Nodegroup mutators gate on orchestration deps, which the shim service + // lacks → ServiceUnavailable. The read paths reach input validation and an + // empty body fails with InvalidParameterValue. UpdateNodegroupVersion stays + // NotImplemented (v1 doesn't do AMI upgrades). + unavailable := awserrors.ErrorServiceUnavailable cases := []struct { - subject string - handler nats.MsgHandler + subject string + handler nats.MsgHandler + wantCode string }{ - {"eks.CreateCluster", d.handleEKSCreateCluster}, - {"eks.DescribeCluster", d.handleEKSDescribeCluster}, - {"eks.ListClusters", d.handleEKSListClusters}, - {"eks.UpdateClusterConfig", d.handleEKSUpdateClusterConfig}, - {"eks.UpdateClusterVersion", d.handleEKSUpdateClusterVersion}, - {"eks.DeleteCluster", d.handleEKSDeleteCluster}, - {"eks.CreateNodegroup", d.handleEKSCreateNodegroup}, - {"eks.DescribeNodegroup", d.handleEKSDescribeNodegroup}, - {"eks.ListNodegroups", d.handleEKSListNodegroups}, - {"eks.UpdateNodegroupConfig", d.handleEKSUpdateNodegroupConfig}, - {"eks.UpdateNodegroupVersion", d.handleEKSUpdateNodegroupVersion}, - {"eks.DeleteNodegroup", d.handleEKSDeleteNodegroup}, - {"eks.CreateAccessEntry", d.handleEKSCreateAccessEntry}, - {"eks.DescribeAccessEntry", d.handleEKSDescribeAccessEntry}, - {"eks.ListAccessEntries", d.handleEKSListAccessEntries}, - {"eks.UpdateAccessEntry", d.handleEKSUpdateAccessEntry}, - {"eks.DeleteAccessEntry", d.handleEKSDeleteAccessEntry}, - {"eks.AssociateAccessPolicy", d.handleEKSAssociateAccessPolicy}, - {"eks.DisassociateAccessPolicy", d.handleEKSDisassociateAccessPolicy}, - {"eks.ListAssociatedAccessPolicies", d.handleEKSListAssociatedAccessPolicies}, - {"eks.ListAccessPolicies", d.handleEKSListAccessPolicies}, - {"eks.ListAddons", d.handleEKSListAddons}, - {"eks.DescribeAddonVersions", d.handleEKSDescribeAddonVersions}, - {"eks.CreateAddon", d.handleEKSCreateAddon}, - {"eks.DeleteAddon", d.handleEKSDeleteAddon}, - {"eks.DescribeAddon", d.handleEKSDescribeAddon}, - {"eks.UpdateAddon", d.handleEKSUpdateAddon}, - {"eks.AssociateIdentityProviderConfig", d.handleEKSAssociateIdentityProviderConfig}, - {"eks.DescribeIdentityProviderConfig", d.handleEKSDescribeIdentityProviderConfig}, - {"eks.ListIdentityProviderConfigs", d.handleEKSListIdentityProviderConfigs}, - {"eks.DisassociateIdentityProviderConfig", d.handleEKSDisassociateIdentityProviderConfig}, - {"eks.TagResource", d.handleEKSTagResource}, - {"eks.UntagResource", d.handleEKSUntagResource}, - {"eks.ListTagsForResource", d.handleEKSListTagsForResource}, + {"eks.UpdateClusterConfig", d.handleEKSUpdateClusterConfig, notImpl}, + {"eks.UpdateClusterVersion", d.handleEKSUpdateClusterVersion, notImpl}, + {"eks.CreateNodegroup", d.handleEKSCreateNodegroup, unavailable}, + {"eks.DescribeNodegroup", d.handleEKSDescribeNodegroup, invalid}, + {"eks.ListNodegroups", d.handleEKSListNodegroups, invalid}, + {"eks.UpdateNodegroupConfig", d.handleEKSUpdateNodegroupConfig, unavailable}, + {"eks.UpdateNodegroupVersion", d.handleEKSUpdateNodegroupVersion, notImpl}, + {"eks.DeleteNodegroup", d.handleEKSDeleteNodegroup, unavailable}, + {"eks.CreateAccessEntry", d.handleEKSCreateAccessEntry, invalid}, + {"eks.DescribeAccessEntry", d.handleEKSDescribeAccessEntry, invalid}, + {"eks.ListAccessEntries", d.handleEKSListAccessEntries, invalid}, + {"eks.UpdateAccessEntry", d.handleEKSUpdateAccessEntry, invalid}, + {"eks.DeleteAccessEntry", d.handleEKSDeleteAccessEntry, invalid}, + {"eks.AssociateAccessPolicy", d.handleEKSAssociateAccessPolicy, invalid}, + {"eks.DisassociateAccessPolicy", d.handleEKSDisassociateAccessPolicy, invalid}, + {"eks.ListAssociatedAccessPolicies", d.handleEKSListAssociatedAccessPolicies, invalid}, + {"eks.ListAccessPolicies", d.handleEKSListAccessPolicies, ""}, + // Addon handlers are implemented (Sprint 6 P1): an empty body fails + // cluster validation with InvalidParameterValue, except + // DescribeAddonVersions which returns the static catalogue (success). + {"eks.ListAddons", d.handleEKSListAddons, invalid}, + {"eks.DescribeAddonVersions", d.handleEKSDescribeAddonVersions, ""}, + {"eks.CreateAddon", d.handleEKSCreateAddon, invalid}, + {"eks.DeleteAddon", d.handleEKSDeleteAddon, invalid}, + {"eks.DescribeAddon", d.handleEKSDescribeAddon, invalid}, + {"eks.UpdateAddon", d.handleEKSUpdateAddon, invalid}, + {"eks.AssociateIdentityProviderConfig", d.handleEKSAssociateIdentityProviderConfig, notImpl}, + {"eks.DescribeIdentityProviderConfig", d.handleEKSDescribeIdentityProviderConfig, notImpl}, + {"eks.ListIdentityProviderConfigs", d.handleEKSListIdentityProviderConfigs, notImpl}, + {"eks.DisassociateIdentityProviderConfig", d.handleEKSDisassociateIdentityProviderConfig, notImpl}, + {"eks.TagResource", d.handleEKSTagResource, notImpl}, + {"eks.UntagResource", d.handleEKSUntagResource, notImpl}, + {"eks.ListTagsForResource", d.handleEKSListTagsForResource, notImpl}, } - require.Equal(t, 34, len(cases), "expected exactly one handler per AWS EKS action") + require.Equal(t, 30, len(cases), "expected exactly one handler per non-lifecycle AWS EKS action") for _, c := range cases { t.Run(c.subject, func(t *testing.T) { @@ -99,7 +114,16 @@ func TestDaemonHandleEKS_AllHandlersDispatchToService(t *testing.T) { t.Cleanup(func() { _ = sub.Unsubscribe() }) payload := requestEKS(t, nc, c.subject) - assertNotImpl(t, payload) + if c.wantCode == "" { + // Success payload: dispatch is proven by a decodable non-error body. + var env struct { + Code *string `json:"Code"` + } + require.NoError(t, json.Unmarshal(payload, &env), "decode payload %q", payload) + assert.Nil(t, env.Code, "expected success, got error %q", payload) + return + } + assertErrorCode(t, payload, c.wantCode) }) } } diff --git a/spinifex/daemon/daemon_handlers_elbv2.go b/spinifex/daemon/daemon_handlers_elbv2.go index df70be1df..7614d559b 100644 --- a/spinifex/daemon/daemon_handlers_elbv2.go +++ b/spinifex/daemon/daemon_handlers_elbv2.go @@ -82,6 +82,14 @@ func (d *Daemon) handleELBv2DescribeTags(msg *nats.Msg) { handleNATSRequest(msg, d.elbv2Service.DescribeTags) } +func (d *Daemon) handleELBv2AddTags(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.AddTags) +} + +func (d *Daemon) handleELBv2RemoveTags(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.RemoveTags) +} + func (d *Daemon) handleELBv2LBAgentHeartbeat(msg *nats.Msg) { handleNATSRequest(msg, d.elbv2Service.LBAgentHeartbeat) } @@ -105,3 +113,31 @@ func (d *Daemon) handleELBv2ModifyLoadBalancerAttributes(msg *nats.Msg) { func (d *Daemon) handleELBv2DescribeLoadBalancerAttributes(msg *nats.Msg) { handleNATSRequest(msg, d.elbv2Service.DescribeLoadBalancerAttributes) } + +func (d *Daemon) handleELBv2SetSecurityGroups(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.SetSecurityGroups) +} + +func (d *Daemon) handleELBv2SetIpAddressType(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.SetIpAddressType) +} + +func (d *Daemon) handleELBv2SetSubnets(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.SetSubnets) +} + +func (d *Daemon) handleELBv2AddListenerCertificates(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.AddListenerCertificates) +} + +func (d *Daemon) handleELBv2RemoveListenerCertificates(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.RemoveListenerCertificates) +} + +func (d *Daemon) handleELBv2DescribeListenerCertificates(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.DescribeListenerCertificates) +} + +func (d *Daemon) handleELBv2DescribeSSLPolicies(msg *nats.Msg) { + handleNATSRequest(msg, d.elbv2Service.DescribeSSLPolicies) +} diff --git a/spinifex/daemon/daemon_handlers_eni_test.go b/spinifex/daemon/daemon_handlers_eni_test.go new file mode 100644 index 000000000..a055db7f1 --- /dev/null +++ b/spinifex/daemon/daemon_handlers_eni_test.go @@ -0,0 +1,441 @@ +package daemon + +import ( + "encoding/json" + "errors" + "fmt" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" + "github.com/mulgadc/spinifex/spinifex/qmp" + spxtypes "github.com/mulgadc/spinifex/spinifex/types" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/mulgadc/spinifex/spinifex/vm" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// --- eniHotplugErrorCode mapping --- + +// TestENIHotplugErrorCode locks down the vm.Manager error → AWS API code +// mapping. Wrong mapping silently breaks AWS-SDK retry semantics: clients +// expect 4xx codes for caller-fixable problems and 5xx for server faults. +func TestENIHotplugErrorCode(t *testing.T) { + tests := []struct { + name string + err error + want string + }{ + {"ErrInstanceNotFound", vm.ErrInstanceNotFound, awserrors.ErrorInvalidInstanceIDNotFound}, + {"wrapped ErrInstanceNotFound", fmt.Errorf("ctx: %w", vm.ErrInstanceNotFound), awserrors.ErrorInvalidInstanceIDNotFound}, + {"ErrInvalidTransition", vm.ErrInvalidTransition, awserrors.ErrorIncorrectInstanceState}, + {"ErrAttachmentLimitExceeded", vm.ErrAttachmentLimitExceeded, awserrors.ErrorAttachmentLimitExceeded}, + {"ErrENINotAttached", vm.ErrENINotAttached, awserrors.ErrorInvalidAttachmentIDNotFound}, + {"wrapped ErrENINotAttached", fmt.Errorf("%w: eni-x", vm.ErrENINotAttached), awserrors.ErrorInvalidAttachmentIDNotFound}, + {"ErrQMPUnavailable", vm.ErrQMPUnavailable, awserrors.ErrorServerInternal}, + {"ErrENIPipelineTimeout", vm.ErrENIPipelineTimeout, awserrors.ErrorServerInternal}, + {"unknown error", errors.New("network unreachable"), awserrors.ErrorServerInternal}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, eniHotplugErrorCode(tt.err)) + }) + } +} + +// --- publishENIHotplugEvent --- + +func TestPublishENIHotplugEvent_NilConnNoop(t *testing.T) { + publishENIHotplugEvent(nil, "vpc.eni-hotplug.attached", "i-x", map[string]any{"k": "v"}) +} + +func TestPublishENIHotplugEvent_Publishes(t *testing.T) { + d := createVPCTestDaemon(t) + + got := make(chan *nats.Msg, 1) + sub, err := d.natsConn.SubscribeSync("vpc.eni-hotplug.attached.i-evt") + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + go func() { + msg, _ := sub.NextMsg(2 * time.Second) + got <- msg + }() + + publishENIHotplugEvent(d.natsConn, "vpc.eni-hotplug.attached", "i-evt", map[string]any{ + "eniId": "eni-evt", + "slot": 1.0, + }) + + msg := <-got + require.NotNil(t, msg) + var payload map[string]any + require.NoError(t, json.Unmarshal(msg.Data, &payload)) + assert.Equal(t, "eni-evt", payload["eniId"]) + assert.Equal(t, 1.0, payload["slot"]) +} + +// --- shared fixture --- + +// eniHotPlugFixture builds a daemon with vpcService + a running VM +// registered in vmMgr, plus a stub DeviceController bound via +// vm.SetHotPlugTestSeams. The returned ENI record is already in the KV +// in "available" state and the VM has 4 free hot-plug slots. +type eniHotPlugFixture struct { + daemon *Daemon + vmInst *vm.VM + stub *vm.StubDeviceController + subnetID string + vpcID string + eniID string + mac string +} + +func newENIHotPlugFixture(t *testing.T) *eniHotPlugFixture { + t.Helper() + d := createVPCTestDaemon(t) + + vpcOut, err := d.vpcService.CreateVpc(&ec2.CreateVpcInput{ + CidrBlock: aws.String("10.0.0.0/16"), + }, testAccountID) + require.NoError(t, err) + subnetOut, err := d.vpcService.CreateSubnet(&ec2.CreateSubnetInput{ + VpcId: vpcOut.Vpc.VpcId, + CidrBlock: aws.String("10.0.1.0/24"), + }, testAccountID) + require.NoError(t, err) + eniOut, err := d.vpcService.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{ + SubnetId: subnetOut.Subnet.SubnetId, + }, testAccountID) + require.NoError(t, err) + + rec, err := d.vpcService.GetENIRecord(testAccountID, *eniOut.NetworkInterface.NetworkInterfaceId) + require.NoError(t, err) + + v := &vm.VM{ + ID: "i-hp-test", + Status: vm.StateRunning, + QMPClient: &qmp.QMPClient{}, + ENIRequests: spxtypes.ENIRequests{ + AvailableSlots: []int{1, 2, 3, 4}, + AttachedByENIID: map[string]int{}, + }, + } + d.vmMgr.Insert(v) + + stub := vm.NewStubDeviceController() + restore := vm.SetHotPlugTestSeams( + func(*vm.VM) vm.DeviceController { return stub }, + func(time.Duration) {}, + ) + t.Cleanup(restore) + + return &eniHotPlugFixture{ + daemon: d, + vmInst: v, + stub: stub, + subnetID: *subnetOut.Subnet.SubnetId, + vpcID: *vpcOut.Vpc.VpcId, + eniID: rec.NetworkInterfaceId, + mac: rec.MacAddress, + } +} + +// driveHandler subscribes to subject, dispatches the supplied handler +// from the subscriber, and returns the reply payload. Mirrors how the +// daemon dispatches AttachENI / DetachENI off the ec2.cmd channel in +// production. +func driveHandler(t *testing.T, nc *nats.Conn, subject string, handler func(*nats.Msg)) []byte { + t.Helper() + sub, err := nc.Subscribe(subject, handler) + require.NoError(t, err) + defer func() { _ = sub.Unsubscribe() }() + + reqMsg := nats.NewMsg(subject) + reqMsg.Header.Set(utils.AccountIDHeader, testAccountID) + reply, err := nc.RequestMsg(reqMsg, 5*time.Second) + require.NoError(t, err) + return reply.Data +} + +// assertErrorCode is shared with daemon_handlers_eks_test.go (same package). + +// --- handleAttachNetworkInterface --- + +func TestHandleAttachNetworkInterface_NilData(t *testing.T) { + f := newENIHotPlugFixture(t) + cmd := spxtypes.EC2InstanceCommand{ID: f.vmInst.ID} + payload := driveHandler(t, f.daemon.natsConn, "test.attach.nildata", func(msg *nats.Msg) { + f.daemon.handleAttachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorInvalidParameterValue) +} + +func TestHandleAttachNetworkInterface_EmptyEniID(t *testing.T) { + f := newENIHotPlugFixture(t) + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + AttachENIData: &spxtypes.AttachENIData{NetworkInterfaceID: "", DeviceIndex: 0}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.attach.emptyid", func(msg *nats.Msg) { + f.daemon.handleAttachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorInvalidParameterValue) +} + +func TestHandleAttachNetworkInterface_NotRunning(t *testing.T) { + f := newENIHotPlugFixture(t) + f.vmInst.Status = vm.StateStopped + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + AttachENIData: &spxtypes.AttachENIData{NetworkInterfaceID: f.eniID, DeviceIndex: 1}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.attach.notrunning", func(msg *nats.Msg) { + f.daemon.handleAttachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorIncorrectInstanceState) +} + +func TestHandleAttachNetworkInterface_ENINotFound(t *testing.T) { + f := newENIHotPlugFixture(t) + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + AttachENIData: &spxtypes.AttachENIData{NetworkInterfaceID: "eni-missing", DeviceIndex: 1}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.attach.eninotfound", func(msg *nats.Msg) { + f.daemon.handleAttachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorInvalidNetworkInterfaceIDNotFound) +} + +func TestHandleAttachNetworkInterface_WrongOwner(t *testing.T) { + f := newENIHotPlugFixture(t) + _, err := f.daemon.vpcService.AttachENI(testAccountID, f.eniID, "i-other-instance", 0) + require.NoError(t, err) + + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + AttachENIData: &spxtypes.AttachENIData{NetworkInterfaceID: f.eniID, DeviceIndex: 1}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.attach.wrongowner", func(msg *nats.Msg) { + f.daemon.handleAttachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorInvalidNetworkInterfaceInUse) +} + +func TestHandleAttachNetworkInterface_Success(t *testing.T) { + f := newENIHotPlugFixture(t) + + gotEvent := make(chan []byte, 1) + evtSub, err := f.daemon.natsConn.Subscribe("vpc.eni-hotplug.attached."+f.vmInst.ID, func(msg *nats.Msg) { + gotEvent <- msg.Data + }) + require.NoError(t, err) + t.Cleanup(func() { _ = evtSub.Unsubscribe() }) + + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + AttachENIData: &spxtypes.AttachENIData{NetworkInterfaceID: f.eniID, DeviceIndex: 1}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.attach.success", func(msg *nats.Msg) { + f.daemon.handleAttachNetworkInterface(msg, cmd, f.vmInst) + }) + + var out ec2.AttachNetworkInterfaceOutput + require.NoError(t, json.Unmarshal(payload, &out)) + require.NotNil(t, out.AttachmentId) + assert.Contains(t, *out.AttachmentId, "eni-attach-") + + rec, err := f.daemon.vpcService.GetENIRecord(testAccountID, f.eniID) + require.NoError(t, err) + assert.Equal(t, "attached", rec.AttachmentStatus) + assert.Equal(t, 1, rec.HotPlugSlot) + assert.Equal(t, f.vmInst.ID, rec.InstanceId) + + assert.True(t, f.stub.HasDevice("net-eni-1")) + assert.True(t, f.stub.HasNetdev("hostnet-eni-1")) + + select { + case evtData := <-gotEvent: + var evt map[string]any + require.NoError(t, json.Unmarshal(evtData, &evt)) + assert.Equal(t, f.eniID, evt["eniId"]) + assert.Equal(t, float64(1), evt["hotPlugSlot"]) + case <-time.After(1 * time.Second): + t.Fatal("expected vpc.eni-hotplug.attached event, none received") + } +} + +func TestHandleAttachNetworkInterface_HotPlugFails_RollsBackKV(t *testing.T) { + f := newENIHotPlugFixture(t) + f.stub.SetFailNext("device_add", errors.New("simulated QMP failure")) + + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + AttachENIData: &spxtypes.AttachENIData{NetworkInterfaceID: f.eniID, DeviceIndex: 1}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.attach.hotplugfails", func(msg *nats.Msg) { + f.daemon.handleAttachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorServerInternal) + + rec, err := f.daemon.vpcService.GetENIRecord(testAccountID, f.eniID) + require.NoError(t, err) + assert.Equal(t, "available", rec.Status, "KV should have rolled back to available") + assert.Empty(t, rec.AttachmentStatus, "AttachmentStatus should be cleared on rollback") + assert.Empty(t, rec.InstanceId) + assert.NotEmpty(t, rec.LastAttachError, "LastAttachError should record the rollback reason") +} + +// --- handleDetachNetworkInterface --- + +// attachedFixture extends the base fixture by pre-attaching the ENI to +// the running VM via the full handler, so detach tests start from the +// realistic post-attach state. +func attachedFixture(t *testing.T) (*eniHotPlugFixture, string) { + t.Helper() + f := newENIHotPlugFixture(t) + attachID, err := f.daemon.vpcService.AttachENI(testAccountID, f.eniID, f.vmInst.ID, 1) + require.NoError(t, err) + require.NoError(t, f.daemon.vpcService.UpdateENI(testAccountID, f.eniID, func(r *handlers_ec2_vpc.ENIRecord) { + r.AttachmentStatus = "attached" + r.HotPlugSlot = 1 + })) + f.vmInst.ENIRequests.Mu.Lock() + f.vmInst.ENIRequests.AvailableSlots = []int{2, 3, 4} + f.vmInst.ENIRequests.AttachedByENIID[f.eniID] = 1 + f.vmInst.ENIRequests.Mu.Unlock() + // Pre-seed the stub so HotUnplugENI's device_del + query-pci absence + // poll has something to remove. + require.NoError(t, f.stub.NetdevAdd(map[string]any{"id": "hostnet-eni-1"})) + require.NoError(t, f.stub.DeviceAdd(map[string]any{"id": "net-eni-1", "bus": "hotplug-eni1"})) + return f, attachID +} + +func TestHandleDetachNetworkInterface_NilData(t *testing.T) { + f, _ := attachedFixture(t) + cmd := spxtypes.EC2InstanceCommand{ID: f.vmInst.ID} + payload := driveHandler(t, f.daemon.natsConn, "test.detach.nildata", func(msg *nats.Msg) { + f.daemon.handleDetachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorInvalidParameterValue) +} + +func TestHandleDetachNetworkInterface_EmptyAttachID(t *testing.T) { + f, _ := attachedFixture(t) + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + DetachENIData: &spxtypes.DetachENIData{AttachmentID: ""}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.detach.emptyid", func(msg *nats.Msg) { + f.daemon.handleDetachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorInvalidParameterValue) +} + +func TestHandleDetachNetworkInterface_UnknownAttachment(t *testing.T) { + f, _ := attachedFixture(t) + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + DetachENIData: &spxtypes.DetachENIData{AttachmentID: "eni-attach-missing"}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.detach.unknown", func(msg *nats.Msg) { + f.daemon.handleDetachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorInvalidAttachmentIDNotFound) +} + +func TestHandleDetachNetworkInterface_WrongOwner(t *testing.T) { + f, attachID := attachedFixture(t) + otherVM := &vm.VM{ + ID: "i-other", + Status: vm.StateRunning, + ENIRequests: spxtypes.ENIRequests{ + AvailableSlots: []int{1}, + AttachedByENIID: map[string]int{}, + }, + } + f.daemon.vmMgr.Insert(otherVM) + cmd := spxtypes.EC2InstanceCommand{ + ID: otherVM.ID, + DetachENIData: &spxtypes.DetachENIData{AttachmentID: attachID}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.detach.wrongowner", func(msg *nats.Msg) { + f.daemon.handleDetachNetworkInterface(msg, cmd, otherVM) + }) + assertErrorCode(t, payload, awserrors.ErrorInvalidAttachmentIDNotFound) +} + +func TestHandleDetachNetworkInterface_NotRunning(t *testing.T) { + f, attachID := attachedFixture(t) + f.vmInst.Status = vm.StateStopped + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + DetachENIData: &spxtypes.DetachENIData{AttachmentID: attachID}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.detach.notrunning", func(msg *nats.Msg) { + f.daemon.handleDetachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorIncorrectInstanceState) +} + +func TestHandleDetachNetworkInterface_Success(t *testing.T) { + f, attachID := attachedFixture(t) + + gotEvent := make(chan []byte, 1) + evtSub, err := f.daemon.natsConn.Subscribe("vpc.eni-hotplug.detached."+f.vmInst.ID, func(msg *nats.Msg) { + gotEvent <- msg.Data + }) + require.NoError(t, err) + t.Cleanup(func() { _ = evtSub.Unsubscribe() }) + + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + DetachENIData: &spxtypes.DetachENIData{AttachmentID: attachID, Force: false}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.detach.success", func(msg *nats.Msg) { + f.daemon.handleDetachNetworkInterface(msg, cmd, f.vmInst) + }) + var out ec2.DetachNetworkInterfaceOutput + require.NoError(t, json.Unmarshal(payload, &out)) + + rec, err := f.daemon.vpcService.GetENIRecord(testAccountID, f.eniID) + require.NoError(t, err) + assert.Equal(t, "available", rec.Status) + assert.Empty(t, rec.AttachmentStatus) + assert.False(t, rec.DetachInFlight) + assert.False(t, rec.DetachForce) + assert.Equal(t, 0, rec.HotPlugSlot) + + assert.False(t, f.stub.HasDevice("net-eni-1")) + + select { + case <-gotEvent: + case <-time.After(1 * time.Second): + t.Fatal("expected vpc.eni-hotplug.detached event, none received") + } +} + +func TestHandleDetachNetworkInterface_HotUnplugFails(t *testing.T) { + f, attachID := attachedFixture(t) + f.stub.SetFailNext("device_del", errors.New("simulated QMP failure")) + + cmd := spxtypes.EC2InstanceCommand{ + ID: f.vmInst.ID, + DetachENIData: &spxtypes.DetachENIData{AttachmentID: attachID, Force: false}, + } + payload := driveHandler(t, f.daemon.natsConn, "test.detach.hotunplugfails", func(msg *nats.Msg) { + f.daemon.handleDetachNetworkInterface(msg, cmd, f.vmInst) + }) + assertErrorCode(t, payload, awserrors.ErrorServerInternal) + + rec, err := f.daemon.vpcService.GetENIRecord(testAccountID, f.eniID) + require.NoError(t, err) + assert.Equal(t, "in-use", rec.Status, "KV should remain in-use after detach failure") + assert.Equal(t, "detaching", rec.AttachmentStatus, "AttachmentStatus stays detaching for reconciler pickup") + assert.False(t, rec.DetachInFlight, "DetachInFlight cleared so reconciler can re-claim") +} diff --git a/spinifex/daemon/daemon_handlers_test.go b/spinifex/daemon/daemon_handlers_test.go index c1119f817..86108dbfe 100644 --- a/spinifex/daemon/daemon_handlers_test.go +++ b/spinifex/daemon/daemon_handlers_test.go @@ -1658,7 +1658,10 @@ func TestHandleEC2DescribeInstanceAttribute_UserData(t *testing.T) { Status: vm.StateStopped, InstanceType: "t3.micro", AccountID: testAccountID, - UserData: "#!/bin/bash", + RunInstancesInput: &ec2.RunInstancesInput{ + // base64("#!/bin/bash") — DescribeInstanceAttribute returns user-data base64-encoded. + UserData: aws.String("IyEvYmluL2Jhc2g="), + }, Instance: &ec2.Instance{ InstanceId: aws.String(instanceID), }, @@ -1679,7 +1682,7 @@ func TestHandleEC2DescribeInstanceAttribute_UserData(t *testing.T) { err = json.Unmarshal(reply.Data, &output) require.NoError(t, err) require.NotNil(t, output.UserData) - assert.Equal(t, "#!/bin/bash", *output.UserData.Value) + assert.Equal(t, "IyEvYmluL2Jhc2g=", *output.UserData.Value) } func TestHandleEC2DescribeInstanceAttribute_DefaultAttribute_DisableApiTermination(t *testing.T) { diff --git a/spinifex/daemon/daemon_system_dispatch.go b/spinifex/daemon/daemon_system_dispatch.go index 7b77dc9a8..2208fcdc6 100644 --- a/spinifex/daemon/daemon_system_dispatch.go +++ b/spinifex/daemon/daemon_system_dispatch.go @@ -67,6 +67,15 @@ func (d *Daemon) subscribeSystemTerminate(instanceID string) error { } d.mu.Lock() defer d.mu.Unlock() + return d.subscribeSystemTerminateLocked(instanceID) +} + +// subscribeSystemTerminateLocked is the body of subscribeSystemTerminate for +// callers that already hold d.mu (e.g. onInstanceUpHook). Idempotent. +func (d *Daemon) subscribeSystemTerminateLocked(instanceID string) error { + if d.natsConn == nil { + return nil + } subject := fmt.Sprintf("system.TerminateInstance.%s", instanceID) if _, exists := d.natsSubscriptions[subject]; exists { return nil diff --git a/spinifex/daemon/daemon_system_instance.go b/spinifex/daemon/daemon_system_instance.go index 948c20f80..bc4c47485 100644 --- a/spinifex/daemon/daemon_system_instance.go +++ b/spinifex/daemon/daemon_system_instance.go @@ -1,6 +1,8 @@ package daemon import ( + "encoding/base64" + "errors" "fmt" "log/slog" "os" @@ -12,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/mulgadc/spinifex/spinifex/handlers/sysinstance" "github.com/mulgadc/spinifex/spinifex/network/topology" "github.com/mulgadc/spinifex/spinifex/tags" "github.com/mulgadc/spinifex/spinifex/utils" @@ -27,6 +30,9 @@ var _ handlers_elbv2.SystemInstanceLauncher = (*Daemon)(nil) // (bundled vmlinuz+initramfs, fw_cfg-delivered config). There is no AMI, // volume, or cloud-init path. func (d *Daemon) LaunchSystemInstance(input *handlers_elbv2.SystemInstanceInput) (*handlers_elbv2.SystemInstanceOutput, error) { + if input.BootMode == sysinstance.BootAMI { + return d.launchAMISystemInstance(input) + } accountID := utils.GlobalAccountID // ENI account may differ from system account — the ENI is created under // the caller's account, so lookups/updates must use that account ID. @@ -365,11 +371,129 @@ func (d *Daemon) LaunchSystemInstance(input *handlers_elbv2.SystemInstanceInput) }, nil } +// launchAMISystemInstance is the BootAMI branch of LaunchSystemInstance: a full +// AMI boot (root volume cloned from an AMI, cloud-init user-data + network +// config seed) for a system-managed VM, with a management-bridge NIC so the +// guest can reach the daemon (NATS/AWSGW) off its tenant VPC subnet. +// +// It mirrors the daemon RunInstances handler's Prepare → Insert → Launch split, +// allocating the mgmt NIC on the VM record between Insert and Launch so the +// cloud-init network-config rendered during Launch carries a static mgmt0 +// interface. The instance is owned by input.AccountID (the account its +// pre-created ENI lives in) and tagged input.ManagedBy so customer listings +// hide it. +func (d *Daemon) launchAMISystemInstance(input *sysinstance.SystemInstanceInput) (*sysinstance.SystemInstanceOutput, error) { + if d.instanceService == nil { + return nil, errors.New("sysinstance: instance service not initialized") + } + if input.ImageID == "" { + return nil, errors.New("sysinstance: BootAMI requires ImageID") + } + if input.AccountID == "" { + return nil, errors.New("sysinstance: BootAMI requires AccountID") + } + if input.ENIID == "" { + return nil, errors.New("sysinstance: BootAMI requires a pre-created ENI") + } + + runInput := &ec2.RunInstancesInput{ + ImageId: aws.String(input.ImageID), + InstanceType: aws.String(input.InstanceType), + MinCount: aws.Int64(1), + MaxCount: aws.Int64(1), + NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{{ + NetworkInterfaceId: aws.String(input.ENIID), + DeviceIndex: aws.Int64(0), + }}, + } + if input.UserData != "" { + runInput.UserData = aws.String(base64.StdEncoding.EncodeToString([]byte(input.UserData))) + } + if input.ManagedBy != "" { + runInput.TagSpecifications = []*ec2.TagSpecification{{ + ResourceType: aws.String("instance"), + Tags: []*ec2.Tag{{Key: aws.String(tags.ManagedByKey), Value: aws.String(input.ManagedBy)}}, + }} + } + + _, instances, instanceType, err := d.instanceService.PrepareRunInstances(runInput, input.AccountID) + if err != nil { + return nil, err + } + if len(instances) == 0 { + return nil, errors.New("sysinstance: PrepareRunInstances returned no instance") + } + inst := instances[0] + inst.ManagedBy = input.ManagedBy + + for _, instance := range instances { + d.vmMgr.Insert(instance) + } + + // Management NIC must be set before LaunchRunInstances so the cloud-init + // network-config (built during launch) renders a static mgmt0 interface. + if err := d.attachSystemMgmtNIC(inst); err != nil { + d.vmMgr.MarkFailed(inst, "mgmt_nic_setup_failed") + return nil, err + } + + d.instanceService.LaunchRunInstances(instances, runInput, instanceType) + + privateIP := input.ENIIP + if privateIP == "" && inst.Instance != nil { + privateIP = aws.StringValue(inst.Instance.PrivateIpAddress) + } + slog.Info("LaunchSystemInstance (AMI) completed", + "instanceId", inst.ID, + "managedBy", input.ManagedBy, + "imageId", input.ImageID, + "mgmtIP", inst.MgmtIP, + "privateIp", privateIP, + ) + return &sysinstance.SystemInstanceOutput{ + InstanceID: inst.ID, + PrivateIP: privateIP, + MgmtIP: inst.MgmtIP, + }, nil +} + +// attachSystemMgmtNIC allocates a management-bridge IP + MAC for a system VM and +// creates the host-side tap on br-mgmt. The mgmt NIC is mandatory for system +// instances that boot from an AMI: their VPC ENI has no route off the tenant +// subnet, so the mgmt NIC is the only path to the daemon (NATS/AWSGW). On +// failure it releases the IP and returns an error so the caller can fail the +// launch. +func (d *Daemon) attachSystemMgmtNIC(inst *vm.VM) error { + if d.mgmtIPAllocator == nil || d.mgmtBridgeIP == "" { + return errors.New("sysinstance: management bridge unavailable; system VM would have no route to the daemon") + } + mgmtBridge := "br-mgmt" + if d.config.Daemon.MgmtBridge != "" { + mgmtBridge = d.config.Daemon.MgmtBridge + } + mgmtIP, err := d.mgmtIPAllocator.Allocate(inst.ID) + if err != nil { + return fmt.Errorf("allocate mgmt IP: %w", err) + } + inst.MgmtMAC = vm.GenerateMgmtMAC(inst.ID) + inst.MgmtIP = mgmtIP + tapName := vm.MgmtTapName(inst.ID) + if err := d.networkPlumber.SetupTap(vm.TapSpec{Name: tapName, Bridge: mgmtBridge}); err != nil { + d.mgmtIPAllocator.Release(inst.ID) + inst.MgmtMAC = "" + inst.MgmtIP = "" + return fmt.Errorf("setup mgmt tap: %w", err) + } + slog.Info("System instance mgmt NIC configured", + "instanceId", inst.ID, "mgmtIP", mgmtIP, "mgmtMAC", inst.MgmtMAC, "mgmtTap", tapName) + return nil +} + // TerminateSystemInstance stops and cleans up a system-managed VM. func (d *Daemon) TerminateSystemInstance(instanceID string) error { instance, exists := d.vmMgr.Get(instanceID) if !exists { - return fmt.Errorf("instance %s not found", instanceID) + return fmt.Errorf("%w: %s", sysinstance.ErrSystemInstanceNotFound, instanceID) } // Release EIP through the EIP service for system VMs whose public IP was diff --git a/spinifex/daemon/eks_adapter.go b/spinifex/daemon/eks_adapter.go new file mode 100644 index 000000000..c68d3e28f --- /dev/null +++ b/spinifex/daemon/eks_adapter.go @@ -0,0 +1,52 @@ +package daemon + +import ( + "errors" + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" +) + +// daemonEKSSubnetResolver adapts the daemon's VPCServiceImpl to the +// handlers_eks subnetVPCResolver-equivalent. handlers/eks declares the +// interface privately; the daemon needs an exported shape only because the +// constructor wires it via EKSServiceDeps.SubnetResolver = ... +// +// The translation is trivial: VPCService.GetSubnet returns a SubnetRecord +// with VpcId, which is the only field the EKS launcher needs. +type daemonEKSSubnetResolver struct { + d *Daemon +} + +func (a *daemonEKSSubnetResolver) GetSubnetVPC(accountID, subnetID string) (string, error) { + if a.d == nil || a.d.vpcService == nil { + return "", errors.New("eks: VPC service not initialized") + } + rec, err := a.d.vpcService.GetSubnet(accountID, subnetID) + if err != nil { + return "", err + } + return rec.VpcId, nil +} + +func (a *daemonEKSSubnetResolver) GetVPCCIDR(accountID, vpcID string) (string, error) { + if a.d == nil || a.d.vpcService == nil { + return "", errors.New("eks: VPC service not initialized") + } + out, err := a.d.vpcService.DescribeVpcs(&ec2.DescribeVpcsInput{ + VpcIds: aws.StringSlice([]string{vpcID}), + }, accountID) + if err != nil { + return "", err + } + for _, v := range out.Vpcs { + if v != nil && aws.StringValue(v.VpcId) == vpcID { + return aws.StringValue(v.CidrBlock), nil + } + } + return "", fmt.Errorf("eks: VPC %s not found", vpcID) +} + +var _ handlers_eks.SubnetVPCResolver = (*daemonEKSSubnetResolver)(nil) diff --git a/spinifex/daemon/eks_deps.go b/spinifex/daemon/eks_deps.go new file mode 100644 index 000000000..6284e3150 --- /dev/null +++ b/spinifex/daemon/eks_deps.go @@ -0,0 +1,146 @@ +package daemon + +import ( + "net" + "os" + "path/filepath" + + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "log/slog" +) + +// buildEKSServiceDeps assembles the EKSServiceDeps the EKS service needs to +// run CreateCluster/DescribeCluster/ListClusters/DeleteCluster end-to-end. +// All collaborators must already be initialized; callers invoke this from +// Daemon.startCluster after VPC, ELBv2, Instance, and Image services are +// ready. +// +// MasterKey is loaded best-effort from the config dir (the directory holding +// spinifex.toml — same place admin init writes master.key, and the same +// resolution daemon TLS + predastore config use). A missing key disables OIDC +// envelope encryption, which in turn fails CreateCluster at the +// depsReadyForOrchestration gate — the daemon logs and continues. +func (d *Daemon) buildEKSServiceDeps() handlers_eks.EKSServiceDeps { + masterKey, err := handlers_iam.LoadMasterKey(filepath.Join(filepath.Dir(d.configPath), "master.key")) + if err != nil { + slog.Warn("EKS: LoadMasterKey failed; CreateCluster + DeleteCluster will reject until key is provisioned", + "err", err) + masterKey = nil + } + + // CA PEM the K3s server VM uses to verify the AWS gateway's HTTPS cert when + // the eks-gateway-publish helper POSTs its bootstrap envelopes + state + // reports. The same CA signs the AWSGW server cert and the NATS server cert, + // so it is read from the configured NATS CA path. + gatewayCA := "" + if d.config.NATS.CACert != "" { + if caBytes, readErr := os.ReadFile(d.config.NATS.CACert); readErr == nil { + gatewayCA = string(caBytes) + } else { + slog.Warn("EKS: read gateway CACert failed; K3s server VM will not verify the gateway over TLS", + "path", d.config.NATS.CACert, "err", readErr) + } + } + + return handlers_eks.EKSServiceDeps{ + Config: d.config, + NATSConn: d.natsConn, + MasterKey: masterKey, + GatewayBaseURL: d.resolveGatewayBaseURL(), + Region: d.config.Region, + HolderID: d.node, + SystemGatewayURL: d.resolveSystemGatewayBaseURL(), + SystemAccessKey: d.config.Predastore.AccessKey, + SystemSecretKey: d.config.Predastore.SecretKey, + GatewayCACert: gatewayCA, + VPCSG: d.vpcService, + VPCK3s: d.vpcService, + VPCSubnet: &daemonEKSSubnetResolver{d: d}, + NLB: d.elbv2Service, + Instance: d, + Image: d.imageService, + EIP: d.eipService, + Worker: d, + } +} + +// resolveGatewayHost is the single source of truth for the off-host-reachable +// address LB VMs, EKS OIDC issuers, and the EKS NATS URL all dial. startCluster +// and buildEKSServiceDeps both call it so the OIDC issuer host can never +// diverge from the host the lb-agent actually reaches (M7). Precedence: +// +// 1. br-mgmt present + AWSGW on a dedicated non-loopback IP distinct from +// AdvertiseIP (multi-node: AWSGW on a mgmt-only IP, VPC path can't reach +// it) → the AWSGW bind IP. startCluster additionally adds a bootcmd host +// route via br-mgmt for this case only. +// 2. AdvertiseIP set → AdvertiseIP (single-node, or multi-node where AWSGW +// binds the advertised IP; VMs reach it via VPC → external). +// 3. br-mgmt present + AWSGW on 0.0.0.0, no AdvertiseIP → the br-mgmt IP. +// 4. DevNetworking shim → 10.0.2.2. +// 5. AWSGW bound to a specific IP (no br-mgmt, no advertise) → that IP. +// +// Returns "" if no reachable host can be derived. +func (d *Daemon) resolveGatewayHost() string { + awsgwBindIP := "" + if d.config.AWSGW.Host != "" { + if h, _, splitErr := net.SplitHostPort(d.config.AWSGW.Host); splitErr == nil { + awsgwBindIP = h + } + } + advertiseIP := d.config.AdvertiseIP + + switch { + case d.mgmtBridgeIP != "" && awsgwBindIP != "" && awsgwBindIP != "0.0.0.0" && + !net.ParseIP(awsgwBindIP).IsLoopback() && awsgwBindIP != advertiseIP: + return awsgwBindIP + case advertiseIP != "" && advertiseIP != "0.0.0.0": + return advertiseIP + case d.mgmtBridgeIP != "": + return d.mgmtBridgeIP + case d.config.Daemon.DevNetworking: + return "10.0.2.2" + case awsgwBindIP != "" && awsgwBindIP != "0.0.0.0": + return awsgwBindIP + } + return "" +} + +// resolveGatewayBaseURL is the HTTPS AWSGW endpoint EKS OIDC issuers are built +// from. Returns "" if no reachable host can be derived, in which case +// CreateCluster fails at depsReadyForOrchestration. +func (d *Daemon) resolveGatewayBaseURL() string { + host := d.resolveGatewayHost() + if host == "" { + return "" + } + gatewayPort := "9999" + if d.config.AWSGW.Host != "" { + if _, port, splitErr := net.SplitHostPort(d.config.AWSGW.Host); splitErr == nil && port != "" { + gatewayPort = port + } + } + return "https://" + net.JoinHostPort(host, gatewayPort) +} + +// resolveSystemGatewayBaseURL derives the AWS gateway URL a system instance +// (the K3s server VM) POSTs its bootstrap envelopes + state reports to. Unlike +// a customer LB VM — which reaches the gateway via VPC→external (AdvertiseIP) — +// a system instance reaches the daemon only over the mgmt bridge: its cloud-init +// mgmt0 NIC is on-link to mgmtBridgeIP with no off-link route, so the gateway +// must be dialed at the bridge address directly. AWSGW binds 0.0.0.0 (or the +// mgmt IP), and the server cert SANs include the bridge IP, so HTTPS validates. +// Falls back to resolveGatewayBaseURL() (gateway-host precedence) when no mgmt +// bridge exists — e.g. dev-shim networking. +func (d *Daemon) resolveSystemGatewayBaseURL() string { + if d.mgmtBridgeIP == "" { + return d.resolveGatewayBaseURL() + } + gatewayPort := "9999" + if d.config.AWSGW.Host != "" { + if _, port, splitErr := net.SplitHostPort(d.config.AWSGW.Host); splitErr == nil && port != "" { + gatewayPort = port + } + } + return "https://" + net.JoinHostPort(d.mgmtBridgeIP, gatewayPort) +} diff --git a/spinifex/daemon/eks_deps_test.go b/spinifex/daemon/eks_deps_test.go new file mode 100644 index 000000000..3b5499440 --- /dev/null +++ b/spinifex/daemon/eks_deps_test.go @@ -0,0 +1,86 @@ +package daemon + +import ( + "testing" + + "github.com/mulgadc/spinifex/spinifex/config" + "github.com/stretchr/testify/assert" +) + +// TestResolveGatewayHost covers all five host-selection branches plus the +// no-reachable-host fallthrough. resolveGatewayHost is the single source of +// truth for the OIDC issuer host, EKS NATS URL, and lb-agent gateway URL, so +// each branch is pinned here to prevent silent divergence (M7). +func TestResolveGatewayHost(t *testing.T) { + tests := []struct { + name string + awsgwHost string + advertiseIP string + mgmtBridge string + devNet bool + want string + }{ + { + name: "1_mgmt_dedicated_awsgw_ip", + awsgwHost: "10.20.0.5:9999", + advertiseIP: "203.0.113.7", + mgmtBridge: "10.15.8.1", + want: "10.20.0.5", + }, + { + name: "1_skipped_when_bind_equals_advertise_falls_to_advertise", + awsgwHost: "203.0.113.7:9999", + advertiseIP: "203.0.113.7", + mgmtBridge: "10.15.8.1", + want: "203.0.113.7", + }, + { + name: "1_skipped_when_bind_loopback_falls_to_mgmt", + awsgwHost: "127.0.0.1:9999", + mgmtBridge: "10.15.8.1", + want: "10.15.8.1", + }, + { + name: "2_advertise_ip", + awsgwHost: "0.0.0.0:9999", + advertiseIP: "203.0.113.7", + want: "203.0.113.7", + }, + { + name: "3_mgmt_bridge_when_awsgw_wildcard", + awsgwHost: "0.0.0.0:9999", + mgmtBridge: "10.15.8.1", + want: "10.15.8.1", + }, + { + name: "4_dev_networking_shim", + awsgwHost: "0.0.0.0:9999", + devNet: true, + want: "10.0.2.2", + }, + { + name: "5_awsgw_specific_ip_no_mgmt_no_advertise", + awsgwHost: "198.51.100.9:9999", + want: "198.51.100.9", + }, + { + name: "6_no_reachable_host", + awsgwHost: "0.0.0.0:9999", + want: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + d := &Daemon{ + mgmtBridgeIP: tc.mgmtBridge, + config: &config.Config{ + AdvertiseIP: tc.advertiseIP, + AWSGW: config.AWSGWConfig{Host: tc.awsgwHost}, + Daemon: config.DaemonConfig{DevNetworking: tc.devNet}, + }, + } + assert.Equal(t, tc.want, d.resolveGatewayHost()) + }) + } +} diff --git a/spinifex/daemon/eks_worker_launch.go b/spinifex/daemon/eks_worker_launch.go new file mode 100644 index 000000000..82e2e1893 --- /dev/null +++ b/spinifex/daemon/eks_worker_launch.go @@ -0,0 +1,67 @@ +package daemon + +import ( + "encoding/base64" + "errors" + "fmt" + "log/slog" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + spxtypes "github.com/mulgadc/spinifex/spinifex/types" +) + +// Compile-time check that Daemon satisfies the EKS worker-launch surface. +var _ handlers_eks.WorkerLauncher = (*Daemon)(nil) + +// RunWorkerInstance launches a nodegroup worker through the normal customer +// RunInstances path: customer-owned, primary ENI auto-created in input.SubnetId +// with input.SecurityGroupIds, no ManagedBy tag, no mgmt NIC — so the worker is +// visible in DescribeInstances and reclaimed by TerminateInstances like any +// other customer EC2. The EKS service hands us raw cloud-config YAML in +// input.UserData; RunInstances expects it base64-encoded, so wrap it here. +func (d *Daemon) RunWorkerInstance(input *ec2.RunInstancesInput, accountID string) (*ec2.Reservation, error) { + if d.instanceService == nil { + return nil, errors.New("eks worker: instance service not initialized") + } + if input == nil { + return nil, errors.New("eks worker: nil RunInstancesInput") + } + if input.UserData != nil && *input.UserData != "" { + input.UserData = aws.String(base64.StdEncoding.EncodeToString([]byte(*input.UserData))) + } + return d.instanceService.RunInstances(input, accountID) +} + +// TerminateWorkerInstances terminates each nodegroup worker via the same path +// the EC2 NATS terminate handler uses. A worker no longer tracked by the local +// vmMgr is treated as already-gone (idempotent success) so a retried +// DeleteNodegroup / scale-down does not wedge on instances that already drained. +func (d *Daemon) TerminateWorkerInstances(instanceIDs []string, accountID string) error { + if d.instanceService == nil { + return errors.New("eks worker: instance service not initialized") + } + var errs []error + for _, id := range instanceIDs { + if id == "" { + continue + } + instance, ok := d.vmMgr.Get(id) + if !ok { + slog.Debug("TerminateWorkerInstances: instance already gone", "instanceId", id) + continue + } + cmd := spxtypes.EC2InstanceCommand{ + ID: id, + Attributes: spxtypes.EC2CommandAttributes{TerminateInstance: true}, + } + if err := d.instanceService.StopOrTerminateInstance(instance, cmd); err != nil { + errs = append(errs, fmt.Errorf("terminate worker %s: %w", id, err)) + } + } + if len(errs) > 0 { + return errors.Join(errs...) + } + return nil +} diff --git a/spinifex/daemon/eks_worker_launch_test.go b/spinifex/daemon/eks_worker_launch_test.go new file mode 100644 index 000000000..33b4ebb58 --- /dev/null +++ b/spinifex/daemon/eks_worker_launch_test.go @@ -0,0 +1,37 @@ +package daemon + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + handlers_ec2_instance "github.com/mulgadc/spinifex/spinifex/handlers/ec2/instance" + "github.com/stretchr/testify/require" +) + +// A worker whose ID is not tracked by the local vmMgr is already-gone, so +// terminating it is an idempotent no-op success (a retried DeleteNodegroup / +// scale-down must not wedge on drained instances). +func TestTerminateWorkerInstances_NotFoundIsIdempotent(t *testing.T) { + d := newDaemonWithVMs() + // Non-nil service passes the init guard; no VM matches the IDs so + // StopOrTerminateInstance is never reached — the not-found branch returns + // success. + d.instanceService = &handlers_ec2_instance.InstanceServiceImpl{} + + require.NoError(t, d.TerminateWorkerInstances([]string{"i-gone1", "i-gone2"}, "111122223333")) + // Empty / blank IDs are skipped. + require.NoError(t, d.TerminateWorkerInstances([]string{"", ""}, "111122223333")) + require.NoError(t, d.TerminateWorkerInstances(nil, "111122223333")) +} + +// Both worker methods must guard against an uninitialized instance service +// rather than nil-panic. +func TestWorkerLauncher_NilInstanceService(t *testing.T) { + d := &Daemon{} + + _, err := d.RunWorkerInstance(&ec2.RunInstancesInput{ImageId: aws.String("ami-1")}, "111122223333") + require.Error(t, err) + + require.Error(t, d.TerminateWorkerInstances([]string{"i-1"}, "111122223333")) +} diff --git a/spinifex/daemon/main_test.go b/spinifex/daemon/main_test.go index bcb6f0df9..cd725ec0c 100644 --- a/spinifex/daemon/main_test.go +++ b/spinifex/daemon/main_test.go @@ -18,6 +18,14 @@ var ( ) func TestMain(m *testing.M) { + // Pin host vCPU so the resource manager is deterministic regardless of the + // runner's CPU topology. Without this, scheduling is denominated by physical + // cores (physicalCoreCount), and a 2-physical-core CI runner would leave 0 + // schedulable after the default 2-vCPU reserve, so NewResourceManager would + // refuse to start and every daemon-constructing test would fail. 4 keeps the + // default reserve intact with 2 schedulable cores — enough for these tests. + os.Setenv("SPINIFEX_HOST_VCPU", "4") + // Start a plain NATS server (no JetStream) ns, err := server.NewServer(&server.Options{ Host: "127.0.0.1", diff --git a/spinifex/daemon/resource.go b/spinifex/daemon/resource.go index bbd0cf806..a16a08240 100644 --- a/spinifex/daemon/resource.go +++ b/spinifex/daemon/resource.go @@ -52,6 +52,23 @@ func resolveHostReserve(getenv func(string) string) hostReserve { return r } +// resolveHostVCPU returns the detected core count, overridden by +// SPINIFEX_HOST_VCPU when set to a valid positive integer. The override is an +// escape hatch: it pins the schedulable core count for hosts where +// /proc/cpuinfo topology detection misreports (some VMs/containers expose no +// "core id", or fewer physical cores than the host can actually schedule), and +// lets tests decouple from runner topology. Invalid values are logged and +// ignored so a typo can't silently shrink capacity below the reserve. +func resolveHostVCPU(getenv func(string) string, detected int) int { + if v := getenv("SPINIFEX_HOST_VCPU"); v != "" { + if n, err := strconv.Atoi(v); err == nil && n > 0 { + return n + } + slog.Warn("ignoring SPINIFEX_HOST_VCPU", "value", v) + } + return detected +} + // minHostMemHeadroomGB is the minimum schedulable memory we require above // the reserve, so a host that just meets the reserve still has a small // amount left to launch the smallest guest type. diff --git a/spinifex/daemon/resource_test.go b/spinifex/daemon/resource_test.go index 267e8501d..8ae5e9a94 100644 --- a/spinifex/daemon/resource_test.go +++ b/spinifex/daemon/resource_test.go @@ -442,3 +442,24 @@ func TestResolveHostReserve(t *testing.T) { }) } } + +func TestResolveHostVCPU(t *testing.T) { + const detected = 12 + tests := []struct { + name string + env string + want int + }{ + {name: "no env returns detected", env: "", want: detected}, + {name: "valid override", env: "8", want: 8}, + {name: "non-numeric falls back to detected", env: "garbage", want: detected}, + {name: "zero falls back to detected", env: "0", want: detected}, + {name: "negative falls back to detected", env: "-4", want: detected}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := resolveHostVCPU(func(string) string { return tc.env }, detected) + assert.Equal(t, tc.want, got) + }) + } +} diff --git a/spinifex/daemon/vm_adapters.go b/spinifex/daemon/vm_adapters.go index 95891d638..fb4e6f9f1 100644 --- a/spinifex/daemon/vm_adapters.go +++ b/spinifex/daemon/vm_adapters.go @@ -5,13 +5,13 @@ import ( "errors" "fmt" "log/slog" - "strings" "time" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mulgadc/spinifex/spinifex/awserrors" handlers_ec2_placementgroup "github.com/mulgadc/spinifex/spinifex/handlers/ec2/placementgroup" "github.com/mulgadc/spinifex/spinifex/network/topology" + "github.com/mulgadc/spinifex/spinifex/tags" "github.com/mulgadc/spinifex/spinifex/types" "github.com/mulgadc/spinifex/spinifex/utils" "github.com/mulgadc/spinifex/spinifex/vm" @@ -399,6 +399,19 @@ func (d *Daemon) onInstanceUpHook() func(*vm.VM) error { } d.natsSubscriptions[consoleSubKey] = consoleSub + // Re-arm the per-instance terminate subscription for system-managed VMs + // (ELBv2 load balancers). The launch handler binds it at first launch, + // but it is lost across a daemon restart; OnInstanceUp fires on both the + // relaunch and the reconnect-to-surviving-QEMU recovery paths, so without + // this system.TerminateInstance.{id} has no responder and the VM can + // never be torn down (SetSubnets relaunch, DeleteLoadBalancer). + if instance.ManagedBy == tags.ManagedByELBv2 { + if subErr := d.subscribeSystemTerminateLocked(instance.ID); subErr != nil { + slog.Error("OnInstanceUp: failed to re-arm system terminate subscription", + "instanceId", instance.ID, "err", subErr) + } + } + // Re-claim GPU(s) after a daemon restart with a still-running QEMU // process: the manager's reconnect path fires OnInstanceUp without // going through the handler-side Claim, so the GPU pool would @@ -470,6 +483,16 @@ func (d *Daemon) onInstanceDownHook() func(string) { } delete(d.natsSubscriptions, consoleSubKey) } + // Drop the system terminate subscription (system VMs only; a no-op for + // regular instances that never registered one). + terminateSubKey := fmt.Sprintf("system.TerminateInstance.%s", instanceID) + if sub, ok := d.natsSubscriptions[terminateSubKey]; ok { + if err := sub.Unsubscribe(); err != nil { + slog.Error("OnInstanceDown: failed to unsubscribe system terminate topic", + "instanceId", instanceID, "err", err) + } + delete(d.natsSubscriptions, terminateSubKey) + } } } @@ -631,7 +654,7 @@ func (a *instanceCleanerAdapter) DetachAndDeleteENI(instance *vm.VM) { if _, eniErr := a.d.vpcService.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ NetworkInterfaceId: &instance.ENIId, }, instance.AccountID); eniErr != nil { - if strings.Contains(eniErr.Error(), awserrors.ErrorInvalidNetworkInterfaceIDNotFound) { + if awserrors.IsErrorCode(eniErr, awserrors.ErrorInvalidNetworkInterfaceIDNotFound) { slog.Debug("ENI already cleaned up on termination", "eni", instance.ENIId) } else { slog.Error("Failed to delete ENI on termination", diff --git a/spinifex/daemon/vm_adapters_test.go b/spinifex/daemon/vm_adapters_test.go index 602a0325a..413a0f826 100644 --- a/spinifex/daemon/vm_adapters_test.go +++ b/spinifex/daemon/vm_adapters_test.go @@ -46,6 +46,45 @@ func TestOnInstanceUpHook_RegistersBothPerInstanceTopics(t *testing.T) { assert.Equal(t, "ec2.i-up-basic.GetConsoleOutput", consoleSub.Subject) } +func TestOnInstanceUpHook_ReArmsSystemTerminateForELBv2(t *testing.T) { + d, _ := newHookTestDaemon(t) + instance := &vm.VM{ID: "i-up-sys", ManagedBy: tags.ManagedByELBv2} + + require.NoError(t, d.onInstanceUpHook()(instance)) + + // OnInstanceUp fires on both the relaunch and reconnect recovery paths, so + // the terminate subject must be re-bound or the recovered LB VM can never + // be torn down (SetSubnets relaunch, DeleteLoadBalancer). + sub, ok := d.natsSubscriptions["system.TerminateInstance.i-up-sys"] + require.True(t, ok, "system terminate subscription must be re-armed for ELBv2 VMs") + assert.Equal(t, "system.TerminateInstance.i-up-sys", sub.Subject) +} + +func TestOnInstanceUpHook_NoSystemTerminateForRegularInstance(t *testing.T) { + d, _ := newHookTestDaemon(t) + instance := &vm.VM{ID: "i-up-regular"} + + require.NoError(t, d.onInstanceUpHook()(instance)) + + _, ok := d.natsSubscriptions["system.TerminateInstance.i-up-regular"] + assert.False(t, ok, "regular instances must not register a system terminate subscription") +} + +func TestOnInstanceDownHook_DropsSystemTerminateSubscription(t *testing.T) { + d, _ := newHookTestDaemon(t) + instance := &vm.VM{ID: "i-down-sys", ManagedBy: tags.ManagedByELBv2} + + require.NoError(t, d.onInstanceUpHook()(instance)) + termSub := d.natsSubscriptions["system.TerminateInstance.i-down-sys"] + require.NotNil(t, termSub) + + d.onInstanceDownHook()(instance.ID) + + _, present := d.natsSubscriptions["system.TerminateInstance.i-down-sys"] + assert.False(t, present, "system terminate sub must be deleted from map") + assert.False(t, termSub.IsValid(), "system terminate sub must be unsubscribed") +} + func TestOnInstanceUpHook_ReplacesExistingSubsOnDoubleUp(t *testing.T) { d, _ := newHookTestDaemon(t) instance := &vm.VM{ID: "i-up-twice"} diff --git a/spinifex/daemon/vpc_adapter.go b/spinifex/daemon/vpc_adapter.go index da9ca0466..b44431230 100644 --- a/spinifex/daemon/vpc_adapter.go +++ b/spinifex/daemon/vpc_adapter.go @@ -39,6 +39,22 @@ func (a *daemonENICreator) GetSubnet(accountID, subnetID string) (*handlers_ec2_ }, nil } +func (a *daemonENICreator) GetENI(accountID, eniID string) (*handlers_ec2_instance.ENIInfo, error) { + rec, err := a.d.vpcService.GetENIRecord(accountID, eniID) + if err != nil { + return nil, err + } + return &handlers_ec2_instance.ENIInfo{ + NetworkInterfaceID: rec.NetworkInterfaceId, + SubnetID: rec.SubnetId, + VpcID: rec.VpcId, + PrivateIpAddress: rec.PrivateIpAddress, + MacAddress: rec.MacAddress, + Status: rec.Status, + SecurityGroupIDs: rec.SecurityGroupIds, + }, nil +} + func (a *daemonENICreator) CreateNetworkInterface(input *ec2.CreateNetworkInterfaceInput, accountID string) (*ec2.CreateNetworkInterfaceOutput, error) { return a.d.vpcService.CreateNetworkInterface(input, accountID) } diff --git a/spinifex/gateway/acm.go b/spinifex/gateway/acm.go new file mode 100644 index 000000000..33ae324b2 --- /dev/null +++ b/spinifex/gateway/acm.go @@ -0,0 +1,91 @@ +package gateway + +import ( + "errors" + "io" + "log/slog" + "net/http" + "strings" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + gateway_acm "github.com/mulgadc/spinifex/spinifex/gateway/acm" +) + +// acmHandler invokes a per-action gateway function: it receives the parent +// gateway config, the caller's accountID, and the raw request body, and +// returns a typed *acm.Output (as any) or an error whose Error() is an +// awserrors code. +type acmHandler func(gw *GatewayConfig, accountID string, body []byte) (any, error) + +// acmActions maps an ACM action (the suffix of the X-Amz-Target header) to its +// handler. ACM speaks AWS JSON 1.1: the action is carried in +// "X-Amz-Target: CertificateManager.", not the query string. +var acmActions = map[string]acmHandler{ + "ImportCertificate": func(gw *GatewayConfig, acct string, b []byte) (any, error) { + return gateway_acm.ImportCertificate(gw.NATSConn, acct, b) + }, + "DescribeCertificate": func(gw *GatewayConfig, acct string, b []byte) (any, error) { + return gateway_acm.DescribeCertificate(gw.NATSConn, acct, b) + }, + "ListCertificates": func(gw *GatewayConfig, acct string, b []byte) (any, error) { + return gateway_acm.ListCertificates(gw.NATSConn, acct, b) + }, + "DeleteCertificate": func(gw *GatewayConfig, acct string, b []byte) (any, error) { + return gateway_acm.DeleteCertificate(gw.NATSConn, acct, b) + }, +} + +// acmActionFromTarget extracts the bare action name from an X-Amz-Target +// header value of the form "CertificateManager.". The service prefix +// is optional/ignored so any "." or bare "" resolves. +func acmActionFromTarget(target string) string { + if i := strings.LastIndex(target, "."); i >= 0 { + return target[i+1:] + } + return target +} + +// ACM_Request dispatches AWS JSON 1.1 ACM requests. The action is read from +// the X-Amz-Target header; the body is the JSON-encoded operation input. +// Errors are returned as plain awserrors codes; the caller (gateway.Request) +// routes them through ErrorHandler, which emits the JSON error envelope. +func (gw *GatewayConfig) ACM_Request(w http.ResponseWriter, r *http.Request) error { + action := acmActionFromTarget(r.Header.Get("X-Amz-Target")) + if action == "" { + return errors.New(awserrors.ErrorMissingAction) + } + + handler, ok := acmActions[action] + if !ok { + slog.Debug("ACM: unknown action", "action", action) + return errors.New(awserrors.ErrorInvalidAction) + } + + if err := gw.checkPolicy(r, "acm", action); err != nil { + return err + } + + if gw.NATSConn == nil { + return errors.New(awserrors.ErrorServerInternal) + } + + accountID, _ := r.Context().Value(ctxAccountID).(string) + if accountID == "" { + slog.Error("ACM_Request: no account ID in auth context") + return errors.New(awserrors.ErrorServerInternal) + } + + body, err := io.ReadAll(r.Body) + if err != nil { + slog.Error("ACM_Request: failed to read body", "err", err) + return errors.New(awserrors.ErrorInvalidParameterValue) + } + + output, err := handler(gw, accountID, body) + if err != nil { + return err + } + + gateway_acm.WriteJSONResponse(w, output) + return nil +} diff --git a/spinifex/gateway/acm/certificates.go b/spinifex/gateway/acm/certificates.go new file mode 100644 index 000000000..14c66e8ef --- /dev/null +++ b/spinifex/gateway/acm/certificates.go @@ -0,0 +1,46 @@ +package gateway_acm + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/acm" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" + "github.com/nats-io/nats.go" +) + +// ImportCertificate — CertificateManager.ImportCertificate +func ImportCertificate(natsConn *nats.Conn, accountID string, body []byte) (*acm.ImportCertificateOutput, error) { + input := new(acm.ImportCertificateInput) + if err := unmarshalIfBody(body, input); err != nil { + return nil, errors.New(awserrors.ErrorInvalidParameter) + } + return handlers_acm.NewNATSACMService(natsConn).ImportCertificate(input, accountID) +} + +// DescribeCertificate — CertificateManager.DescribeCertificate +func DescribeCertificate(natsConn *nats.Conn, accountID string, body []byte) (*acm.DescribeCertificateOutput, error) { + input := new(acm.DescribeCertificateInput) + if err := unmarshalIfBody(body, input); err != nil { + return nil, errors.New(awserrors.ErrorInvalidParameter) + } + return handlers_acm.NewNATSACMService(natsConn).DescribeCertificate(input, accountID) +} + +// ListCertificates — CertificateManager.ListCertificates +func ListCertificates(natsConn *nats.Conn, accountID string, body []byte) (*acm.ListCertificatesOutput, error) { + input := new(acm.ListCertificatesInput) + if err := unmarshalIfBody(body, input); err != nil { + return nil, errors.New(awserrors.ErrorInvalidParameter) + } + return handlers_acm.NewNATSACMService(natsConn).ListCertificates(input, accountID) +} + +// DeleteCertificate — CertificateManager.DeleteCertificate +func DeleteCertificate(natsConn *nats.Conn, accountID string, body []byte) (*acm.DeleteCertificateOutput, error) { + input := new(acm.DeleteCertificateInput) + if err := unmarshalIfBody(body, input); err != nil { + return nil, errors.New(awserrors.ErrorInvalidParameter) + } + return handlers_acm.NewNATSACMService(natsConn).DeleteCertificate(input, accountID) +} diff --git a/spinifex/gateway/acm/certificates_test.go b/spinifex/gateway/acm/certificates_test.go new file mode 100644 index 000000000..006b43fbb --- /dev/null +++ b/spinifex/gateway/acm/certificates_test.go @@ -0,0 +1,55 @@ +package gateway_acm + +import ( + "net/http/httptest" + "testing" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUnmarshalIfBody(t *testing.T) { + var out struct { + A string `json:"a"` + } + require.NoError(t, unmarshalIfBody(nil, &out)) + require.NoError(t, unmarshalIfBody([]byte(`{"a":"x"}`), &out)) + assert.Equal(t, "x", out.A) + require.Error(t, unmarshalIfBody([]byte("{bad"), &out)) +} + +func TestWriteJSONResponse(t *testing.T) { + w := httptest.NewRecorder() + WriteJSONResponse(w, map[string]string{"CertificateArn": "arn:aws:acm:x"}) + assert.Equal(t, JSONContentType, w.Header().Get("Content-Type")) + assert.Contains(t, w.Body.String(), "CertificateArn") +} + +// Invalid JSON bodies are rejected before any NATS round-trip. +func TestOps_InvalidBodyRejected(t *testing.T) { + bad := []byte("{not json") + _, err := ImportCertificate(nil, "acct", bad) + assert.ErrorContains(t, err, awserrors.ErrorInvalidParameter) + _, err = DescribeCertificate(nil, "acct", bad) + assert.ErrorContains(t, err, awserrors.ErrorInvalidParameter) + _, err = ListCertificates(nil, "acct", bad) + assert.ErrorContains(t, err, awserrors.ErrorInvalidParameter) + _, err = DeleteCertificate(nil, "acct", bad) + assert.ErrorContains(t, err, awserrors.ErrorInvalidParameter) +} + +// With a live conn but no daemon subscriber, the delegate path fast-fails with +// no-responders — exercising the NATSRequest call site in each op. +func TestOps_DelegateNoResponder(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + _, err := ImportCertificate(nc, "acct", []byte(`{}`)) + require.Error(t, err) + _, err = DescribeCertificate(nc, "acct", []byte(`{}`)) + require.Error(t, err) + _, err = ListCertificates(nc, "acct", []byte(`{}`)) + require.Error(t, err) + _, err = DeleteCertificate(nc, "acct", []byte(`{}`)) + require.Error(t, err) +} diff --git a/spinifex/gateway/acm/handler.go b/spinifex/gateway/acm/handler.go new file mode 100644 index 000000000..df02430c0 --- /dev/null +++ b/spinifex/gateway/acm/handler.go @@ -0,0 +1,45 @@ +// Package gateway_acm is the HTTP-side glue between awsgw and the acm handlers +// package. ACM speaks AWS JSON 1.1 (X-Amz-Target dispatch, Content-Type +// application/x-amz-json-1.1), so responses are JSON throughout. Error +// envelopes are emitted centrally by the shared gateway ErrorHandler. +package gateway_acm + +import ( + "encoding/json" + "log/slog" + "net/http" + + "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +// JSONContentType is the AWS JSON 1.1 content type ACM clients expect. +const JSONContentType = "application/x-amz-json-1.1" + +// WriteJSONResponse serialises obj as a 200 AWS JSON 1.1 response. obj is an +// aws-sdk-go *Output struct whose time.Time fields must serialise as epoch +// seconds (the AWS JSON 1.1 timestamp wire format); encoding/json would emit +// RFC3339 strings the AWS SDKs reject with "Expected real number". jsonutil +// is the marshaler the SDK itself uses for this protocol. +func WriteJSONResponse(w http.ResponseWriter, obj any) { + body, err := jsonutil.BuildJSON(obj) + if err != nil { + slog.Error("ACM: failed to marshal response JSON", "err", err) + http.Error(w, awserrors.ErrorInternalError, http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", JSONContentType) + w.WriteHeader(http.StatusOK) + if _, err := w.Write(body); err != nil { + slog.Error("ACM: failed to write response", "err", err) + } +} + +// unmarshalIfBody decodes body into out only when body is non-empty, so +// actions with an optional body share one helper. +func unmarshalIfBody(body []byte, out any) error { + if len(body) == 0 { + return nil + } + return json.Unmarshal(body, out) +} diff --git a/spinifex/gateway/acm_test.go b/spinifex/gateway/acm_test.go new file mode 100644 index 000000000..8e24dffab --- /dev/null +++ b/spinifex/gateway/acm_test.go @@ -0,0 +1,69 @@ +package gateway + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupACMRequest(target, body string) *http.Request { + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)) + if target != "" { + req.Header.Set("X-Amz-Target", target) + } + ctx := context.WithValue(req.Context(), ctxService, "acm") + ctx = context.WithValue(ctx, ctxAccountID, "123456789012") + return req.WithContext(ctx) +} + +func TestACMActionFromTarget(t *testing.T) { + assert.Equal(t, "ImportCertificate", acmActionFromTarget("CertificateManager.ImportCertificate")) + assert.Equal(t, "ListCertificates", acmActionFromTarget("ListCertificates")) + assert.Equal(t, "", acmActionFromTarget("")) +} + +func TestACMActionsMap_AllActionsRegistered(t *testing.T) { + expected := []string{ + "ImportCertificate", + "DescribeCertificate", + "ListCertificates", + "DeleteCertificate", + } + for _, action := range expected { + _, ok := acmActions[action] + assert.True(t, ok, "action %q should be registered in acmActions", action) + } + assert.Len(t, acmActions, len(expected)) +} + +func TestACMRequest_MissingTarget(t *testing.T) { + gw := &GatewayConfig{DisableLogging: true} + w := httptest.NewRecorder() + err := gw.ACM_Request(w, setupACMRequest("", "")) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorMissingAction, err.Error()) +} + +func TestACMRequest_UnknownAction(t *testing.T) { + gw := &GatewayConfig{DisableLogging: true} + w := httptest.NewRecorder() + err := gw.ACM_Request(w, setupACMRequest("CertificateManager.RequestCertificate", "{}")) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorInvalidAction, err.Error()) +} + +// A known action with no NATS connection passes routing + policy and fails at +// the NATS-availability guard. +func TestACMRequest_KnownActionNoNATS(t *testing.T) { + gw := &GatewayConfig{DisableLogging: true} + w := httptest.NewRecorder() + err := gw.ACM_Request(w, setupACMRequest("CertificateManager.ListCertificates", "{}")) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorServerInternal, err.Error()) +} diff --git a/spinifex/gateway/auth.go b/spinifex/gateway/auth.go index ebe0d87e3..2923e928f 100644 --- a/spinifex/gateway/auth.go +++ b/spinifex/gateway/auth.go @@ -16,6 +16,7 @@ import ( "github.com/mulgadc/predastore/auth" "github.com/mulgadc/spinifex/spinifex/awserrors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/mulgadc/spinifex/spinifex/utils" ) // Maximum request body size for signature validation (10 MB). @@ -36,7 +37,7 @@ func (gw *GatewayConfig) SigV4AuthMiddleware() func(http.Handler) http.Handler { } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - clientIP := extractClientIP(r) + clientIP := utils.ClientIP(r.RemoteAddr) if errCode := gw.RateLimiter.CheckIP(clientIP); errCode != "" { gw.writeSigV4Error(w, r, errCode) return @@ -261,6 +262,21 @@ func (gw *GatewayConfig) resolveSessionAKID(r *http.Request, accessKeyID, client gw.RateLimiter.RecordFailure(clientIP) return "", principalContext{}, awserrors.ErrorInvalidClientTokenId } + if cred.PrincipalType == principalTypeUser { + // Minted by GetSessionToken for an IAM user: resolve straight back to + // that user so buildCallerARN yields arn:aws:iam::A:user/N and IAM + // policy is evaluated against the user, not a synthesised role session. + // The assumed-role fields stay empty by construction. + return secret, principalContext{ + identity: cred.SessionName, + accountID: cred.AccountID, + principalType: principalTypeUser, + }, "" + } + + // "assumed-role" or empty — the latter covers records minted before + // PrincipalType existed, so an absent value keeps the original role-session + // behaviour (see SessionCredential.PrincipalType backward-compat note). return secret, principalContext{ identity: cred.SessionName, accountID: cred.AccountID, diff --git a/spinifex/gateway/auth_test.go b/spinifex/gateway/auth_test.go index 51c1a6fea..e5e65dd02 100644 --- a/spinifex/gateway/auth_test.go +++ b/spinifex/gateway/auth_test.go @@ -1565,9 +1565,15 @@ type mockSTSService struct { func (m *mockSTSService) AssumeRole(_, _, _ string, _ *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) { return nil, nil } +func (m *mockSTSService) AssumeRoleForInstance(_, _, _ string, _ int64) (*sts.AssumeRoleOutput, error) { + return nil, nil +} func (m *mockSTSService) GetCallerIdentity(_, _, _ string, _ *sts.GetCallerIdentityInput) (*sts.GetCallerIdentityOutput, error) { return nil, nil } +func (m *mockSTSService) GetSessionToken(_, _, _, _ string, _ *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) { + return nil, nil +} func (m *mockSTSService) LookupSessionCredential(accessKeyID string) (*handlers_sts.SessionCredential, error) { m.lookups.Add(1) if m.lookupErr != nil { @@ -1626,6 +1632,7 @@ func setupSessionTestApp(t *testing.T, expiresAt time.Time) (http.Handler, *mock SecretEncrypted: encryptedSecret, SessionTokenHMAC: "ignored-in-mock", // mock verifies by plaintext map AccountID: "123456789012", + PrincipalType: principalTypeAssumedRole, AssumedRoleARN: testAssumedARN, UnderlyingRoleARN: testRoleARN, RoleID: "AROAEXAMPLEAAAAAA", @@ -1634,10 +1641,22 @@ func setupSessionTestApp(t *testing.T, expiresAt time.Time) (http.Handler, *mock ExpiresAt: expiresAt, CreatedAt: time.Now().UTC().Add(-time.Minute), } + return mountSessionGateway(t, cred) +} + +// mountSessionGateway wires the SigV4 middleware over a handler that echoes the +// resolved principal context, backed by a mock STS service seeded with cred. +// The stored secret is real (AES-GCM under testMasterKey), so the verify path +// exercises the same decrypt code as production; tests vary cred to drive the +// principal-type branch in resolveSessionAKID. +func mountSessionGateway(t *testing.T, cred *handlers_sts.SessionCredential) (http.Handler, *mockSTSService) { + t.Helper() + encryptedSecret, err := handlers_iam.EncryptSecret(testSecretKey, testMasterKey) + require.NoError(t, err) stsMock := &mockSTSService{ - sessions: map[string]*handlers_sts.SessionCredential{testSessionAKID: cred}, - tokens: map[string]string{testSessionAKID: testSessionToken}, + sessions: map[string]*handlers_sts.SessionCredential{cred.AccessKeyID: cred}, + tokens: map[string]string{cred.AccessKeyID: testSessionToken}, } iamMock := &mockIAMService{ @@ -1702,6 +1721,76 @@ func TestSigV4Auth_Session_ValidSignature(t *testing.T) { assert.Contains(t, bodyStr, "assumedRoleARN="+testAssumedARN) } +func TestSigV4Auth_Session_UserPrincipal(t *testing.T) { + // A GetSessionToken-minted session (PrincipalType "user") must resolve back + // to the IAM user: user identity, user principal type, and NO assumed-role + // fields — so buildCallerARN yields arn:aws:iam::A:user/N and downstream + // policy is evaluated against the user. + encryptedSecret, err := handlers_iam.EncryptSecret(testSecretKey, testMasterKey) + require.NoError(t, err) + cred := &handlers_sts.SessionCredential{ + AccessKeyID: testSessionAKID, + SecretEncrypted: encryptedSecret, + SessionTokenHMAC: "ignored-in-mock", + AccountID: "123456789012", + PrincipalType: principalTypeUser, + SessionName: "alice", + ExpiresAt: time.Now().UTC().Add(time.Hour), + CreatedAt: time.Now().UTC().Add(-time.Minute), + } + handler, _ := mountSessionGateway(t, cred) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Host = "localhost:9999" + signSessionRequest(t, req, nil, testSessionAKID, testSecretKey, testSessionToken) + + resp := doRequest(handler, req) + body, _ := io.ReadAll(resp.Body) + + require.Equalf(t, http.StatusOK, resp.StatusCode, "body: %s", string(body)) + bodyStr := string(body) + assert.Contains(t, bodyStr, "identity=alice") + assert.Contains(t, bodyStr, "accountId=123456789012") + assert.Contains(t, bodyStr, "principalType=user") + // A user session carries no assumed-role ARN, so the context value is unset. + assert.Contains(t, bodyStr, "assumedRoleARN=") +} + +func TestSigV4Auth_Session_EmptyPrincipalType_ResolvesAssumedRole(t *testing.T) { + // Backward compat: records minted before PrincipalType existed have an empty + // value and MUST still resolve as an assumed-role session, never as a user. + encryptedSecret, err := handlers_iam.EncryptSecret(testSecretKey, testMasterKey) + require.NoError(t, err) + cred := &handlers_sts.SessionCredential{ + AccessKeyID: testSessionAKID, + SecretEncrypted: encryptedSecret, + SessionTokenHMAC: "ignored-in-mock", + AccountID: "123456789012", + PrincipalType: "", // pre-migration record + AssumedRoleARN: testAssumedARN, + UnderlyingRoleARN: testRoleARN, + RoleID: "AROAEXAMPLEAAAAAA", + AssumedRoleID: "AROAEXAMPLEAAAAAA:test-session", + SessionName: "test-session", + ExpiresAt: time.Now().UTC().Add(time.Hour), + CreatedAt: time.Now().UTC().Add(-time.Minute), + } + handler, _ := mountSessionGateway(t, cred) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Host = "localhost:9999" + signSessionRequest(t, req, nil, testSessionAKID, testSecretKey, testSessionToken) + + resp := doRequest(handler, req) + body, _ := io.ReadAll(resp.Body) + + require.Equalf(t, http.StatusOK, resp.StatusCode, "body: %s", string(body)) + bodyStr := string(body) + assert.Contains(t, bodyStr, "identity=test-session") + assert.Contains(t, bodyStr, "principalType=assumed-role") + assert.Contains(t, bodyStr, "assumedRoleARN="+testAssumedARN) +} + func TestSigV4Auth_Session_Expired(t *testing.T) { // Past expiry but still within the janitor grace window — record still // resolvable, must reject as ExpiredToken (not InvalidClientTokenId). diff --git a/spinifex/gateway/ec2/instance/oidc_provider_mock_test.go b/spinifex/gateway/ec2/instance/oidc_provider_mock_test.go new file mode 100644 index 000000000..77884d123 --- /dev/null +++ b/spinifex/gateway/ec2/instance/oidc_provider_mock_test.go @@ -0,0 +1,19 @@ +package gateway_ec2_instance + +import "github.com/aws/aws-sdk-go/service/iam" + +// OIDC identity-provider registry stubs so fakeIAMService satisfies the +// IAMService interface; EC2 instance-profile paths never call these. + +func (f *fakeIAMService) CreateOpenIDConnectProvider(string, *iam.CreateOpenIDConnectProviderInput) (*iam.CreateOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (f *fakeIAMService) GetOpenIDConnectProvider(string, *iam.GetOpenIDConnectProviderInput) (*iam.GetOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (f *fakeIAMService) ListOpenIDConnectProviders(string, *iam.ListOpenIDConnectProvidersInput) (*iam.ListOpenIDConnectProvidersOutput, error) { + return nil, nil +} +func (f *fakeIAMService) DeleteOpenIDConnectProvider(string, *iam.DeleteOpenIDConnectProviderInput) (*iam.DeleteOpenIDConnectProviderOutput, error) { + return nil, nil +} diff --git a/spinifex/gateway/ec2/vpc/network_interface_hotplug_test.go b/spinifex/gateway/ec2/vpc/network_interface_hotplug_test.go new file mode 100644 index 000000000..e1cef6798 --- /dev/null +++ b/spinifex/gateway/ec2/vpc/network_interface_hotplug_test.go @@ -0,0 +1,324 @@ +package gateway_ec2_vpc + +import ( + "encoding/json" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/types" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats-server/v2/server" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestNATS(t *testing.T) *nats.Conn { + t.Helper() + opts := &server.Options{ + Host: "127.0.0.1", + Port: -1, + NoLog: true, + NoSigs: true, + } + ns, err := server.NewServer(opts) + require.NoError(t, err) + go ns.Start() + require.True(t, ns.ReadyForConnections(5*time.Second)) + t.Cleanup(func() { ns.Shutdown() }) + + nc, err := nats.Connect(ns.ClientURL()) + require.NoError(t, err) + t.Cleanup(func() { nc.Close() }) + return nc +} + +// describeNetworkInterfacesResponder seeds a DescribeNetworkInterfaces NATS +// responder that returns the supplied attachment-id → instance-id mapping. +func describeNetworkInterfacesResponder(t *testing.T, nc *nats.Conn, attachID, instanceID, eniID string) { + t.Helper() + sub, err := nc.Subscribe("ec2.DescribeNetworkInterfaces", func(msg *nats.Msg) { + resp := ec2.DescribeNetworkInterfacesOutput{ + NetworkInterfaces: []*ec2.NetworkInterface{ + { + NetworkInterfaceId: aws.String(eniID), + Attachment: &ec2.NetworkInterfaceAttachment{ + AttachmentId: aws.String(attachID), + InstanceId: aws.String(instanceID), + }, + }, + }, + } + data, _ := json.Marshal(resp) + _ = msg.Respond(data) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) +} + +// --- AttachNetworkInterface --- + +func TestAttachNetworkInterface_NilInput(t *testing.T) { + _, err := AttachNetworkInterface(nil, nil, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestAttachNetworkInterface_MissingNetworkInterfaceID(t *testing.T) { + _, err := AttachNetworkInterface(&ec2.AttachNetworkInterfaceInput{ + InstanceId: aws.String("i-abc"), + DeviceIndex: aws.Int64(0), + }, nil, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestAttachNetworkInterface_EmptyNetworkInterfaceID(t *testing.T) { + _, err := AttachNetworkInterface(&ec2.AttachNetworkInterfaceInput{ + NetworkInterfaceId: aws.String(""), + InstanceId: aws.String("i-abc"), + DeviceIndex: aws.Int64(0), + }, nil, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestAttachNetworkInterface_MissingInstanceID(t *testing.T) { + _, err := AttachNetworkInterface(&ec2.AttachNetworkInterfaceInput{ + NetworkInterfaceId: aws.String("eni-1"), + DeviceIndex: aws.Int64(0), + }, nil, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestAttachNetworkInterface_MissingDeviceIndex(t *testing.T) { + _, err := AttachNetworkInterface(&ec2.AttachNetworkInterfaceInput{ + NetworkInterfaceId: aws.String("eni-1"), + InstanceId: aws.String("i-abc"), + }, nil, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestAttachNetworkInterface_NoResponders(t *testing.T) { + nc := newTestNATS(t) + _, err := AttachNetworkInterface(&ec2.AttachNetworkInterfaceInput{ + NetworkInterfaceId: aws.String("eni-1"), + InstanceId: aws.String("i-no-responder"), + DeviceIndex: aws.Int64(0), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidInstanceIDNotFound) +} + +func TestAttachNetworkInterface_Success(t *testing.T) { + nc := newTestNATS(t) + + var received types.EC2InstanceCommand + var receivedAccount string + sub, err := nc.Subscribe("ec2.cmd.i-success", func(msg *nats.Msg) { + receivedAccount = msg.Header.Get(utils.AccountIDHeader) + _ = json.Unmarshal(msg.Data, &received) + data, _ := json.Marshal(ec2.AttachNetworkInterfaceOutput{ + AttachmentId: aws.String("eni-attach-xyz"), + }) + _ = msg.Respond(data) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + out, err := AttachNetworkInterface(&ec2.AttachNetworkInterfaceInput{ + NetworkInterfaceId: aws.String("eni-feedface"), + InstanceId: aws.String("i-success"), + DeviceIndex: aws.Int64(2), + }, nc, testAccountID) + + require.NoError(t, err) + require.NotNil(t, out.AttachmentId) + assert.Equal(t, "eni-attach-xyz", *out.AttachmentId) + assert.Equal(t, testAccountID, receivedAccount) + assert.True(t, received.Attributes.AttachENI) + require.NotNil(t, received.AttachENIData) + assert.Equal(t, "eni-feedface", received.AttachENIData.NetworkInterfaceID) + assert.Equal(t, int64(2), received.AttachENIData.DeviceIndex) + assert.Equal(t, "i-success", received.ID) +} + +func TestAttachNetworkInterface_DaemonErrorResponse(t *testing.T) { + nc := newTestNATS(t) + + sub, err := nc.Subscribe("ec2.cmd.i-err", func(msg *nats.Msg) { + _ = msg.Respond(utils.GenerateErrorPayload(awserrors.ErrorIncorrectInstanceState)) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + _, err = AttachNetworkInterface(&ec2.AttachNetworkInterfaceInput{ + NetworkInterfaceId: aws.String("eni-bad"), + InstanceId: aws.String("i-err"), + DeviceIndex: aws.Int64(0), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorIncorrectInstanceState) +} + +func TestAttachNetworkInterface_MalformedDaemonResponse(t *testing.T) { + nc := newTestNATS(t) + + sub, err := nc.Subscribe("ec2.cmd.i-malformed", func(msg *nats.Msg) { + _ = msg.Respond([]byte("not-json")) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + _, err = AttachNetworkInterface(&ec2.AttachNetworkInterfaceInput{ + NetworkInterfaceId: aws.String("eni-bad"), + InstanceId: aws.String("i-malformed"), + DeviceIndex: aws.Int64(0), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorServerInternal) +} + +// --- DetachNetworkInterface --- + +func TestDetachNetworkInterface_NilInput(t *testing.T) { + _, err := DetachNetworkInterface(nil, nil, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestDetachNetworkInterface_MissingAttachmentID(t *testing.T) { + _, err := DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{}, nil, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestDetachNetworkInterface_EmptyAttachmentID(t *testing.T) { + _, err := DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String(""), + }, nil, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestDetachNetworkInterface_UnknownAttachment(t *testing.T) { + nc := newTestNATS(t) + + sub, err := nc.Subscribe("ec2.DescribeNetworkInterfaces", func(msg *nats.Msg) { + data, _ := json.Marshal(ec2.DescribeNetworkInterfacesOutput{}) + _ = msg.Respond(data) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + _, err = DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String("eni-attach-missing"), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidAttachmentIDNotFound) +} + +func TestDetachNetworkInterface_DescribeErrorResponse(t *testing.T) { + nc := newTestNATS(t) + + sub, err := nc.Subscribe("ec2.DescribeNetworkInterfaces", func(msg *nats.Msg) { + _ = msg.Respond(utils.GenerateErrorPayload(awserrors.ErrorServerInternal)) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + _, err = DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String("eni-attach-1"), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidAttachmentIDNotFound) +} + +func TestDetachNetworkInterface_AttachmentWithoutInstance(t *testing.T) { + nc := newTestNATS(t) + + sub, err := nc.Subscribe("ec2.DescribeNetworkInterfaces", func(msg *nats.Msg) { + resp := ec2.DescribeNetworkInterfacesOutput{ + NetworkInterfaces: []*ec2.NetworkInterface{ + { + NetworkInterfaceId: aws.String("eni-orphan"), + Attachment: &ec2.NetworkInterfaceAttachment{ + AttachmentId: aws.String("eni-attach-orphan"), + }, + }, + }, + } + data, _ := json.Marshal(resp) + _ = msg.Respond(data) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + _, err = DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String("eni-attach-orphan"), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidAttachmentIDNotFound) +} + +func TestDetachNetworkInterface_NoResponders(t *testing.T) { + nc := newTestNATS(t) + describeNetworkInterfacesResponder(t, nc, "eni-attach-1", "i-no-daemon", "eni-1") + + _, err := DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String("eni-attach-1"), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidInstanceIDNotFound) +} + +func TestDetachNetworkInterface_Success(t *testing.T) { + nc := newTestNATS(t) + describeNetworkInterfacesResponder(t, nc, "eni-attach-ok", "i-detach", "eni-detachable") + + var received types.EC2InstanceCommand + var receivedAccount string + sub, err := nc.Subscribe("ec2.cmd.i-detach", func(msg *nats.Msg) { + receivedAccount = msg.Header.Get(utils.AccountIDHeader) + _ = json.Unmarshal(msg.Data, &received) + data, _ := json.Marshal(ec2.DetachNetworkInterfaceOutput{}) + _ = msg.Respond(data) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + _, err = DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String("eni-attach-ok"), + Force: aws.Bool(true), + }, nc, testAccountID) + require.NoError(t, err) + + assert.Equal(t, testAccountID, receivedAccount) + assert.True(t, received.Attributes.DetachENI) + require.NotNil(t, received.DetachENIData) + assert.Equal(t, "eni-attach-ok", received.DetachENIData.AttachmentID) + assert.True(t, received.DetachENIData.Force) + assert.Equal(t, "i-detach", received.ID) +} + +func TestDetachNetworkInterface_DaemonErrorResponse(t *testing.T) { + nc := newTestNATS(t) + describeNetworkInterfacesResponder(t, nc, "eni-attach-err", "i-err", "eni-x") + + sub, err := nc.Subscribe("ec2.cmd.i-err", func(msg *nats.Msg) { + _ = msg.Respond(utils.GenerateErrorPayload(awserrors.ErrorIncorrectInstanceState)) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + _, err = DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String("eni-attach-err"), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorIncorrectInstanceState) +} + +func TestDetachNetworkInterface_MalformedDaemonResponse(t *testing.T) { + nc := newTestNATS(t) + describeNetworkInterfacesResponder(t, nc, "eni-attach-bad", "i-bad", "eni-bad") + + sub, err := nc.Subscribe("ec2.cmd.i-bad", func(msg *nats.Msg) { + _ = msg.Respond([]byte("not-json")) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + _, err = DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String("eni-attach-bad"), + }, nc, testAccountID) + assert.EqualError(t, err, awserrors.ErrorServerInternal) +} diff --git a/spinifex/gateway/eks.go b/spinifex/gateway/eks.go index 69a17751a..9db5e0e97 100644 --- a/spinifex/gateway/eks.go +++ b/spinifex/gateway/eks.go @@ -5,6 +5,7 @@ import ( "io" "log/slog" "net/http" + "net/url" "regexp" "github.com/mulgadc/spinifex/spinifex/awserrors" @@ -30,10 +31,12 @@ type eksRoute struct { // eksRouteHandler invokes the per-action gateway function. It receives the // path-capture values in declaration order, the request body, the parent -// gateway config (NATS conn + ServiceURL), and the caller's accountID. It -// returns the response payload object (already a typed *eks.Output) or an -// error whose Error() is an awserrors code. -type eksRouteHandler func(gw *GatewayConfig, accountID string, params []string, body []byte) (any, error) +// gateway config (NATS conn + ServiceURL), the caller's accountID, and the +// caller's resolved IAM principal ARN (used by CreateCluster for the +// bootstrap-creator-admin AccessEntry; ignored by the rest). It returns the +// response payload object (already a typed *eks.Output) or an error whose +// Error() is an awserrors code. +type eksRouteHandler func(gw *GatewayConfig, accountID, callerARN string, params []string, body []byte) (any, error) // eksRoutes is the dispatch table. Order matters: more-specific paths must // come before less-specific ones with the same prefix (e.g. the @@ -42,127 +45,143 @@ type eksRouteHandler func(gw *GatewayConfig, accountID string, params []string, var eksRoutes = []eksRoute{ // Cluster {"POST", regexp.MustCompile(`^/clusters$`), "CreateCluster", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { - return gateway_eks.CreateCluster(gw.NATSConn, acct, b) + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { + return gateway_eks.CreateCluster(gw.NATSConn, acct, callerARN, b) }}, {"GET", regexp.MustCompile(`^/clusters$`), "ListClusters", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.ListClusters(gw.NATSConn, acct) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/update-config$`), "UpdateClusterConfig", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.UpdateClusterConfig(gw.NATSConn, acct, p[0], b) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/update-version$`), "UpdateClusterVersion", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.UpdateClusterVersion(gw.NATSConn, acct, p[0], b) }}, + // Control-plane VM broker: the K3s server VM POSTs bootstrap envelopes and + // state reports here (system SigV4 creds) and the gateway relays them onto + // the existing eks.bus.*/eks.state.* NATS subjects. acct (system account) + // and callerARN are ignored — the cluster account comes from the body. + {"POST", regexp.MustCompile(`^/clusters/([^/]+)/internal-publish$`), "PublishInternal", + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { + return gateway_eks.PublishInternal(gw.NATSConn, p[0], b) + }}, + // Control-plane VM TokenReview broker: the eks-token-webhook POSTs the + // `aws eks get-token` bearer token here (system SigV4 creds) and the gateway + // resolves it host-side (STS verify + AccessEntry lookup), so the webhook + // never speaks NATS. Cluster account comes from the body. + {"POST", regexp.MustCompile(`^/clusters/([^/]+)/token-review$`), "WebhookTokenReview", + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { + return gateway_eks.WebhookTokenReview(gw.NATSConn, p[0], b) + }}, // Nodegroup {"POST", regexp.MustCompile(`^/clusters/([^/]+)/node-groups$`), "CreateNodegroup", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.CreateNodegroup(gw.NATSConn, acct, p[0], b) }}, {"GET", regexp.MustCompile(`^/clusters/([^/]+)/node-groups$`), "ListNodegroups", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.ListNodegroups(gw.NATSConn, acct, p[0]) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/node-groups/([^/]+)/update-config$`), "UpdateNodegroupConfig", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.UpdateNodegroupConfig(gw.NATSConn, acct, p[0], p[1], b) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/node-groups/([^/]+)/update-version$`), "UpdateNodegroupVersion", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.UpdateNodegroupVersion(gw.NATSConn, acct, p[0], p[1], b) }}, {"GET", regexp.MustCompile(`^/clusters/([^/]+)/node-groups/([^/]+)$`), "DescribeNodegroup", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DescribeNodegroup(gw.NATSConn, acct, p[0], p[1]) }}, {"DELETE", regexp.MustCompile(`^/clusters/([^/]+)/node-groups/([^/]+)$`), "DeleteNodegroup", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DeleteNodegroup(gw.NATSConn, acct, p[0], p[1]) }}, // AccessEntry / AccessPolicy {"POST", regexp.MustCompile(`^/clusters/([^/]+)/access-entries$`), "CreateAccessEntry", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.CreateAccessEntry(gw.NATSConn, acct, p[0], b) }}, {"GET", regexp.MustCompile(`^/clusters/([^/]+)/access-entries$`), "ListAccessEntries", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.ListAccessEntries(gw.NATSConn, acct, p[0]) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/access-entries/([^/]+)/access-policies$`), "AssociateAccessPolicy", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.AssociateAccessPolicy(gw.NATSConn, acct, p[0], p[1], b) }}, {"DELETE", regexp.MustCompile(`^/clusters/([^/]+)/access-entries/([^/]+)/access-policies$`), "DisassociateAccessPolicy", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DisassociateAccessPolicy(gw.NATSConn, acct, p[0], p[1], b) }}, {"GET", regexp.MustCompile(`^/clusters/([^/]+)/access-entries/([^/]+)/access-policies$`), "ListAssociatedAccessPolicies", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.ListAssociatedAccessPolicies(gw.NATSConn, acct, p[0], p[1]) }}, {"GET", regexp.MustCompile(`^/clusters/([^/]+)/access-entries/([^/]+)$`), "DescribeAccessEntry", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DescribeAccessEntry(gw.NATSConn, acct, p[0], p[1]) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/access-entries/([^/]+)$`), "UpdateAccessEntry", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.UpdateAccessEntry(gw.NATSConn, acct, p[0], p[1], b) }}, {"DELETE", regexp.MustCompile(`^/clusters/([^/]+)/access-entries/([^/]+)$`), "DeleteAccessEntry", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DeleteAccessEntry(gw.NATSConn, acct, p[0], p[1]) }}, {"GET", regexp.MustCompile(`^/access-policies$`), "ListAccessPolicies", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.ListAccessPolicies(gw.NATSConn, acct) }}, // Addons {"GET", regexp.MustCompile(`^/addons/supported-versions$`), "DescribeAddonVersions", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DescribeAddonVersions(gw.NATSConn, acct) }}, - {"GET", regexp.MustCompile(`^/addons$`), "ListAddons", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { - return gateway_eks.ListAddons(gw.NATSConn, acct) + {"GET", regexp.MustCompile(`^/clusters/([^/]+)/addons$`), "ListAddons", + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { + return gateway_eks.ListAddons(gw.NATSConn, acct, p[0]) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/addons$`), "CreateAddon", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.CreateAddon(gw.NATSConn, acct, p[0], b) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/addons/([^/]+)/update$`), "UpdateAddon", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.UpdateAddon(gw.NATSConn, acct, p[0], p[1], b) }}, {"GET", regexp.MustCompile(`^/clusters/([^/]+)/addons/([^/]+)$`), "DescribeAddon", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DescribeAddon(gw.NATSConn, acct, p[0], p[1]) }}, {"DELETE", regexp.MustCompile(`^/clusters/([^/]+)/addons/([^/]+)$`), "DeleteAddon", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DeleteAddon(gw.NATSConn, acct, p[0], p[1]) }}, // OIDC identity-provider configs {"POST", regexp.MustCompile(`^/clusters/([^/]+)/identity-provider-configs/associate$`), "AssociateIdentityProviderConfig", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.AssociateIdentityProviderConfig(gw.NATSConn, acct, p[0], b) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/identity-provider-configs/describe$`), "DescribeIdentityProviderConfig", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DescribeIdentityProviderConfig(gw.NATSConn, acct, p[0], b) }}, {"POST", regexp.MustCompile(`^/clusters/([^/]+)/identity-provider-configs/disassociate$`), "DisassociateIdentityProviderConfig", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DisassociateIdentityProviderConfig(gw.NATSConn, acct, p[0], b) }}, {"GET", regexp.MustCompile(`^/clusters/([^/]+)/identity-provider-configs$`), "ListIdentityProviderConfigs", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.ListIdentityProviderConfigs(gw.NATSConn, acct, p[0]) }}, @@ -170,25 +189,25 @@ var eksRoutes = []eksRoute{ // /clusters/{name}/... routes so the regexp matcher prefers the deeper // matches first when iterating in declaration order. {"GET", regexp.MustCompile(`^/clusters/([^/]+)$`), "DescribeCluster", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DescribeCluster(gw.NATSConn, acct, p[0]) }}, {"DELETE", regexp.MustCompile(`^/clusters/([^/]+)$`), "DeleteCluster", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.DeleteCluster(gw.NATSConn, acct, p[0]) }}, // Tags {"POST", regexp.MustCompile(`^/tags/(.+)$`), "TagResource", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.TagResource(gw.NATSConn, acct, p[0], b) }}, {"DELETE", regexp.MustCompile(`^/tags/(.+)$`), "UntagResource", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.UntagResource(gw.NATSConn, acct, p[0], b) }}, {"GET", regexp.MustCompile(`^/tags/(.+)$`), "ListTagsForResource", - func(gw *GatewayConfig, acct string, p []string, b []byte) (any, error) { + func(gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) { return gateway_eks.ListTagsForResource(gw.NATSConn, acct, p[0]) }}, } @@ -196,6 +215,12 @@ var eksRoutes = []eksRoute{ // lookupEKSAction walks eksRoutes in declaration order, returning the matched // route's action name + path params, or ("", nil, false) when nothing matches. // Used by EKS_Request and unit tests to verify routing. +// +// path must be the *escaped* request path (r.URL.EscapedPath()): IAM principal +// ARNs are passed as a path segment and the CLI percent-encodes the embedded +// `/` (`user%2Fadmin`). Matching the decoded path would split that ARN across +// the `([^/]+)` capture and no route would match. Captured params are +// PathUnescape'd here so handlers receive the real value. func lookupEKSAction(method, path string) (string, []string, eksRouteHandler, bool) { for _, route := range eksRoutes { if route.method != method { @@ -207,7 +232,15 @@ func lookupEKSAction(method, path string) (string, []string, eksRouteHandler, bo } var params []string if len(m) > 1 { - params = m[1:] + params = make([]string, 0, len(m)-1) + for _, raw := range m[1:] { + decoded, err := url.PathUnescape(raw) + if err != nil { + slog.Debug("EKS: bad percent-encoding in path param", "param", raw, "err", err) + decoded = raw + } + params = append(params, decoded) + } } return route.action, params, route.handler, true } @@ -220,7 +253,7 @@ func lookupEKSAction(method, path string) (string, []string, eksRouteHandler, bo // are returned as plain awserrors codes; the caller (gateway.Request) routes // them through the shared ErrorHandler, which now emits EKS REST-JSON. func (gw *GatewayConfig) EKS_Request(w http.ResponseWriter, r *http.Request) error { - action, params, handler, ok := lookupEKSAction(r.Method, r.URL.Path) + action, params, handler, ok := lookupEKSAction(r.Method, r.URL.EscapedPath()) if !ok { slog.Debug("EKS: no route for request", "method", r.Method, "path", r.URL.Path) return errors.New(awserrors.ErrorInvalidAction) @@ -246,7 +279,11 @@ func (gw *GatewayConfig) EKS_Request(w http.ResponseWriter, r *http.Request) err return errors.New(awserrors.ErrorInvalidParameterValue) } - output, err := handler(gw, accountID, params, body) + // Resolve the caller's IAM principal ARN best-effort; only CreateCluster + // consumes it (bootstrap-creator-admin) and it degrades gracefully to "". + callerARN := eksCallerPrincipalARN(r) + + output, err := handler(gw, accountID, callerARN, params, body) if err != nil { return err } @@ -254,3 +291,21 @@ func (gw *GatewayConfig) EKS_Request(w http.ResponseWriter, r *http.Request) err gateway_eks.WriteJSONResponse(w, output) return nil } + +// eksCallerPrincipalARN resolves the caller's IAM principal ARN from the SigV4 +// auth context, best-effort. Returns "" when the context is incomplete or the +// ARN can't be composed — CreateCluster then skips the creator-admin entry +// rather than failing the request. +func eksCallerPrincipalARN(r *http.Request) string { + ctx := r.Context() + accountID, _ := ctx.Value(ctxAccountID).(string) + identity, _ := ctx.Value(ctxIdentity).(string) + principalType, _ := ctx.Value(ctxPrincipalType).(string) + assumedRoleARN, _ := ctx.Value(ctxAssumedRoleARN).(string) + arn, err := buildCallerARN(accountID, identity, principalType, assumedRoleARN) + if err != nil { + slog.Debug("EKS_Request: could not resolve caller principal ARN", "err", err) + return "" + } + return arn +} diff --git a/spinifex/gateway/eks/addons.go b/spinifex/gateway/eks/addons.go index 04033d2bb..43848676a 100644 --- a/spinifex/gateway/eks/addons.go +++ b/spinifex/gateway/eks/addons.go @@ -7,9 +7,11 @@ import ( "github.com/nats-io/nats.go" ) -// ListAddons — GET /addons -func ListAddons(natsConn *nats.Conn, accountID string) (*eks.ListAddonsOutput, error) { - return handlers_eks.NewNATSEKSService(natsConn).ListAddons(&eks.ListAddonsInput{}, accountID) +// ListAddons — GET /clusters/{name}/addons +func ListAddons(natsConn *nats.Conn, accountID, cluster string) (*eks.ListAddonsOutput, error) { + return handlers_eks.NewNATSEKSService(natsConn).ListAddons(&eks.ListAddonsInput{ + ClusterName: aws.String(cluster), + }, accountID) } // DescribeAddonVersions — GET /addons/supported-versions diff --git a/spinifex/gateway/eks/cluster.go b/spinifex/gateway/eks/cluster.go index c4168a573..22ed19dcb 100644 --- a/spinifex/gateway/eks/cluster.go +++ b/spinifex/gateway/eks/cluster.go @@ -9,13 +9,15 @@ import ( "github.com/nats-io/nats.go" ) -// CreateCluster — POST /clusters -func CreateCluster(natsConn *nats.Conn, accountID string, body []byte) (*eks.CreateClusterOutput, error) { +// CreateCluster — POST /clusters. callerPrincipalARN is the resolved caller IAM +// principal ARN, plumbed through so the control plane can mint the +// bootstrap-creator-admin AccessEntry for the caller. +func CreateCluster(natsConn *nats.Conn, accountID, callerPrincipalARN string, body []byte) (*eks.CreateClusterOutput, error) { input := new(eks.CreateClusterInput) if err := unmarshalIfBody(body, input); err != nil { return nil, err } - return handlers_eks.NewNATSEKSService(natsConn).CreateCluster(input, accountID) + return handlers_eks.NewNATSEKSService(natsConn).CreateCluster(input, accountID, callerPrincipalARN) } // DescribeCluster — GET /clusters/{name} diff --git a/spinifex/gateway/eks/handler.go b/spinifex/gateway/eks/handler.go index 2c45485f2..9558ec3b7 100644 --- a/spinifex/gateway/eks/handler.go +++ b/spinifex/gateway/eks/handler.go @@ -11,7 +11,9 @@ import ( "io" "log/slog" "net/http" + "strings" + "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" "github.com/mulgadc/spinifex/spinifex/awserrors" ) @@ -27,11 +29,18 @@ type EKSJSONError struct { } // GenerateEKSErrorResponse marshals the AWS REST-JSON error envelope. -// The trailing "Exception" suffix matches the convention AWS uses -// (e.g. ResourceNotFoundException, InvalidParameterException). +// The wire __type carries the trailing "Exception" suffix AWS uses +// (e.g. ResourceNotFoundException, InvalidParameterException). The suffix is +// appended idempotently: many awserrors codes already end in "Exception" +// (e.g. ErrorEKSResourceNotFound = "ResourceNotFoundException"), and adding a +// second would emit ResourceNotFoundExceptionException, which SDK clients +// reject when matching eks.ErrCodeResourceNotFoundException. func GenerateEKSErrorResponse(code, message string) []byte { + if !strings.HasSuffix(code, "Exception") { + code += "Exception" + } body, err := json.Marshal(EKSJSONError{ - Type: code + "Exception", + Type: code, Message: message, }) if err != nil { @@ -41,9 +50,13 @@ func GenerateEKSErrorResponse(code, message string) []byte { return body } -// WriteJSONResponse serializes obj as JSON and writes it as a 200 response. +// WriteJSONResponse serializes obj as AWS REST-JSON and writes it as a 200 +// response. aws-sdk-go *Output structs tag their fields with locationName (the +// restjson wire name) and carry no json: tags, so encoding/json would emit Go +// PascalCase keys the AWS SDK cannot parse. jsonutil.BuildJSON is the same +// marshaler the SDK uses for restjson bodies and honors locationName. func WriteJSONResponse(w http.ResponseWriter, obj any) { - body, err := json.Marshal(obj) + body, err := jsonutil.BuildJSON(obj) if err != nil { slog.Error("Failed to marshal EKS response JSON", "err", err) WriteJSONError(w, awserrors.ErrorInternalError, "failed to marshal response", http.StatusInternalServerError) diff --git a/spinifex/gateway/eks/handler_test.go b/spinifex/gateway/eks/handler_test.go index 711366bc3..61eb300b8 100644 --- a/spinifex/gateway/eks/handler_test.go +++ b/spinifex/gateway/eks/handler_test.go @@ -7,6 +7,8 @@ import ( "net/http/httptest" "testing" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -19,6 +21,16 @@ func TestGenerateEKSErrorResponse_ShapesExceptionSuffix(t *testing.T) { assert.Equal(t, "Cluster does not exist", env.Message) } +// Codes that already carry the "Exception" suffix (e.g. +// awserrors.ErrorEKSResourceNotFound = "ResourceNotFoundException") must not be +// doubled into ResourceNotFoundExceptionException, which SDK clients reject. +func TestGenerateEKSErrorResponse_DoesNotDoubleExceptionSuffix(t *testing.T) { + body := GenerateEKSErrorResponse("ResourceNotFoundException", "Cluster does not exist") + var env EKSJSONError + require.NoError(t, json.Unmarshal(body, &env)) + assert.Equal(t, eks.ErrCodeResourceNotFoundException, env.Type) +} + func TestWriteJSONError_SetsContentTypeAndStatus(t *testing.T) { w := httptest.NewRecorder() WriteJSONError(w, "NotImplemented", "Operation not implemented", http.StatusNotImplemented) @@ -42,6 +54,64 @@ func TestWriteJSONResponse_SerializesObject(t *testing.T) { assert.Equal(t, "bar", got["foo"]) } +// TestWriteJSONResponse_RestJSONWireShape guards against regressing to +// encoding/json, which ignores aws-sdk-go's locationName tags and emits Go +// PascalCase keys the AWS SDK cannot parse. The body must carry the lowercase +// restjson field names, nested all the way down. +func TestWriteJSONResponse_RestJSONWireShape(t *testing.T) { + w := httptest.NewRecorder() + out := &eks.DescribeClusterOutput{ + Cluster: &eks.Cluster{ + Name: aws.String("smoke"), + Status: aws.String("ACTIVE"), + Endpoint: aws.String("https://internal-eks-smoke:443"), + Identity: &eks.Identity{ + Oidc: &eks.OIDC{Issuer: aws.String("https://gw/oidc/eks/x")}, + }, + }, + } + WriteJSONResponse(w, out) + require.Equal(t, http.StatusOK, w.Code) + + body := w.Body.String() + for _, k := range []string{`"cluster"`, `"name"`, `"status"`, `"endpoint"`, `"identity"`, `"oidc"`, `"issuer"`} { + assert.Contains(t, body, k, "restjson body missing lowercase key %s", k) + } + for _, k := range []string{`"Cluster"`, `"Status"`, `"Endpoint"`, `"Identity"`, `"Issuer"`} { + assert.NotContains(t, body, k, "restjson body leaked PascalCase key %s — encoding/json regression", k) + } + + // The AWS SDK round-trips through the same restjson nested keys. + var got struct { + Cluster struct { + Status string `json:"status"` + Endpoint string `json:"endpoint"` + Identity struct { + Oidc struct { + Issuer string `json:"issuer"` + } `json:"oidc"` + } `json:"identity"` + } `json:"cluster"` + } + require.NoError(t, json.Unmarshal(w.Body.Bytes(), &got)) + assert.Equal(t, "ACTIVE", got.Cluster.Status) + assert.Equal(t, "https://internal-eks-smoke:443", got.Cluster.Endpoint) + assert.Equal(t, "https://gw/oidc/eks/x", got.Cluster.Identity.Oidc.Issuer) +} + +func TestWriteJSONResponse_ListClustersWireShape(t *testing.T) { + w := httptest.NewRecorder() + WriteJSONResponse(w, &eks.ListClustersOutput{ + Clusters: aws.StringSlice([]string{"alpha", "beta"}), + NextToken: aws.String("tok"), + }) + body := w.Body.String() + assert.Contains(t, body, `"clusters"`) + assert.Contains(t, body, `"nextToken"`) + assert.NotContains(t, body, `"Clusters"`) + assert.NotContains(t, body, `"NextToken"`) +} + type sampleInput struct { Name string `json:"name"` } diff --git a/spinifex/gateway/eks/internal_publish.go b/spinifex/gateway/eks/internal_publish.go new file mode 100644 index 000000000..e66b8a4bf --- /dev/null +++ b/spinifex/gateway/eks/internal_publish.go @@ -0,0 +1,91 @@ +package gateway_eks + +import ( + "encoding/json" + "errors" + "log/slog" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + "github.com/nats-io/nats.go" +) + +// internalPublishChannels are the two VM→host channels the broker relays. +const ( + internalChannelBootstrap = "bootstrap" + internalChannelState = "state" +) + +// internalPublishRequest is the wire shape the on-VM eks-gateway-publish helper +// POSTs to /clusters/{name}/internal-publish. The control-plane VM holds system +// SigV4 creds (system account, not the cluster account), so AccountID names the +// customer cluster account explicitly rather than being taken from the SigV4 +// auth context. Payload is the already-encoded subject body (a BootstrapEnvelope +// for the bootstrap channel, a ServerStateReport for the state channel) — the +// broker relays it verbatim onto the existing NATS subject, so the daemon-side +// subscriber, KV persistence, and reconciler are unchanged. +type internalPublishRequest struct { + AccountID string `json:"accountId"` + Channel string `json:"channel"` + Kind string `json:"kind"` + Payload json.RawMessage `json:"payload"` +} + +// publishInternalOutput is the empty success body (`{}`). +type publishInternalOutput struct{} + +// validBootstrapKinds is the closed set of bootstrap subject suffixes the broker +// will relay — a malformed/hostile kind cannot be turned into an arbitrary +// subject suffix. +var validBootstrapKinds = map[string]struct{}{ + handlers_eks.BootstrapSubjectToken: {}, + handlers_eks.BootstrapSubjectKubeconfig: {}, + handlers_eks.BootstrapSubjectJWKS: {}, + handlers_eks.BootstrapSubjectCA: {}, +} + +// PublishInternal — POST /clusters/{name}/internal-publish. Relays a +// control-plane VM publication onto the existing bootstrap/state NATS subjects. +// This is the gateway-broker replacement for the VM dialing core NATS directly: +// the VM reaches the daemon over the mgmt-reachable AWSGW with SigV4 + retry +// (DDIL-correct), and NATS stays cluster-internal. +func PublishInternal(natsConn *nats.Conn, clusterName string, body []byte) (*publishInternalOutput, error) { + if natsConn == nil { + return nil, errors.New(awserrors.ErrorServerInternal) + } + if clusterName == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + var req internalPublishRequest + if err := json.Unmarshal(body, &req); err != nil { + slog.Debug("PublishInternal: bad body", "cluster", clusterName, "err", err) + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + if req.AccountID == "" || len(req.Payload) == 0 { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + var subject string + switch req.Channel { + case internalChannelBootstrap: + if _, ok := validBootstrapKinds[req.Kind]; !ok { + slog.Debug("PublishInternal: unknown bootstrap kind", "cluster", clusterName, "kind", req.Kind) + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + subject = handlers_eks.BootstrapSubject(req.AccountID, clusterName, req.Kind) + case internalChannelState: + subject = handlers_eks.StateSubject(req.AccountID, clusterName) + default: + slog.Debug("PublishInternal: unknown channel", "cluster", clusterName, "channel", req.Channel) + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + if err := natsConn.Publish(subject, req.Payload); err != nil { + slog.Error("PublishInternal: NATS publish failed", "subject", subject, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + slog.Debug("PublishInternal: relayed", "subject", subject, "bytes", len(req.Payload)) + + return &publishInternalOutput{}, nil +} diff --git a/spinifex/gateway/eks/internal_publish_test.go b/spinifex/gateway/eks/internal_publish_test.go new file mode 100644 index 000000000..9a8bf592c --- /dev/null +++ b/spinifex/gateway/eks/internal_publish_test.go @@ -0,0 +1,80 @@ +package gateway_eks + +import ( + "testing" + "time" + + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPublishInternal_RelaysToSubjects(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + + cases := []struct { + name string + body string + subject string + payload string + }{ + { + name: "bootstrap token", + body: `{"accountId":"111122223333","channel":"bootstrap","kind":"k3s-bootstrap-token","payload":{"token":"k3s-secret"}}`, + subject: handlers_eks.BootstrapSubject("111122223333", "alpha", handlers_eks.BootstrapSubjectToken), + payload: `{"token":"k3s-secret"}`, + }, + { + name: "state report", + body: `{"accountId":"111122223333","channel":"state","payload":{"healthz":"ok","node_count":1,"ts":42}}`, + subject: handlers_eks.StateSubject("111122223333", "alpha"), + payload: `{"healthz":"ok","node_count":1,"ts":42}`, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + sub, err := nc.SubscribeSync(tc.subject) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + out, err := PublishInternal(nc, "alpha", []byte(tc.body)) + require.NoError(t, err) + require.NotNil(t, out) + + msg, err := sub.NextMsg(2 * time.Second) + require.NoError(t, err) + assert.JSONEq(t, tc.payload, string(msg.Data)) + }) + } +} + +func TestPublishInternal_Rejects(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + + cases := []struct { + name string + body string + }{ + {"empty account", `{"accountId":"","channel":"state","payload":{"healthz":"ok"}}`}, + {"empty payload", `{"accountId":"111122223333","channel":"state"}`}, + {"unknown channel", `{"accountId":"111122223333","channel":"wat","payload":{"x":1}}`}, + {"unknown bootstrap kind", `{"accountId":"111122223333","channel":"bootstrap","kind":"evil","payload":{"x":1}}`}, + {"malformed json", `{not-json`}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, err := PublishInternal(nc, "alpha", []byte(tc.body)) + require.Error(t, err) + }) + } +} + +func TestPublishInternal_NilConnAndEmptyCluster(t *testing.T) { + _, err := PublishInternal(nil, "alpha", []byte(`{}`)) + require.Error(t, err) + + _, nc := testutil.StartTestNATS(t) + _, err = PublishInternal(nc, "", []byte(`{"accountId":"1","channel":"state","payload":{"x":1}}`)) + require.Error(t, err) +} diff --git a/spinifex/gateway/eks/token_review.go b/spinifex/gateway/eks/token_review.go new file mode 100644 index 000000000..284532595 --- /dev/null +++ b/spinifex/gateway/eks/token_review.go @@ -0,0 +1,54 @@ +package gateway_eks + +import ( + "encoding/json" + "errors" + "log/slog" + "time" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + "github.com/nats-io/nats.go" +) + +// tokenReviewVerifyTimeout bounds the host-side STS verify NATS round-trip. The +// apiserver fails the TokenReview on its own timeout, so keep this tight. +const tokenReviewVerifyTimeout = 5 * time.Second + +// webhookTokenReviewRequest is the body the on-VM eks-token-webhook POSTs to +// /clusters/{name}/token-review. The webhook holds system SigV4 creds (system +// account, not the cluster account), so AccountID names the cluster account +// explicitly — same pattern as PublishInternal. +type webhookTokenReviewRequest struct { + AccountID string `json:"accountId"` + Token string `json:"token"` +} + +// WebhookTokenReview — POST /clusters/{name}/token-review. Resolves an +// `aws eks get-token` bearer token to a K8s identity host-side (STS verify + +// AccessEntry lookup) and returns it. This is the gateway-broker replacement +// for the webhook dialing core NATS/STS/KV directly. +func WebhookTokenReview(natsConn *nats.Conn, clusterName string, body []byte) (*handlers_eks.WebhookTokenReviewResult, error) { + if natsConn == nil { + return nil, errors.New(awserrors.ErrorServerInternal) + } + if clusterName == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + var req webhookTokenReviewRequest + if err := json.Unmarshal(body, &req); err != nil { + slog.Debug("WebhookTokenReview: bad body", "cluster", clusterName, "err", err) + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + if req.AccountID == "" || req.Token == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + res, err := handlers_eks.ResolveTokenReview(natsConn, req.AccountID, clusterName, req.Token, tokenReviewVerifyTimeout) + if err != nil { + slog.Error("WebhookTokenReview: resolve failed", "cluster", clusterName, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + return &res, nil +} diff --git a/spinifex/gateway/eks/token_review_test.go b/spinifex/gateway/eks/token_review_test.go new file mode 100644 index 000000000..5d62d57f0 --- /dev/null +++ b/spinifex/gateway/eks/token_review_test.go @@ -0,0 +1,86 @@ +package gateway_eks + +import ( + "encoding/base64" + "encoding/json" + "testing" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const testARN = "arn:aws:iam::111122223333:role/admin" + +// validToken builds a k8s-aws-v1 bearer token (the `aws eks get-token` shape): +// the literal prefix + base64 raw-url of the presigned GetCallerIdentity URL. +func validToken(url string) string { + return "k8s-aws-v1." + base64.RawURLEncoding.EncodeToString([]byte(url)) +} + +func TestWebhookTokenReview_Rejects(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + + // nil conn → ServerInternal + _, err := WebhookTokenReview(nil, "alpha", []byte(`{"accountId":"1","token":"t"}`)) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorServerInternal, err.Error()) + + cases := []struct { + name, cluster, body, want string + }{ + {"empty cluster", "", `{"accountId":"1","token":"t"}`, awserrors.ErrorInvalidParameterValue}, + {"malformed json", "alpha", `{not-json`, awserrors.ErrorInvalidParameterValue}, + {"empty account", "alpha", `{"accountId":"","token":"t"}`, awserrors.ErrorInvalidParameterValue}, + {"empty token", "alpha", `{"accountId":"1","token":""}`, awserrors.ErrorInvalidParameterValue}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, err := WebhookTokenReview(nc, tc.cluster, []byte(tc.body)) + require.Error(t, err) + assert.Equal(t, tc.want, err.Error()) + }) + } +} + +// A genuine infra fault (account bucket absent) maps to ServerInternal. +func TestWebhookTokenReview_InfraFaultIsServerInternal(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + _, err := WebhookTokenReview(nc, "alpha", []byte(`{"accountId":"111122223333","token":"`+validToken("https://sts/?x=1")+`"}`)) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorServerInternal, err.Error()) +} + +func TestWebhookTokenReview_ResolvesIdentity(t *testing.T) { + _, nc, js := testutil.StartTestJetStream(t) + kv := testutil.SeedKV(t, js, handlers_eks.AccountBucketName("111122223333"), nil) + require.NoError(t, handlers_eks.PutAccessEntryRecord(kv, &handlers_eks.AccessEntryRecord{ + ClusterName: "alpha", + PrincipalARN: testARN, + KubernetesUsername: testARN, + KubernetesGroups: []string{"system:masters"}, + Type: handlers_eks.AccessEntryTypeStandard, + })) + + sub, err := nc.Subscribe(handlers_eks.TokenVerifySubject, func(m *nats.Msg) { + resp, _ := json.Marshal(handlers_eks.TokenVerifyResponse{ + AccountID: "111122223333", + ARN: testARN, + UserID: "AROAEXAMPLE:session", + PrincipalType: "AssumedRole", + }) + _ = m.Respond(resp) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + out, err := WebhookTokenReview(nc, "alpha", []byte(`{"accountId":"111122223333","token":"`+validToken("https://sts/?Action=GetCallerIdentity")+`"}`)) + require.NoError(t, err) + require.NotNil(t, out) + assert.True(t, out.Authenticated) + assert.Equal(t, testARN, out.Username) + assert.Equal(t, []string{"system:masters"}, out.Groups) +} diff --git a/spinifex/gateway/eks/wrappers_test.go b/spinifex/gateway/eks/wrappers_test.go index a97405dc0..00cf68ded 100644 --- a/spinifex/gateway/eks/wrappers_test.go +++ b/spinifex/gateway/eks/wrappers_test.go @@ -30,11 +30,11 @@ func TestGatewayWrappers_Cluster(t *testing.T) { _, nc := testutil.StartTestNATS(t) stubEKSResponder(t, nc) - out1, err := CreateCluster(nc, acct, []byte(`{"name":"alpha"}`)) + out1, err := CreateCluster(nc, acct, "arn:aws:iam::111122223333:role/dev", []byte(`{"name":"alpha"}`)) require.NoError(t, err) assert.NotNil(t, out1) - out2, err := CreateCluster(nc, acct, nil) + out2, err := CreateCluster(nc, acct, "", nil) require.NoError(t, err) assert.NotNil(t, out2) @@ -71,7 +71,7 @@ func TestGatewayWrappers_Cluster_BadJSON(t *testing.T) { _, nc := testutil.StartTestNATS(t) stubEKSResponder(t, nc) - _, err := CreateCluster(nc, acct, []byte(`{not-json`)) + _, err := CreateCluster(nc, acct, "", []byte(`{not-json`)) require.Error(t, err) _, err = UpdateClusterConfig(nc, acct, "alpha", []byte(`{not-json`)) require.Error(t, err) @@ -207,7 +207,7 @@ func TestGatewayWrappers_Addons(t *testing.T) { _, nc := testutil.StartTestNATS(t) stubEKSResponder(t, nc) - out1, err := ListAddons(nc, acct) + out1, err := ListAddons(nc, acct, "alpha") require.NoError(t, err) assert.NotNil(t, out1) diff --git a/spinifex/gateway/eks_test.go b/spinifex/gateway/eks_test.go index 3398f218b..b4562fdf1 100644 --- a/spinifex/gateway/eks_test.go +++ b/spinifex/gateway/eks_test.go @@ -41,7 +41,7 @@ func TestLookupEKSAction_ResolvesKnownRoutes(t *testing.T) { {"DELETE", "/clusters/alpha/access-entries/arn-user/access-policies", "DisassociateAccessPolicy", []string{"alpha", "arn-user"}}, {"GET", "/clusters/alpha/access-entries/arn-user/access-policies", "ListAssociatedAccessPolicies", []string{"alpha", "arn-user"}}, {"GET", "/access-policies", "ListAccessPolicies", nil}, - {"GET", "/addons", "ListAddons", nil}, + {"GET", "/clusters/alpha/addons", "ListAddons", []string{"alpha"}}, {"GET", "/addons/supported-versions", "DescribeAddonVersions", nil}, {"POST", "/clusters/alpha/addons", "CreateAddon", []string{"alpha"}}, {"GET", "/clusters/alpha/addons/vpc-cni", "DescribeAddon", []string{"alpha", "vpc-cni"}}, @@ -66,9 +66,45 @@ func TestLookupEKSAction_ResolvesKnownRoutes(t *testing.T) { } } -func TestLookupEKSAction_CoversAll34Actions(t *testing.T) { +// IAM principal ARNs contain a `/` (`user/admin`) and a `:` which the AWS CLI +// percent-encodes in the request path. lookupEKSAction is fed r.URL.EscapedPath() +// so `%2F` stays a single `([^/]+)` segment; the captured param is then +// PathUnescape'd back to the real ARN before reaching the handler. +func TestLookupEKSAction_EncodedPrincipalARN(t *testing.T) { + const ( + arn = "arn:aws:iam::000000000001:user/admin" + escaped = "arn%3Aaws%3Aiam%3A%3A000000000001%3Auser%2Fadmin" + ) + cases := []struct { + method, path string + wantAction string + }{ + {"GET", "/clusters/alpha/access-entries/" + escaped, "DescribeAccessEntry"}, + {"POST", "/clusters/alpha/access-entries/" + escaped, "UpdateAccessEntry"}, + {"DELETE", "/clusters/alpha/access-entries/" + escaped, "DeleteAccessEntry"}, + {"POST", "/clusters/alpha/access-entries/" + escaped + "/access-policies", "AssociateAccessPolicy"}, + {"DELETE", "/clusters/alpha/access-entries/" + escaped + "/access-policies", "DisassociateAccessPolicy"}, + {"GET", "/clusters/alpha/access-entries/" + escaped + "/access-policies", "ListAssociatedAccessPolicies"}, + } + for _, tc := range cases { + t.Run(tc.method+"_"+tc.wantAction, func(t *testing.T) { + action, params, handler, ok := lookupEKSAction(tc.method, tc.path) + require.True(t, ok, "encoded ARN path should match: %s %s", tc.method, tc.path) + require.NotNil(t, handler) + assert.Equal(t, tc.wantAction, action) + assert.Equal(t, []string{"alpha", arn}, params) + }) + } +} + +func TestLookupEKSAction_CoversAllActions(t *testing.T) { expected := map[string]bool{ - "CreateCluster": false, + "CreateCluster": false, + // PublishInternal + WebhookTokenReview are internal control-plane VM + // broker routes, not AWS-SDK EKS actions — registered alongside the + // public actions. + "PublishInternal": false, + "WebhookTokenReview": false, "DescribeCluster": false, "ListClusters": false, "UpdateClusterConfig": false, diff --git a/spinifex/gateway/elbv2.go b/spinifex/gateway/elbv2.go index 4bdf0a3e7..e54e31604 100644 --- a/spinifex/gateway/elbv2.go +++ b/spinifex/gateway/elbv2.go @@ -99,6 +99,12 @@ var elbv2Actions = map[string]ELBv2Handler{ "DescribeTags": elbv2Handler(func(input *elbv2.DescribeTagsInput, gw *GatewayConfig, accountID string) (any, error) { return gateway_elbv2.DescribeTags(input, gw.NATSConn, accountID) }), + "AddTags": elbv2Handler(func(input *elbv2.AddTagsInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.AddTags(input, gw.NATSConn, accountID) + }), + "RemoveTags": elbv2Handler(func(input *elbv2.RemoveTagsInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.RemoveTags(input, gw.NATSConn, accountID) + }), "LBAgentHeartbeat": elbv2Handler(func(input *handlers_elbv2.LBAgentHeartbeatInput, gw *GatewayConfig, accountID string) (any, error) { return gateway_elbv2.LBAgentHeartbeat(input, gw.NATSConn, accountID) }), @@ -120,6 +126,27 @@ var elbv2Actions = map[string]ELBv2Handler{ "DescribeLoadBalancerAttributes": elbv2Handler(func(input *elbv2.DescribeLoadBalancerAttributesInput, gw *GatewayConfig, accountID string) (any, error) { return gateway_elbv2.DescribeLoadBalancerAttributes(input, gw.NATSConn, accountID) }), + "SetSecurityGroups": elbv2Handler(func(input *elbv2.SetSecurityGroupsInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.SetSecurityGroups(input, gw.NATSConn, accountID) + }), + "SetIpAddressType": elbv2Handler(func(input *elbv2.SetIpAddressTypeInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.SetIpAddressType(input, gw.NATSConn, accountID) + }), + "SetSubnets": elbv2Handler(func(input *elbv2.SetSubnetsInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.SetSubnets(input, gw.NATSConn, accountID) + }), + "AddListenerCertificates": elbv2Handler(func(input *elbv2.AddListenerCertificatesInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.AddListenerCertificates(input, gw.NATSConn, accountID) + }), + "RemoveListenerCertificates": elbv2Handler(func(input *elbv2.RemoveListenerCertificatesInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.RemoveListenerCertificates(input, gw.NATSConn, accountID) + }), + "DescribeListenerCertificates": elbv2Handler(func(input *elbv2.DescribeListenerCertificatesInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.DescribeListenerCertificates(input, gw.NATSConn, accountID) + }), + "DescribeSSLPolicies": elbv2Handler(func(input *elbv2.DescribeSSLPoliciesInput, gw *GatewayConfig, accountID string) (any, error) { + return gateway_elbv2.DescribeSSLPolicies(input, gw.NATSConn, accountID) + }), "DescribeListenerAttributes": elbv2Handler(func(input *gateway_elbv2.DescribeListenerAttributesInput, gw *GatewayConfig, accountID string) (any, error) { return gateway_elbv2.DescribeListenerAttributes(input, accountID) }), diff --git a/spinifex/gateway/elbv2/AddListenerCertificates.go b/spinifex/gateway/elbv2/AddListenerCertificates.go new file mode 100644 index 000000000..bec99a5d6 --- /dev/null +++ b/spinifex/gateway/elbv2/AddListenerCertificates.go @@ -0,0 +1,28 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// AddListenerCertificates handles the ELBv2 AddListenerCertificates API call. It +// attaches additional SNI certificates to a secure listener. +func AddListenerCertificates(input *elbv2.AddListenerCertificatesInput, natsConn *nats.Conn, accountID string) (elbv2.AddListenerCertificatesOutput, error) { + var output elbv2.AddListenerCertificatesOutput + + if input == nil || input.ListenerArn == nil || *input.ListenerArn == "" || len(input.Certificates) == 0 { + return output, errors.New(awserrors.ErrorMissingParameter) + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.AddListenerCertificates(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/AddTags.go b/spinifex/gateway/elbv2/AddTags.go new file mode 100644 index 000000000..c58b24d73 --- /dev/null +++ b/spinifex/gateway/elbv2/AddTags.go @@ -0,0 +1,29 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// AddTags handles the ELBv2 AddTags API call. It adds or overwrites the given +// tags on each ELBv2 resource (load balancer, target group, listener, listener +// rule) named in ResourceArns. +func AddTags(input *elbv2.AddTagsInput, natsConn *nats.Conn, accountID string) (elbv2.AddTagsOutput, error) { + var output elbv2.AddTagsOutput + + if input == nil || len(input.ResourceArns) == 0 || len(input.Tags) == 0 { + return output, errors.New(awserrors.ErrorMissingParameter) + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.AddTags(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/DescribeListenerCertificates.go b/spinifex/gateway/elbv2/DescribeListenerCertificates.go new file mode 100644 index 000000000..7913c34b0 --- /dev/null +++ b/spinifex/gateway/elbv2/DescribeListenerCertificates.go @@ -0,0 +1,28 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// DescribeListenerCertificates handles the ELBv2 DescribeListenerCertificates +// API call. It returns the certificates attached to a listener. +func DescribeListenerCertificates(input *elbv2.DescribeListenerCertificatesInput, natsConn *nats.Conn, accountID string) (elbv2.DescribeListenerCertificatesOutput, error) { + var output elbv2.DescribeListenerCertificatesOutput + + if input == nil || input.ListenerArn == nil || *input.ListenerArn == "" { + return output, errors.New(awserrors.ErrorMissingParameter) + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.DescribeListenerCertificates(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/DescribeLoadBalancerAttributes.go b/spinifex/gateway/elbv2/DescribeLoadBalancerAttributes.go deleted file mode 100644 index 0ed9a930f..000000000 --- a/spinifex/gateway/elbv2/DescribeLoadBalancerAttributes.go +++ /dev/null @@ -1,37 +0,0 @@ -package gateway_elbv2 - -import ( - "errors" - - "github.com/aws/aws-sdk-go/service/elbv2" - "github.com/mulgadc/spinifex/spinifex/awserrors" - handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" - "github.com/nats-io/nats.go" -) - -func ValidateDescribeLoadBalancerAttributesInput(input *elbv2.DescribeLoadBalancerAttributesInput) error { - if input == nil { - return errors.New(awserrors.ErrorInvalidParameterValue) - } - if input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { - return errors.New(awserrors.ErrorMissingParameter) - } - return nil -} - -// DescribeLoadBalancerAttributes handles the ELBv2 DescribeLoadBalancerAttributes API call. -func DescribeLoadBalancerAttributes(input *elbv2.DescribeLoadBalancerAttributesInput, natsConn *nats.Conn, accountID string) (elbv2.DescribeLoadBalancerAttributesOutput, error) { - var output elbv2.DescribeLoadBalancerAttributesOutput - - if err := ValidateDescribeLoadBalancerAttributesInput(input); err != nil { - return output, err - } - - svc := handlers_elbv2.NewNATSELBv2Service(natsConn) - result, err := svc.DescribeLoadBalancerAttributes(input, accountID) - if err != nil { - return output, err - } - - return *result, nil -} diff --git a/spinifex/gateway/elbv2/DescribeSSLPolicies.go b/spinifex/gateway/elbv2/DescribeSSLPolicies.go new file mode 100644 index 000000000..43df8fa32 --- /dev/null +++ b/spinifex/gateway/elbv2/DescribeSSLPolicies.go @@ -0,0 +1,21 @@ +package gateway_elbv2 + +import ( + "github.com/aws/aws-sdk-go/service/elbv2" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// DescribeSSLPolicies handles the ELBv2 DescribeSSLPolicies API call. It serves +// the fixed catalog of supported security policies. +func DescribeSSLPolicies(input *elbv2.DescribeSSLPoliciesInput, natsConn *nats.Conn, accountID string) (elbv2.DescribeSSLPoliciesOutput, error) { + var output elbv2.DescribeSSLPoliciesOutput + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.DescribeSSLPolicies(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/DescribeTargetGroupAttributes.go b/spinifex/gateway/elbv2/DescribeTargetGroupAttributes.go deleted file mode 100644 index 2402be09d..000000000 --- a/spinifex/gateway/elbv2/DescribeTargetGroupAttributes.go +++ /dev/null @@ -1,37 +0,0 @@ -package gateway_elbv2 - -import ( - "errors" - - "github.com/aws/aws-sdk-go/service/elbv2" - "github.com/mulgadc/spinifex/spinifex/awserrors" - handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" - "github.com/nats-io/nats.go" -) - -func ValidateDescribeTargetGroupAttributesInput(input *elbv2.DescribeTargetGroupAttributesInput) error { - if input == nil { - return errors.New(awserrors.ErrorInvalidParameterValue) - } - if input.TargetGroupArn == nil || *input.TargetGroupArn == "" { - return errors.New(awserrors.ErrorMissingParameter) - } - return nil -} - -// DescribeTargetGroupAttributes handles the ELBv2 DescribeTargetGroupAttributes API call. -func DescribeTargetGroupAttributes(input *elbv2.DescribeTargetGroupAttributesInput, natsConn *nats.Conn, accountID string) (elbv2.DescribeTargetGroupAttributesOutput, error) { - var output elbv2.DescribeTargetGroupAttributesOutput - - if err := ValidateDescribeTargetGroupAttributesInput(input); err != nil { - return output, err - } - - svc := handlers_elbv2.NewNATSELBv2Service(natsConn) - result, err := svc.DescribeTargetGroupAttributes(input, accountID) - if err != nil { - return output, err - } - - return *result, nil -} diff --git a/spinifex/gateway/elbv2/ModifyLoadBalancerAttributes.go b/spinifex/gateway/elbv2/ModifyLoadBalancerAttributes.go deleted file mode 100644 index 0e57ca24e..000000000 --- a/spinifex/gateway/elbv2/ModifyLoadBalancerAttributes.go +++ /dev/null @@ -1,40 +0,0 @@ -package gateway_elbv2 - -import ( - "errors" - - "github.com/aws/aws-sdk-go/service/elbv2" - "github.com/mulgadc/spinifex/spinifex/awserrors" - handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" - "github.com/nats-io/nats.go" -) - -func ValidateModifyLoadBalancerAttributesInput(input *elbv2.ModifyLoadBalancerAttributesInput) error { - if input == nil { - return errors.New(awserrors.ErrorInvalidParameterValue) - } - if input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { - return errors.New(awserrors.ErrorMissingParameter) - } - if len(input.Attributes) == 0 { - return errors.New(awserrors.ErrorMissingParameter) - } - return nil -} - -// ModifyLoadBalancerAttributes handles the ELBv2 ModifyLoadBalancerAttributes API call. -func ModifyLoadBalancerAttributes(input *elbv2.ModifyLoadBalancerAttributesInput, natsConn *nats.Conn, accountID string) (elbv2.ModifyLoadBalancerAttributesOutput, error) { - var output elbv2.ModifyLoadBalancerAttributesOutput - - if err := ValidateModifyLoadBalancerAttributesInput(input); err != nil { - return output, err - } - - svc := handlers_elbv2.NewNATSELBv2Service(natsConn) - result, err := svc.ModifyLoadBalancerAttributes(input, accountID) - if err != nil { - return output, err - } - - return *result, nil -} diff --git a/spinifex/gateway/elbv2/ModifyTargetGroupAttributes.go b/spinifex/gateway/elbv2/ModifyTargetGroupAttributes.go deleted file mode 100644 index f759608db..000000000 --- a/spinifex/gateway/elbv2/ModifyTargetGroupAttributes.go +++ /dev/null @@ -1,40 +0,0 @@ -package gateway_elbv2 - -import ( - "errors" - - "github.com/aws/aws-sdk-go/service/elbv2" - "github.com/mulgadc/spinifex/spinifex/awserrors" - handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" - "github.com/nats-io/nats.go" -) - -func ValidateModifyTargetGroupAttributesInput(input *elbv2.ModifyTargetGroupAttributesInput) error { - if input == nil { - return errors.New(awserrors.ErrorInvalidParameterValue) - } - if input.TargetGroupArn == nil || *input.TargetGroupArn == "" { - return errors.New(awserrors.ErrorMissingParameter) - } - if len(input.Attributes) == 0 { - return errors.New(awserrors.ErrorMissingParameter) - } - return nil -} - -// ModifyTargetGroupAttributes handles the ELBv2 ModifyTargetGroupAttributes API call. -func ModifyTargetGroupAttributes(input *elbv2.ModifyTargetGroupAttributesInput, natsConn *nats.Conn, accountID string) (elbv2.ModifyTargetGroupAttributesOutput, error) { - var output elbv2.ModifyTargetGroupAttributesOutput - - if err := ValidateModifyTargetGroupAttributesInput(input); err != nil { - return output, err - } - - svc := handlers_elbv2.NewNATSELBv2Service(natsConn) - result, err := svc.ModifyTargetGroupAttributes(input, accountID) - if err != nil { - return output, err - } - - return *result, nil -} diff --git a/spinifex/gateway/elbv2/RemoveListenerCertificates.go b/spinifex/gateway/elbv2/RemoveListenerCertificates.go new file mode 100644 index 000000000..29714ccbd --- /dev/null +++ b/spinifex/gateway/elbv2/RemoveListenerCertificates.go @@ -0,0 +1,28 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// RemoveListenerCertificates handles the ELBv2 RemoveListenerCertificates API +// call. It detaches the named certificates from a listener. +func RemoveListenerCertificates(input *elbv2.RemoveListenerCertificatesInput, natsConn *nats.Conn, accountID string) (elbv2.RemoveListenerCertificatesOutput, error) { + var output elbv2.RemoveListenerCertificatesOutput + + if input == nil || input.ListenerArn == nil || *input.ListenerArn == "" || len(input.Certificates) == 0 { + return output, errors.New(awserrors.ErrorMissingParameter) + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.RemoveListenerCertificates(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/RemoveTags.go b/spinifex/gateway/elbv2/RemoveTags.go new file mode 100644 index 000000000..e40ac1b0d --- /dev/null +++ b/spinifex/gateway/elbv2/RemoveTags.go @@ -0,0 +1,29 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// RemoveTags handles the ELBv2 RemoveTags API call. It removes the given tag +// keys from each ELBv2 resource (load balancer, target group, listener, +// listener rule) named in ResourceArns. Removal is idempotent. +func RemoveTags(input *elbv2.RemoveTagsInput, natsConn *nats.Conn, accountID string) (elbv2.RemoveTagsOutput, error) { + var output elbv2.RemoveTagsOutput + + if input == nil || len(input.ResourceArns) == 0 || len(input.TagKeys) == 0 { + return output, errors.New(awserrors.ErrorMissingParameter) + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.RemoveTags(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/SetIpAddressType.go b/spinifex/gateway/elbv2/SetIpAddressType.go new file mode 100644 index 000000000..87f7a2bd8 --- /dev/null +++ b/spinifex/gateway/elbv2/SetIpAddressType.go @@ -0,0 +1,31 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// SetIpAddressType handles the ELBv2 SetIpAddressType API call. It sets the IP +// address type of the load balancer. +func SetIpAddressType(input *elbv2.SetIpAddressTypeInput, natsConn *nats.Conn, accountID string) (elbv2.SetIpAddressTypeOutput, error) { + var output elbv2.SetIpAddressTypeOutput + + if input == nil || input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { + return output, errors.New(awserrors.ErrorMissingParameter) + } + if input.IpAddressType == nil || *input.IpAddressType == "" { + return output, errors.New(awserrors.ErrorMissingParameter) + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.SetIpAddressType(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/SetSecurityGroups.go b/spinifex/gateway/elbv2/SetSecurityGroups.go new file mode 100644 index 000000000..893f32e2c --- /dev/null +++ b/spinifex/gateway/elbv2/SetSecurityGroups.go @@ -0,0 +1,31 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// SetSecurityGroups handles the ELBv2 SetSecurityGroups API call. It replaces +// the security groups associated with the load balancer. +func SetSecurityGroups(input *elbv2.SetSecurityGroupsInput, natsConn *nats.Conn, accountID string) (elbv2.SetSecurityGroupsOutput, error) { + var output elbv2.SetSecurityGroupsOutput + + if input == nil || input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { + return output, errors.New(awserrors.ErrorMissingParameter) + } + if len(input.SecurityGroups) == 0 { + return output, errors.New(awserrors.ErrorMissingParameter) + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.SetSecurityGroups(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/SetSubnets.go b/spinifex/gateway/elbv2/SetSubnets.go new file mode 100644 index 000000000..039e2b146 --- /dev/null +++ b/spinifex/gateway/elbv2/SetSubnets.go @@ -0,0 +1,32 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// SetSubnets handles the ELBv2 SetSubnets API call. It enables the subnets (and +// their backing ENIs) for the load balancer, adding and removing them to match +// the request. +func SetSubnets(input *elbv2.SetSubnetsInput, natsConn *nats.Conn, accountID string) (elbv2.SetSubnetsOutput, error) { + var output elbv2.SetSubnetsOutput + + if input == nil || input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { + return output, errors.New(awserrors.ErrorMissingParameter) + } + if len(input.Subnets) == 0 && len(input.SubnetMappings) == 0 { + return output, errors.New(awserrors.ErrorMissingParameter) + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + result, err := svc.SetSubnets(input, accountID) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/spinifex/gateway/elbv2/attributes.go b/spinifex/gateway/elbv2/attributes.go new file mode 100644 index 000000000..bd245d3cb --- /dev/null +++ b/spinifex/gateway/elbv2/attributes.go @@ -0,0 +1,106 @@ +package gateway_elbv2 + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// validateAttrArn rejects a nil or empty resource ARN, shared by every +// attribute Modify/Describe validator. +func validateAttrArn(arn *string) error { + if arn == nil || *arn == "" { + return errors.New(awserrors.ErrorMissingParameter) + } + return nil +} + +// attributeOp validates an attribute request and dispatches it to its NATS +// service call, returning a zero output on any error. Shared by all four +// Modify/Describe attribute handlers. +func attributeOp[I, O any](input *I, validate func(*I) error, call func(*I) (*O, error)) (O, error) { + var output O + if err := validate(input); err != nil { + return output, err + } + result, err := call(input) + if err != nil { + return output, err + } + return *result, nil +} + +func ValidateModifyTargetGroupAttributesInput(input *elbv2.ModifyTargetGroupAttributesInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + if err := validateAttrArn(input.TargetGroupArn); err != nil { + return err + } + if len(input.Attributes) == 0 { + return errors.New(awserrors.ErrorMissingParameter) + } + return nil +} + +func ValidateDescribeTargetGroupAttributesInput(input *elbv2.DescribeTargetGroupAttributesInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return validateAttrArn(input.TargetGroupArn) +} + +func ValidateModifyLoadBalancerAttributesInput(input *elbv2.ModifyLoadBalancerAttributesInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + if err := validateAttrArn(input.LoadBalancerArn); err != nil { + return err + } + if len(input.Attributes) == 0 { + return errors.New(awserrors.ErrorMissingParameter) + } + return nil +} + +func ValidateDescribeLoadBalancerAttributesInput(input *elbv2.DescribeLoadBalancerAttributesInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return validateAttrArn(input.LoadBalancerArn) +} + +func ModifyTargetGroupAttributes(input *elbv2.ModifyTargetGroupAttributesInput, natsConn *nats.Conn, accountID string) (elbv2.ModifyTargetGroupAttributesOutput, error) { + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + return attributeOp(input, ValidateModifyTargetGroupAttributesInput, + func(i *elbv2.ModifyTargetGroupAttributesInput) (*elbv2.ModifyTargetGroupAttributesOutput, error) { + return svc.ModifyTargetGroupAttributes(i, accountID) + }) +} + +func DescribeTargetGroupAttributes(input *elbv2.DescribeTargetGroupAttributesInput, natsConn *nats.Conn, accountID string) (elbv2.DescribeTargetGroupAttributesOutput, error) { + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + return attributeOp(input, ValidateDescribeTargetGroupAttributesInput, + func(i *elbv2.DescribeTargetGroupAttributesInput) (*elbv2.DescribeTargetGroupAttributesOutput, error) { + return svc.DescribeTargetGroupAttributes(i, accountID) + }) +} + +func ModifyLoadBalancerAttributes(input *elbv2.ModifyLoadBalancerAttributesInput, natsConn *nats.Conn, accountID string) (elbv2.ModifyLoadBalancerAttributesOutput, error) { + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + return attributeOp(input, ValidateModifyLoadBalancerAttributesInput, + func(i *elbv2.ModifyLoadBalancerAttributesInput) (*elbv2.ModifyLoadBalancerAttributesOutput, error) { + return svc.ModifyLoadBalancerAttributes(i, accountID) + }) +} + +func DescribeLoadBalancerAttributes(input *elbv2.DescribeLoadBalancerAttributesInput, natsConn *nats.Conn, accountID string) (elbv2.DescribeLoadBalancerAttributesOutput, error) { + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + return attributeOp(input, ValidateDescribeLoadBalancerAttributesInput, + func(i *elbv2.DescribeLoadBalancerAttributesInput) (*elbv2.DescribeLoadBalancerAttributesOutput, error) { + return svc.DescribeLoadBalancerAttributes(i, accountID) + }) +} diff --git a/spinifex/gateway/elbv2/listener_certs_test.go b/spinifex/gateway/elbv2/listener_certs_test.go new file mode 100644 index 000000000..a3c9abe95 --- /dev/null +++ b/spinifex/gateway/elbv2/listener_certs_test.go @@ -0,0 +1,100 @@ +package gateway_elbv2 + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const testListenerArn = "arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/lb/abc/def" +const testCertArn = "arn:aws:acm:us-east-1:123456789012:certificate/aaaa-1111" + +// Validation guards (no NATS call reached). + +func TestAddListenerCertificates_NilInput(t *testing.T) { + _, err := AddListenerCertificates(nil, nil, "123456789012") + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestAddListenerCertificates_MissingArn(t *testing.T) { + _, err := AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + }, nil, "123456789012") + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestAddListenerCertificates_MissingCerts(t *testing.T) { + _, err := AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + ListenerArn: aws.String(testListenerArn), + }, nil, "123456789012") + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestRemoveListenerCertificates_NilInput(t *testing.T) { + _, err := RemoveListenerCertificates(nil, nil, "123456789012") + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestRemoveListenerCertificates_MissingArn(t *testing.T) { + _, err := RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{ + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + }, nil, "123456789012") + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestRemoveListenerCertificates_MissingCerts(t *testing.T) { + _, err := RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{ + ListenerArn: aws.String(testListenerArn), + }, nil, "123456789012") + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestDescribeListenerCertificates_NilInput(t *testing.T) { + _, err := DescribeListenerCertificates(nil, nil, "123456789012") + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +func TestDescribeListenerCertificates_MissingArn(t *testing.T) { + _, err := DescribeListenerCertificates(&elbv2.DescribeListenerCertificatesInput{}, nil, "123456789012") + assert.EqualError(t, err, awserrors.ErrorMissingParameter) +} + +// Delegation paths: a valid input with no daemon listening fast-fails on the +// NATS request, exercising the svc.X call + error return in each gateway op. + +func TestAddListenerCertificates_DelegatesNoResponder(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + _, err := AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + ListenerArn: aws.String(testListenerArn), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + }, nc, "123456789012") + require.Error(t, err) +} + +func TestRemoveListenerCertificates_DelegatesNoResponder(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + _, err := RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{ + ListenerArn: aws.String(testListenerArn), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + }, nc, "123456789012") + require.Error(t, err) +} + +func TestDescribeListenerCertificates_DelegatesNoResponder(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + _, err := DescribeListenerCertificates(&elbv2.DescribeListenerCertificatesInput{ + ListenerArn: aws.String(testListenerArn), + }, nc, "123456789012") + require.Error(t, err) +} + +func TestDescribeSSLPolicies_DelegatesNoResponder(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + _, err := DescribeSSLPolicies(&elbv2.DescribeSSLPoliciesInput{}, nc, "123456789012") + require.Error(t, err) +} diff --git a/spinifex/gateway/elbv2_test.go b/spinifex/gateway/elbv2_test.go index ad81cab0f..6af333fda 100644 --- a/spinifex/gateway/elbv2_test.go +++ b/spinifex/gateway/elbv2_test.go @@ -52,6 +52,11 @@ func TestELBv2ActionsMap_AllActionsRegistered(t *testing.T) { "ModifyListener", "DescribeListeners", "DescribeTags", + "AddTags", + "RemoveTags", + "SetSecurityGroups", + "SetIpAddressType", + "SetSubnets", "LBAgentHeartbeat", "GetLBConfig", "ModifyTargetGroupAttributes", @@ -65,6 +70,10 @@ func TestELBv2ActionsMap_AllActionsRegistered(t *testing.T) { "DeleteRule", "DescribeRules", "SetRulePriorities", + "AddListenerCertificates", + "RemoveListenerCertificates", + "DescribeListenerCertificates", + "DescribeSSLPolicies", } for _, action := range expectedActions { diff --git a/spinifex/gateway/gateway.go b/spinifex/gateway/gateway.go index 0ae19d4de..689acc8ad 100644 --- a/spinifex/gateway/gateway.go +++ b/spinifex/gateway/gateway.go @@ -75,6 +75,8 @@ var supportedServices = map[string]bool{ "account": true, "elasticloadbalancing": true, "eks": true, + "acm": true, + "tagging": true, "spinifex": true, } @@ -134,19 +136,37 @@ func (gw *GatewayConfig) SetupRoutes() http.Handler { r.Use(slogRequestLogger) } - // AWS SigV4 authentication middleware - r.Use(gw.SigV4AuthMiddleware()) + // Anonymous STS bootstrap (AssumeRoleWithWebIdentity) is dispatched here, + // ahead of the SigV4 surface — these calls hold no AWS credentials, only a + // web-identity JWT in the body. Signed requests pass straight through. + r.Use(gw.anonymousSTSInterceptor) - // API request throttling (post-auth, per-account+action token bucket) - if gw.Throttler != nil { - r.Use(gw.Throttler.Middleware( - gw.throttleKeyFuncs(), - gw.writeThrottleError, - )) - } + // Public, unauthenticated OIDC discovery endpoints (IRSA). They serve the + // per-cluster issuer discovery document + JWKS so AWS STS / SDKs can + // validate ServiceAccount tokens. These carry no SigV4 and must bypass the + // auth + throttle middleware, so they live in their own group registered + // ahead of the authenticated surface. + r.Group(func(pub chi.Router) { + pub.Get("/oidc/eks/{region}/{accountID}/{clusterName}/.well-known/openid-configuration", gw.OIDCDiscoveryDocument) + pub.Get("/oidc/eks/{region}/{accountID}/{clusterName}/keys", gw.OIDCJWKS) + }) - // Catch-all routes - r.HandleFunc("/*", gw.Request) + // Authenticated AWS API surface. + r.Group(func(auth chi.Router) { + // AWS SigV4 authentication middleware + auth.Use(gw.SigV4AuthMiddleware()) + + // API request throttling (post-auth, per-account+action token bucket) + if gw.Throttler != nil { + auth.Use(gw.Throttler.Middleware( + gw.throttleKeyFuncs(), + gw.writeThrottleError, + )) + } + + // Catch-all routes + auth.HandleFunc("/*", gw.Request) + }) return r } @@ -305,6 +325,10 @@ func (gw *GatewayConfig) Request(w http.ResponseWriter, r *http.Request) { err = gw.ELBv2_Request(w, r) case "eks": err = gw.EKS_Request(w, r) + case "acm": + err = gw.ACM_Request(w, r) + case "tagging": + err = gw.Tagging_Request(w, r) case "spinifex": err = gw.Spinifex_Request(w, r) default: @@ -457,9 +481,12 @@ func (gw *GatewayConfig) ErrorHandler(w http.ResponseWriter, r *http.Request, er errorMsg.HTTPCode = 500 } - if svc == "eks" { + // EKS, ACM, and the Resource Groups Tagging API all use the AWS JSON 1.1 + // error envelope ({"__type":"Exception","message":...}, Content-Type + // application/x-amz-json-1.1). Query/XML services fall through below. + if svc == "eks" || svc == "acm" || svc == "tagging" { body := GenerateEKSErrorResponse(err.Error(), errorMsg.Message, requestId) - slog.Debug("Generated EKS error response", "error", err.Error(), "json", string(body), "requestId", requestId) + slog.Debug("Generated JSON error response", "service", svc, "error", err.Error(), "json", string(body), "requestId", requestId) w.Header().Set("Content-Type", eksJSONContentType) w.WriteHeader(errorMsg.HTTPCode) if _, err := w.Write(body); err != nil { diff --git a/spinifex/gateway/gateway_test.go b/spinifex/gateway/gateway_test.go index 2bb22bc78..445e7979e 100644 --- a/spinifex/gateway/gateway_test.go +++ b/spinifex/gateway/gateway_test.go @@ -508,6 +508,11 @@ func TestGetService(t *testing.T) { ctxVal: "account", wantSvc: "account", }, + { + name: "tagging service", + ctxVal: "tagging", + wantSvc: "tagging", + }, } for _, tc := range tests { diff --git a/spinifex/gateway/iam.go b/spinifex/gateway/iam.go index 5cb344834..444035d0f 100644 --- a/spinifex/gateway/iam.go +++ b/spinifex/gateway/iam.go @@ -154,6 +154,20 @@ var iamActions = map[string]IAMHandler{ "RemoveRoleFromInstanceProfile": iamHandler(func(accountID string, input *iam.RemoveRoleFromInstanceProfileInput, gw *GatewayConfig) (any, error) { return gateway_iam.RemoveRoleFromInstanceProfile(accountID, input, gw.IAMService) }), + + // OIDC identity-provider registry (IRSA) + "CreateOpenIDConnectProvider": iamHandler(func(accountID string, input *iam.CreateOpenIDConnectProviderInput, gw *GatewayConfig) (any, error) { + return gateway_iam.CreateOpenIDConnectProvider(accountID, input, gw.IAMService) + }), + "GetOpenIDConnectProvider": iamHandler(func(accountID string, input *iam.GetOpenIDConnectProviderInput, gw *GatewayConfig) (any, error) { + return gateway_iam.GetOpenIDConnectProvider(accountID, input, gw.IAMService) + }), + "ListOpenIDConnectProviders": iamHandler(func(accountID string, input *iam.ListOpenIDConnectProvidersInput, gw *GatewayConfig) (any, error) { + return gateway_iam.ListOpenIDConnectProviders(accountID, input, gw.IAMService) + }), + "DeleteOpenIDConnectProvider": iamHandler(func(accountID string, input *iam.DeleteOpenIDConnectProviderInput, gw *GatewayConfig) (any, error) { + return gateway_iam.DeleteOpenIDConnectProvider(accountID, input, gw.IAMService) + }), } func (gw *GatewayConfig) IAM_Request(w http.ResponseWriter, r *http.Request) error { diff --git a/spinifex/gateway/iam/oidc_provider.go b/spinifex/gateway/iam/oidc_provider.go new file mode 100644 index 000000000..e57310c63 --- /dev/null +++ b/spinifex/gateway/iam/oidc_provider.go @@ -0,0 +1,34 @@ +package gateway_iam + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/iam" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" +) + +func CreateOpenIDConnectProvider(accountID string, input *iam.CreateOpenIDConnectProviderInput, svc handlers_iam.IAMService) (*iam.CreateOpenIDConnectProviderOutput, error) { + if input.Url == nil || *input.Url == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + return svc.CreateOpenIDConnectProvider(accountID, input) +} + +func GetOpenIDConnectProvider(accountID string, input *iam.GetOpenIDConnectProviderInput, svc handlers_iam.IAMService) (*iam.GetOpenIDConnectProviderOutput, error) { + if input.OpenIDConnectProviderArn == nil || *input.OpenIDConnectProviderArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + return svc.GetOpenIDConnectProvider(accountID, input) +} + +func ListOpenIDConnectProviders(accountID string, input *iam.ListOpenIDConnectProvidersInput, svc handlers_iam.IAMService) (*iam.ListOpenIDConnectProvidersOutput, error) { + return svc.ListOpenIDConnectProviders(accountID, input) +} + +func DeleteOpenIDConnectProvider(accountID string, input *iam.DeleteOpenIDConnectProviderInput, svc handlers_iam.IAMService) (*iam.DeleteOpenIDConnectProviderOutput, error) { + if input.OpenIDConnectProviderArn == nil || *input.OpenIDConnectProviderArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + return svc.DeleteOpenIDConnectProvider(accountID, input) +} diff --git a/spinifex/gateway/iam/oidc_provider_mock_test.go b/spinifex/gateway/iam/oidc_provider_mock_test.go new file mode 100644 index 000000000..c08575a62 --- /dev/null +++ b/spinifex/gateway/iam/oidc_provider_mock_test.go @@ -0,0 +1,19 @@ +package gateway_iam + +import "github.com/aws/aws-sdk-go/service/iam" + +// OIDC identity-provider registry stubs so stubIAMService satisfies the +// IAMService interface. Dedicated CRUD coverage lives in handlers/iam. + +func (s *stubIAMService) CreateOpenIDConnectProvider(string, *iam.CreateOpenIDConnectProviderInput) (*iam.CreateOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (s *stubIAMService) GetOpenIDConnectProvider(string, *iam.GetOpenIDConnectProviderInput) (*iam.GetOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (s *stubIAMService) ListOpenIDConnectProviders(string, *iam.ListOpenIDConnectProvidersInput) (*iam.ListOpenIDConnectProvidersOutput, error) { + return nil, nil +} +func (s *stubIAMService) DeleteOpenIDConnectProvider(string, *iam.DeleteOpenIDConnectProviderInput) (*iam.DeleteOpenIDConnectProviderOutput, error) { + return nil, nil +} diff --git a/spinifex/gateway/oidc_discovery.go b/spinifex/gateway/oidc_discovery.go new file mode 100644 index 000000000..c7c5ec598 --- /dev/null +++ b/spinifex/gateway/oidc_discovery.go @@ -0,0 +1,116 @@ +package gateway + +import ( + "encoding/json" + "errors" + "log/slog" + "net/http" + + "github.com/go-chi/chi/v5" + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + "github.com/nats-io/nats.go" +) + +// oidcDiscoveryDocument is the subset of the OpenID Connect Discovery metadata +// (RFC 8414) that AWS STS and the AWS SDKs consume to validate IRSA +// ServiceAccount tokens. Served unauthenticated at the per-cluster issuer URL. +type oidcDiscoveryDocument struct { + Issuer string `json:"issuer"` + JWKSURI string `json:"jwks_uri"` + ResponseTypesSupported []string `json:"response_types_supported"` + SubjectTypesSupported []string `json:"subject_types_supported"` + IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"` + ClaimsSupported []string `json:"claims_supported"` +} + +// eksAccountKV opens the read-only per-account EKS bucket. Returns +// nats.ErrBucketNotFound (unwrapped) when the account has no clusters, which +// the OIDC handlers translate to 404. +func (gw *GatewayConfig) eksAccountKV(accountID string) (nats.KeyValue, error) { + if gw.NATSConn == nil { + return nil, errors.New("gateway NATS connection not initialised") + } + js, err := gw.NATSConn.JetStream() + if err != nil { + return nil, err + } + return js.KeyValue(handlers_eks.AccountBucketName(accountID)) +} + +// OIDCDiscoveryDocument serves the cluster's OpenID discovery document. The +// `issuer` it returns is the cluster's stored IssuerURL — byte-identical to the +// apiserver's `iss` claim — so an AWS SDK that fetches this document while +// registering the provider sees a matching issuer. +func (gw *GatewayConfig) OIDCDiscoveryDocument(w http.ResponseWriter, r *http.Request) { + accountID := chi.URLParam(r, "accountID") + clusterName := chi.URLParam(r, "clusterName") + + kv, err := gw.eksAccountKV(accountID) + if err != nil { + if errors.Is(err, nats.ErrBucketNotFound) { + http.NotFound(w, r) + return + } + slog.Error("OIDC discovery: open account bucket", "accountID", accountID, "err", err) + http.Error(w, "internal error", http.StatusInternalServerError) + return + } + + meta, err := handlers_eks.GetClusterMeta(kv, clusterName) + if err != nil || meta == nil || meta.OIDCIssuer == "" { + http.NotFound(w, r) + return + } + + gw.writeOIDCJSON(w, oidcDiscoveryDocument{ + Issuer: meta.OIDCIssuer, + JWKSURI: meta.OIDCIssuer + "/keys", + ResponseTypesSupported: []string{"id_token"}, + SubjectTypesSupported: []string{"public"}, + IDTokenSigningAlgValuesSupported: []string{"ES256"}, + ClaimsSupported: []string{"sub", "iss", "aud", "exp", "iat"}, + }) +} + +// OIDCJWKS serves the cluster's JWKS document (the public verification key) as +// referenced by the discovery document's jwks_uri. +func (gw *GatewayConfig) OIDCJWKS(w http.ResponseWriter, r *http.Request) { + accountID := chi.URLParam(r, "accountID") + clusterName := chi.URLParam(r, "clusterName") + + kv, err := gw.eksAccountKV(accountID) + if err != nil { + if errors.Is(err, nats.ErrBucketNotFound) { + http.NotFound(w, r) + return + } + slog.Error("OIDC JWKS: open account bucket", "accountID", accountID, "err", err) + http.Error(w, "internal error", http.StatusInternalServerError) + return + } + + entry, err := kv.Get(handlers_eks.OIDCJWKSKey(clusterName)) + if err != nil { + http.NotFound(w, r) + return + } + + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Cache-Control", "public, max-age=300") + if _, err := w.Write(entry.Value()); err != nil { + slog.Debug("OIDC JWKS: write response", "err", err) + } +} + +func (gw *GatewayConfig) writeOIDCJSON(w http.ResponseWriter, v any) { + body, err := json.Marshal(v) + if err != nil { + http.Error(w, "internal error", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Cache-Control", "public, max-age=300") + if _, err := w.Write(body); err != nil { + slog.Debug("OIDC discovery: write response", "err", err) + } +} diff --git a/spinifex/gateway/oidc_discovery_test.go b/spinifex/gateway/oidc_discovery_test.go new file mode 100644 index 000000000..a8203ed81 --- /dev/null +++ b/spinifex/gateway/oidc_discovery_test.go @@ -0,0 +1,123 @@ +package gateway + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + "github.com/mulgadc/spinifex/spinifex/testutil" +) + +const ( + testDiscAccount = "000000000001" + testDiscCluster = "h1" + testDiscIssuer = "https://10.0.0.1:9999/oidc/eks/ap-southeast-2/000000000001/h1" +) + +// seedDiscoveryCluster writes a ClusterMeta (with OIDCIssuer) and a JWKS doc +// into the per-account EKS bucket, returning a router wired to the same NATS. +func seedDiscoveryCluster(t *testing.T) http.Handler { + t.Helper() + _, nc, js := testutil.StartTestJetStream(t) + + kv, err := handlers_eks.GetOrCreateAccountBucket(js, testDiscAccount) + if err != nil { + t.Fatalf("account bucket: %v", err) + } + if err := handlers_eks.PutClusterMeta(kv, &handlers_eks.ClusterMeta{ + Name: testDiscCluster, + Status: handlers_eks.ClusterStatusActive, + OIDCIssuer: testDiscIssuer, + }); err != nil { + t.Fatalf("put cluster meta: %v", err) + } + if _, err := kv.Put(handlers_eks.OIDCJWKSKey(testDiscCluster), []byte(`{"keys":[{"kty":"EC","kid":"abc"}]}`)); err != nil { + t.Fatalf("put jwks: %v", err) + } + + gw := &GatewayConfig{DisableLogging: true, NATSConn: nc} + return gw.SetupRoutes() +} + +func TestOIDCDiscoveryDocument(t *testing.T) { + h := seedDiscoveryCluster(t) + + req := httptest.NewRequest(http.MethodGet, + "/oidc/eks/ap-southeast-2/"+testDiscAccount+"/"+testDiscCluster+"/.well-known/openid-configuration", nil) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, body=%s", rec.Code, rec.Body.String()) + } + var doc oidcDiscoveryDocument + if err := json.Unmarshal(rec.Body.Bytes(), &doc); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if doc.Issuer != testDiscIssuer { + t.Fatalf("issuer = %q, want %q", doc.Issuer, testDiscIssuer) + } + if doc.JWKSURI != testDiscIssuer+"/keys" { + t.Fatalf("jwks_uri = %q, want issuer+/keys", doc.JWKSURI) + } + if len(doc.IDTokenSigningAlgValuesSupported) != 1 || doc.IDTokenSigningAlgValuesSupported[0] != "ES256" { + t.Fatalf("alg = %v, want [ES256]", doc.IDTokenSigningAlgValuesSupported) + } +} + +func TestOIDCJWKS(t *testing.T) { + h := seedDiscoveryCluster(t) + + req := httptest.NewRequest(http.MethodGet, + "/oidc/eks/ap-southeast-2/"+testDiscAccount+"/"+testDiscCluster+"/keys", nil) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d", rec.Code) + } + if ct := rec.Header().Get("Content-Type"); ct != "application/json" { + t.Fatalf("content-type = %q", ct) + } + var jwks struct { + Keys []map[string]string `json:"keys"` + } + if err := json.Unmarshal(rec.Body.Bytes(), &jwks); err != nil { + t.Fatalf("unmarshal jwks: %v", err) + } + if len(jwks.Keys) != 1 || jwks.Keys[0]["kid"] != "abc" { + t.Fatalf("jwks = %s", rec.Body.String()) + } +} + +func TestOIDCDiscovery_UnknownCluster404(t *testing.T) { + h := seedDiscoveryCluster(t) + + for _, path := range []string{ + "/oidc/eks/ap-southeast-2/" + testDiscAccount + "/nope/.well-known/openid-configuration", + "/oidc/eks/ap-southeast-2/" + testDiscAccount + "/nope/keys", + "/oidc/eks/ap-southeast-2/999999999999/h1/keys", + } { + req := httptest.NewRequest(http.MethodGet, path, nil) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + if rec.Code != http.StatusNotFound { + t.Fatalf("path %s: status = %d, want 404", path, rec.Code) + } + } +} + +// TestOIDCDiscovery_NoAuth confirms the discovery routes bypass SigV4 — they +// return 200 with no Authorization header, unlike the authenticated surface. +func TestOIDCDiscovery_NoAuth(t *testing.T) { + h := seedDiscoveryCluster(t) + req := httptest.NewRequest(http.MethodGet, + "/oidc/eks/ap-southeast-2/"+testDiscAccount+"/"+testDiscCluster+"/keys", nil) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("unauthenticated discovery status = %d, want 200", rec.Code) + } +} diff --git a/spinifex/gateway/oidc_provider_mock_test.go b/spinifex/gateway/oidc_provider_mock_test.go new file mode 100644 index 000000000..404926706 --- /dev/null +++ b/spinifex/gateway/oidc_provider_mock_test.go @@ -0,0 +1,33 @@ +package gateway + +import "github.com/aws/aws-sdk-go/service/iam" + +// OIDC identity-provider registry methods — IRSA stubs so the mock IAM +// services satisfy the IAMService interface. No gateway test exercises these +// paths directly (covered by handlers/iam unit tests). + +func (m *mockIAMService) CreateOpenIDConnectProvider(string, *iam.CreateOpenIDConnectProviderInput) (*iam.CreateOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (m *mockIAMService) GetOpenIDConnectProvider(string, *iam.GetOpenIDConnectProviderInput) (*iam.GetOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (m *mockIAMService) ListOpenIDConnectProviders(string, *iam.ListOpenIDConnectProvidersInput) (*iam.ListOpenIDConnectProvidersOutput, error) { + return nil, nil +} +func (m *mockIAMService) DeleteOpenIDConnectProvider(string, *iam.DeleteOpenIDConnectProviderInput) (*iam.DeleteOpenIDConnectProviderOutput, error) { + return nil, nil +} + +func (m *flexMockIAMService) CreateOpenIDConnectProvider(string, *iam.CreateOpenIDConnectProviderInput) (*iam.CreateOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (m *flexMockIAMService) GetOpenIDConnectProvider(string, *iam.GetOpenIDConnectProviderInput) (*iam.GetOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (m *flexMockIAMService) ListOpenIDConnectProviders(string, *iam.ListOpenIDConnectProvidersInput) (*iam.ListOpenIDConnectProvidersOutput, error) { + return nil, nil +} +func (m *flexMockIAMService) DeleteOpenIDConnectProvider(string, *iam.DeleteOpenIDConnectProviderInput) (*iam.DeleteOpenIDConnectProviderOutput, error) { + return nil, nil +} diff --git a/spinifex/gateway/ratelimit.go b/spinifex/gateway/ratelimit.go index 42155715c..05b40aa50 100644 --- a/spinifex/gateway/ratelimit.go +++ b/spinifex/gateway/ratelimit.go @@ -2,8 +2,6 @@ package gateway import ( "log/slog" - "net" - "net/http" "sync" "time" @@ -187,17 +185,6 @@ func (rl *AuthRateLimiter) cleanup() { } } -// extractClientIP returns the IP address from the request's RemoteAddr, -// stripping the port. Handles both IPv4 and IPv6. -func extractClientIP(r *http.Request) string { - host, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - // RemoteAddr might already be bare IP (no port). - return r.RemoteAddr - } - return host -} - // pruneOldFailures returns only the failures within the sliding window. func pruneOldFailures(failures []time.Time, now time.Time) []time.Time { cutoff := now.Add(-failureWindow) diff --git a/spinifex/gateway/ratelimit_test.go b/spinifex/gateway/ratelimit_test.go index 9ce9e9ecf..4ae068e10 100644 --- a/spinifex/gateway/ratelimit_test.go +++ b/spinifex/gateway/ratelimit_test.go @@ -311,29 +311,6 @@ func TestConcurrentAccess(t *testing.T) { wg.Wait() } -func TestExtractClientIP(t *testing.T) { - tests := []struct { - name string - remoteAddr string - expected string - }{ - {"IPv4 with port", "192.168.1.1:12345", "192.168.1.1"}, - {"IPv6 with port", "[::1]:12345", "::1"}, - {"IPv4 bare", "192.168.1.1", "192.168.1.1"}, - {"IPv6 full with port", "[2001:db8::1]:443", "2001:db8::1"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := &http.Request{RemoteAddr: tt.remoteAddr} - got := extractClientIP(r) - if got != tt.expected { - t.Errorf("extractClientIP(%q) = %q, want %q", tt.remoteAddr, got, tt.expected) - } - }) - } -} - // setupTestAppWithRateLimiter creates a test HTTP handler with SigV4 auth and // the given rate limiter attached. A real NATS test connection is attached so // the SigV4 middleware's cluster-unavailable short-circuit (mulga-siv-23) does diff --git a/spinifex/gateway/sts.go b/spinifex/gateway/sts.go index a29e79c8a..5ec599f11 100644 --- a/spinifex/gateway/sts.go +++ b/spinifex/gateway/sts.go @@ -58,19 +58,20 @@ var stsActions = map[string]STSHandler{ "AssumeRole": stsHandler(func(c stsCaller, input *sts.AssumeRoleInput, gw *GatewayConfig) (any, error) { return gateway_sts.AssumeRole(c.accountID, c.arn, c.identity, input, gw.STSService) }), - // TODO: AssumeRoleWithWebIdentity is anonymous on AWS — the caller is - // authenticated by the JWT body, not by a SigV4 envelope. The current - // dispatch path runs SigV4 first, then enters this handler with a synthetic - // stsCaller. That works for in-cluster callers that already hold SigV4 - // credentials but rejects the true anonymous code path (no AWS creds, only - // a projected ServiceAccount token). The AWS gateway mux needs a pre-auth - // route for this action so the JWT can be the sole authenticator. + // AssumeRoleWithWebIdentity is anonymous on AWS — the caller is authenticated + // by the JWT body, not by a SigV4 envelope. anonymousSTSInterceptor routes it + // to this dispatcher ahead of the SigV4 middleware, so STS_Request enters with + // a zero stsCaller (see anonymousSTSActions); the handler ignores it and + // gates the request on the token + the role's web-identity trust policy. "AssumeRoleWithWebIdentity": stsHandler(func(_ stsCaller, input *sts.AssumeRoleWithWebIdentityInput, gw *GatewayConfig) (any, error) { return gateway_sts.AssumeRoleWithWebIdentity(input, gw.STSService) }), "GetCallerIdentity": stsHandler(func(c stsCaller, input *sts.GetCallerIdentityInput, gw *GatewayConfig) (any, error) { return gateway_sts.GetCallerIdentity(c.accountID, c.arn, c.principalType, c.identity, c.assumedRoleID, input, gw.IAMService, gw.STSService) }), + "GetSessionToken": stsHandler(func(c stsCaller, input *sts.GetSessionTokenInput, gw *GatewayConfig) (any, error) { + return gateway_sts.GetSessionToken(c.accountID, c.identity, c.principalType, c.accessKey, input, gw.STSService) + }), } // stsSkipPolicyCheck lists the actions whose authorization is NOT gated by an @@ -78,11 +79,24 @@ var stsActions = map[string]STSHandler{ // trust policy (evaluated inside the handler); GetCallerIdentity is always // allowed per AWS so SDK init flows do not break; // AssumeRoleWithWebIdentity is anonymous — caller is identified by the JWT, -// not by SigV4 — and trust-policy-gated inside the handler. +// not by SigV4 — and trust-policy-gated inside the handler. GetSessionToken is +// allowed for any authenticated caller (consistent with GetCallerIdentity so +// SDK/console init flows are not blocked) and enforces its user-only +// constraint inside the handler — assumed-role callers are denied by principal +// type, session (ASIA) callers by their access-key prefix. var stsSkipPolicyCheck = map[string]bool{ "AssumeRole": true, "AssumeRoleWithWebIdentity": true, "GetCallerIdentity": true, + "GetSessionToken": true, +} + +// anonymousSTSActions lists STS actions AWS accepts without SigV4: the caller +// holds no AWS credentials and is authenticated solely by a web-identity JWT in +// the request body. anonymousSTSInterceptor routes these ahead of the SigV4 +// middleware, and STS_Request skips SigV4-caller resolution for them. +var anonymousSTSActions = map[string]bool{ + "AssumeRoleWithWebIdentity": true, } func (gw *GatewayConfig) STS_Request(w http.ResponseWriter, r *http.Request) error { @@ -113,9 +127,14 @@ func (gw *GatewayConfig) STS_Request(w http.ResponseWriter, r *http.Request) err } } - caller, err := gw.resolveSTSCaller(r) - if err != nil { - return err + // Anonymous actions (AssumeRoleWithWebIdentity) carry no SigV4 envelope, so + // there is no caller to resolve — the handler ignores it. + var caller stsCaller + if !anonymousSTSActions[action] { + caller, err = gw.resolveSTSCaller(r) + if err != nil { + return err + } } xmlOutput, err := handler(action, queryArgs, gw, caller) diff --git a/spinifex/gateway/sts/GetSessionToken.go b/spinifex/gateway/sts/GetSessionToken.go new file mode 100644 index 000000000..b3983d49f --- /dev/null +++ b/spinifex/gateway/sts/GetSessionToken.go @@ -0,0 +1,14 @@ +package gateway_sts + +import ( + "github.com/aws/aws-sdk-go/service/sts" + handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" +) + +// GetSessionToken delegates to the STSService. Like GetCallerIdentity it is not +// gated by checkPolicy at the dispatcher (see stsSkipPolicyCheck); the handler +// holds the authoritative user-only check. Caller fields come from the SigV4 +// context: callerUserName is c.identity, callerAccessKeyID is c.accessKey. +func GetSessionToken(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID string, input *sts.GetSessionTokenInput, svc handlers_sts.STSService) (*sts.GetSessionTokenOutput, error) { + return svc.GetSessionToken(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID, input) +} diff --git a/spinifex/gateway/sts/handler_test.go b/spinifex/gateway/sts/handler_test.go index 2eda64bab..c038d0c1f 100644 --- a/spinifex/gateway/sts/handler_test.go +++ b/spinifex/gateway/sts/handler_test.go @@ -20,6 +20,7 @@ import ( type stubSTSService struct { assumeRoleFn func(callerAccountID, callerARN, callerIdentity string, input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) getCallerIdentityFn func(callerAccountID, callerARN, callerUserID string, input *sts.GetCallerIdentityInput) (*sts.GetCallerIdentityOutput, error) + getSessionTokenFn func(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID string, input *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) lookupSessionFn func(akid string) (*handlers_sts.SessionCredential, error) } @@ -32,6 +33,13 @@ func (s *stubSTSService) AssumeRole(callerAccountID, callerARN, callerIdentity s return &sts.AssumeRoleOutput{}, nil } +// AssumeRoleForInstance is the in-process IMDS entry point and is never +// dispatched over the HTTPS gateway; the stub exists only to satisfy the +// interface. +func (s *stubSTSService) AssumeRoleForInstance(_, _, _ string, _ int64) (*sts.AssumeRoleOutput, error) { + return &sts.AssumeRoleOutput{}, nil +} + func (s *stubSTSService) GetCallerIdentity(callerAccountID, callerARN, callerUserID string, input *sts.GetCallerIdentityInput) (*sts.GetCallerIdentityOutput, error) { if s.getCallerIdentityFn != nil { return s.getCallerIdentityFn(callerAccountID, callerARN, callerUserID, input) @@ -43,6 +51,13 @@ func (s *stubSTSService) GetCallerIdentity(callerAccountID, callerARN, callerUse }, nil } +func (s *stubSTSService) GetSessionToken(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID string, input *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) { + if s.getSessionTokenFn != nil { + return s.getSessionTokenFn(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID, input) + } + return &sts.GetSessionTokenOutput{}, nil +} + func (s *stubSTSService) LookupSessionCredential(akid string) (*handlers_sts.SessionCredential, error) { if s.lookupSessionFn != nil { return s.lookupSessionFn(akid) @@ -421,3 +436,45 @@ func TestGetCallerIdentity_UnknownPrincipalType(t *testing.T) { require.Error(t, err) assert.Equal(t, awserrors.ErrorInternalError, err.Error()) } + +func TestGetSessionToken_ForwardsCallerFields(t *testing.T) { + var got struct { + accountID string + userName string + principalType string + accessKeyID string + } + svc := &stubSTSService{ + getSessionTokenFn: func(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID string, _ *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) { + got.accountID = callerAccountID + got.userName = callerUserName + got.principalType = callerPrincipalType + got.accessKeyID = callerAccessKeyID + return &sts.GetSessionTokenOutput{ + Credentials: &sts.Credentials{AccessKeyId: aws.String("ASIAEXAMPLE")}, + }, nil + }, + } + // The wrapper's job is to forward c.identity as the user name, plus + // c.principalType and c.accessKey, through to the service so the handler can + // enforce its user-only / no-session rule; assert that mapping holds. + out, err := GetSessionToken("000000000000", "alice", PrincipalTypeUser, "AKIAEXAMPLE", &sts.GetSessionTokenInput{}, svc) + require.NoError(t, err) + require.NotNil(t, out) + assert.Equal(t, "ASIAEXAMPLE", aws.StringValue(out.Credentials.AccessKeyId)) + assert.Equal(t, "000000000000", got.accountID) + assert.Equal(t, "alice", got.userName) + assert.Equal(t, PrincipalTypeUser, got.principalType) + assert.Equal(t, "AKIAEXAMPLE", got.accessKeyID) +} + +func TestGetSessionToken_PropagatesServiceError(t *testing.T) { + svc := &stubSTSService{ + getSessionTokenFn: func(string, string, string, string, *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) { + return nil, errors.New(awserrors.ErrorAccessDenied) + }, + } + _, err := GetSessionToken("000000000000", "s1", PrincipalTypeAssumedRole, "ASIAEXAMPLE", &sts.GetSessionTokenInput{}, svc) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorAccessDenied, err.Error()) +} diff --git a/spinifex/gateway/sts/oidc_provider_mock_test.go b/spinifex/gateway/sts/oidc_provider_mock_test.go new file mode 100644 index 000000000..f42503d02 --- /dev/null +++ b/spinifex/gateway/sts/oidc_provider_mock_test.go @@ -0,0 +1,19 @@ +package gateway_sts + +import "github.com/aws/aws-sdk-go/service/iam" + +// OIDC identity-provider registry stubs so stubIAMService satisfies the +// IAMService interface; STS gateway tests never call these. + +func (s *stubIAMService) CreateOpenIDConnectProvider(string, *iam.CreateOpenIDConnectProviderInput) (*iam.CreateOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (s *stubIAMService) GetOpenIDConnectProvider(string, *iam.GetOpenIDConnectProviderInput) (*iam.GetOpenIDConnectProviderOutput, error) { + return nil, nil +} +func (s *stubIAMService) ListOpenIDConnectProviders(string, *iam.ListOpenIDConnectProvidersInput) (*iam.ListOpenIDConnectProvidersOutput, error) { + return nil, nil +} +func (s *stubIAMService) DeleteOpenIDConnectProvider(string, *iam.DeleteOpenIDConnectProviderInput) (*iam.DeleteOpenIDConnectProviderOutput, error) { + return nil, nil +} diff --git a/spinifex/gateway/sts_anonymous.go b/spinifex/gateway/sts_anonymous.go new file mode 100644 index 000000000..c6a183215 --- /dev/null +++ b/spinifex/gateway/sts_anonymous.go @@ -0,0 +1,49 @@ +package gateway + +import ( + "bytes" + "context" + "io" + "net/http" +) + +// anonymousSTSInterceptor routes the credential-bootstrap STS actions +// (AssumeRoleWithWebIdentity) to the STS dispatcher ahead of the SigV4 +// middleware. These calls carry no AWS credentials — the caller is +// authenticated by the projected ServiceAccount JWT in the request body — so +// the signed surface would reject them as unauthenticated. Requests bearing an +// Authorization header skip this path untouched, keeping the signed hot path +// free of body buffering. +func (gw *GatewayConfig) anonymousSTSInterceptor(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && r.Header.Get("Authorization") == "" { + if args, ok := gw.anonymousSTSArgs(r); ok { + ctx := context.WithValue(r.Context(), ctxQueryArgs, args) + ctx = context.WithValue(ctx, ctxService, "sts") + r = r.WithContext(ctx) + if err := gw.STS_Request(w, r); err != nil { + gw.ErrorHandler(w, r, err) + } + return + } + } + next.ServeHTTP(w, r) + }) +} + +// anonymousSTSArgs buffers and restores the request body, parses the AWS query +// args, and reports whether Action is an STS action permitted without SigV4. A +// non-anonymous request flows on unchanged with its body intact. +func (gw *GatewayConfig) anonymousSTSArgs(r *http.Request) (map[string]string, bool) { + body, err := io.ReadAll(r.Body) + if err != nil { + return nil, false + } + r.Body = io.NopCloser(bytes.NewReader(body)) + + args, err := ParseAWSQueryArgs(string(body)) + if err != nil || !anonymousSTSActions[args["Action"]] { + return nil, false + } + return args, true +} diff --git a/spinifex/gateway/sts_anonymous_test.go b/spinifex/gateway/sts_anonymous_test.go new file mode 100644 index 000000000..b7dd1ef32 --- /dev/null +++ b/spinifex/gateway/sts_anonymous_test.go @@ -0,0 +1,105 @@ +package gateway + +import ( + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestAnonymousSTS_WebIdentityNoAuthReachesHandler proves the IRSA bootstrap +// call — AssumeRoleWithWebIdentity carrying no Authorization header — is routed +// to the STS dispatcher ahead of the SigV4 middleware, rather than rejected as +// unauthenticated. The mock asserts the parsed token reached the handler. +func TestAnonymousSTS_WebIdentityNoAuthReachesHandler(t *testing.T) { + var seenToken string + gw := &GatewayConfig{ + DisableLogging: true, + IAMService: &flexMockIAMService{}, + STSService: &flexMockSTSService{ + assumeWebIdentityFn: func(in *sts.AssumeRoleWithWebIdentityInput) (*sts.AssumeRoleWithWebIdentityOutput, error) { + seenToken = aws.StringValue(in.WebIdentityToken) + return &sts.AssumeRoleWithWebIdentityOutput{ + Credentials: &sts.Credentials{ + AccessKeyId: aws.String("ASIAIRSA"), + SecretAccessKey: aws.String("secret"), + SessionToken: aws.String("token"), + }, + }, nil + }, + }, + } + handler := gw.SetupRoutes() + + body := "Action=AssumeRoleWithWebIdentity&Version=2011-06-15" + + "&RoleArn=arn:aws:iam::000000000001:role/demo&RoleSessionName=s1" + + "&WebIdentityToken=header.payload.sig" + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + // No Authorization header — this is the unsigned bootstrap path. + + rec := httptest.NewRecorder() + handler.ServeHTTP(rec, req) + + require.Equal(t, http.StatusOK, rec.Code, "body=%s", rec.Body.String()) + b, _ := io.ReadAll(rec.Body) + assert.Contains(t, string(b), "AssumeRoleWithWebIdentityResult") + assert.Contains(t, string(b), "ASIAIRSA") + assert.Equal(t, "header.payload.sig", seenToken) +} + +// TestAnonymousSTS_SignedRequestFallsThrough confirms a request bearing an +// Authorization header is NOT intercepted — it flows to the signed surface, so +// the anonymous dispatcher never runs and the body is left for the SigV4 path. +func TestAnonymousSTS_SignedRequestFallsThrough(t *testing.T) { + gw := &GatewayConfig{ + DisableLogging: true, + STSService: &flexMockSTSService{ + assumeWebIdentityFn: func(*sts.AssumeRoleWithWebIdentityInput) (*sts.AssumeRoleWithWebIdentityOutput, error) { + t.Fatal("signed request must not reach the anonymous STS dispatcher") + return nil, nil + }, + }, + } + + nextCalled := false + next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) { nextCalled = true }) + + req := httptest.NewRequest(http.MethodPost, "/", + strings.NewReader("Action=AssumeRoleWithWebIdentity&WebIdentityToken=x")) + req.Header.Set("Authorization", "AWS4-HMAC-SHA256 Credential=...") + + rec := httptest.NewRecorder() + gw.anonymousSTSInterceptor(next).ServeHTTP(rec, req) + assert.True(t, nextCalled, "signed request must pass through to next handler") +} + +// TestAnonymousSTSArgs_Classification covers the body-peek helper: an anonymous +// action is recognised and the body is restored for the downstream handler; a +// non-anonymous action is rejected and the body left intact for SigV4. +func TestAnonymousSTSArgs_Classification(t *testing.T) { + gw := &GatewayConfig{DisableLogging: true} + + t.Run("anonymous action recognised, body restored", func(t *testing.T) { + const body = "Action=AssumeRoleWithWebIdentity&WebIdentityToken=tok" + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)) + args, ok := gw.anonymousSTSArgs(req) + require.True(t, ok) + assert.Equal(t, "AssumeRoleWithWebIdentity", args["Action"]) + + restored, _ := io.ReadAll(req.Body) + assert.Equal(t, body, string(restored), "body must be restored for downstream read") + }) + + t.Run("non-anonymous action rejected", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("Action=GetCallerIdentity")) + _, ok := gw.anonymousSTSArgs(req) + assert.False(t, ok) + }) +} diff --git a/spinifex/gateway/sts_request_test.go b/spinifex/gateway/sts_request_test.go index 0ffd9c64d..f23b084a7 100644 --- a/spinifex/gateway/sts_request_test.go +++ b/spinifex/gateway/sts_request_test.go @@ -25,7 +25,9 @@ import ( type flexMockSTSService struct { assumeRoleFn func(string, string, string, *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) getCallerIdentityFn func(string, string, string, *sts.GetCallerIdentityInput) (*sts.GetCallerIdentityOutput, error) + getSessionTokenFn func(string, string, string, string, *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) lookupSessionFn func(string) (*handlers_sts.SessionCredential, error) + assumeWebIdentityFn func(*sts.AssumeRoleWithWebIdentityInput) (*sts.AssumeRoleWithWebIdentityOutput, error) } var _ handlers_sts.STSService = (*flexMockSTSService)(nil) @@ -37,6 +39,12 @@ func (m *flexMockSTSService) AssumeRole(callerAccountID, callerARN, callerIdenti return &sts.AssumeRoleOutput{}, nil } +// AssumeRoleForInstance is the in-process IMDS entry point, never dispatched +// over the HTTPS gateway; the mock exists only to satisfy the interface. +func (m *flexMockSTSService) AssumeRoleForInstance(_, _, _ string, _ int64) (*sts.AssumeRoleOutput, error) { + return &sts.AssumeRoleOutput{}, nil +} + func (m *flexMockSTSService) GetCallerIdentity(callerAccountID, callerARN, callerUserID string, input *sts.GetCallerIdentityInput) (*sts.GetCallerIdentityOutput, error) { if m.getCallerIdentityFn != nil { return m.getCallerIdentityFn(callerAccountID, callerARN, callerUserID, input) @@ -48,6 +56,13 @@ func (m *flexMockSTSService) GetCallerIdentity(callerAccountID, callerARN, calle }, nil } +func (m *flexMockSTSService) GetSessionToken(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID string, input *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) { + if m.getSessionTokenFn != nil { + return m.getSessionTokenFn(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID, input) + } + return &sts.GetSessionTokenOutput{}, nil +} + func (m *flexMockSTSService) LookupSessionCredential(akid string) (*handlers_sts.SessionCredential, error) { if m.lookupSessionFn != nil { return m.lookupSessionFn(akid) @@ -59,7 +74,10 @@ func (m *flexMockSTSService) VerifySessionToken(*handlers_sts.SessionCredential, return true } -func (m *flexMockSTSService) AssumeRoleWithWebIdentity(*sts.AssumeRoleWithWebIdentityInput) (*sts.AssumeRoleWithWebIdentityOutput, error) { +func (m *flexMockSTSService) AssumeRoleWithWebIdentity(input *sts.AssumeRoleWithWebIdentityInput) (*sts.AssumeRoleWithWebIdentityOutput, error) { + if m.assumeWebIdentityFn != nil { + return m.assumeWebIdentityFn(input) + } return nil, errors.New(awserrors.ErrorNotImplemented) } @@ -206,6 +224,62 @@ func TestSTSRequest_GetCallerIdentity_RootShortcircuitsIAMLookup(t *testing.T) { assert.Contains(t, xmlStr, "000000000000") } +func TestSTSRequest_GetSessionToken_Success(t *testing.T) { + var got struct { + accountID string + userName string + principalType string + accessKeyID string + duration int64 + } + svc := &flexMockSTSService{ + getSessionTokenFn: func(accountID, userName, principalType, accessKeyID string, input *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) { + got.accountID = accountID + got.userName = userName + got.principalType = principalType + got.accessKeyID = accessKeyID + got.duration = aws.Int64Value(input.DurationSeconds) + return &sts.GetSessionTokenOutput{ + Credentials: &sts.Credentials{ + AccessKeyId: aws.String("ASIAEXAMPLE123"), + SecretAccessKey: aws.String("secret"), + SessionToken: aws.String("token"), + }, + }, nil + }, + } + handler := setupSTSRequestHandler(stsRequestParams{ + accountID: utils.GlobalAccountID, + identity: "alice", + principalType: principalTypeUser, + accessKey: "AKIAEXAMPLE", + stsSvc: svc, + }) + + body := "Action=GetSessionToken&DurationSeconds=3600" + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp := doRequest(handler, req) + require.Equal(t, 200, resp.StatusCode) + + b, _ := io.ReadAll(resp.Body) + xmlStr := string(b) + assert.Contains(t, xmlStr, "GetSessionTokenResult") + assert.Contains(t, xmlStr, "ASIAEXAMPLE123") + + // The dispatcher must forward the SigV4 user name (c.identity), principal + // type, and access key (c.accessKey) so the handler can enforce its + // user-only / no-session constraint, plus the parsed DurationSeconds. + // GetSessionToken is in stsSkipPolicyCheck, so reaching the service at all + // also confirms it is not blocked by checkPolicy. + assert.Equal(t, utils.GlobalAccountID, got.accountID) + assert.Equal(t, "alice", got.userName) + assert.Equal(t, principalTypeUser, got.principalType) + assert.Equal(t, "AKIAEXAMPLE", got.accessKeyID) + assert.Equal(t, int64(3600), got.duration) +} + func TestSTSRequest_UnknownAction(t *testing.T) { handler := setupSTSRequestHandler(stsRequestParams{ accountID: utils.GlobalAccountID, diff --git a/spinifex/gateway/tagging.go b/spinifex/gateway/tagging.go new file mode 100644 index 000000000..f5a558585 --- /dev/null +++ b/spinifex/gateway/tagging.go @@ -0,0 +1,82 @@ +package gateway + +import ( + "errors" + "io" + "log/slog" + "net/http" + "strings" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + gateway_tagging "github.com/mulgadc/spinifex/spinifex/gateway/tagging" +) + +// taggingHandler invokes a per-action Resource Groups Tagging API function: it +// receives the parent gateway config, the caller's accountID, and the raw JSON +// request body, and returns a typed *resourcegroupstaggingapi.Output (as +// any) or an error whose Error() is an awserrors code. +type taggingHandler func(gw *GatewayConfig, accountID string, body []byte) (any, error) + +// taggingActions maps a tagging action (the suffix of the X-Amz-Target header) +// to its handler. The Resource Groups Tagging API speaks AWS JSON 1.1: the +// action is carried in "X-Amz-Target: ResourceGroupsTaggingAPI_20170126.". +var taggingActions = map[string]taggingHandler{ + "GetResources": func(gw *GatewayConfig, acct string, b []byte) (any, error) { + return gateway_tagging.GetResources(gw.NATSConn, gw.Region, acct, b) + }, +} + +// taggingActionFromTarget extracts the bare action name from an X-Amz-Target +// header value of the form "ResourceGroupsTaggingAPI_20170126.". +func taggingActionFromTarget(target string) string { + if i := strings.LastIndex(target, "."); i >= 0 { + return target[i+1:] + } + return target +} + +// Tagging_Request dispatches AWS JSON 1.1 Resource Groups Tagging API requests. +// The action is read from the X-Amz-Target header; the body is the JSON-encoded +// operation input. Errors are returned as plain awserrors codes; the caller +// (gateway.Request) routes them through ErrorHandler, which emits the JSON +// error envelope. +func (gw *GatewayConfig) Tagging_Request(w http.ResponseWriter, r *http.Request) error { + action := taggingActionFromTarget(r.Header.Get("X-Amz-Target")) + if action == "" { + return errors.New(awserrors.ErrorMissingAction) + } + + handler, ok := taggingActions[action] + if !ok { + slog.Debug("tagging: unknown action", "action", action) + return errors.New(awserrors.ErrorInvalidAction) + } + + if err := gw.checkPolicy(r, "tagging", action); err != nil { + return err + } + + if gw.NATSConn == nil { + return errors.New(awserrors.ErrorServerInternal) + } + + accountID, _ := r.Context().Value(ctxAccountID).(string) + if accountID == "" { + slog.Error("Tagging_Request: no account ID in auth context") + return errors.New(awserrors.ErrorServerInternal) + } + + body, err := io.ReadAll(r.Body) + if err != nil { + slog.Error("Tagging_Request: failed to read body", "err", err) + return errors.New(awserrors.ErrorInvalidParameterValue) + } + + output, err := handler(gw, accountID, body) + if err != nil { + return err + } + + gateway_tagging.WriteJSONResponse(w, output) + return nil +} diff --git a/spinifex/gateway/tagging/GetResources.go b/spinifex/gateway/tagging/GetResources.go new file mode 100644 index 000000000..0f7196f85 --- /dev/null +++ b/spinifex/gateway/tagging/GetResources.go @@ -0,0 +1,334 @@ +package gateway_tagging + +import ( + "errors" + "fmt" + "sort" + "strconv" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/elbv2" + rgt "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_ec2_tags "github.com/mulgadc/spinifex/spinifex/handlers/ec2/tags" + handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" + "github.com/nats-io/nats.go" +) + +// describeTagsBatchSize is the AWS ceiling on ARNs per ELBv2 DescribeTags call. +const describeTagsBatchSize = 20 + +// defaultResourcesPerPage caps a page when the caller omits ResourcesPerPage. +// AWS allows up to 100; matching that keeps the controller's reconcile fast. +const defaultResourcesPerPage = 100 + +// resourceLister fetches an account's tagged resources by family, already +// shaped as RGT mappings. Splitting the fetch out of getResources keeps the +// filter/sort/paginate orchestration testable without a live NATS backend. +type resourceLister interface { + listELBv2(typeFilters map[string]bool) ([]*rgt.ResourceTagMapping, error) + listEC2(typeFilters map[string]bool) ([]*rgt.ResourceTagMapping, error) +} + +// GetResources implements the Resource Groups Tagging API GetResources call by +// aggregating tagged ELBv2 (load balancers + target groups) and EC2 resources +// for the account, applying the request's resource-type and tag filters. The +// aws-load-balancer-controller uses this to discover and adopt the resources it +// manages (filtering on its cluster/stack tags). Pagination is an opaque +// integer offset into a deterministically ARN-sorted result set. +func GetResources(natsConn *nats.Conn, region, accountID string, body []byte) (any, error) { + return getResources(&natsLister{natsConn: natsConn, region: region, accountID: accountID}, body) +} + +// getResources is the protocol-independent core: it parses the request, asks +// the lister for the in-scope resource families, then applies tag filters, +// stable ordering, and pagination. +func getResources(lister resourceLister, body []byte) (any, error) { + var input rgt.GetResourcesInput + if err := unmarshalIfBody(body, &input); err != nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + typeFilters := lowerStringSlice(input.ResourceTypeFilters) + + var mappings []*rgt.ResourceTagMapping + + elbMappings, err := lister.listELBv2(typeFilters) + if err != nil { + return nil, err + } + mappings = append(mappings, elbMappings...) + + ec2Mappings, err := lister.listEC2(typeFilters) + if err != nil { + return nil, err + } + mappings = append(mappings, ec2Mappings...) + + mappings = filterByTags(mappings, input.TagFilters) + + // Deterministic order so pagination tokens are stable across pages. + sort.Slice(mappings, func(i, j int) bool { + return aws.StringValue(mappings[i].ResourceARN) < aws.StringValue(mappings[j].ResourceARN) + }) + + page, next, err := paginate(mappings, input.PaginationToken, input.ResourcesPerPage) + if err != nil { + return nil, err + } + + out := &rgt.GetResourcesOutput{ResourceTagMappingList: page} + // AWS returns an empty (not absent) token on the last page. + out.PaginationToken = aws.String(next) + return out, nil +} + +// natsLister fetches tagged resources from the elbv2 and ec2 tag stores over +// NATS. +type natsLister struct { + natsConn *nats.Conn + region string + accountID string +} + +func (l *natsLister) listELBv2(typeFilters map[string]bool) ([]*rgt.ResourceTagMapping, error) { + return elbv2Resources(l.natsConn, l.accountID, typeFilters) +} + +func (l *natsLister) listEC2(typeFilters map[string]bool) ([]*rgt.ResourceTagMapping, error) { + svc := handlers_ec2_tags.NewNATSTagsService(l.natsConn) + tagsOut, err := svc.DescribeTags(&ec2.DescribeTagsInput{}, l.accountID) + if err != nil { + return nil, err + } + return buildEC2Mappings(tagsOut.Tags, l.region, l.accountID, typeFilters), nil +} + +// elbv2Resources lists the account's load balancers and target groups (when the +// type filters admit them) and attaches their tags via batched DescribeTags. +func elbv2Resources(natsConn *nats.Conn, accountID string, typeFilters map[string]bool) ([]*rgt.ResourceTagMapping, error) { + wantLB := matchesType(typeFilters, "elasticloadbalancing:loadbalancer") + wantTG := matchesType(typeFilters, "elasticloadbalancing:targetgroup") + if !wantLB && !wantTG { + return nil, nil + } + + svc := handlers_elbv2.NewNATSELBv2Service(natsConn) + var arns []string + + if wantLB { + lbs, err := svc.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{}, accountID) + if err != nil { + return nil, err + } + for _, lb := range lbs.LoadBalancers { + if lb.LoadBalancerArn != nil { + arns = append(arns, *lb.LoadBalancerArn) + } + } + } + if wantTG { + tgs, err := svc.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{}, accountID) + if err != nil { + return nil, err + } + for _, tg := range tgs.TargetGroups { + if tg.TargetGroupArn != nil { + arns = append(arns, *tg.TargetGroupArn) + } + } + } + if len(arns) == 0 { + return nil, nil + } + + mappings := make([]*rgt.ResourceTagMapping, 0, len(arns)) + for start := 0; start < len(arns); start += describeTagsBatchSize { + end := min(start+describeTagsBatchSize, len(arns)) + tagsOut, err := svc.DescribeTags(&elbv2.DescribeTagsInput{ + ResourceArns: aws.StringSlice(arns[start:end]), + }, accountID) + if err != nil { + return nil, err + } + for _, td := range tagsOut.TagDescriptions { + mappings = append(mappings, &rgt.ResourceTagMapping{ + ResourceARN: td.ResourceArn, + Tags: elbv2TagsToRGT(td.Tags), + }) + } + } + return mappings, nil +} + +// buildEC2Mappings groups EC2 tag descriptions by resource, synthesises an ARN +// per resource, and keeps those that pass the type filters. EC2 tags carry a +// bare resource ID and a type word (e.g. "subnet"); the RGT type string is +// "ec2:". Pure (no NATS) so the grouping/ARN logic is unit-tested. +func buildEC2Mappings(tagDescriptions []*ec2.TagDescription, region, accountID string, typeFilters map[string]bool) []*rgt.ResourceTagMapping { + type resourceTags struct { + fullType string + tags map[string]string + } + byResource := make(map[string]*resourceTags) + for _, td := range tagDescriptions { + id := aws.StringValue(td.ResourceId) + if id == "" { + continue + } + fullType := "ec2:" + aws.StringValue(td.ResourceType) + rt := byResource[id] + if rt == nil { + rt = &resourceTags{fullType: fullType, tags: map[string]string{}} + byResource[id] = rt + } + rt.tags[aws.StringValue(td.Key)] = aws.StringValue(td.Value) + } + + var mappings []*rgt.ResourceTagMapping + for id, rt := range byResource { + if !matchesType(typeFilters, rt.fullType) { + continue + } + mappings = append(mappings, &rgt.ResourceTagMapping{ + ResourceARN: aws.String(ec2ARN(region, accountID, rt.fullType, id)), + Tags: tagMapToRGT(rt.tags), + }) + } + return mappings +} + +// ec2ARN builds an arn:aws:ec2 ARN for a resource. fullType is "ec2:". +func ec2ARN(region, accountID, fullType, id string) string { + resourceType := strings.TrimPrefix(fullType, "ec2:") + return fmt.Sprintf("arn:aws:ec2:%s:%s:%s/%s", region, accountID, resourceType, id) +} + +// matchesType reports whether a resource of fullType (e.g. +// "elasticloadbalancing:loadbalancer") passes the request's type filters. An +// empty filter set admits everything. A service-only filter ("ec2") admits all +// of that service's types; a fully-qualified filter must match exactly. +func matchesType(typeFilters map[string]bool, fullType string) bool { + if len(typeFilters) == 0 { + return true + } + if typeFilters[fullType] { + return true + } + if svc, _, ok := strings.Cut(fullType, ":"); ok { + if typeFilters[svc] { + return true + } + } + return false +} + +// filterByTags keeps only resources matching every TagFilter (AND across +// filters). Within a filter, a non-empty Values list is an OR over values; an +// empty Values list matches any value for that key. +func filterByTags(mappings []*rgt.ResourceTagMapping, filters []*rgt.TagFilter) []*rgt.ResourceTagMapping { + if len(filters) == 0 { + return mappings + } + out := mappings[:0] + for _, m := range mappings { + if matchesAllTagFilters(m.Tags, filters) { + out = append(out, m) + } + } + return out +} + +func matchesAllTagFilters(tags []*rgt.Tag, filters []*rgt.TagFilter) bool { + tagMap := make(map[string]string, len(tags)) + for _, t := range tags { + tagMap[aws.StringValue(t.Key)] = aws.StringValue(t.Value) + } + for _, f := range filters { + key := aws.StringValue(f.Key) + if key == "" { + continue + } + val, ok := tagMap[key] + if !ok { + return false + } + if len(f.Values) == 0 { + continue + } + matched := false + for _, v := range f.Values { + if aws.StringValue(v) == val { + matched = true + break + } + } + if !matched { + return false + } + } + return true +} + +// paginate slices an offset-token page out of the sorted mappings and returns +// the page plus the next token ("" when exhausted). +func paginate(mappings []*rgt.ResourceTagMapping, token *string, perPage *int64) ([]*rgt.ResourceTagMapping, string, error) { + start := 0 + if token != nil && *token != "" { + n, err := strconv.Atoi(*token) + if err != nil || n < 0 { + return nil, "", errors.New(awserrors.ErrorInvalidParameterValue) + } + start = n + } + if start > len(mappings) { + start = len(mappings) + } + + size := defaultResourcesPerPage + if perPage != nil && *perPage > 0 { + size = int(*perPage) + } + + end := start + size + if end >= len(mappings) { + return mappings[start:], "", nil + } + return mappings[start:end], strconv.Itoa(end), nil +} + +func elbv2TagsToRGT(tags []*elbv2.Tag) []*rgt.Tag { + out := make([]*rgt.Tag, 0, len(tags)) + for _, t := range tags { + out = append(out, &rgt.Tag{Key: t.Key, Value: t.Value}) + } + return out +} + +func tagMapToRGT(tags map[string]string) []*rgt.Tag { + keys := make([]string, 0, len(tags)) + for k := range tags { + keys = append(keys, k) + } + sort.Strings(keys) + out := make([]*rgt.Tag, 0, len(keys)) + for _, k := range keys { + out = append(out, &rgt.Tag{Key: aws.String(k), Value: aws.String(tags[k])}) + } + return out +} + +func lowerStringSlice(in []*string) map[string]bool { + if len(in) == 0 { + return nil + } + out := make(map[string]bool, len(in)) + for _, s := range in { + if s != nil { + out[strings.ToLower(*s)] = true + } + } + return out +} diff --git a/spinifex/gateway/tagging/GetResources_test.go b/spinifex/gateway/tagging/GetResources_test.go new file mode 100644 index 000000000..fd7b445ac --- /dev/null +++ b/spinifex/gateway/tagging/GetResources_test.go @@ -0,0 +1,231 @@ +package gateway_tagging + +import ( + "encoding/json" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + rgt "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// fakeLister returns canned mappings so getResources can be exercised without a +// NATS backend. +type fakeLister struct { + elbv2 []*rgt.ResourceTagMapping + ec2 []*rgt.ResourceTagMapping + elbv2Err error + ec2Err error + gotFilter map[string]bool +} + +func (f *fakeLister) listELBv2(typeFilters map[string]bool) ([]*rgt.ResourceTagMapping, error) { + f.gotFilter = typeFilters + return f.elbv2, f.elbv2Err +} + +func (f *fakeLister) listEC2(typeFilters map[string]bool) ([]*rgt.ResourceTagMapping, error) { + return f.ec2, f.ec2Err +} + +func mustJSON(t *testing.T, v any) []byte { + t.Helper() + b, err := json.Marshal(v) + require.NoError(t, err) + return b +} + +func TestMatchesType(t *testing.T) { + // Empty filter set admits everything. + assert.True(t, matchesType(nil, "elasticloadbalancing:loadbalancer")) + + exact := map[string]bool{"elasticloadbalancing:loadbalancer": true} + assert.True(t, matchesType(exact, "elasticloadbalancing:loadbalancer")) + assert.False(t, matchesType(exact, "elasticloadbalancing:targetgroup")) + assert.False(t, matchesType(exact, "ec2:subnet")) + + // Service-only filter admits all of that service's types. + svcOnly := map[string]bool{"ec2": true} + assert.True(t, matchesType(svcOnly, "ec2:subnet")) + assert.True(t, matchesType(svcOnly, "ec2:security-group")) + assert.False(t, matchesType(svcOnly, "elasticloadbalancing:loadbalancer")) +} + +func TestEC2ARN(t *testing.T) { + assert.Equal(t, + "arn:aws:ec2:us-east-1:123456789012:subnet/subnet-abc", + ec2ARN("us-east-1", "123456789012", "ec2:subnet", "subnet-abc")) +} + +func mapping(arn string, kv ...string) *rgt.ResourceTagMapping { + var tags []*rgt.Tag + for i := 0; i+1 < len(kv); i += 2 { + tags = append(tags, &rgt.Tag{Key: aws.String(kv[i]), Value: aws.String(kv[i+1])}) + } + return &rgt.ResourceTagMapping{ResourceARN: aws.String(arn), Tags: tags} +} + +func tagFilter(key string, values ...string) *rgt.TagFilter { + return &rgt.TagFilter{Key: aws.String(key), Values: aws.StringSlice(values)} +} + +func TestFilterByTags(t *testing.T) { + in := []*rgt.ResourceTagMapping{ + mapping("arn:a", "elbv2.k8s.aws/cluster", "prod", "env", "live"), + mapping("arn:b", "elbv2.k8s.aws/cluster", "dev"), + mapping("arn:c", "env", "live"), + } + + // No filters: passthrough. + assert.Len(t, filterByTags(in, nil), 3) + + // Key-only filter: any value for the key. + got := filterByTags(in, []*rgt.TagFilter{tagFilter("elbv2.k8s.aws/cluster")}) + require.Len(t, got, 2) + + // Key+value filter: exact value match (OR over values). + got = filterByTags(in, []*rgt.TagFilter{tagFilter("elbv2.k8s.aws/cluster", "prod", "staging")}) + require.Len(t, got, 1) + assert.Equal(t, "arn:a", aws.StringValue(got[0].ResourceARN)) + + // AND across filters. + got = filterByTags(in, []*rgt.TagFilter{ + tagFilter("elbv2.k8s.aws/cluster", "prod"), + tagFilter("env", "live"), + }) + require.Len(t, got, 1) + assert.Equal(t, "arn:a", aws.StringValue(got[0].ResourceARN)) + + // Filter on a key no resource has: empty result. + assert.Empty(t, filterByTags(in, []*rgt.TagFilter{tagFilter("missing")})) +} + +func TestPaginate(t *testing.T) { + all := []*rgt.ResourceTagMapping{ + mapping("arn:0"), mapping("arn:1"), mapping("arn:2"), mapping("arn:3"), mapping("arn:4"), + } + + // First page of 2, expect a next token. + page, next, err := paginate(all, nil, aws.Int64(2)) + require.NoError(t, err) + assert.Len(t, page, 2) + assert.Equal(t, "2", next) + + // Follow the token to the middle page. + page, next, err = paginate(all, aws.String("2"), aws.Int64(2)) + require.NoError(t, err) + assert.Len(t, page, 2) + assert.Equal(t, "4", next) + + // Last partial page: no next token. + page, next, err = paginate(all, aws.String("4"), aws.Int64(2)) + require.NoError(t, err) + assert.Len(t, page, 1) + assert.Equal(t, "", next) + + // Default page size returns everything in one page. + page, next, err = paginate(all, nil, nil) + require.NoError(t, err) + assert.Len(t, page, 5) + assert.Equal(t, "", next) + + // Bad token is rejected. + _, _, err = paginate(all, aws.String("not-a-number"), nil) + require.Error(t, err) +} + +func TestGetResources_MergesFiltersSortsPaginates(t *testing.T) { + lister := &fakeLister{ + elbv2: []*rgt.ResourceTagMapping{ + mapping("arn:elb:c", "elbv2.k8s.aws/cluster", "prod"), + mapping("arn:elb:a", "elbv2.k8s.aws/cluster", "dev"), + }, + ec2: []*rgt.ResourceTagMapping{ + mapping("arn:ec2:b", "elbv2.k8s.aws/cluster", "prod"), + }, + } + + // Tag-filter to prod + page size 1: two matches (arn:ec2:b, arn:elb:c), + // ARN-sorted, first page returns arn:ec2:b with a next token. + body := mustJSON(t, rgt.GetResourcesInput{ + TagFilters: []*rgt.TagFilter{tagFilter("elbv2.k8s.aws/cluster", "prod")}, + ResourcesPerPage: aws.Int64(1), + }) + out, err := getResources(lister, body) + require.NoError(t, err) + res, ok := out.(*rgt.GetResourcesOutput) + require.True(t, ok) + require.Len(t, res.ResourceTagMappingList, 1) + assert.Equal(t, "arn:ec2:b", aws.StringValue(res.ResourceTagMappingList[0].ResourceARN)) + assert.Equal(t, "1", aws.StringValue(res.PaginationToken)) + + // Follow the token: last page, arn:elb:c, empty token. + body = mustJSON(t, rgt.GetResourcesInput{ + TagFilters: []*rgt.TagFilter{tagFilter("elbv2.k8s.aws/cluster", "prod")}, + ResourcesPerPage: aws.Int64(1), + PaginationToken: aws.String("1"), + }) + out, err = getResources(lister, body) + require.NoError(t, err) + res, ok = out.(*rgt.GetResourcesOutput) + require.True(t, ok) + require.Len(t, res.ResourceTagMappingList, 1) + assert.Equal(t, "arn:elb:c", aws.StringValue(res.ResourceTagMappingList[0].ResourceARN)) + assert.Equal(t, "", aws.StringValue(res.PaginationToken)) +} + +func TestGetResources_ResourceTypeFiltersLowercasedAndPassed(t *testing.T) { + lister := &fakeLister{} + body := mustJSON(t, rgt.GetResourcesInput{ + ResourceTypeFilters: aws.StringSlice([]string{"ElasticLoadBalancing:LoadBalancer"}), + }) + _, err := getResources(lister, body) + require.NoError(t, err) + assert.True(t, lister.gotFilter["elasticloadbalancing:loadbalancer"]) +} + +func TestGetResources_ListerErrorPropagates(t *testing.T) { + lister := &fakeLister{elbv2Err: assert.AnError} + _, err := getResources(lister, []byte("{}")) + require.ErrorIs(t, err, assert.AnError) +} + +func TestGetResources_InvalidBody(t *testing.T) { + _, err := getResources(&fakeLister{}, []byte("not-json")) + require.Error(t, err) +} + +func TestBuildEC2Mappings(t *testing.T) { + tags := []*ec2.TagDescription{ + {ResourceId: aws.String("subnet-1"), ResourceType: aws.String("subnet"), Key: aws.String("Name"), Value: aws.String("a")}, + {ResourceId: aws.String("subnet-1"), ResourceType: aws.String("subnet"), Key: aws.String("env"), Value: aws.String("prod")}, + {ResourceId: aws.String("sg-1"), ResourceType: aws.String("security-group"), Key: aws.String("Name"), Value: aws.String("b")}, + {ResourceId: aws.String(""), ResourceType: aws.String("subnet"), Key: aws.String("skip"), Value: aws.String("me")}, + } + + // No type filter: both resources, tags merged per resource. + all := buildEC2Mappings(tags, "us-east-1", "123456789012", nil) + require.Len(t, all, 2) + bySubnet := map[string]*rgt.ResourceTagMapping{} + for _, m := range all { + bySubnet[aws.StringValue(m.ResourceARN)] = m + } + subnet := bySubnet["arn:aws:ec2:us-east-1:123456789012:subnet/subnet-1"] + require.NotNil(t, subnet) + assert.Len(t, subnet.Tags, 2) + + // Type filter narrows to subnets only. + onlySubnets := buildEC2Mappings(tags, "us-east-1", "123456789012", map[string]bool{"ec2:subnet": true}) + require.Len(t, onlySubnets, 1) + assert.Equal(t, "arn:aws:ec2:us-east-1:123456789012:subnet/subnet-1", aws.StringValue(onlySubnets[0].ResourceARN)) +} + +func TestTagMapToRGT_SortedDeterministic(t *testing.T) { + out := tagMapToRGT(map[string]string{"b": "2", "a": "1", "c": "3"}) + require.Len(t, out, 3) + assert.Equal(t, "a", aws.StringValue(out[0].Key)) + assert.Equal(t, "b", aws.StringValue(out[1].Key)) + assert.Equal(t, "c", aws.StringValue(out[2].Key)) +} diff --git a/spinifex/gateway/tagging/handler.go b/spinifex/gateway/tagging/handler.go new file mode 100644 index 000000000..0616318b5 --- /dev/null +++ b/spinifex/gateway/tagging/handler.go @@ -0,0 +1,43 @@ +// Package gateway_tagging is the HTTP-side glue between awsgw and the Resource +// Groups Tagging API. Like ACM it speaks AWS JSON 1.1 (X-Amz-Target dispatch, +// Content-Type application/x-amz-json-1.1). GetResources is answered by +// aggregating the elbv2 and ec2 tag stores over NATS — there is no dedicated +// tagging store. Error envelopes are emitted centrally by the shared gateway +// ErrorHandler. +package gateway_tagging + +import ( + "encoding/json" + "log/slog" + "net/http" + + "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +// JSONContentType is the AWS JSON 1.1 content type tagging clients expect. +const JSONContentType = "application/x-amz-json-1.1" + +// WriteJSONResponse serialises obj as a 200 AWS JSON 1.1 response using the +// SDK's own marshaler (epoch-seconds timestamps), the same path ACM uses. +func WriteJSONResponse(w http.ResponseWriter, obj any) { + body, err := jsonutil.BuildJSON(obj) + if err != nil { + slog.Error("tagging: failed to marshal response JSON", "err", err) + http.Error(w, awserrors.ErrorInternalError, http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", JSONContentType) + w.WriteHeader(http.StatusOK) + if _, err := w.Write(body); err != nil { + slog.Error("tagging: failed to write response", "err", err) + } +} + +// unmarshalIfBody decodes body into out only when body is non-empty. +func unmarshalIfBody(body []byte, out any) error { + if len(body) == 0 { + return nil + } + return json.Unmarshal(body, out) +} diff --git a/spinifex/gateway/tagging_test.go b/spinifex/gateway/tagging_test.go new file mode 100644 index 000000000..76425cb7f --- /dev/null +++ b/spinifex/gateway/tagging_test.go @@ -0,0 +1,66 @@ +package gateway + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupTaggingRequest(target, body string) *http.Request { + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)) + if target != "" { + req.Header.Set("X-Amz-Target", target) + } + ctx := context.WithValue(req.Context(), ctxService, "tagging") + ctx = context.WithValue(ctx, ctxAccountID, "123456789012") + return req.WithContext(ctx) +} + +func TestTaggingActionFromTarget(t *testing.T) { + assert.Equal(t, "GetResources", taggingActionFromTarget("ResourceGroupsTaggingAPI_20170126.GetResources")) + assert.Equal(t, "GetResources", taggingActionFromTarget("GetResources")) + assert.Equal(t, "", taggingActionFromTarget("")) +} + +func TestTaggingActionsMap_AllActionsRegistered(t *testing.T) { + expected := []string{ + "GetResources", + } + for _, action := range expected { + _, ok := taggingActions[action] + assert.True(t, ok, "action %q should be registered in taggingActions", action) + } + assert.Len(t, taggingActions, len(expected)) +} + +func TestTaggingRequest_MissingTarget(t *testing.T) { + gw := &GatewayConfig{DisableLogging: true} + w := httptest.NewRecorder() + err := gw.Tagging_Request(w, setupTaggingRequest("", "")) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorMissingAction, err.Error()) +} + +func TestTaggingRequest_UnknownAction(t *testing.T) { + gw := &GatewayConfig{DisableLogging: true} + w := httptest.NewRecorder() + err := gw.Tagging_Request(w, setupTaggingRequest("ResourceGroupsTaggingAPI_20170126.TagResources", "{}")) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorInvalidAction, err.Error()) +} + +// A known action with no NATS connection passes routing + policy and fails at +// the NATS-availability guard. +func TestTaggingRequest_KnownActionNoNATS(t *testing.T) { + gw := &GatewayConfig{DisableLogging: true} + w := httptest.NewRecorder() + err := gw.Tagging_Request(w, setupTaggingRequest("ResourceGroupsTaggingAPI_20170126.GetResources", "{}")) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorServerInternal, err.Error()) +} diff --git a/spinifex/gpu/mig_parsers_test.go b/spinifex/gpu/mig_parsers_test.go index a96974b50..926132fcc 100644 --- a/spinifex/gpu/mig_parsers_test.go +++ b/spinifex/gpu/mig_parsers_test.go @@ -650,9 +650,10 @@ func TestIsMIGCapable(t *testing.T) { {"10de", "20f1"}, // A100 PCIe 40GB {"10de", "20f3"}, // A100 PCIe 80GB {"10de", "2233"}, // A30 - {"10de", "2331"}, // H100 SXM - {"10de", "2330"}, // H100 PCIe + {"10de", "2330"}, // H100 SXM + {"10de", "2331"}, // H100 PCIe {"10de", "233a"}, // H100 NVL + {"10de", "2335"}, // H200 SXM {"10de", "2bb5"}, // RTX Pro 6000 Blackwell Server Edition } for _, ids := range capable { diff --git a/spinifex/gpu/models.go b/spinifex/gpu/models.go index 9bf441cba..5352f6796 100644 --- a/spinifex/gpu/models.go +++ b/spinifex/gpu/models.go @@ -14,9 +14,10 @@ var knownModels = map[string]modelInfo{ "10de:20b5": {"NVIDIA A100 SXM 80GB", 81920}, "10de:20f1": {"NVIDIA A100 PCIe 40GB", 40960}, "10de:20f3": {"NVIDIA A100 PCIe 80GB", 81920}, - "10de:2331": {"NVIDIA H100 SXM", 81920}, - "10de:2330": {"NVIDIA H100 PCIe", 81920}, + "10de:2330": {"NVIDIA H100 SXM", 81920}, + "10de:2331": {"NVIDIA H100 PCIe", 81920}, "10de:233a": {"NVIDIA H100 NVL", 94208}, + "10de:2335": {"NVIDIA H200 SXM", 144384}, "10de:2233": {"NVIDIA A30", 24576}, "10de:26b5": {"NVIDIA L40", 46068}, "10de:26b9": {"NVIDIA L40S", 46068}, @@ -44,9 +45,10 @@ var computeModels = map[string]bool{ "10de:20b5": true, // A100 SXM 80GB "10de:20f1": true, // A100 PCIe 40GB "10de:20f3": true, // A100 PCIe 80GB - "10de:2331": true, // H100 SXM - "10de:2330": true, // H100 PCIe + "10de:2330": true, // H100 SXM + "10de:2331": true, // H100 PCIe "10de:233a": true, // H100 NVL + "10de:2335": true, // H200 SXM "10de:2233": true, // A30 "10de:26b5": true, // L40 "10de:26b9": true, // L40S @@ -65,9 +67,10 @@ var migCapableModels = map[string]bool{ "10de:20b5": true, // A100 SXM 80GB "10de:20f1": true, // A100 PCIe 40GB "10de:20f3": true, // A100 PCIe 80GB - "10de:2331": true, // H100 SXM - "10de:2330": true, // H100 PCIe + "10de:2330": true, // H100 SXM + "10de:2331": true, // H100 PCIe "10de:233a": true, // H100 NVL + "10de:2335": true, // H200 SXM "10de:2233": true, // A30 "10de:2bb5": true, // RTX Pro 6000 Blackwell Server Edition } diff --git a/spinifex/handlers/acm/service.go b/spinifex/handlers/acm/service.go new file mode 100644 index 000000000..c4cd937d1 --- /dev/null +++ b/spinifex/handlers/acm/service.go @@ -0,0 +1,15 @@ +package handlers_acm + +import "github.com/aws/aws-sdk-go/service/acm" + +// ACMService is the minimal AWS Certificate Manager-alike control plane: BYO +// certificate import and lifecycle. It covers only what ELBv2 HTTPS listeners +// need to resolve a Certificates[].CertificateArn to PEM material. Request +// flows (RequestCertificate + DNS/email validation), renewal, export, and +// tagging are intentionally out of scope for now. +type ACMService interface { + ImportCertificate(input *acm.ImportCertificateInput, accountID string) (*acm.ImportCertificateOutput, error) + DescribeCertificate(input *acm.DescribeCertificateInput, accountID string) (*acm.DescribeCertificateOutput, error) + ListCertificates(input *acm.ListCertificatesInput, accountID string) (*acm.ListCertificatesOutput, error) + DeleteCertificate(input *acm.DeleteCertificateInput, accountID string) (*acm.DeleteCertificateOutput, error) +} diff --git a/spinifex/handlers/acm/service_impl.go b/spinifex/handlers/acm/service_impl.go new file mode 100644 index 000000000..6ca51de2f --- /dev/null +++ b/spinifex/handlers/acm/service_impl.go @@ -0,0 +1,236 @@ +package handlers_acm + +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "log/slog" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/acm" + "github.com/google/uuid" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/config" + "github.com/nats-io/nats.go" +) + +const ( + defaultRegion = "ap-southeast-2" + + certStatusIssued = "ISSUED" + certTypeImported = "IMPORTED" +) + +// ACMServiceImpl is the local, KV-backed implementation of ACMService. +type ACMServiceImpl struct { + store *Store + region string +} + +var _ ACMService = (*ACMServiceImpl)(nil) + +// NewACMServiceImplWithNATS builds an ACM service backed by a JetStream KV +// store. cfg may be nil (tests); region then falls back to the default. +func NewACMServiceImplWithNATS(cfg *config.Config, nc *nats.Conn) (*ACMServiceImpl, error) { + store, err := NewStore(nc) + if err != nil { + return nil, err + } + region := defaultRegion + if cfg != nil && cfg.Region != "" { + region = cfg.Region + } + return &ACMServiceImpl{store: store, region: region}, nil +} + +// mintCertificateArn generates an ACM-style certificate ARN for accountID. +func (s *ACMServiceImpl) mintCertificateArn(accountID string) string { + return fmt.Sprintf("arn:aws:acm:%s:%s:certificate/%s", s.region, accountID, uuid.NewString()) +} + +// ImportCertificate validates the PEM material, parses the leaf for metadata, +// and stores it under a new (or, on re-import, the supplied) ACM ARN. +func (s *ACMServiceImpl) ImportCertificate(input *acm.ImportCertificateInput, accountID string) (*acm.ImportCertificateOutput, error) { + if input == nil || len(input.Certificate) == 0 || len(input.PrivateKey) == 0 { + return nil, errors.New(awserrors.ErrorInvalidParameter) + } + + // The leaf cert and private key must form a valid keypair. + if _, err := tls.X509KeyPair(input.Certificate, input.PrivateKey); err != nil { + slog.Debug("ImportCertificate: keypair validation failed", "err", err) + return nil, errors.New(awserrors.ErrorInvalidParameter) + } + + leaf, err := parseLeaf(input.Certificate) + if err != nil { + return nil, errors.New(awserrors.ErrorInvalidParameter) + } + + certArn := aws.StringValue(input.CertificateArn) + if certArn == "" { + certArn = s.mintCertificateArn(accountID) + } else { + // Re-import: the ARN must already exist and belong to the caller. + existing, gErr := s.store.GetCert(certArn) + if gErr != nil { + return nil, errors.New(awserrors.ErrorInternalError) + } + if existing == nil || existing.AccountID != accountID { + return nil, errors.New(awserrors.ErrorResourceNotFound) + } + } + + rec := &CertRecord{ + CertificateArn: certArn, + AccountID: accountID, + Certificate: string(input.Certificate), + CertificateChain: string(input.CertificateChain), + PrivateKey: string(input.PrivateKey), + DomainName: leafDomain(leaf), + SubjectAltNames: leaf.DNSNames, + Serial: leaf.SerialNumber.Text(16), + Subject: leaf.Subject.String(), + Issuer: leaf.Issuer.String(), + KeyAlgorithm: keyAlgorithm(leaf), + NotBefore: leaf.NotBefore, + NotAfter: leaf.NotAfter, + ImportedAt: time.Now().UTC(), + } + if err := s.store.PutCert(rec); err != nil { + slog.Error("ImportCertificate: store failed", "err", err) + return nil, errors.New(awserrors.ErrorInternalError) + } + + slog.Info("ImportCertificate: stored", "arn", certArn, "domain", rec.DomainName, "account", accountID) + return &acm.ImportCertificateOutput{CertificateArn: aws.String(certArn)}, nil +} + +// DescribeCertificate returns the CertificateDetail for an owned ARN. +func (s *ACMServiceImpl) DescribeCertificate(input *acm.DescribeCertificateInput, accountID string) (*acm.DescribeCertificateOutput, error) { + if input == nil || aws.StringValue(input.CertificateArn) == "" { + return nil, errors.New(awserrors.ErrorACMInvalidArn) + } + rec, err := s.lookupOwned(aws.StringValue(input.CertificateArn), accountID) + if err != nil { + return nil, err + } + return &acm.DescribeCertificateOutput{Certificate: recordToDetail(rec)}, nil +} + +// ListCertificates returns summaries for every cert owned by accountID. +func (s *ACMServiceImpl) ListCertificates(input *acm.ListCertificatesInput, accountID string) (*acm.ListCertificatesOutput, error) { + recs, err := s.store.ListCerts(accountID) + if err != nil { + return nil, errors.New(awserrors.ErrorInternalError) + } + out := &acm.ListCertificatesOutput{} + for _, rec := range recs { + out.CertificateSummaryList = append(out.CertificateSummaryList, &acm.CertificateSummary{ + CertificateArn: aws.String(rec.CertificateArn), + DomainName: aws.String(rec.DomainName), + Status: aws.String(certStatusIssued), + Type: aws.String(certTypeImported), + KeyAlgorithm: aws.String(rec.KeyAlgorithm), + NotBefore: timePtr(rec.NotBefore), + NotAfter: timePtr(rec.NotAfter), + ImportedAt: timePtr(rec.ImportedAt), + InUse: aws.Bool(false), + }) + } + return out, nil +} + +// DeleteCertificate removes an owned cert; unknown ARN → ResourceNotFound. +func (s *ACMServiceImpl) DeleteCertificate(input *acm.DeleteCertificateInput, accountID string) (*acm.DeleteCertificateOutput, error) { + if input == nil || aws.StringValue(input.CertificateArn) == "" { + return nil, errors.New(awserrors.ErrorACMInvalidArn) + } + if _, err := s.lookupOwned(aws.StringValue(input.CertificateArn), accountID); err != nil { + return nil, err + } + if _, err := s.store.DeleteCert(aws.StringValue(input.CertificateArn)); err != nil { + return nil, errors.New(awserrors.ErrorInternalError) + } + return &acm.DeleteCertificateOutput{}, nil +} + +// lookupOwned fetches a cert by ARN, returning ResourceNotFound when absent or +// owned by a different account (no cross-account disclosure). +func (s *ACMServiceImpl) lookupOwned(certArn, accountID string) (*CertRecord, error) { + rec, err := s.store.GetCert(certArn) + if err != nil { + return nil, errors.New(awserrors.ErrorInternalError) + } + if rec == nil || rec.AccountID != accountID { + return nil, errors.New(awserrors.ErrorResourceNotFound) + } + return rec, nil +} + +func recordToDetail(rec *CertRecord) *acm.CertificateDetail { + detail := &acm.CertificateDetail{ + CertificateArn: aws.String(rec.CertificateArn), + DomainName: aws.String(rec.DomainName), + Serial: aws.String(rec.Serial), + Subject: aws.String(rec.Subject), + Issuer: aws.String(rec.Issuer), + KeyAlgorithm: aws.String(rec.KeyAlgorithm), + Status: aws.String(certStatusIssued), + Type: aws.String(certTypeImported), + NotBefore: timePtr(rec.NotBefore), + NotAfter: timePtr(rec.NotAfter), + ImportedAt: timePtr(rec.ImportedAt), + InUseBy: []*string{}, + } + if len(rec.SubjectAltNames) > 0 { + detail.SubjectAlternativeNames = aws.StringSlice(rec.SubjectAltNames) + } + return detail +} + +func timePtr(t time.Time) *time.Time { + if t.IsZero() { + return nil + } + return &t +} + +// parseLeaf decodes the first CERTIFICATE block from certPEM and parses it. +func parseLeaf(certPEM []byte) (*x509.Certificate, error) { + block, _ := pem.Decode(certPEM) + if block == nil || block.Type != "CERTIFICATE" { + return nil, fmt.Errorf("no PEM CERTIFICATE block") + } + return x509.ParseCertificate(block.Bytes) +} + +// leafDomain returns the certificate's primary domain: CommonName, else the +// first SAN, else empty. +func leafDomain(leaf *x509.Certificate) string { + if cn := strings.TrimSpace(leaf.Subject.CommonName); cn != "" { + return cn + } + if len(leaf.DNSNames) > 0 { + return leaf.DNSNames[0] + } + return "" +} + +// keyAlgorithm maps the leaf public key to an ACM-style algorithm string +// (RSA_2048, EC_prime256v1, ...). +func keyAlgorithm(leaf *x509.Certificate) string { + switch pub := leaf.PublicKey.(type) { + case *rsa.PublicKey: + return fmt.Sprintf("RSA_%d", pub.N.BitLen()) + case *ecdsa.PublicKey: + return "EC_" + pub.Curve.Params().Name + default: + return "UNKNOWN" + } +} diff --git a/spinifex/handlers/acm/service_impl_test.go b/spinifex/handlers/acm/service_impl_test.go new file mode 100644 index 000000000..7c8729176 --- /dev/null +++ b/spinifex/handlers/acm/service_impl_test.go @@ -0,0 +1,166 @@ +package handlers_acm + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/acm" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const testAccountID = "000000000001" + +func setupACMService(t *testing.T) *ACMServiceImpl { + t.Helper() + _, nc, _ := testutil.StartTestJetStream(t) + svc, err := NewACMServiceImplWithNATS(nil, nc) + require.NoError(t, err) + return svc +} + +// genCert returns a self-signed leaf certificate + private key as PEM for the +// given common name. +func genCert(t *testing.T, cn string, dnsNames ...string) (certPEM, keyPEM []byte) { + t.Helper() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + tmpl := &x509.Certificate{ + SerialNumber: big.NewInt(time.Now().UnixNano()), + Subject: pkix.Name{CommonName: cn}, + DNSNames: dnsNames, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key) + require.NoError(t, err) + keyDER, err := x509.MarshalECPrivateKey(key) + require.NoError(t, err) + + certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) + keyPEM = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER}) + return certPEM, keyPEM +} + +func TestImportCertificate_MintsArnAndParsesLeaf(t *testing.T) { + svc := setupACMService(t) + certPEM, keyPEM := genCert(t, "example.com", "example.com", "www.example.com") + + out, err := svc.ImportCertificate(&acm.ImportCertificateInput{ + Certificate: certPEM, + PrivateKey: keyPEM, + }, testAccountID) + require.NoError(t, err) + require.NotNil(t, out.CertificateArn) + assert.Contains(t, aws.StringValue(out.CertificateArn), "arn:aws:acm:") + assert.Contains(t, aws.StringValue(out.CertificateArn), ":"+testAccountID+":certificate/") + + desc, err := svc.DescribeCertificate(&acm.DescribeCertificateInput{ + CertificateArn: out.CertificateArn, + }, testAccountID) + require.NoError(t, err) + d := desc.Certificate + assert.Equal(t, "example.com", aws.StringValue(d.DomainName)) + assert.Equal(t, certStatusIssued, aws.StringValue(d.Status)) + assert.Equal(t, certTypeImported, aws.StringValue(d.Type)) + assert.Equal(t, "EC_P-256", aws.StringValue(d.KeyAlgorithm)) + assert.ElementsMatch(t, []string{"example.com", "www.example.com"}, aws.StringValueSlice(d.SubjectAlternativeNames)) + require.NotNil(t, d.NotAfter) +} + +func TestImportCertificate_MismatchedKeyRejected(t *testing.T) { + svc := setupACMService(t) + certPEM, _ := genCert(t, "a.example.com") + _, otherKey := genCert(t, "b.example.com") + + _, err := svc.ImportCertificate(&acm.ImportCertificateInput{ + Certificate: certPEM, + PrivateKey: otherKey, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorInvalidParameter) +} + +func TestImportCertificate_GarbagePEMRejected(t *testing.T) { + svc := setupACMService(t) + _, err := svc.ImportCertificate(&acm.ImportCertificateInput{ + Certificate: []byte("not a pem"), + PrivateKey: []byte("nope"), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorInvalidParameter) +} + +func TestListCertificates_ScopedToAccount(t *testing.T) { + svc := setupACMService(t) + c1, k1 := genCert(t, "one.example.com") + c2, k2 := genCert(t, "two.example.com") + + _, err := svc.ImportCertificate(&acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) + require.NoError(t, err) + _, err = svc.ImportCertificate(&acm.ImportCertificateInput{Certificate: c2, PrivateKey: k2}, "000000000002") + require.NoError(t, err) + + list, err := svc.ListCertificates(&acm.ListCertificatesInput{}, testAccountID) + require.NoError(t, err) + require.Len(t, list.CertificateSummaryList, 1) + assert.Equal(t, "one.example.com", aws.StringValue(list.CertificateSummaryList[0].DomainName)) +} + +func TestDescribeCertificate_CrossAccountHidden(t *testing.T) { + svc := setupACMService(t) + c1, k1 := genCert(t, "secret.example.com") + out, err := svc.ImportCertificate(&acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) + require.NoError(t, err) + + _, err = svc.DescribeCertificate(&acm.DescribeCertificateInput{ + CertificateArn: out.CertificateArn, + }, "000000000002") + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorResourceNotFound) +} + +func TestDeleteCertificate(t *testing.T) { + svc := setupACMService(t) + c1, k1 := genCert(t, "del.example.com") + out, err := svc.ImportCertificate(&acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) + require.NoError(t, err) + + _, err = svc.DeleteCertificate(&acm.DeleteCertificateInput{CertificateArn: out.CertificateArn}, testAccountID) + require.NoError(t, err) + + // Gone now. + _, err = svc.DescribeCertificate(&acm.DescribeCertificateInput{CertificateArn: out.CertificateArn}, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorResourceNotFound) + + // Deleting again → ResourceNotFound. + _, err = svc.DeleteCertificate(&acm.DeleteCertificateInput{CertificateArn: out.CertificateArn}, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorResourceNotFound) +} + +func TestImportCertificate_ReimportUnknownArnRejected(t *testing.T) { + svc := setupACMService(t) + c1, k1 := genCert(t, "re.example.com") + _, err := svc.ImportCertificate(&acm.ImportCertificateInput{ + Certificate: c1, + PrivateKey: k1, + CertificateArn: aws.String("arn:aws:acm:ap-southeast-2:000000000001:certificate/does-not-exist"), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorResourceNotFound) +} diff --git a/spinifex/handlers/acm/service_nats.go b/spinifex/handlers/acm/service_nats.go new file mode 100644 index 000000000..da5ad276f --- /dev/null +++ b/spinifex/handlers/acm/service_nats.go @@ -0,0 +1,40 @@ +package handlers_acm + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/acm" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" +) + +const defaultTimeout = 30 * time.Second + +// NATSACMService is the gateway-side client that forwards ACM operations to +// the daemon over NATS request/response. +type NATSACMService struct { + natsConn *nats.Conn +} + +var _ ACMService = (*NATSACMService)(nil) + +// NewNATSACMService creates a NATS-backed ACM service client. +func NewNATSACMService(conn *nats.Conn) ACMService { + return &NATSACMService{natsConn: conn} +} + +func (s *NATSACMService) ImportCertificate(input *acm.ImportCertificateInput, accountID string) (*acm.ImportCertificateOutput, error) { + return utils.NATSRequest[acm.ImportCertificateOutput](s.natsConn, "acm.ImportCertificate", input, defaultTimeout, accountID) +} + +func (s *NATSACMService) DescribeCertificate(input *acm.DescribeCertificateInput, accountID string) (*acm.DescribeCertificateOutput, error) { + return utils.NATSRequest[acm.DescribeCertificateOutput](s.natsConn, "acm.DescribeCertificate", input, defaultTimeout, accountID) +} + +func (s *NATSACMService) ListCertificates(input *acm.ListCertificatesInput, accountID string) (*acm.ListCertificatesOutput, error) { + return utils.NATSRequest[acm.ListCertificatesOutput](s.natsConn, "acm.ListCertificates", input, defaultTimeout, accountID) +} + +func (s *NATSACMService) DeleteCertificate(input *acm.DeleteCertificateInput, accountID string) (*acm.DeleteCertificateOutput, error) { + return utils.NATSRequest[acm.DeleteCertificateOutput](s.natsConn, "acm.DeleteCertificate", input, defaultTimeout, accountID) +} diff --git a/spinifex/handlers/acm/service_nats_test.go b/spinifex/handlers/acm/service_nats_test.go new file mode 100644 index 000000000..b2e0c8e64 --- /dev/null +++ b/spinifex/handlers/acm/service_nats_test.go @@ -0,0 +1,25 @@ +package handlers_acm + +import ( + "testing" + + "github.com/aws/aws-sdk-go/service/acm" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/require" +) + +// With no daemon subscribed to acm.*, each NATS request fast-fails with +// no-responders. The point is to exercise the client delegation paths. +func TestNATSACMService_NoResponder(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + svc := NewNATSACMService(nc) + + _, err := svc.ImportCertificate(&acm.ImportCertificateInput{}, testAccountID) + require.Error(t, err) + _, err = svc.DescribeCertificate(&acm.DescribeCertificateInput{}, testAccountID) + require.Error(t, err) + _, err = svc.ListCertificates(&acm.ListCertificatesInput{}, testAccountID) + require.Error(t, err) + _, err = svc.DeleteCertificate(&acm.DeleteCertificateInput{}, testAccountID) + require.Error(t, err) +} diff --git a/spinifex/handlers/acm/store.go b/spinifex/handlers/acm/store.go new file mode 100644 index 000000000..7764f045f --- /dev/null +++ b/spinifex/handlers/acm/store.go @@ -0,0 +1,143 @@ +package handlers_acm + +import ( + "encoding/json" + "errors" + "fmt" + "log/slog" + "strings" + "time" + + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" +) + +const ( + KVBucketACM = "spinifex-acm" + KVBucketACMVersion = 1 + + // KeyPrefixCert namespaces certificate records within the bucket. + KeyPrefixCert = "cert." +) + +// CertRecord is the stored representation of an imported certificate. PEM +// material (leaf, chain, private key) is held alongside the parsed metadata +// surfaced by DescribeCertificate. AccountID scopes ownership so list/describe +// never cross account boundaries. +type CertRecord struct { + CertificateArn string `json:"certificate_arn"` + AccountID string `json:"account_id"` + Certificate string `json:"certificate"` + CertificateChain string `json:"certificate_chain,omitempty"` + PrivateKey string `json:"private_key"` + DomainName string `json:"domain_name"` + SubjectAltNames []string `json:"subject_alt_names,omitempty"` + Serial string `json:"serial"` + Subject string `json:"subject"` + Issuer string `json:"issuer"` + KeyAlgorithm string `json:"key_algorithm"` + NotBefore time.Time `json:"not_before"` + NotAfter time.Time `json:"not_after"` + ImportedAt time.Time `json:"imported_at"` +} + +// Store provides CRUD for ACM certificate records backed by JetStream KV. +type Store struct { + kv nats.KeyValue +} + +// NewStore creates an ACM store using the provided NATS connection. +func NewStore(nc *nats.Conn) (*Store, error) { + js, err := nc.JetStream() + if err != nil { + return nil, fmt.Errorf("failed to get JetStream context: %w", err) + } + + kv, err := utils.GetOrCreateKVBucket(js, KVBucketACM, KVBucketACMVersion) + if err != nil { + return nil, fmt.Errorf("failed to create KV bucket %s: %w", KVBucketACM, err) + } + + slog.Info("ACM store initialized", "bucket", KVBucketACM) + return &Store{kv: kv}, nil +} + +// certKey derives the KV key from a certificate ARN. The ARN's certificate ID +// (the UUID after "certificate/") is the stable per-record key. +func certKey(certArn string) string { + id := certArn + if i := strings.LastIndex(certArn, "/"); i >= 0 { + id = certArn[i+1:] + } + return KeyPrefixCert + id +} + +// PutCert stores (or replaces) a certificate record. +func (s *Store) PutCert(rec *CertRecord) error { + data, err := json.Marshal(rec) + if err != nil { + return fmt.Errorf("marshal cert: %w", err) + } + _, err = s.kv.Put(certKey(rec.CertificateArn), data) + return err +} + +// GetCert retrieves a certificate by ARN, returning (nil, nil) when absent. +func (s *Store) GetCert(certArn string) (*CertRecord, error) { + entry, err := s.kv.Get(certKey(certArn)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, nil + } + return nil, err + } + var rec CertRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal cert: %w", err) + } + return &rec, nil +} + +// DeleteCert removes a certificate by ARN. Returns (false, nil) when absent. +func (s *Store) DeleteCert(certArn string) (bool, error) { + if _, err := s.kv.Get(certKey(certArn)); err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return false, nil + } + return false, err + } + if err := s.kv.Delete(certKey(certArn)); err != nil { + return false, err + } + return true, nil +} + +// ListCerts returns all certificate records owned by accountID. +func (s *Store) ListCerts(accountID string) ([]*CertRecord, error) { + keys, err := s.kv.Keys() + if err != nil { + if errors.Is(err, nats.ErrNoKeysFound) { + return nil, nil + } + return nil, err + } + var out []*CertRecord + for _, key := range keys { + if !strings.HasPrefix(key, KeyPrefixCert) { + continue + } + entry, err := s.kv.Get(key) + if err != nil { + continue + } + var rec CertRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + continue + } + if rec.AccountID != accountID { + continue + } + out = append(out, &rec) + } + return out, nil +} diff --git a/spinifex/handlers/ec2/eip/service_impl_test.go b/spinifex/handlers/ec2/eip/service_impl_test.go index f0c35df94..44df84e15 100644 --- a/spinifex/handlers/ec2/eip/service_impl_test.go +++ b/spinifex/handlers/ec2/eip/service_impl_test.go @@ -82,22 +82,23 @@ func TestEIP_Release(t *testing.T) { }, testAccountID) require.NoError(t, err) - // Verify IP returned to pool by allocating again — should get same IP + // Round-robin: re-allocating does NOT hand back the just-released IP + // (siv-246); the cursor advances, so the new EIP differs. out2, err := svc.AllocateAddress(&ec2.AllocateAddressInput{}, testAccountID) require.NoError(t, err) - assert.Equal(t, allocatedIP, *out2.PublicIp) + assert.NotEqual(t, allocatedIP, *out2.PublicIp, "released EIP must not be reused immediately") - // Verify the pool shows the IP was released and re-allocated + // The released IP stays free (not re-allocated) until the cursor cycles. record, err := ipam.GetPoolRecord("test-pool") require.NoError(t, err) - _, allocated := record.Allocated[allocatedIP] - // After re-allocation, IP should be allocated again by the EIP service - // but let's just verify the release worked by checking describe returns nothing for old alloc + _, stillAllocated := record.Allocated[allocatedIP] + assert.False(t, stillAllocated, "released IP stays free until the cursor cycles the range") + + // The released allocation is gone; describing it by its old ID errors. _, descErr := svc.DescribeAddresses(&ec2.DescribeAddressesInput{ AllocationIds: []*string{out.AllocationId}, }, testAccountID) assert.Error(t, descErr) - _ = allocated // suppress unused } func TestEIP_ReleaseWhileAssociated(t *testing.T) { diff --git a/spinifex/handlers/ec2/instance/helpers_test.go b/spinifex/handlers/ec2/instance/helpers_test.go index 5f690b912..11687ee04 100644 --- a/spinifex/handlers/ec2/instance/helpers_test.go +++ b/spinifex/handlers/ec2/instance/helpers_test.go @@ -1,6 +1,7 @@ package handlers_ec2_instance import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -9,21 +10,24 @@ import ( // --- generateNetworkConfig --- func TestGenerateNetworkConfig_BothEmpty(t *testing.T) { - cfg := generateNetworkConfig("", "", "", "", nil) + cfg := generateNetworkConfig("", "", "", "", nil, true) assert.Equal(t, cloudInitNetworkConfigWildcard, cfg) } func TestGenerateNetworkConfig_OneEmpty(t *testing.T) { - cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "", "", "", nil) + cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "", "", "", nil, true) assert.Contains(t, cfg, "vpc0:", "eniMAC alone should produce per-interface config") assert.NotContains(t, cfg, "dev0:", "no dev NIC without devMAC") + // The IMDS on-link route rides vpc0 even with no dev NIC / extra ENIs — the + // common production shape — so guard it here too, not only on the multi-NIC path. + assert.Contains(t, cfg, "to: 169.254.169.254/32", "vpc0 must carry the IMDS on-link route") - cfg = generateNetworkConfig("", "02:00:00:dd:ee:ff", "", "", nil) + cfg = generateNetworkConfig("", "02:00:00:dd:ee:ff", "", "", nil, true) assert.Equal(t, cloudInitNetworkConfigWildcard, cfg, "should fall back to wildcard if eniMAC empty") } func TestGenerateNetworkConfig_DualNIC(t *testing.T) { - cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:00:00:dd:ee:ff", "", "", nil) + cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:00:00:dd:ee:ff", "", "", nil, true) assert.Contains(t, cfg, "version: 2") assert.Contains(t, cfg, `macaddress: "02:00:00:aa:bb:cc"`) assert.Contains(t, cfg, `macaddress: "02:00:00:dd:ee:ff"`) @@ -35,7 +39,7 @@ func TestGenerateNetworkConfig_DualNIC(t *testing.T) { } func TestGenerateNetworkConfig_TripleNIC(t *testing.T) { - cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:de:00:dd:ee:ff", "02:a0:00:11:22:33", "10.15.8.101", nil) + cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:de:00:dd:ee:ff", "02:a0:00:11:22:33", "10.15.8.101", nil, true) assert.Contains(t, cfg, "version: 2") assert.Contains(t, cfg, `macaddress: "02:00:00:aa:bb:cc"`) assert.Contains(t, cfg, `macaddress: "02:de:00:dd:ee:ff"`) @@ -49,7 +53,7 @@ func TestGenerateNetworkConfig_TripleNIC(t *testing.T) { func TestGenerateNetworkConfig_MgmtWithoutDev(t *testing.T) { // System instances: eniMAC + mgmtMAC, no devMAC — should get per-interface config with mgmt NIC - cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "", "02:a0:00:11:22:33", "10.15.8.101", nil) + cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "", "02:a0:00:11:22:33", "10.15.8.101", nil, true) assert.Contains(t, cfg, "vpc0:") assert.NotContains(t, cfg, "dev0:", "no dev NIC without devMAC") assert.Contains(t, cfg, "mgmt0:") @@ -58,14 +62,62 @@ func TestGenerateNetworkConfig_MgmtWithoutDev(t *testing.T) { } func TestGenerateNetworkConfig_MgmtMACWithoutIP(t *testing.T) { - cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:de:00:dd:ee:ff", "02:a0:00:11:22:33", "", nil) + cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:de:00:dd:ee:ff", "02:a0:00:11:22:33", "", nil, true) assert.NotContains(t, cfg, "mgmt0:", "mgmt NIC should not appear without IP") } func TestGenerateNetworkConfig_MgmtIPWithoutMAC(t *testing.T) { - cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:de:00:dd:ee:ff", "", "10.15.8.101", nil) + cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:de:00:dd:ee:ff", "", "10.15.8.101", nil, true) assert.NotContains(t, cfg, "mgmt0:", "mgmt NIC should not appear without MAC") } +func TestGenerateNetworkConfig_IMDSOnLinkRoute(t *testing.T) { + // The primary VPC NIC carries an on-link route to the IMDS metadata IP so the + // guest ARPs 169.254.169.254 directly; the per-subnet localport answers. + cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:00:00:dd:ee:ff", "", "", + []string{"02:00:00:bb:bb:bb"}, true) + assert.Contains(t, cfg, "to: 169.254.169.254/32") + assert.Contains(t, cfg, "scope: link") + // Only the primary NIC (vpc0) gets it — not extra VPC NICs or the dev NIC, + // matching AWS (IMDS reached via the primary ENI). + assert.Equal(t, 1, strings.Count(cfg, "169.254.169.254/32")) +} + +func TestGenerateNetworkConfig_IMDSRouteOmittedWhenDisabled(t *testing.T) { + // Alpine's eni renderer crashes on a gateway-less route, so the route is + // omitted from network-config and delivered out-of-band (see + // buildAlpineCloudInit). emitIMDSRoute=false must drop it entirely. + cfg := generateNetworkConfig("02:00:00:aa:bb:cc", "02:00:00:dd:ee:ff", "", "", nil, false) + assert.Contains(t, cfg, "vpc0:") + assert.NotContains(t, cfg, "169.254.169.254/32", "route must not render when emitIMDSRoute=false") + assert.NotContains(t, cfg, " routes:", "no vpc0 routes block when the only route is the IMDS one") +} + +// selectNetworkConfigForFamily must omit the IMDS route for Alpine (eni renderer +// can't handle the gateway-less route) but keep it for everyone else. +func TestSelectNetworkConfigForFamily_AlpineOmitsIMDSRoute(t *testing.T) { + alpine := selectNetworkConfigForFamily("alpine", "02:00:00:aa:bb:cc", "02:00:00:dd:ee:ff", "", "", nil) + assert.NotContains(t, alpine, "169.254.169.254/32", "Alpine network-config must not carry the IMDS route") + + deb := selectNetworkConfigForFamily("debian", "02:00:00:aa:bb:cc", "02:00:00:dd:ee:ff", "", "", nil) + assert.Contains(t, deb, "169.254.169.254/32", "non-Alpine families keep the netplan IMDS route") +} + +// buildAlpineCloudInit delivers the IMDS on-link route via a persistent +// /etc/local.d script + OpenRC local service, since the eni renderer can't. +func TestBuildAlpineCloudInit(t *testing.T) { + wf, rc := buildAlpineCloudInit("02:00:00:aa:bb:cc") + assert.Contains(t, wf, "/etc/local.d/imds-onlink-route.start") + assert.Contains(t, wf, "ip route show default") + assert.Contains(t, wf, `ip route replace 169.254.169.254/32 dev "$dev" scope link`) + assert.Contains(t, rc, "rc-update") + assert.Contains(t, rc, "local") + + // No VPC NIC → nothing to add. + wf, rc = buildAlpineCloudInit("") + assert.Empty(t, wf) + assert.Empty(t, rc) +} + // Route for multi-node LB mgmt traffic is handled via the fw_cfg netcfg // blob delivered to the LB microVM, not in the network-config. diff --git a/spinifex/handlers/ec2/instance/service.go b/spinifex/handlers/ec2/instance/service.go index c552157c3..8c7e40d42 100644 --- a/spinifex/handlers/ec2/instance/service.go +++ b/spinifex/handlers/ec2/instance/service.go @@ -129,15 +129,32 @@ type SubnetInfo struct { MapPublicIpOnLaunch bool } +// ENIInfo carries the subset of ENI metadata RunInstances needs to attach a +// pre-created ENI as the primary interface. Translated from +// handlers/ec2/vpc.ENIRecord by the daemon adapter to avoid a cyclic +// instance↔vpc import. +type ENIInfo struct { + NetworkInterfaceID string + SubnetID string + VpcID string + PrivateIpAddress string + MacAddress string + Status string + SecurityGroupIDs []string +} + // ENICreator covers the VPC/ENI operations RunInstances performs while // auto-attaching a primary interface. Implemented via an adapter over // handlers/ec2/vpc.VPCServiceImpl on the daemon. DetachENI lives here (not on // ENIDeleter) because the launch-time NAT-rollback path needs to flip the ENI // to "available" before ENIDeleter.DeleteNetworkInterface, which rejects -// in-use ENIs. +// in-use ENIs. GetENI supports the pre-created-ENI launch path where the +// caller passes NetworkInterfaces[0].NetworkInterfaceId and RunInstances +// attaches the existing ENI instead of creating a new one. type ENICreator interface { GetDefaultSubnet(accountID string) (*SubnetInfo, error) GetSubnet(accountID, subnetID string) (*SubnetInfo, error) + GetENI(accountID, eniID string) (*ENIInfo, error) CreateNetworkInterface(input *ec2.CreateNetworkInterfaceInput, accountID string) (*ec2.CreateNetworkInterfaceOutput, error) AttachENI(accountID, eniID, instanceID string, deviceIndex int64) (string, error) DetachENI(accountID, eniID string) error diff --git a/spinifex/handlers/ec2/instance/service_impl.go b/spinifex/handlers/ec2/instance/service_impl.go index 5e0c17b53..80e881d10 100644 --- a/spinifex/handlers/ec2/instance/service_impl.go +++ b/spinifex/handlers/ec2/instance/service_impl.go @@ -24,6 +24,7 @@ import ( "github.com/mulgadc/spinifex/spinifex/filterutil" "github.com/mulgadc/spinifex/spinifex/gpu" handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" + handlers_imds "github.com/mulgadc/spinifex/spinifex/handlers/imds" "github.com/mulgadc/spinifex/spinifex/instancetypes" "github.com/mulgadc/spinifex/spinifex/network/topology" "github.com/mulgadc/spinifex/spinifex/objectstore" @@ -63,19 +64,21 @@ ca_certs: {{.UserDataCloudConfig}} {{end}} -{{if or .RHELWriteFiles .UserDataScript}} +{{if or .RHELWriteFiles .AlpineWriteFiles .UserDataScript}} write_files: {{- if .RHELWriteFiles}} -{{.RHELWriteFiles}}{{end}}{{if .UserDataScript}} +{{.RHELWriteFiles}}{{end}}{{if .AlpineWriteFiles}} +{{.AlpineWriteFiles}}{{end}}{{if .UserDataScript}} - path: /tmp/cloud-init-startup.sh permissions: '0755' content: | {{.UserDataScript}}{{end}} {{end}} -{{if or .RHELRunCmd .UserDataScript}} +{{if or .RHELRunCmd .AlpineRunCmd .UserDataScript}} runcmd: {{- if .RHELRunCmd}} -{{.RHELRunCmd}}{{end}}{{if .UserDataScript}} +{{.RHELRunCmd}}{{end}}{{if .AlpineRunCmd}} +{{.AlpineRunCmd}}{{end}}{{if .UserDataScript}} - [ "/bin/bash", "/tmp/cloud-init-startup.sh" ] {{end}} {{end}} @@ -117,7 +120,11 @@ func selectNetworkConfigForFamily(distroFamily, eniMAC, devMAC, mgmtMAC, mgmtIP if distroFamily == "rhel" { return cloudInitNetworkConfigDisabled } - return generateNetworkConfig(eniMAC, devMAC, mgmtMAC, mgmtIP, extraENIMACs) + // Alpine renders via cloud-init's eni backend, which cannot emit a + // gateway-less route; the IMDS on-link route is delivered via + // buildAlpineCloudInit instead. Debian/Ubuntu render via netplan, which + // handles the on-link route natively. + return generateNetworkConfig(eniMAC, devMAC, mgmtMAC, mgmtIP, extraENIMACs, distroFamily != "alpine") } // generateNetworkConfig produces the cloud-init network-config for the instance. @@ -133,7 +140,15 @@ func selectNetworkConfigForFamily(distroFamily, eniMAC, devMAC, mgmtMAC, mgmtIP // // The dev NIC still gets an IP via DHCP (needed for hostfwd port forwarding) // but dhcp4-overrides prevents it from installing routes or DNS. -func generateNetworkConfig(eniMAC, devMAC, mgmtMAC, mgmtIP string, extraENIMACs []string) string { +// +// The primary VPC NIC (vpc0) also carries an on-link route to the IMDS metadata +// IP (169.254.169.254): under the L2 datapath the metadata localport lives on +// the guest's subnet switch and answers ARP directly, so the guest must treat +// the address as on-link rather than steering it via the default gateway. The +// route is reached via the primary ENI, matching AWS; it is not delivered via +// DHCP option 121 (which would force the default route into option 121 per +// RFC 3442) and the default gateway stays option 3. +func generateNetworkConfig(eniMAC, devMAC, mgmtMAC, mgmtIP string, extraENIMACs []string, emitIMDSRoute bool) string { if eniMAC == "" { return cloudInitNetworkConfigWildcard } @@ -146,6 +161,16 @@ func generateNetworkConfig(eniMAC, devMAC, mgmtMAC, mgmtIP string, extraENIMACs dhcp4: true dhcp-identifier: mac `, eniMAC) + // The gateway-less IMDS on-link route renders only under netplan. cloud-init's + // eni renderer (Alpine) crashes on a route with no gateway, taking the whole + // network-config down with it, so eni-rendered guests get this route via + // buildAlpineCloudInit instead (emitIMDSRoute=false). + if emitIMDSRoute { + cfg += fmt.Sprintf(` routes: + - to: %s/32 + scope: link +`, handlers_imds.MetaDataServerIP) + } for i, mac := range extraENIMACs { if mac == "" { @@ -208,6 +233,12 @@ type CloudInitData struct { // + nmcli reload/up) needed to load and bring up the keyfiles. Empty on // non-RHEL guests. RHELRunCmd string + // AlpineWriteFiles / AlpineRunCmd carry the IMDS on-link route delivery for + // Alpine guests (a persistent /etc/local.d script + OpenRC `local` enable), + // since the eni renderer cannot emit the gateway-less route in the + // network-config. Empty on non-Alpine guests and on non-VPC instances. + AlpineWriteFiles string + AlpineRunCmd string } // buildRHELCloudInit produces the cloud-init write_files (NM keyfiles) and @@ -215,9 +246,9 @@ type CloudInitData struct { // RHEL-family guest. Returns empty strings when eniMAC is empty (wildcard / // non-VPC path) so NM's default autoconnect handles the interface. // -// Mirrors generateNetworkConfig's per-interface structure: vpc0 + optional -// vpc1..N for extra ENIs, dev0 (DHCP with route/DNS suppression), mgmt0 -// (static). Owning the keyfile bytes directly avoids relying on cloud-init's +// Mirrors generateNetworkConfig's per-interface structure: vpc0 (DHCP + on-link +// IMDS route) + optional vpc1..N for extra ENIs, dev0 (DHCP with route/DNS +// suppression), mgmt0 (static). Owning the keyfile bytes directly avoids relying on cloud-init's // v2-to-NM renderer round-tripping dhcp-identifier: mac → dhcp-client-id=mac, // which OVN's MAC-keyed DHCP requires. // @@ -236,7 +267,7 @@ func buildRHELCloudInit(eniMAC, devMAC, mgmtMAC, mgmtIP string, extraENIMACs []s rc.WriteString(" - [ restorecon, -R, /etc/NetworkManager/system-connections/ ]\n") rc.WriteString(" - [ nmcli, connection, reload ]\n") - appendRHELDHCPKeyfile(&wf, "vpc0", eniMAC, false) + appendRHELDHCPKeyfile(&wf, "vpc0", eniMAC, false, true) rc.WriteString(" - [ nmcli, connection, up, vpc0 ]\n") for i, mac := range extraENIMACs { @@ -244,12 +275,12 @@ func buildRHELCloudInit(eniMAC, devMAC, mgmtMAC, mgmtIP string, extraENIMACs []s continue } name := fmt.Sprintf("vpc%d", i+1) - appendRHELDHCPKeyfile(&wf, name, mac, false) + appendRHELDHCPKeyfile(&wf, name, mac, false, false) fmt.Fprintf(&rc, " - [ nmcli, connection, up, %s ]\n", name) } if devMAC != "" { - appendRHELDHCPKeyfile(&wf, "dev0", devMAC, true) + appendRHELDHCPKeyfile(&wf, "dev0", devMAC, true, false) rc.WriteString(" - [ nmcli, connection, up, dev0 ]\n") } @@ -264,7 +295,7 @@ func buildRHELCloudInit(eniMAC, devMAC, mgmtMAC, mgmtIP string, extraENIMACs []s return strings.TrimRight(wf.String(), "\n"), strings.TrimRight(rc.String(), "\n") } -func appendRHELDHCPKeyfile(b *strings.Builder, name, mac string, suppressDefaults bool) { +func appendRHELDHCPKeyfile(b *strings.Builder, name, mac string, suppressDefaults, imdsRoute bool) { fmt.Fprintf(b, " - path: /etc/NetworkManager/system-connections/%s.nmconnection\n", name) b.WriteString(" owner: root:root\n") b.WriteString(" permissions: '0600'\n") @@ -283,6 +314,12 @@ func appendRHELDHCPKeyfile(b *strings.Builder, name, mac string, suppressDefault b.WriteString(" never-default=true\n") b.WriteString(" ignore-auto-dns=true\n") } + if imdsRoute { + // On-link route to the IMDS metadata IP so the guest ARPs 169.254.169.254 + // directly on this NIC; the per-subnet localport answers. No next hop = + // link scope. Matches the netplan path in generateNetworkConfig. + fmt.Fprintf(b, " route1=%s/32\n", handlers_imds.MetaDataServerIP) + } b.WriteString(" [ipv6]\n") b.WriteString(" method=disabled\n") } @@ -304,6 +341,43 @@ func appendRHELStaticKeyfile(b *strings.Builder, name, mac, ip string) { b.WriteString(" method=disabled\n") } +// buildAlpineCloudInit produces the cloud-init write_files + runcmd blocks that +// deliver the IMDS on-link route on Alpine guests. Returns empty strings when +// eniMAC is empty (wildcard / non-VPC path). +// +// Alpine renders networking via cloud-init's eni backend, which crashes on a +// gateway-less route (it assumes every route has a next hop), so the route is +// omitted from the netplan network-config (see generateNetworkConfig) and added +// here instead. A persistent /etc/local.d script keeps the on-link route present +// across reboots — runcmd runs once per instance, so the OpenRC `local` service +// re-applies it on every boot. cloud-init's eni renderer renames the primary +// VPC NIC to vpc0 via a 70-persistent-net.rules udev rule, so the device name is +// stable. `ip route replace` is idempotent. +func buildAlpineCloudInit(eniMAC string) (writeFiles, runCmd string) { + if eniMAC == "" { + return "", "" + } + + var wf strings.Builder + wf.WriteString(" - path: /etc/local.d/imds-onlink-route.start\n") + wf.WriteString(" owner: root:root\n") + wf.WriteString(" permissions: '0755'\n") + wf.WriteString(" content: |\n") + wf.WriteString(" #!/bin/sh\n") + // Resolve the primary VPC NIC via its default route, not by name: the + // persistent-net udev rename to vpc0 doesn't apply on the Alpine AMI, so the + // kernel name is eth0 at first boot. The mgmt NIC (eth1) carries no default + // route, so this always picks the VPC egress NIC — the one IMDS rides. + wf.WriteString(" dev=$(ip route show default | awk '{print $5; exit}')\n") + fmt.Fprintf(&wf, " [ -n \"$dev\" ] && ip route replace %s/32 dev \"$dev\" scope link\n", handlers_imds.MetaDataServerIP) + + var rc strings.Builder + rc.WriteString(" - [ rc-update, add, local, default ]\n") + rc.WriteString(" - [ rc-service, local, start ]\n") + + return strings.TrimRight(wf.String(), "\n"), strings.TrimRight(rc.String(), "\n") +} + type CloudInitMetaData struct { InstanceID string Hostname string @@ -631,6 +705,35 @@ func (s *InstanceServiceImpl) PrepareRunInstances(input *ec2.RunInstancesInput, } } + // Pre-created ENI path: when the caller passes + // NetworkInterfaces[0].NetworkInterfaceId, attach that existing ENI + // instead of auto-creating one. AWS-parity behaviour; consumed by the + // EKS cluster launcher which needs the ENI to live under the customer + // account before the K3s server VM is created. + preExistingENIID := "" + if len(input.NetworkInterfaces) > 0 && input.NetworkInterfaces[0] != nil { + preExistingENIID = aws.StringValue(input.NetworkInterfaces[0].NetworkInterfaceId) + } + if preExistingENIID != "" { + if s.eniCreator == nil { + slog.Error("PrepareRunInstances: pre-created ENI specified but ENI service unavailable", + "instanceId", instance.ID, "eniId", preExistingENIID) + lastRunErr = errors.New(awserrors.ErrorServerInternal) + s.resourceMgr.Deallocate(instanceType) + continue + } + if err := s.attachPreCreatedENI(accountID, preExistingENIID, instance, ec2Instance); err != nil { + slog.Error("PrepareRunInstances: pre-created ENI attach failed", + "instanceId", instance.ID, "eniId", preExistingENIID, "err", err) + lastRunErr = err + s.resourceMgr.Deallocate(instanceType) + continue + } + instances = append(instances, instance) + allEC2Instances = append(allEC2Instances, ec2Instance) + continue + } + if (input.SubnetId == nil || *input.SubnetId == "") && s.eniCreator != nil { defaultSubnet, dsErr := s.eniCreator.GetDefaultSubnet(accountID) if dsErr == nil && defaultSubnet != nil { @@ -774,6 +877,62 @@ func (s *InstanceServiceImpl) PrepareRunInstances(input *ec2.RunInstancesInput, return reservation, instances, instanceType, nil } +// attachPreCreatedENI looks up an existing ENI in accountID, verifies it is +// not already in-use, attaches it as device-0 to instance, and populates the +// VM + ec2.Instance with the ENI's network identity (private IP, MAC, subnet, +// VPC, security groups). Returns an error without mutating any state if the +// lookup or attach fails. Public-IP auto-assignment is skipped on the +// pre-created path because the caller is expected to manage that out-of-band. +func (s *InstanceServiceImpl) attachPreCreatedENI(accountID, eniID string, instance *vm.VM, ec2Instance *ec2.Instance) error { + eniInfo, err := s.eniCreator.GetENI(accountID, eniID) + if err != nil { + return err + } + if eniInfo == nil { + return errors.New(awserrors.ErrorInvalidNetworkInterfaceIDNotFound) + } + if eniInfo.Status == "in-use" { + return errors.New(awserrors.ErrorInvalidNetworkInterfaceInUse) + } + if _, err := s.eniCreator.AttachENI(accountID, eniID, instance.ID, 0); err != nil { + return err + } + + instance.ENIId = eniID + instance.ENIMac = eniInfo.MacAddress + + ec2Instance.SetPrivateIpAddress(eniInfo.PrivateIpAddress) + ec2Instance.SetSubnetId(eniInfo.SubnetID) + ec2Instance.SetVpcId(eniInfo.VpcID) + if len(eniInfo.SecurityGroupIDs) > 0 { + groups := make([]*ec2.GroupIdentifier, 0, len(eniInfo.SecurityGroupIDs)) + for _, id := range eniInfo.SecurityGroupIDs { + groups = append(groups, &ec2.GroupIdentifier{GroupId: aws.String(id)}) + } + ec2Instance.SecurityGroups = groups + } + ec2Instance.NetworkInterfaces = []*ec2.InstanceNetworkInterface{{ + NetworkInterfaceId: aws.String(eniID), + PrivateIpAddress: aws.String(eniInfo.PrivateIpAddress), + MacAddress: aws.String(eniInfo.MacAddress), + SubnetId: aws.String(eniInfo.SubnetID), + VpcId: aws.String(eniInfo.VpcID), + Status: aws.String("in-use"), + Attachment: &ec2.InstanceNetworkInterfaceAttachment{ + DeviceIndex: aws.Int64(0), + Status: aws.String("attached"), + }, + }} + + slog.Info("PrepareRunInstances: attached pre-created ENI", + "instanceId", instance.ID, + "eniId", eniID, + "privateIp", eniInfo.PrivateIpAddress, + "mac", eniInfo.MacAddress, + ) + return nil +} + // LaunchRunInstances takes the VMs created by PrepareRunInstances (already // inserted into vmMgr by the caller) and runs the heavyweight launch loop: // volume preparation, GPU claim, vmMgr.Run. Failures mark the VM as failed @@ -1768,7 +1927,8 @@ func (s *InstanceServiceImpl) createCloudInitISO(input *ec2.RunInstancesInput, i sudoGroup := "sudo" var rhelWriteFiles, rhelRunCmd string - if distroFamily == "rhel" { + switch distroFamily { + case "rhel": sudoGroup = "wheel" rhelWriteFiles, rhelRunCmd = buildRHELCloudInit(instance.ENIMac, instance.DevMAC, instance.MgmtMAC, instance.MgmtIP, extraMACs) } @@ -1806,6 +1966,15 @@ func (s *InstanceServiceImpl) createCloudInitISO(input *ec2.RunInstancesInput, i } } + // Alpine's eni renderer can't emit the gateway-less IMDS route, so deliver it + // out-of-band via a local.d script. Only when the caller didn't supply its own + // #cloud-config: that payload owns the write_files/runcmd keys (e.g. the EKS + // k3s server VM, which bakes the same route into its own block), and a second + // write_files key here would collide — last key wins, silently dropping a block. + if distroFamily == "alpine" && userData.UserDataCloudConfig == "" { + userData.AlpineWriteFiles, userData.AlpineRunCmd = buildAlpineCloudInit(instance.ENIMac) + } + var buf bytes.Buffer t := template.Must(template.New("cloud-init").Parse(cloudInitUserDataTemplate)) @@ -2052,6 +2221,16 @@ func (s *InstanceServiceImpl) DescribeInstances(input *ec2.DescribeInstancesInpu if !IsInstanceVisible(accountID, instance.AccountID) { continue } + // Platform-managed system VMs (LB HAProxy, EKS control plane) carry a + // ManagedBy tag and must stay out of customer EC2 listings, the same + // way they're filtered from the UI Nodes page. LB VMs are owned by the + // system account so IsInstanceVisible already hides them, but the EKS + // control-plane VM is owned by the customer account (its ENI lives in + // the customer VPC), so guard on ManagedBy here. Root/operator callers + // still see system instances. + if instance.ManagedBy != "" && accountID != utils.GlobalAccountID { + continue + } if len(instanceIDFilter) > 0 && !instanceIDFilter[instance.ID] { continue } @@ -2341,10 +2520,10 @@ func (s *InstanceServiceImpl) ModifyInstanceAttribute(input *ec2.ModifyInstanceA if input.UserData != nil && input.UserData.Value != nil { slog.Info("ModifyInstanceAttribute: changing user data", "instanceId", instanceID) - instance.UserData = string(input.UserData.Value) - if instance.RunInstancesInput != nil { - instance.RunInstancesInput.UserData = aws.String(base64.StdEncoding.EncodeToString(input.UserData.Value)) + if instance.RunInstancesInput == nil { + instance.RunInstancesInput = &ec2.RunInstancesInput{} } + instance.RunInstancesInput.UserData = aws.String(base64.StdEncoding.EncodeToString(input.UserData.Value)) } if input.DisableApiTermination != nil && input.DisableApiTermination.Value != nil { @@ -2718,7 +2897,12 @@ func (s *InstanceServiceImpl) DescribeInstanceAttribute(input *ec2.DescribeInsta output.InstanceType = &ec2.AttributeValue{Value: &val} case ec2.InstanceAttributeNameUserData: - val := instance.UserData + // AWS returns user-data base64-encoded; RunInstancesInput.UserData is the + // canonical base64 store, set at launch and kept in sync by Modify. + var val string + if instance.RunInstancesInput != nil && instance.RunInstancesInput.UserData != nil { + val = *instance.RunInstancesInput.UserData + } output.UserData = &ec2.AttributeValue{Value: &val} case ec2.InstanceAttributeNameDisableApiTermination: diff --git a/spinifex/handlers/ec2/instance/service_impl_test.go b/spinifex/handlers/ec2/instance/service_impl_test.go index 9d8e81b4f..fe26bbe19 100644 --- a/spinifex/handlers/ec2/instance/service_impl_test.go +++ b/spinifex/handlers/ec2/instance/service_impl_test.go @@ -15,6 +15,7 @@ import ( "github.com/mulgadc/spinifex/spinifex/awserrors" "github.com/mulgadc/spinifex/spinifex/config" "github.com/mulgadc/spinifex/spinifex/gpu" + "github.com/mulgadc/spinifex/spinifex/tags" spxtypes "github.com/mulgadc/spinifex/spinifex/types" "github.com/mulgadc/spinifex/spinifex/utils" "github.com/mulgadc/spinifex/spinifex/vm" @@ -383,6 +384,8 @@ func TestBuildRHELCloudInit(t *testing.T) { assert.Contains(t, wf, "dhcp-client-id=mac") assert.Contains(t, wf, "method=auto") assert.NotContains(t, wf, "never-default") + // The IMDS on-link route rides vpc0 even with no dev NIC / extra ENIs. + assert.Contains(t, wf, "route1=169.254.169.254/32", "vpc0 must carry the IMDS on-link route") assert.Contains(t, rc, " - [ restorecon, -R, /etc/NetworkManager/system-connections/ ]") assert.Contains(t, rc, " - [ nmcli, connection, reload ]") assert.Contains(t, rc, " - [ nmcli, connection, up, vpc0 ]") @@ -416,6 +419,18 @@ func TestBuildRHELCloudInit(t *testing.T) { assert.NotContains(t, wf, "vpc1.nmconnection") assert.Contains(t, wf, "vpc2.nmconnection") }) + + t.Run("vpc0 carries on-link IMDS route, others do not", func(t *testing.T) { + wf, _ := buildRHELCloudInit( + "02:00:00:00:00:01", + "02:00:00:00:00:99", + "", "", + []string{"02:00:00:00:00:02"}, + ) + assert.Contains(t, wf, "route1=169.254.169.254/32") + // Primary NIC only — not extra VPC NICs or the dev NIC. + assert.Equal(t, 1, strings.Count(wf, "route1=169.254.169.254/32")) + }) } func TestCloudInitTemplate_FamilyBranching(t *testing.T) { @@ -648,7 +663,7 @@ func TestParseVolumeParams_PartialEbs(t *testing.T) { func TestCloudInitNetworkConfigWildcard(t *testing.T) { // No MACs → wildcard config (non-VPC or VPC without DEV_NETWORKING) - cfg := generateNetworkConfig("", "", "", "", nil) + cfg := generateNetworkConfig("", "", "", "", nil, true) assert.Contains(t, cfg, "version: 2") assert.Contains(t, cfg, "dhcp4: true") assert.Contains(t, cfg, "dhcp-identifier: mac") @@ -660,7 +675,7 @@ func TestCloudInitNetworkConfigDualNIC(t *testing.T) { eniMAC := "02:00:00:61:ef:c2" devMAC := "02:de:00:60:83:0d" - cfg := generateNetworkConfig(eniMAC, devMAC, "", "", nil) + cfg := generateNetworkConfig(eniMAC, devMAC, "", "", nil, true) // Both MACs present in config assert.Contains(t, cfg, eniMAC) @@ -680,12 +695,12 @@ func TestCloudInitNetworkConfigDualNIC(t *testing.T) { func TestCloudInitNetworkConfigPartialMAC(t *testing.T) { // Only ENI MAC (VPC without dev) → per-interface config with VPC NIC only - cfg := generateNetworkConfig("02:00:00:61:ef:c2", "", "", "", nil) + cfg := generateNetworkConfig("02:00:00:61:ef:c2", "", "", "", nil, true) assert.Contains(t, cfg, "vpc0:") assert.NotContains(t, cfg, "dev0:") // Only dev MAC (shouldn't happen, but defensive) → wildcard - cfg = generateNetworkConfig("", "02:de:00:60:83:0d", "", "", nil) + cfg = generateNetworkConfig("", "02:de:00:60:83:0d", "", "", nil, true) assert.Contains(t, cfg, `name: "e*"`) assert.NotContains(t, cfg, "use-routes") } @@ -696,7 +711,7 @@ func TestCloudInitNetworkConfigMultiVPCNICs(t *testing.T) { "02:00:00:bb:bb:bb", "02:00:00:cc:cc:cc", } - cfg := generateNetworkConfig("02:00:00:aa:aa:aa", "", "", "", extras) + cfg := generateNetworkConfig("02:00:00:aa:aa:aa", "", "", "", extras, true) assert.Contains(t, cfg, "vpc0:") assert.Contains(t, cfg, "vpc1:") @@ -715,7 +730,7 @@ func TestCloudInitNetworkConfigMultiVPCNICs(t *testing.T) { func TestCloudInitNetworkConfigEmptyExtraMACSkipped(t *testing.T) { // Empty strings inside the extras slice are ignored rather than producing // a malformed ethernets block. - cfg := generateNetworkConfig("02:00:00:aa:aa:aa", "", "", "", []string{""}) + cfg := generateNetworkConfig("02:00:00:aa:aa:aa", "", "", "", []string{""}, true) assert.Contains(t, cfg, "vpc0:") assert.NotContains(t, cfg, "vpc1:") } @@ -1067,6 +1082,70 @@ func TestDescribeInstances_AccountFilteringHidesOtherTenant(t *testing.T) { assert.Empty(t, out.Reservations) } +// EKS control-plane VMs are owned by the customer account (their ENI lives in +// the customer VPC) but are platform-managed system instances, so they must be +// hidden from the owning customer's DescribeInstances the same way LB VMs are. +func TestDescribeInstances_HidesManagedSystemVMFromCustomer(t *testing.T) { + owner := "111122223333" + v := &vm.VM{ + ID: "i-ekscp", + AccountID: owner, + ManagedBy: tags.ManagedByEKS, + Reservation: &ec2.Reservation{ + ReservationId: aws.String("r-ekscp"), + OwnerId: aws.String(owner), + }, + Instance: &ec2.Instance{InstanceId: aws.String("i-ekscp")}, + } + svc := &InstanceServiceImpl{vmMgr: mgrWith(map[string]*vm.VM{v.ID: v})} + + out, err := svc.DescribeInstances(&ec2.DescribeInstancesInput{}, owner) + require.NoError(t, err) + assert.Empty(t, out.Reservations, "managed system VM must not appear in customer listing") +} + +// Root/operator callers still see managed system VMs. +func TestDescribeInstances_RootSeesManagedSystemVM(t *testing.T) { + v := &vm.VM{ + ID: "i-lb", + AccountID: utils.GlobalAccountID, + ManagedBy: tags.ManagedByELBv2, + Reservation: &ec2.Reservation{ + ReservationId: aws.String("r-lb"), + OwnerId: aws.String(utils.GlobalAccountID), + }, + Instance: &ec2.Instance{InstanceId: aws.String("i-lb")}, + } + svc := &InstanceServiceImpl{vmMgr: mgrWith(map[string]*vm.VM{v.ID: v})} + + out, err := svc.DescribeInstances(&ec2.DescribeInstancesInput{}, utils.GlobalAccountID) + require.NoError(t, err) + require.Len(t, out.Reservations, 1) + assert.Equal(t, "i-lb", *out.Reservations[0].Instances[0].InstanceId) +} + +// A customer's own unmanaged instance (no ManagedBy) stays visible — confirms +// the exclusion keys on ManagedBy, not on account scope. Future EKS worker +// nodes (customer-owned, no ManagedBy tag) follow this path. +func TestDescribeInstances_CustomerWorkloadStaysVisible(t *testing.T) { + owner := "111122223333" + v := &vm.VM{ + ID: "i-worker", + AccountID: owner, + Reservation: &ec2.Reservation{ + ReservationId: aws.String("r-worker"), + OwnerId: aws.String(owner), + }, + Instance: &ec2.Instance{InstanceId: aws.String("i-worker")}, + } + svc := &InstanceServiceImpl{vmMgr: mgrWith(map[string]*vm.VM{v.ID: v})} + + out, err := svc.DescribeInstances(&ec2.DescribeInstancesInput{}, owner) + require.NoError(t, err) + require.Len(t, out.Reservations, 1) + assert.Equal(t, "i-worker", *out.Reservations[0].Instances[0].InstanceId) +} + func TestDescribeInstances_MalformedID(t *testing.T) { svc := &InstanceServiceImpl{vmMgr: mgrWith(map[string]*vm.VM{})} input := &ec2.DescribeInstancesInput{ @@ -1129,7 +1208,10 @@ func TestDescribeInstanceAttribute_RunningInstance(t *testing.T) { ID: id, InstanceType: "t3.large", AccountID: owner, - UserData: "raw-user-data", + RunInstancesInput: &ec2.RunInstancesInput{ + // base64("raw-user-data") — DescribeInstanceAttribute returns user-data base64-encoded. + UserData: aws.String("cmF3LXVzZXItZGF0YQ=="), + }, } svc := &InstanceServiceImpl{vmMgr: mgrWith(map[string]*vm.VM{id: v})} @@ -1151,7 +1233,7 @@ func TestDescribeInstanceAttribute_RunningInstance(t *testing.T) { attribute: ec2.InstanceAttributeNameUserData, assertions: func(t *testing.T, out *ec2.DescribeInstanceAttributeOutput) { require.NotNil(t, out.UserData) - assert.Equal(t, "raw-user-data", *out.UserData.Value) + assert.Equal(t, "cmF3LXVzZXItZGF0YQ==", *out.UserData.Value) }, }, { @@ -1530,7 +1612,6 @@ func TestModifyInstanceAttribute_ChangeUserData(t *testing.T) { ID: id, Status: vm.StateStopped, AccountID: "acc", - UserData: "old", RunInstancesInput: &ec2.RunInstancesInput{ UserData: aws.String("b2xk"), }, @@ -1548,7 +1629,6 @@ func TestModifyInstanceAttribute_ChangeUserData(t *testing.T) { updated := store.wroteStopped[id] require.NotNil(t, updated) - assert.Equal(t, newContent, updated.UserData) assert.Equal(t, "IyEvYmluL2Jhc2g=", *updated.RunInstancesInput.UserData) } @@ -2451,7 +2531,10 @@ func TestStopOrTerminateInstance_TerminationProtection(t *testing.T) { type fakeENICreator struct { defaultSubnet *SubnetInfo subnet *SubnetInfo + getENIByID map[string]*ENIInfo + getENIErr error createOut *ec2.CreateNetworkInterfaceOutput + createCalls int createErr error attachErr error attachCalls int @@ -2474,7 +2557,22 @@ func (f *fakeENICreator) GetSubnet(_, _ string) (*SubnetInfo, error) { return f.subnet, nil } +func (f *fakeENICreator) GetENI(_, eniID string) (*ENIInfo, error) { + if f.getENIErr != nil { + return nil, f.getENIErr + } + if f.getENIByID == nil { + return nil, errors.New("no ENI configured") + } + info, ok := f.getENIByID[eniID] + if !ok { + return nil, errors.New("eni not found") + } + return info, nil +} + func (f *fakeENICreator) CreateNetworkInterface(_ *ec2.CreateNetworkInterfaceInput, _ string) (*ec2.CreateNetworkInterfaceOutput, error) { + f.createCalls++ if f.createErr != nil { return nil, f.createErr } @@ -3188,3 +3286,115 @@ func TestInstanceArchitecture(t *testing.T) { }) } } + +func TestPrepareRunInstances_PreCreatedENIAttachedSkipsAutoCreate(t *testing.T) { + eni := &fakeENICreator{ + getENIByID: map[string]*ENIInfo{ + "eni-pre": { + NetworkInterfaceID: "eni-pre", + SubnetID: "subnet-pre", + VpcID: "vpc-pre", + PrivateIpAddress: "10.0.5.42", + MacAddress: "aa:bb:cc:dd:ee:01", + Status: "available", + SecurityGroupIDs: []string{"sg-pre"}, + }, + }, + } + svc, _ := prepareSvcWithENI(t, eni, nil) + + _, instances, _, err := svc.PrepareRunInstances(&ec2.RunInstancesInput{ + InstanceType: aws.String("t3.micro"), + ImageId: aws.String("ami-1"), + MinCount: aws.Int64(1), + MaxCount: aws.Int64(1), + NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{{ + NetworkInterfaceId: aws.String("eni-pre"), + DeviceIndex: aws.Int64(0), + }}, + }, "acc") + require.NoError(t, err) + require.Len(t, instances, 1) + assert.Equal(t, "eni-pre", instances[0].ENIId) + assert.Equal(t, "aa:bb:cc:dd:ee:01", instances[0].ENIMac) + assert.Equal(t, 1, eni.attachCalls) + assert.Equal(t, 0, eni.createCalls, "auto-create must not run when NetworkInterfaceId is specified") +} + +func TestPrepareRunInstances_PreCreatedENIInUseRejected(t *testing.T) { + eni := &fakeENICreator{ + getENIByID: map[string]*ENIInfo{ + "eni-busy": { + NetworkInterfaceID: "eni-busy", + Status: "in-use", + }, + }, + } + svc, _ := prepareSvcWithENI(t, eni, nil) + + _, _, _, err := svc.PrepareRunInstances(&ec2.RunInstancesInput{ + InstanceType: aws.String("t3.micro"), + ImageId: aws.String("ami-1"), + MinCount: aws.Int64(1), + MaxCount: aws.Int64(1), + NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{{ + NetworkInterfaceId: aws.String("eni-busy"), + }}, + }, "acc") + require.Error(t, err) + assert.Equal(t, awserrors.ErrorInvalidNetworkInterfaceInUse, err.Error()) + assert.Equal(t, 0, eni.attachCalls, "in-use ENI must not be attached") + assert.Equal(t, 0, eni.createCalls, "auto-create must not run when NetworkInterfaceId is specified") +} + +func TestPrepareRunInstances_PreCreatedENILookupErrorSurfaced(t *testing.T) { + eni := &fakeENICreator{ + getENIErr: errors.New(awserrors.ErrorInvalidNetworkInterfaceIDNotFound), + } + svc, _ := prepareSvcWithENI(t, eni, nil) + + _, _, _, err := svc.PrepareRunInstances(&ec2.RunInstancesInput{ + InstanceType: aws.String("t3.micro"), + ImageId: aws.String("ami-1"), + MinCount: aws.Int64(1), + MaxCount: aws.Int64(1), + NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{{ + NetworkInterfaceId: aws.String("eni-ghost"), + }}, + }, "acc") + require.Error(t, err) + assert.Equal(t, awserrors.ErrorInvalidNetworkInterfaceIDNotFound, err.Error()) + assert.Equal(t, 0, eni.attachCalls) + assert.Equal(t, 0, eni.createCalls) +} + +func TestPrepareRunInstances_PreCreatedENIAttachErrorSurfaced(t *testing.T) { + eni := &fakeENICreator{ + getENIByID: map[string]*ENIInfo{ + "eni-pre": { + NetworkInterfaceID: "eni-pre", + Status: "available", + PrivateIpAddress: "10.0.5.42", + MacAddress: "aa:bb:cc:dd:ee:01", + SubnetID: "subnet-pre", + VpcID: "vpc-pre", + }, + }, + attachErr: errors.New(awserrors.ErrorServerInternal), + } + svc, _ := prepareSvcWithENI(t, eni, nil) + + _, _, _, err := svc.PrepareRunInstances(&ec2.RunInstancesInput{ + InstanceType: aws.String("t3.micro"), + ImageId: aws.String("ami-1"), + MinCount: aws.Int64(1), + MaxCount: aws.Int64(1), + NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{{ + NetworkInterfaceId: aws.String("eni-pre"), + }}, + }, "acc") + require.Error(t, err) + assert.Equal(t, awserrors.ErrorServerInternal, err.Error()) + assert.Equal(t, 1, eni.attachCalls) + assert.Equal(t, 0, eni.createCalls) +} diff --git a/spinifex/handlers/ec2/snapshot/service_impl.go b/spinifex/handlers/ec2/snapshot/service_impl.go index 48faa14c6..dad9bddad 100644 --- a/spinifex/handlers/ec2/snapshot/service_impl.go +++ b/spinifex/handlers/ec2/snapshot/service_impl.go @@ -181,16 +181,7 @@ func snapshotConfigToEC2(cfg *SnapshotConfig) *ec2.Snapshot { OwnerId: aws.String(cfg.OwnerID), } - if len(cfg.Tags) > 0 { - tags := make([]*ec2.Tag, 0, len(cfg.Tags)) - for key, value := range cfg.Tags { - tags = append(tags, &ec2.Tag{ - Key: aws.String(key), - Value: aws.String(value), - }) - } - snapshot.Tags = tags - } + snapshot.Tags = utils.MapToEC2Tags(cfg.Tags) return snapshot } diff --git a/spinifex/handlers/ec2/vpc/eni.go b/spinifex/handlers/ec2/vpc/eni.go index a90494b10..e28749bcf 100644 --- a/spinifex/handlers/ec2/vpc/eni.go +++ b/spinifex/handlers/ec2/vpc/eni.go @@ -144,6 +144,15 @@ func (s *VPCServiceImpl) CreateNetworkInterface(input *ec2.CreateNetworkInterfac return nil, errors.New(awserrors.ErrorServerInternal) } + // Maintain the eni-by-vpc-ip reverse index after the source-of-truth ENI + // write. A failure here leaves IMDS returning safe 404s for this IP rather + // than phantom permissions, so it is logged, not fatal. + if s.eniIndex != nil { + if err := s.eniIndex.Put(subnet.VpcId, privateIP, eniId, accountID); err != nil { + slog.Warn("CreateNetworkInterface: eni-by-ip index write failed", "eniId", eniId, "vpcId", subnet.VpcId, "ip", privateIP, "err", err) + } + } + slog.Info("CreateNetworkInterface completed", "eniId", eniId, "subnetId", subnetId, "ip", privateIP, "accountID", accountID) // Send vpc.create-port synchronously so vpcd OVSDB errors surface to the @@ -213,6 +222,15 @@ func (s *VPCServiceImpl) DeleteNetworkInterface(input *ec2.DeleteNetworkInterfac return nil, errors.New(awserrors.ErrorServerInternal) } + // Drop the eni-by-vpc-ip reverse index entry. Idempotent and non-fatal: + // the source-of-truth ENI is already gone, so a stale index entry would + // only resolve to a now-missing ENI and surface as a safe 404. + if s.eniIndex != nil { + if err := s.eniIndex.Delete(record.VpcId, record.PrivateIpAddress); err != nil { + slog.Warn("DeleteNetworkInterface: eni-by-ip index delete failed", "eniId", eniId, "vpcId", record.VpcId, "ip", record.PrivateIpAddress, "err", err) + } + } + slog.Info("DeleteNetworkInterface completed", "eniId", eniId, "accountID", accountID) // Publish vpc.delete-port event for vpcd topology cleanup. SG IDs are diff --git a/spinifex/handlers/ec2/vpc/eni_hotplug_helpers_test.go b/spinifex/handlers/ec2/vpc/eni_hotplug_helpers_test.go new file mode 100644 index 000000000..8099151e4 --- /dev/null +++ b/spinifex/handlers/ec2/vpc/eni_hotplug_helpers_test.go @@ -0,0 +1,163 @@ +package handlers_ec2_vpc + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetENIRecord(t *testing.T) { + svc := setupTestVPCService(t) + vpcId := createTestVPC(t, svc, "10.0.0.0/16") + subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24") + eniId := createTestENI(t, svc, subnetId) + + rec, err := svc.GetENIRecord(testAccountID, eniId) + require.NoError(t, err) + assert.Equal(t, eniId, rec.NetworkInterfaceId) + assert.Equal(t, subnetId, rec.SubnetId) + assert.Equal(t, "available", rec.Status) + assert.NotEmpty(t, rec.MacAddress) +} + +func TestGetENIRecord_NotFound(t *testing.T) { + svc := setupTestVPCService(t) + _, err := svc.GetENIRecord(testAccountID, "eni-nonexistent") + assert.ErrorContains(t, err, "InvalidNetworkInterfaceID.NotFound") +} + +func TestUpdateENI_PatchesFields(t *testing.T) { + svc := setupTestVPCService(t) + vpcId := createTestVPC(t, svc, "10.0.0.0/16") + subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24") + eniId := createTestENI(t, svc, subnetId) + + err := svc.UpdateENI(testAccountID, eniId, func(r *ENIRecord) { + r.AttachmentStatus = "attaching" + r.HotPlugSlot = 3 + r.LastAttachError = "prior failure" + }) + require.NoError(t, err) + + rec, err := svc.GetENIRecord(testAccountID, eniId) + require.NoError(t, err) + assert.Equal(t, "attaching", rec.AttachmentStatus) + assert.Equal(t, 3, rec.HotPlugSlot) + assert.Equal(t, "prior failure", rec.LastAttachError) +} + +func TestUpdateENI_NotFound(t *testing.T) { + svc := setupTestVPCService(t) + err := svc.UpdateENI(testAccountID, "eni-nonexistent", func(r *ENIRecord) { + r.AttachmentStatus = "attached" + }) + assert.ErrorContains(t, err, "InvalidNetworkInterfaceID.NotFound") +} + +func TestUpdateENI_NoopPatchPersists(t *testing.T) { + svc := setupTestVPCService(t) + vpcId := createTestVPC(t, svc, "10.0.0.0/16") + subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24") + eniId := createTestENI(t, svc, subnetId) + + err := svc.UpdateENI(testAccountID, eniId, func(*ENIRecord) {}) + require.NoError(t, err) + + rec, err := svc.GetENIRecord(testAccountID, eniId) + require.NoError(t, err) + assert.Equal(t, eniId, rec.NetworkInterfaceId) +} + +func TestFindENIByAttachment(t *testing.T) { + svc := setupTestVPCService(t) + vpcId := createTestVPC(t, svc, "10.0.0.0/16") + subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24") + eniId := createTestENI(t, svc, subnetId) + + attachId, err := svc.AttachENI(testAccountID, eniId, "i-find-test", 2) + require.NoError(t, err) + + rec, err := svc.FindENIByAttachment(testAccountID, attachId) + require.NoError(t, err) + assert.Equal(t, eniId, rec.NetworkInterfaceId) + assert.Equal(t, "i-find-test", rec.InstanceId) + assert.Equal(t, int64(2), rec.DeviceIndex) +} + +func TestFindENIByAttachment_NotFound(t *testing.T) { + svc := setupTestVPCService(t) + _, err := svc.FindENIByAttachment(testAccountID, "eni-attach-missing") + assert.ErrorContains(t, err, "InvalidAttachmentID.NotFound") +} + +func TestFindENIByAttachment_EmptyBucket(t *testing.T) { + svc := setupTestVPCService(t) + _, err := svc.FindENIByAttachment(testAccountID, "eni-attach-anything") + assert.ErrorContains(t, err, "InvalidAttachmentID.NotFound") +} + +func TestFindENIByAttachment_AccountScoped(t *testing.T) { + svc := setupTestVPCService(t) + vpcId := createTestVPC(t, svc, "10.0.0.0/16") + subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24") + eniId := createTestENI(t, svc, subnetId) + + attachId, err := svc.AttachENI(testAccountID, eniId, "i-scoped", 0) + require.NoError(t, err) + + _, err = svc.FindENIByAttachment("999988887777", attachId) + assert.ErrorContains(t, err, "InvalidAttachmentID.NotFound") +} + +// TestUpdateENI_RoundTripsAllNewFields exercises every Sprint 3b field on +// ENIRecord so a struct-field rename would surface as a test failure rather +// than a silent JSON-tag drift. +func TestUpdateENI_RoundTripsAllNewFields(t *testing.T) { + svc := setupTestVPCService(t) + vpcId := createTestVPC(t, svc, "10.0.0.0/16") + subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24") + eniId := createTestENI(t, svc, subnetId) + + require.NoError(t, svc.UpdateENI(testAccountID, eniId, func(r *ENIRecord) { + r.AttachmentStatus = "detaching" + r.HotPlugSlot = 7 + r.LastAttachError = "boom" + r.DetachInFlight = true + r.DetachForce = true + })) + + rec, err := svc.GetENIRecord(testAccountID, eniId) + require.NoError(t, err) + assert.Equal(t, "detaching", rec.AttachmentStatus) + assert.Equal(t, 7, rec.HotPlugSlot) + assert.Equal(t, "boom", rec.LastAttachError) + assert.True(t, rec.DetachInFlight) + assert.True(t, rec.DetachForce) +} + +// TestUpdateENI_PreservesUnrelatedFields ensures a partial patch does not +// clobber pre-existing record state. +func TestUpdateENI_PreservesUnrelatedFields(t *testing.T) { + svc := setupTestVPCService(t) + vpcId := createTestVPC(t, svc, "10.0.0.0/16") + subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24") + out, err := svc.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{ + SubnetId: aws.String(subnetId), + Description: aws.String("preserved-desc"), + }, testAccountID) + require.NoError(t, err) + eniId := *out.NetworkInterface.NetworkInterfaceId + + require.NoError(t, svc.UpdateENI(testAccountID, eniId, func(r *ENIRecord) { + r.AttachmentStatus = "attaching" + })) + + rec, err := svc.GetENIRecord(testAccountID, eniId) + require.NoError(t, err) + assert.Equal(t, "preserved-desc", rec.Description) + assert.Equal(t, subnetId, rec.SubnetId) + assert.NotEmpty(t, rec.MacAddress) +} diff --git a/spinifex/handlers/ec2/vpc/eni_index.go b/spinifex/handlers/ec2/vpc/eni_index.go new file mode 100644 index 000000000..226555a55 --- /dev/null +++ b/spinifex/handlers/ec2/vpc/eni_index.go @@ -0,0 +1,52 @@ +package handlers_ec2_vpc + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/nats-io/nats.go" +) + +// eniByIPValue is the stored shape of a spinifex-network-eni-by-vpc-ip row: the ENI +// ID plus its owning account (the ENI bucket is keyed "{accountID}.{eniID}"). Both are +// immutable identity, letting the handler resolve the ENI's account in a single Get. +type eniByIPValue struct { + ENIId string `json:"eni_id"` + AccountID string `json:"account_id"` +} + +// ENIByIPIndex maintains the spinifex-network-eni-by-vpc-ip reverse index +// (vpcID/ip → eniID), so the IMDS handler resolves a source IP to an ENI in one +// KV read. Written on CreateNetworkInterface, deleted on DeleteNetworkInterface. +type ENIByIPIndex struct { + kv nats.KeyValue +} + +// NewENIByIPIndex wraps an already-opened eni-by-vpc-ip KV bucket. +func NewENIByIPIndex(kv nats.KeyValue) *ENIByIPIndex { return &ENIByIPIndex{kv: kv} } + +// eniByVPCIPKey builds the composite vpcID/ip key. +func eniByVPCIPKey(vpcID, ip string) string { return vpcID + "/" + ip } + +// Put writes the vpcID/ip → {eniID, accountID} mapping, overwriting any prior +// entry. Called after the source-of-truth ENI record is persisted so that a +// failure here leaves IMDS returning safe 404s rather than phantom permissions. +func (i *ENIByIPIndex) Put(vpcID, ip, eniID, accountID string) error { + data, err := json.Marshal(eniByIPValue{ENIId: eniID, AccountID: accountID}) + if err != nil { + return fmt.Errorf("marshal eni-by-ip value: %w", err) + } + if _, err := i.kv.Put(eniByVPCIPKey(vpcID, ip), data); err != nil { + return fmt.Errorf("put eni-by-ip index %s/%s: %w", vpcID, ip, err) + } + return nil +} + +// Delete removes the vpcID/ip mapping. Idempotent: a missing key returns nil. +func (i *ENIByIPIndex) Delete(vpcID, ip string) error { + if err := i.kv.Delete(eniByVPCIPKey(vpcID, ip)); err != nil && !errors.Is(err, nats.ErrKeyNotFound) { + return fmt.Errorf("delete eni-by-ip index %s/%s: %w", vpcID, ip, err) + } + return nil +} diff --git a/spinifex/handlers/ec2/vpc/eni_index_test.go b/spinifex/handlers/ec2/vpc/eni_index_test.go new file mode 100644 index 000000000..58a792add --- /dev/null +++ b/spinifex/handlers/ec2/vpc/eni_index_test.go @@ -0,0 +1,147 @@ +package handlers_ec2_vpc + +import ( + "encoding/json" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + handlers_imds "github.com/mulgadc/spinifex/spinifex/handlers/imds" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// openTestENIByIPBucket opens the eni-by-vpc-ip reverse-index bucket on the +// test JetStream, exercising the same init path the daemon uses. +func openTestENIByIPBucket(t *testing.T, nc *nats.Conn) nats.KeyValue { + t.Helper() + js, err := nc.JetStream() + require.NoError(t, err) + kv, err := handlers_imds.InitENIByIPBucket(js, 1) + require.NoError(t, err) + return kv +} + +// readIndexEntry reads the raw vpcID/ip entry and returns its eni_id and +// account_id, or ("", "") when the key is absent. +func readIndexEntry(t *testing.T, kv nats.KeyValue, vpcID, ip string) (eniID, accountID string) { + t.Helper() + entry, err := kv.Get(vpcID + "/" + ip) + if err != nil { + require.ErrorIs(t, err, nats.ErrKeyNotFound) + return "", "" + } + var v struct { + ENIId string `json:"eni_id"` + AccountID string `json:"account_id"` + } + require.NoError(t, json.Unmarshal(entry.Value(), &v)) + return v.ENIId, v.AccountID +} + +// readIndexENIID is the eni_id-only convenience wrapper. +func readIndexENIID(t *testing.T, kv nats.KeyValue, vpcID, ip string) string { + t.Helper() + eniID, _ := readIndexEntry(t, kv, vpcID, ip) + return eniID +} + +func TestENIByIPIndex_PutGetDelete(t *testing.T) { + _, nc := setupTestVPCServiceWithNC(t) + kv := openTestENIByIPBucket(t, nc) + idx := NewENIByIPIndex(kv) + + require.NoError(t, idx.Put("vpc-abc12345", "10.0.1.5", "eni-deadbeef", "111122223333")) + eniID, accountID := readIndexEntry(t, kv, "vpc-abc12345", "10.0.1.5") + assert.Equal(t, "eni-deadbeef", eniID) + assert.Equal(t, "111122223333", accountID) + + // Overwrite is allowed (rebind is a Put). + require.NoError(t, idx.Put("vpc-abc12345", "10.0.1.5", "eni-cafef00d", "111122223333")) + assert.Equal(t, "eni-cafef00d", readIndexENIID(t, kv, "vpc-abc12345", "10.0.1.5")) + + require.NoError(t, idx.Delete("vpc-abc12345", "10.0.1.5")) + assert.Equal(t, "", readIndexENIID(t, kv, "vpc-abc12345", "10.0.1.5")) +} + +func TestENIByIPIndex_DeleteAbsentIsIdempotent(t *testing.T) { + _, nc := setupTestVPCServiceWithNC(t) + idx := NewENIByIPIndex(openTestENIByIPBucket(t, nc)) + require.NoError(t, idx.Delete("vpc-nope", "10.0.0.1")) +} + +func TestENIByIPIndex_KeysAreVPCScoped(t *testing.T) { + // Same IP in two VPCs must not collide — the key includes the VPC ID. + _, nc := setupTestVPCServiceWithNC(t) + kv := openTestENIByIPBucket(t, nc) + idx := NewENIByIPIndex(kv) + + require.NoError(t, idx.Put("vpc-aaaaaaaa", "10.0.1.5", "eni-aaa", "111122223333")) + require.NoError(t, idx.Put("vpc-bbbbbbbb", "10.0.1.5", "eni-bbb", "444455556666")) + + assert.Equal(t, "eni-aaa", readIndexENIID(t, kv, "vpc-aaaaaaaa", "10.0.1.5")) + assert.Equal(t, "eni-bbb", readIndexENIID(t, kv, "vpc-bbbbbbbb", "10.0.1.5")) +} + +func TestCreateNetworkInterface_WritesENIIndex(t *testing.T) { + svc, nc := setupTestVPCServiceWithNC(t) + kv := openTestENIByIPBucket(t, nc) + svc.SetENIByIPIndex(NewENIByIPIndex(kv)) + + vpcID := createTestVPC(t, svc, "10.0.0.0/16") + subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24") + + out, err := svc.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{ + SubnetId: aws.String(subnetID), + }, testAccountID) + require.NoError(t, err) + + eniID := *out.NetworkInterface.NetworkInterfaceId + ip := *out.NetworkInterface.PrivateIpAddress + gotENI, gotAccount := readIndexEntry(t, kv, vpcID, ip) + assert.Equal(t, eniID, gotENI) + assert.Equal(t, testAccountID, gotAccount) +} + +func TestDeleteNetworkInterface_RemovesENIIndex(t *testing.T) { + svc, nc := setupTestVPCServiceWithNC(t) + kv := openTestENIByIPBucket(t, nc) + svc.SetENIByIPIndex(NewENIByIPIndex(kv)) + + vpcID := createTestVPC(t, svc, "10.0.0.0/16") + subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24") + + out, err := svc.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{ + SubnetId: aws.String(subnetID), + }, testAccountID) + require.NoError(t, err) + eniID := *out.NetworkInterface.NetworkInterfaceId + ip := *out.NetworkInterface.PrivateIpAddress + require.Equal(t, eniID, readIndexENIID(t, kv, vpcID, ip)) + + _, err = svc.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ + NetworkInterfaceId: aws.String(eniID), + }, testAccountID) + require.NoError(t, err) + + assert.Equal(t, "", readIndexENIID(t, kv, vpcID, ip)) +} + +// Regression: ENI lifecycle must not panic or error when no index is wired +// (IMDS-less deployments, focused tests). +func TestCreateDeleteNetworkInterface_NoIndexWired(t *testing.T) { + svc := setupTestVPCService(t) + vpcID := createTestVPC(t, svc, "10.0.0.0/16") + subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24") + + out, err := svc.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{ + SubnetId: aws.String(subnetID), + }, testAccountID) + require.NoError(t, err) + + _, err = svc.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ + NetworkInterfaceId: out.NetworkInterface.NetworkInterfaceId, + }, testAccountID) + require.NoError(t, err) +} diff --git a/spinifex/handlers/ec2/vpc/external_ipam_test.go b/spinifex/handlers/ec2/vpc/external_ipam_test.go index eb625c37c..0408f995c 100644 --- a/spinifex/handlers/ec2/vpc/external_ipam_test.go +++ b/spinifex/handlers/ec2/vpc/external_ipam_test.go @@ -100,10 +100,11 @@ func TestExternalIPAM_Release(t *testing.T) { err = ipam.ReleaseIP("wan", "192.168.1.151") require.NoError(t, err) - // Next allocation reuses released IP + // Round-robin: a released IP is NOT reused immediately (siv-246). Allocation + // resumes past the cursor, so the next IP is .153, not the freed .151. ip3, _, err := ipam.AllocateIP("", "", PurposeENIPublic, "", "eni-3", "i-3") require.NoError(t, err) - assert.Equal(t, "192.168.1.151", ip3) + assert.Equal(t, "192.168.1.153", ip3) } func TestExternalIPAM_Exhaustion(t *testing.T) { diff --git a/spinifex/handlers/ec2/vpc/invariants_test.go b/spinifex/handlers/ec2/vpc/invariants_test.go new file mode 100644 index 000000000..b33a70d7f --- /dev/null +++ b/spinifex/handlers/ec2/vpc/invariants_test.go @@ -0,0 +1,72 @@ +package handlers_ec2_vpc + +import ( + "encoding/json" + "net" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// IMDS-datapath invariant. + +// TestI4_ENIIndexBucketShape asserts the spinifex-network-eni-by-vpc-ip reverse +// index keeps its contract: keys parse as "vpcID/ip" with a valid IP, values +// carry a non-empty eni_id, and no mutable/denormalised fields creep back into +// the value. The index is identity-only (eni_id + the immutable account_id) by +// design — every mutable field (IPs, MAC, instance ID, profile ARN) is read +// live off the ENIRecord + instance record at request time. A denormalised +// field here resurrects the staleness class the design deliberately removed: +// an IMDS handler trusting a stale profile ARN would mint credentials for the +// wrong role. +func TestI4_ENIIndexBucketShape(t *testing.T) { + _, nc := setupTestVPCServiceWithNC(t) + kv := openTestENIByIPBucket(t, nc) + idx := NewENIByIPIndex(kv) + + // Seed a couple of entries through the real writer. + require.NoError(t, idx.Put("vpc-aaaaaaaa", "10.0.1.5", "eni-aaa", "111122223333")) + require.NoError(t, idx.Put("vpc-bbbbbbbb", "10.0.2.9", "eni-bbb", "444455556666")) + + // allowedFields is the closed set the value JSON may contain. Adding a field + // to eniByIPValue without updating this set fails the test by design — a + // reviewer must justify any new field as immutable identity, not cached + // mutable state. + allowedFields := map[string]struct{}{"eni_id": {}, "account_id": {}} + + keys, err := kv.Keys() + require.NoError(t, err) + require.NotEmpty(t, keys) + + for _, key := range keys { + // Skip migration framework metadata (e.g. _version). + if strings.HasPrefix(key, "_") { + continue + } + vpcID, ip, ok := strings.Cut(key, "/") + require.Truef(t, ok, "key %q does not parse as vpcID/ip", key) + assert.NotEmptyf(t, vpcID, "key %q has empty vpcID", key) + assert.NotNilf(t, net.ParseIP(ip), "key %q has invalid IP %q", key, ip) + + entry, err := kv.Get(key) + require.NoError(t, err) + + var raw map[string]json.RawMessage + require.NoError(t, json.Unmarshal(entry.Value(), &raw)) + + var v struct { + ENIId string `json:"eni_id"` + } + require.NoError(t, json.Unmarshal(entry.Value(), &v)) + assert.NotEmptyf(t, v.ENIId, "index entry %q has empty eni_id", key) + + for field := range raw { + _, allowed := allowedFields[field] + assert.Truef(t, allowed, "index entry %q carries unexpected field %q: the "+ + "eni-by-vpc-ip index is IP→ENI identity only — denormalised mutable "+ + "fields reintroduce the staleness class the design removed", key, field) + } + } +} diff --git a/spinifex/handlers/ec2/vpc/service_impl.go b/spinifex/handlers/ec2/vpc/service_impl.go index 6d6b5a9cd..898ad64f0 100644 --- a/spinifex/handlers/ec2/vpc/service_impl.go +++ b/spinifex/handlers/ec2/vpc/service_impl.go @@ -82,6 +82,11 @@ type VPCServiceImpl struct { // Optional: injected after construction for public IP cleanup in DeleteNetworkInterface. externalIPAM *ExternalIPAM eipKV nats.KeyValue + + // Optional: injected after construction. Maintains the eni-by-vpc-ip + // reverse index the IMDS handler reads. Nil-safe — focused tests and + // IMDS-less deployments leave it unset. + eniIndex *ENIByIPIndex } // SetExternalIPAM injects external IPAM and EIP KV store references so that @@ -91,6 +96,13 @@ func (s *VPCServiceImpl) SetExternalIPAM(ipam *ExternalIPAM, eipKV nats.KeyValue s.eipKV = eipKV } +// SetENIByIPIndex injects the eni-by-vpc-ip reverse-index writer so +// CreateNetworkInterface / DeleteNetworkInterface keep the IMDS source-IP→ENI +// lookup index in sync. Optional: when unset, the index is simply not written. +func (s *VPCServiceImpl) SetENIByIPIndex(index *ENIByIPIndex) { + s.eniIndex = index +} + // localAZ returns the node's local availability zone, sourced from // s.config.AZ. Returns "" when no config is wired (mostly test paths), // which the reconciler treats as a legacy record matching every AZ. diff --git a/spinifex/handlers/eks/access_entry.go b/spinifex/handlers/eks/access_entry.go new file mode 100644 index 000000000..736014afc --- /dev/null +++ b/spinifex/handlers/eks/access_entry.go @@ -0,0 +1,263 @@ +package handlers_eks + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/nats-io/nats.go" +) + +// Access-entry types (Q9). v1 supports STANDARD principals only; the node +// types (EC2_LINUX/EC2_WINDOWS/FARGATE_LINUX) are accepted by AWS but the +// nodegroup join path (Sprint 6d) handles node identity, so they are rejected +// here until that lands. +const ( + AccessEntryTypeStandard = "STANDARD" + + accessScopeCluster = "cluster" + accessScopeNamespace = "namespace" +) + +// Supported AWS-managed access policies (Q9). The map value is the K8s +// ClusterRole the policy maps onto; the token webhook + RBAC bindings consume +// it. Any policy ARN outside this set is rejected with InvalidParameterValue. +var supportedAccessPolicies = map[string]string{ + "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy": "cluster-admin", + "arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminPolicy": "admin", + "arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy": "edit", + "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy": "view", +} + +// ErrAccessEntryNotFound is returned by GetAccessEntryRecord / the CAS helper +// when no entry exists for the principal. Callers translate to the AWS shape +// (ResourceNotFoundException) at the service boundary. +var ErrAccessEntryNotFound = errors.New("eks: access entry not found") + +// AccessEntryARN composes the access-entry ARN. AWS uses a per-entry UUID; this +// build keys the discriminator off the principal-ARN hash instead so the ARN is +// deterministic (no RNG) and one-per-principal — clients address access entries +// by principalArn, not by this ARN, so the divergence is informational only. +func AccessEntryARN(region, accountID, cluster, principalARN string) string { + return fmt.Sprintf("arn:aws:eks:%s:%s:access-entry/%s/%s", + region, accountID, cluster, PrincipalARNHash(principalARN)) +} + +// PutAccessEntryRecord writes the entry unconditionally. +func PutAccessEntryRecord(kv nats.KeyValue, rec *AccessEntryRecord) error { + if rec == nil { + return errors.New("eks: PutAccessEntryRecord nil record") + } + if rec.ClusterName == "" || rec.PrincipalARN == "" { + return errors.New("eks: PutAccessEntryRecord missing cluster or principal ARN") + } + data, err := json.Marshal(rec) + if err != nil { + return fmt.Errorf("marshal access entry %s: %w", rec.PrincipalARN, err) + } + key := AccessEntryKey(rec.ClusterName, rec.PrincipalARN) + if _, err := kv.Put(key, data); err != nil { + return fmt.Errorf("kv put %s: %w", key, err) + } + return nil +} + +// GetAccessEntryRecord reads one entry. Returns ErrAccessEntryNotFound if absent. +func GetAccessEntryRecord(kv nats.KeyValue, cluster, principalARN string) (*AccessEntryRecord, error) { + if cluster == "" || principalARN == "" { + return nil, errors.New("eks: GetAccessEntryRecord empty cluster or principal ARN") + } + entry, err := kv.Get(AccessEntryKey(cluster, principalARN)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, ErrAccessEntryNotFound + } + return nil, fmt.Errorf("kv get access entry: %w", err) + } + var rec AccessEntryRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal access entry %s: %w", principalARN, err) + } + return &rec, nil +} + +// ListAccessEntryRecords returns every access entry under a cluster, sorted by +// principal ARN for stable output. +func ListAccessEntryRecords(kv nats.KeyValue, cluster string) ([]*AccessEntryRecord, error) { + if cluster == "" { + return nil, errors.New("eks: ListAccessEntryRecords empty cluster") + } + keys, err := kv.Keys() + if err != nil { + if errors.Is(err, nats.ErrNoKeysFound) { + return nil, nil + } + return nil, fmt.Errorf("kv keys: %w", err) + } + prefix := AccessEntriesPrefix(cluster) + out := make([]*AccessEntryRecord, 0) + for _, k := range keys { + if !strings.HasPrefix(k, prefix) { + continue + } + entry, err := kv.Get(k) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + continue + } + return nil, fmt.Errorf("kv get %s: %w", k, err) + } + var rec AccessEntryRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal access entry %s: %w", k, err) + } + out = append(out, &rec) + } + sortAccessEntries(out) + return out, nil +} + +// DeleteAccessEntryRecord removes one entry. Returns ErrAccessEntryNotFound if +// it did not exist so DeleteAccessEntry can surface ResourceNotFoundException. +func DeleteAccessEntryRecord(kv nats.KeyValue, cluster, principalARN string) error { + key := AccessEntryKey(cluster, principalARN) + if _, err := kv.Get(key); err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return ErrAccessEntryNotFound + } + return fmt.Errorf("kv get %s: %w", key, err) + } + if err := kv.Delete(key); err != nil { + return fmt.Errorf("kv delete %s: %w", key, err) + } + return nil +} + +// casUpdateAccessEntry does a revision-checked read-modify-write. mutate returns +// true if it changed a field. Returns ErrAccessEntryNotFound if absent. +func casUpdateAccessEntry(kv nats.KeyValue, cluster, principalARN string, mutate func(*AccessEntryRecord) bool) (*AccessEntryRecord, error) { + key := AccessEntryKey(cluster, principalARN) + for range maxClusterStateCASRetries { + entry, err := kv.Get(key) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, ErrAccessEntryNotFound + } + return nil, fmt.Errorf("kv get %s: %w", key, err) + } + var rec AccessEntryRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal access entry %s: %w", principalARN, err) + } + if !mutate(&rec) { + return &rec, nil + } + data, err := json.Marshal(&rec) + if err != nil { + return nil, fmt.Errorf("marshal access entry %s: %w", principalARN, err) + } + _, err = kv.Update(key, data, entry.Revision()) + if err == nil { + return &rec, nil + } + if errors.Is(err, nats.ErrKeyExists) { + continue + } + return nil, fmt.Errorf("kv update %s: %w", key, err) + } + return nil, fmt.Errorf("eks: casUpdateAccessEntry %s exhausted CAS retries", principalARN) +} + +func sortAccessEntries(recs []*AccessEntryRecord) { + for i := 1; i < len(recs); i++ { + for j := i; j > 0 && recs[j-1].PrincipalARN > recs[j].PrincipalARN; j-- { + recs[j-1], recs[j] = recs[j], recs[j-1] + } + } +} + +// validateAccessScope checks an AccessScope per AWS rules: type ∈ +// {cluster, namespace}; namespace scope requires at least one namespace and +// cluster scope must not carry any. +func validateAccessScope(scope *eks.AccessScope) (AccessScope, error) { + if scope == nil || aws.StringValue(scope.Type) == "" { + return AccessScope{}, errors.New("eks: accessScope.type is required") + } + t := strings.ToLower(aws.StringValue(scope.Type)) + ns := aws.StringValueSlice(scope.Namespaces) + switch t { + case accessScopeCluster: + if len(ns) > 0 { + return AccessScope{}, errors.New("eks: cluster-scoped policy must not list namespaces") + } + return AccessScope{Type: accessScopeCluster}, nil + case accessScopeNamespace: + if len(ns) == 0 { + return AccessScope{}, errors.New("eks: namespace-scoped policy requires at least one namespace") + } + return AccessScope{Type: accessScopeNamespace, Namespaces: ns}, nil + default: + return AccessScope{}, fmt.Errorf("eks: unsupported accessScope.type %q", t) + } +} + +// accessEntryRecordToAWS converts the persisted record to the SDK shape. +func accessEntryRecordToAWS(rec *AccessEntryRecord) *eks.AccessEntry { + out := &eks.AccessEntry{ + AccessEntryArn: aws.String(rec.ARN), + ClusterName: aws.String(rec.ClusterName), + PrincipalArn: aws.String(rec.PrincipalARN), + Username: aws.String(rec.KubernetesUsername), + KubernetesGroups: aws.StringSlice(rec.KubernetesGroups), + Type: aws.String(rec.Type), + CreatedAt: aws.Time(rec.CreatedAt), + ModifiedAt: aws.Time(rec.ModifiedAt), + } + if len(rec.Tags) > 0 { + out.Tags = aws.StringMap(rec.Tags) + } + return out +} + +// associatedPolicyToAWS converts a persisted associated policy to the SDK shape. +func associatedPolicyToAWS(p AssociatedAccessPolicy) *eks.AssociatedAccessPolicy { + scope := &eks.AccessScope{Type: aws.String(p.AccessScope.Type)} + if len(p.AccessScope.Namespaces) > 0 { + scope.Namespaces = aws.StringSlice(p.AccessScope.Namespaces) + } + return &eks.AssociatedAccessPolicy{ + PolicyArn: aws.String(p.PolicyARN), + AccessScope: scope, + AssociatedAt: aws.Time(p.AssociatedAt), + ModifiedAt: aws.Time(p.ModifiedAt), + } +} + +// defaultKubernetesUsername mirrors AWS: when the caller omits username, EKS +// derives it from the principal ARN. +func defaultKubernetesUsername(principalARN string) string { + return principalARN +} + +// newAccessEntryRecord builds a record, defaulting the username to the +// principal ARN when unset (AWS behaviour). now stamps Created/Modified. +func newAccessEntryRecord(region, accountID, cluster, principalARN, username string, groups []string, entryType string, tags map[string]string, now time.Time) *AccessEntryRecord { + if username == "" { + username = defaultKubernetesUsername(principalARN) + } + return &AccessEntryRecord{ + ARN: AccessEntryARN(region, accountID, cluster, principalARN), + ClusterName: cluster, + PrincipalARN: principalARN, + KubernetesUsername: username, + KubernetesGroups: groups, + Type: entryType, + Tags: tags, + CreatedAt: now, + ModifiedAt: now, + } +} diff --git a/spinifex/handlers/eks/access_entry_test.go b/spinifex/handlers/eks/access_entry_test.go new file mode 100644 index 000000000..882609c28 --- /dev/null +++ b/spinifex/handlers/eks/access_entry_test.go @@ -0,0 +1,95 @@ +package handlers_eks + +import ( + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestValidateAccessScope(t *testing.T) { + cases := []struct { + name string + scope *eks.AccessScope + want AccessScope + wantErr bool + }{ + {"nil scope", nil, AccessScope{}, true}, + {"empty type", &eks.AccessScope{}, AccessScope{}, true}, + {"cluster ok", &eks.AccessScope{Type: aws.String("cluster")}, AccessScope{Type: "cluster"}, false}, + {"cluster mixed case", &eks.AccessScope{Type: aws.String("Cluster")}, AccessScope{Type: "cluster"}, false}, + {"cluster with namespaces", &eks.AccessScope{Type: aws.String("cluster"), Namespaces: aws.StringSlice([]string{"kube-system"})}, AccessScope{}, true}, + {"namespace ok", &eks.AccessScope{Type: aws.String("namespace"), Namespaces: aws.StringSlice([]string{"team-a", "team-b"})}, AccessScope{Type: "namespace", Namespaces: []string{"team-a", "team-b"}}, false}, + {"namespace without namespaces", &eks.AccessScope{Type: aws.String("namespace")}, AccessScope{}, true}, + {"unsupported type", &eks.AccessScope{Type: aws.String("galaxy")}, AccessScope{}, true}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got, err := validateAccessScope(tc.scope) + if tc.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestAccessEntryRecordToAWS_IncludesTags(t *testing.T) { + now := time.Unix(1700000000, 0).UTC() + rec := &AccessEntryRecord{ + ARN: "arn:aws:eks:ap-southeast-2:000000000001:access-entry/dev1/abc", + ClusterName: "dev1", + PrincipalARN: "arn:aws:iam::000000000001:user/admin", + KubernetesUsername: "arn:aws:iam::000000000001:user/admin", + KubernetesGroups: []string{"system:masters"}, + Type: AccessEntryTypeStandard, + Tags: map[string]string{"team": "platform"}, + CreatedAt: now, + ModifiedAt: now, + } + out := accessEntryRecordToAWS(rec) + assert.Equal(t, "dev1", aws.StringValue(out.ClusterName)) + assert.Equal(t, rec.PrincipalARN, aws.StringValue(out.PrincipalArn)) + assert.Equal(t, []string{"system:masters"}, aws.StringValueSlice(out.KubernetesGroups)) + require.NotNil(t, out.Tags) + assert.Equal(t, "platform", aws.StringValue(out.Tags["team"])) +} + +func TestAssociatedPolicyToAWS_ScopeNamespaces(t *testing.T) { + now := time.Unix(1700000000, 0).UTC() + p := AssociatedAccessPolicy{ + PolicyARN: "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy", + AccessScope: AccessScope{Type: "namespace", Namespaces: []string{"team-a"}}, + AssociatedAt: now, + ModifiedAt: now, + } + out := associatedPolicyToAWS(p) + assert.Equal(t, p.PolicyARN, aws.StringValue(out.PolicyArn)) + assert.Equal(t, "namespace", aws.StringValue(out.AccessScope.Type)) + assert.Equal(t, []string{"team-a"}, aws.StringValueSlice(out.AccessScope.Namespaces)) + + cluster := associatedPolicyToAWS(AssociatedAccessPolicy{ + PolicyARN: "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy", + AccessScope: AccessScope{Type: "cluster"}, + }) + assert.Equal(t, "cluster", aws.StringValue(cluster.AccessScope.Type)) + assert.Empty(t, cluster.AccessScope.Namespaces) +} + +// The record-store guard clauses reject malformed input before touching the KV, +// so a nil handle is enough to exercise them. +func TestAccessEntryRecordGuards(t *testing.T) { + require.Error(t, PutAccessEntryRecord(nil, nil)) + require.Error(t, PutAccessEntryRecord(nil, &AccessEntryRecord{PrincipalARN: "arn:aws:iam::000000000001:user/admin"})) + + _, err := GetAccessEntryRecord(nil, "", "arn:aws:iam::000000000001:user/admin") + require.Error(t, err) + + _, err = ListAccessEntryRecords(nil, "") + require.Error(t, err) +} diff --git a/spinifex/handlers/eks/addon_catalog.go b/spinifex/handlers/eks/addon_catalog.go new file mode 100644 index 000000000..e9c30d989 --- /dev/null +++ b/spinifex/handlers/eks/addon_catalog.go @@ -0,0 +1,89 @@ +package handlers_eks + +import "slices" + +// AddonSpec describes one Spinifex-supported managed add-on. The catalog is a +// static in-binary registry (air-gapped; there is no internet add-on catalog), +// so DescribeAddonVersions and CreateAddon validate against these entries. +type AddonSpec struct { + // Name is the AWS add-on name (e.g. "aws-load-balancer-controller"). + Name string + // Versions lists the supported versions newest-first; [0] is the default. + Versions []string + // DefaultVersion is the version CreateAddon applies when none is requested. + // Always equal to Versions[0]. + DefaultVersion string + // RequiresIRSA reports whether the add-on needs an IAM-role-for-service- + // account binding to operate (surfaced as requiresIamPermissions). + RequiresIRSA bool + // Description is a short human-readable summary. + Description string +} + +// addonCatalog is the bundled set of supported add-ons. Keep newest version +// first in each Versions slice; newAddonSpec enforces DefaultVersion = [0]. +var addonCatalog = buildAddonCatalog( + newAddonSpec("aws-load-balancer-controller", true, + "Provisions ELBv2 load balancers for Kubernetes Service/Ingress resources.", + "2.8.1"), + newAddonSpec("coredns", false, + "Cluster DNS server.", + "1.11.1"), +) + +// newAddonSpec builds a spec, pinning DefaultVersion to the first (newest) +// version. Panics on an empty version list — the catalog is build-time data. +func newAddonSpec(name string, requiresIRSA bool, description string, versions ...string) AddonSpec { + if len(versions) == 0 { + panic("eks: addon spec " + name + " has no versions") + } + return AddonSpec{ + Name: name, + Versions: versions, + DefaultVersion: versions[0], + RequiresIRSA: requiresIRSA, + Description: description, + } +} + +// buildAddonCatalog indexes the specs by name. +func buildAddonCatalog(specs ...AddonSpec) map[string]AddonSpec { + out := make(map[string]AddonSpec, len(specs)) + for _, s := range specs { + out[s.Name] = s + } + return out +} + +// lookupAddon returns the spec for name and whether it is in the catalog. +func lookupAddon(name string) (AddonSpec, bool) { + spec, ok := addonCatalog[name] + return spec, ok +} + +// supportsVersion reports whether the spec lists the given version. +func (s AddonSpec) supportsVersion(version string) bool { + return slices.Contains(s.Versions, version) +} + +// catalogSpecs returns the catalog entries sorted by name for stable output. +func catalogSpecs() []AddonSpec { + names := make([]string, 0, len(addonCatalog)) + for n := range addonCatalog { + names = append(names, n) + } + insertionSortStrings(names) + out := make([]AddonSpec, 0, len(names)) + for _, n := range names { + out = append(out, addonCatalog[n]) + } + return out +} + +func insertionSortStrings(s []string) { + for i := 1; i < len(s); i++ { + for j := i; j > 0 && s[j-1] > s[j]; j-- { + s[j-1], s[j] = s[j], s[j-1] + } + } +} diff --git a/spinifex/handlers/eks/addon_catalog_test.go b/spinifex/handlers/eks/addon_catalog_test.go new file mode 100644 index 000000000..12fb1a7ee --- /dev/null +++ b/spinifex/handlers/eks/addon_catalog_test.go @@ -0,0 +1,47 @@ +package handlers_eks + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLookupAddon_KnownAndUnknown(t *testing.T) { + spec, ok := lookupAddon("aws-load-balancer-controller") + require.True(t, ok) + assert.Equal(t, "aws-load-balancer-controller", spec.Name) + assert.True(t, spec.RequiresIRSA) + + _, ok = lookupAddon("does-not-exist") + assert.False(t, ok) +} + +func TestAddonSpec_DefaultVersionIsNewest(t *testing.T) { + for name, spec := range addonCatalog { + require.NotEmpty(t, spec.Versions, "addon %s must list versions", name) + assert.Equal(t, spec.Versions[0], spec.DefaultVersion, + "addon %s default version must be the newest (first) version", name) + } +} + +func TestAddonSpec_SupportsVersion(t *testing.T) { + spec, ok := lookupAddon("aws-load-balancer-controller") + require.True(t, ok) + assert.True(t, spec.supportsVersion(spec.DefaultVersion)) + assert.False(t, spec.supportsVersion("0.0.0-nope")) +} + +func TestCatalogSpecs_SortedByName(t *testing.T) { + specs := catalogSpecs() + require.Len(t, specs, len(addonCatalog)) + for i := 1; i < len(specs); i++ { + assert.LessOrEqual(t, specs[i-1].Name, specs[i].Name, "catalog must be name-sorted") + } +} + +func TestNewAddonSpec_PanicsWithoutVersions(t *testing.T) { + assert.Panics(t, func() { + newAddonSpec("broken", false, "no versions") + }) +} diff --git a/spinifex/handlers/eks/addon_installer.go b/spinifex/handlers/eks/addon_installer.go new file mode 100644 index 000000000..e181bf6ac --- /dev/null +++ b/spinifex/handlers/eks/addon_installer.go @@ -0,0 +1,102 @@ +package handlers_eks + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/nats-io/nats.go" +) + +// AddonInstaller delivers (and removes) a managed add-on's Kubernetes manifests +// to a cluster. It is deliberately transport-agnostic: the add-on state machine +// depends only on this narrow contract so the VM-side delivery transport (which +// drops manifests into the K3s server auto-deploy dir) can drop in without +// touching the op layer. +// +// The reachability constraint (cluster_reconciler.go:73-79) means delivery must +// be VM-side, not a daemon apiserver apply — a private cluster's apiserver is +// not host-reachable. +type AddonInstaller interface { + // Install renders and delivers the add-on described by rec. It does not flip + // the record to ACTIVE — that transition is gated on the cluster's state + // report confirming the manifest applied and pods are ready. + Install(accountID, cluster string, rec *AddonRecord) error + // Uninstall removes the add-on's delivered manifests from the cluster. + Uninstall(accountID, cluster, addon string) error +} + +// stagedManifest is the artifact the VM-side delivery transport consumes. It +// names the bundled add-on + version and carries the operator-supplied config +// so the VM can render the final Kubernetes objects from the baked manifests. +type stagedManifest struct { + AddonName string `json:"addonName"` + AddonVersion string `json:"addonVersion"` + ServiceAccountRoleArn string `json:"serviceAccountRoleArn,omitempty"` + ConfigurationValues string `json:"configurationValues,omitempty"` +} + +// stagingInstaller renders the bundled manifest descriptor for an add-on and +// stages it in the per-account KV at AddonManifestKey. The VM-side delivery +// slice (separate bead) pulls staged manifests and applies them via the K3s +// auto-deploy dir; until then an installed add-on sits CREATING — honest, not a +// false ACTIVE. +type stagingInstaller struct { + nc *nats.Conn +} + +var _ AddonInstaller = (*stagingInstaller)(nil) + +// newStagingInstaller builds the default installer bound to the daemon's NATS +// connection. +func newStagingInstaller(nc *nats.Conn) *stagingInstaller { + return &stagingInstaller{nc: nc} +} + +func (i *stagingInstaller) acctKV(accountID string) (nats.KeyValue, error) { + if i.nc == nil { + return nil, errors.New("eks: stagingInstaller nil NATS connection") + } + js, err := i.nc.JetStream() + if err != nil { + return nil, fmt.Errorf("jetstream: %w", err) + } + return GetOrCreateAccountBucket(js, accountID) +} + +func (i *stagingInstaller) Install(accountID, cluster string, rec *AddonRecord) error { + if rec == nil { + return errors.New("eks: stagingInstaller Install nil record") + } + kv, err := i.acctKV(accountID) + if err != nil { + return err + } + manifest := stagedManifest{ + AddonName: rec.AddonName, + AddonVersion: rec.AddonVersion, + ServiceAccountRoleArn: rec.ServiceAccountRoleArn, + ConfigurationValues: rec.ConfigurationValues, + } + data, err := json.Marshal(&manifest) + if err != nil { + return fmt.Errorf("marshal staged manifest %s: %w", rec.AddonName, err) + } + key := AddonManifestKey(cluster, rec.AddonName) + if _, err := kv.Put(key, data); err != nil { + return fmt.Errorf("kv put %s: %w", key, err) + } + return nil +} + +func (i *stagingInstaller) Uninstall(accountID, cluster, addon string) error { + kv, err := i.acctKV(accountID) + if err != nil { + return err + } + key := AddonManifestKey(cluster, addon) + if err := kv.Delete(key); err != nil && !errors.Is(err, nats.ErrKeyNotFound) { + return fmt.Errorf("kv delete %s: %w", key, err) + } + return nil +} diff --git a/spinifex/handlers/eks/addon_state.go b/spinifex/handlers/eks/addon_state.go new file mode 100644 index 000000000..e9467dc14 --- /dev/null +++ b/spinifex/handlers/eks/addon_state.go @@ -0,0 +1,196 @@ +package handlers_eks + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/nats-io/nats.go" +) + +// AddonStatus mirrors the AWS EKS addon.status enum verbatim so clients match +// against the values without translation. +type AddonStatus string + +const ( + AddonStatusCreating AddonStatus = "CREATING" + AddonStatusActive AddonStatus = "ACTIVE" + AddonStatusUpdating AddonStatus = "UPDATING" + AddonStatusDeleting AddonStatus = "DELETING" + AddonStatusDegraded AddonStatus = "DEGRADED" + AddonStatusCreateFailed AddonStatus = "CREATE_FAILED" +) + +// AddonRecord is the persisted-state envelope for a managed add-on bound to a +// cluster. It tracks the AWS-visible lifecycle status plus the IRSA role and +// opaque configuration the installer needs. +type AddonRecord struct { + AddonName string `json:"addonName"` + AddonVersion string `json:"addonVersion"` + Status AddonStatus `json:"status"` + ServiceAccountRoleArn string `json:"serviceAccountRoleArn,omitempty"` + ConfigurationValues string `json:"configurationValues,omitempty"` + Health string `json:"health,omitempty"` + Arn string `json:"arn"` + Tags map[string]string `json:"tags,omitempty"` + CreatedAt time.Time `json:"createdAt"` + ModifiedAt time.Time `json:"modifiedAt"` +} + +// ErrAddonNotFound is returned by GetAddonRecord / the CAS helper when no +// record exists for the add-on. Callers translate to the AWS shape +// (ResourceNotFoundException) at the service boundary. +var ErrAddonNotFound = errors.New("eks: addon not found") + +// AddonARN composes the deterministic ARN for a managed add-on (one per +// add-on name per cluster). +func AddonARN(region, accountID, cluster, addon string) string { + return fmt.Sprintf("arn:aws:eks:%s:%s:addon/%s/%s", region, accountID, cluster, addon) +} + +// PutAddonRecord writes the record unconditionally. +func PutAddonRecord(kv nats.KeyValue, cluster string, rec *AddonRecord) error { + if rec == nil { + return errors.New("eks: PutAddonRecord nil record") + } + if cluster == "" || rec.AddonName == "" { + return errors.New("eks: PutAddonRecord missing cluster or addon name") + } + data, err := json.Marshal(rec) + if err != nil { + return fmt.Errorf("marshal addon %s: %w", rec.AddonName, err) + } + key := AddonKey(cluster, rec.AddonName) + if _, err := kv.Put(key, data); err != nil { + return fmt.Errorf("kv put %s: %w", key, err) + } + return nil +} + +// GetAddonRecord reads one record. Returns ErrAddonNotFound if absent. +func GetAddonRecord(kv nats.KeyValue, cluster, addon string) (*AddonRecord, error) { + if cluster == "" || addon == "" { + return nil, errors.New("eks: GetAddonRecord empty cluster or addon name") + } + entry, err := kv.Get(AddonKey(cluster, addon)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, ErrAddonNotFound + } + return nil, fmt.Errorf("kv get addon: %w", err) + } + var rec AddonRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal addon %s: %w", addon, err) + } + return &rec, nil +} + +// ListAddonRecords returns every add-on record under a cluster, sorted by add-on +// name for stable output. The staged-manifest sub-keys +// (clusters/{c}/addons/{addon}/manifest) are skipped — only the record keys +// (one path segment under the prefix) are returned. +func ListAddonRecords(kv nats.KeyValue, cluster string) ([]*AddonRecord, error) { + if cluster == "" { + return nil, errors.New("eks: ListAddonRecords empty cluster") + } + keys, err := kv.Keys() + if err != nil { + if errors.Is(err, nats.ErrNoKeysFound) { + return nil, nil + } + return nil, fmt.Errorf("kv keys: %w", err) + } + prefix := AddonsPrefix(cluster) + out := make([]*AddonRecord, 0) + for _, k := range keys { + if !strings.HasPrefix(k, prefix) { + continue + } + // Skip sub-keys (e.g. the staged manifest); a record key is exactly one + // segment under the prefix. + if strings.Contains(strings.TrimPrefix(k, prefix), "/") { + continue + } + entry, err := kv.Get(k) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + continue + } + return nil, fmt.Errorf("kv get %s: %w", k, err) + } + var rec AddonRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal addon %s: %w", k, err) + } + out = append(out, &rec) + } + sortAddonRecords(out) + return out, nil +} + +// DeleteAddonRecord removes one record (and its staged manifest, if any). +// Returns ErrAddonNotFound if the record did not exist. +func DeleteAddonRecord(kv nats.KeyValue, cluster, addon string) error { + key := AddonKey(cluster, addon) + if _, err := kv.Get(key); err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return ErrAddonNotFound + } + return fmt.Errorf("kv get %s: %w", key, err) + } + if err := kv.Delete(key); err != nil { + return fmt.Errorf("kv delete %s: %w", key, err) + } + // Best-effort: drop the staged manifest so a re-create starts clean. + manifestKey := AddonManifestKey(cluster, addon) + if err := kv.Delete(manifestKey); err != nil && !errors.Is(err, nats.ErrKeyNotFound) { + return fmt.Errorf("kv delete %s: %w", manifestKey, err) + } + return nil +} + +// casUpdateAddon does a revision-checked read-modify-write. mutate returns true +// if it changed a field. Returns ErrAddonNotFound if absent. +func casUpdateAddon(kv nats.KeyValue, cluster, addon string, mutate func(*AddonRecord) bool) (*AddonRecord, error) { + key := AddonKey(cluster, addon) + for range maxClusterStateCASRetries { + entry, err := kv.Get(key) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, ErrAddonNotFound + } + return nil, fmt.Errorf("kv get %s: %w", key, err) + } + var rec AddonRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal addon %s: %w", addon, err) + } + if !mutate(&rec) { + return &rec, nil + } + data, err := json.Marshal(&rec) + if err != nil { + return nil, fmt.Errorf("marshal addon %s: %w", addon, err) + } + _, err = kv.Update(key, data, entry.Revision()) + if err == nil { + return &rec, nil + } + if errors.Is(err, nats.ErrKeyExists) { + continue + } + return nil, fmt.Errorf("kv update %s: %w", key, err) + } + return nil, fmt.Errorf("eks: casUpdateAddon %s exhausted CAS retries", addon) +} + +func sortAddonRecords(recs []*AddonRecord) { + for i := 1; i < len(recs); i++ { + for j := i; j > 0 && recs[j-1].AddonName > recs[j].AddonName; j-- { + recs[j-1], recs[j] = recs[j], recs[j-1] + } + } +} diff --git a/spinifex/handlers/eks/addon_state_test.go b/spinifex/handlers/eks/addon_state_test.go new file mode 100644 index 000000000..709e7a10a --- /dev/null +++ b/spinifex/handlers/eks/addon_state_test.go @@ -0,0 +1,108 @@ +package handlers_eks + +import ( + "testing" + "time" + + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func sampleAddonRecord(name string) *AddonRecord { + now := time.Date(2026, 6, 8, 0, 0, 0, 0, time.UTC) + return &AddonRecord{ + AddonName: name, + AddonVersion: "1.0.0", + Status: AddonStatusCreating, + Arn: "arn:aws:eks:us-east-1:111122223333:addon/c1/" + name, + CreatedAt: now, + ModifiedAt: now, + } +} + +func TestAddonRecordGuards(t *testing.T) { + require.Error(t, PutAddonRecord(nil, "c1", nil)) + require.Error(t, PutAddonRecord(nil, "", sampleAddonRecord("x"))) + require.Error(t, PutAddonRecord(nil, "c1", &AddonRecord{})) + + _, err := GetAddonRecord(nil, "", "x") + require.Error(t, err) + + _, err = ListAddonRecords(nil, "") + require.Error(t, err) +} + +func TestAddonRecord_RoundTrip(t *testing.T) { + kv := newClusterStateTestKV(t) + + in := sampleAddonRecord("aws-load-balancer-controller") + in.Tags = map[string]string{"team": "platform"} + require.NoError(t, PutAddonRecord(kv, "c1", in)) + + got, err := GetAddonRecord(kv, "c1", in.AddonName) + require.NoError(t, err) + assert.Equal(t, in.AddonName, got.AddonName) + assert.Equal(t, in.AddonVersion, got.AddonVersion) + assert.Equal(t, in.Status, got.Status) + assert.Equal(t, "platform", got.Tags["team"]) +} + +func TestGetAddonRecord_NotFound(t *testing.T) { + kv := newClusterStateTestKV(t) + _, err := GetAddonRecord(kv, "c1", "ghost") + assert.ErrorIs(t, err, ErrAddonNotFound) +} + +func TestListAddonRecords_SkipsManifestSubKeys(t *testing.T) { + kv := newClusterStateTestKV(t) + + require.NoError(t, PutAddonRecord(kv, "c1", sampleAddonRecord("coredns"))) + require.NoError(t, PutAddonRecord(kv, "c1", sampleAddonRecord("aws-load-balancer-controller"))) + // A staged manifest sub-key must not be mistaken for a record. + _, err := kv.Put(AddonManifestKey("c1", "coredns"), []byte(`{"addonName":"coredns"}`)) + require.NoError(t, err) + + recs, err := ListAddonRecords(kv, "c1") + require.NoError(t, err) + require.Len(t, recs, 2) + // Sorted by name. + assert.Equal(t, "aws-load-balancer-controller", recs[0].AddonName) + assert.Equal(t, "coredns", recs[1].AddonName) +} + +func TestDeleteAddonRecord_RemovesRecordAndManifest(t *testing.T) { + kv := newClusterStateTestKV(t) + require.NoError(t, PutAddonRecord(kv, "c1", sampleAddonRecord("coredns"))) + _, err := kv.Put(AddonManifestKey("c1", "coredns"), []byte(`{}`)) + require.NoError(t, err) + + require.NoError(t, DeleteAddonRecord(kv, "c1", "coredns")) + + _, err = GetAddonRecord(kv, "c1", "coredns") + assert.ErrorIs(t, err, ErrAddonNotFound) + _, err = kv.Get(AddonManifestKey("c1", "coredns")) + assert.ErrorIs(t, err, nats.ErrKeyNotFound) + + // Deleting again is a not-found. + assert.ErrorIs(t, DeleteAddonRecord(kv, "c1", "coredns"), ErrAddonNotFound) +} + +func TestCasUpdateAddon(t *testing.T) { + kv := newClusterStateTestKV(t) + require.NoError(t, PutAddonRecord(kv, "c1", sampleAddonRecord("coredns"))) + + updated, err := casUpdateAddon(kv, "c1", "coredns", func(r *AddonRecord) bool { + r.Status = AddonStatusActive + return true + }) + require.NoError(t, err) + assert.Equal(t, AddonStatusActive, updated.Status) + + got, err := GetAddonRecord(kv, "c1", "coredns") + require.NoError(t, err) + assert.Equal(t, AddonStatusActive, got.Status) + + _, err = casUpdateAddon(kv, "c1", "ghost", func(*AddonRecord) bool { return true }) + assert.ErrorIs(t, err, ErrAddonNotFound) +} diff --git a/spinifex/handlers/eks/addons.go b/spinifex/handlers/eks/addons.go new file mode 100644 index 000000000..8fcb1e0db --- /dev/null +++ b/spinifex/handlers/eks/addons.go @@ -0,0 +1,279 @@ +package handlers_eks + +import ( + "errors" + "log/slog" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/nats-io/nats.go" +) + +// ListAddons returns the names of every managed add-on installed on a cluster. +func (s *EKSServiceImpl) ListAddons(input *eks.ListAddonsInput, accountID string) (*eks.ListAddonsOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + recs, err := ListAddonRecords(acctKV, cluster) + if err != nil { + return nil, err + } + names := make([]string, 0, len(recs)) + for _, rec := range recs { + names = append(names, rec.AddonName) + } + return &eks.ListAddonsOutput{Addons: aws.StringSlice(names)}, nil +} + +// DescribeAddonVersions returns the bundled add-on catalog, optionally filtered +// to a single add-on by name. It is cluster-independent (the catalog is static) +// so it does not touch the KV store. +func (s *EKSServiceImpl) DescribeAddonVersions(input *eks.DescribeAddonVersionsInput, _ string) (*eks.DescribeAddonVersionsOutput, error) { + filter := "" + if input != nil { + filter = aws.StringValue(input.AddonName) + } + specs := catalogSpecs() + out := make([]*eks.AddonInfo, 0, len(specs)) + for _, spec := range specs { + if filter != "" && spec.Name != filter { + continue + } + out = append(out, addonSpecToAWS(spec)) + } + return &eks.DescribeAddonVersionsOutput{Addons: out}, nil +} + +// CreateAddon validates the add-on against the bundled catalog, persists a +// CREATING record, and stages it for delivery. The record reaches ACTIVE only +// once the cluster's state report confirms delivery (separate slice). +func (s *EKSServiceImpl) CreateAddon(input *eks.CreateAddonInput, accountID string) (*eks.CreateAddonOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + addonName := aws.StringValue(input.AddonName) + if addonName == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + spec, ok := lookupAddon(addonName) + if !ok { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + version := aws.StringValue(input.AddonVersion) + if version == "" { + version = spec.DefaultVersion + } else if !spec.supportsVersion(version) { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + if _, err := GetAddonRecord(acctKV, cluster, addonName); err == nil { + return nil, errors.New(awserrors.ErrorEKSResourceInUse) + } else if !errors.Is(err, ErrAddonNotFound) { + return nil, err + } + now := time.Now().UTC() + rec := &AddonRecord{ + AddonName: addonName, + AddonVersion: version, + Status: AddonStatusCreating, + ServiceAccountRoleArn: aws.StringValue(input.ServiceAccountRoleArn), + ConfigurationValues: aws.StringValue(input.ConfigurationValues), + Arn: AddonARN(s.deps.Region, accountID, cluster, addonName), + Tags: aws.StringValueMap(input.Tags), + CreatedAt: now, + ModifiedAt: now, + } + if err := PutAddonRecord(acctKV, cluster, rec); err != nil { + return nil, err + } + if err := s.addonInstaller().Install(accountID, cluster, rec); err != nil { + s.markAddonFailed(acctKV, cluster, addonName, err) + return nil, err + } + return &eks.CreateAddonOutput{Addon: addonRecordToAWS(cluster, rec)}, nil +} + +// DescribeAddon returns one installed add-on's record. +func (s *EKSServiceImpl) DescribeAddon(input *eks.DescribeAddonInput, accountID string) (*eks.DescribeAddonOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + addonName := aws.StringValue(input.AddonName) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + rec, err := GetAddonRecord(acctKV, cluster, addonName) + if err != nil { + if errors.Is(err, ErrAddonNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return &eks.DescribeAddonOutput{Addon: addonRecordToAWS(cluster, rec)}, nil +} + +// UpdateAddon CASes the version/config/role onto the record, marks it UPDATING, +// and re-stages it for delivery. +func (s *EKSServiceImpl) UpdateAddon(input *eks.UpdateAddonInput, accountID string) (*eks.UpdateAddonOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + addonName := aws.StringValue(input.AddonName) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + // Validate a requested version against the catalog before the CAS. + if v := aws.StringValue(input.AddonVersion); v != "" { + spec, ok := lookupAddon(addonName) + if !ok || !spec.supportsVersion(v) { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + } + now := time.Now().UTC() + rec, err := casUpdateAddon(acctKV, cluster, addonName, func(r *AddonRecord) bool { + if v := aws.StringValue(input.AddonVersion); v != "" { + r.AddonVersion = v + } + if input.ConfigurationValues != nil { + r.ConfigurationValues = aws.StringValue(input.ConfigurationValues) + } + if input.ServiceAccountRoleArn != nil { + r.ServiceAccountRoleArn = aws.StringValue(input.ServiceAccountRoleArn) + } + r.Status = AddonStatusUpdating + r.ModifiedAt = now + return true + }) + if err != nil { + if errors.Is(err, ErrAddonNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + if err := s.addonInstaller().Install(accountID, cluster, rec); err != nil { + s.markAddonFailed(acctKV, cluster, addonName, err) + return nil, err + } + return &eks.UpdateAddonOutput{Update: &eks.Update{ + Id: aws.String(rec.Arn), + Status: aws.String(eks.UpdateStatusSuccessful), + Type: aws.String(eks.UpdateTypeAddonUpdate), + CreatedAt: aws.Time(now), + }}, nil +} + +// DeleteAddon marks the record DELETING, removes the staged manifest, and +// deletes the record. +func (s *EKSServiceImpl) DeleteAddon(input *eks.DeleteAddonInput, accountID string) (*eks.DeleteAddonOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + addonName := aws.StringValue(input.AddonName) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + rec, err := GetAddonRecord(acctKV, cluster, addonName) + if err != nil { + if errors.Is(err, ErrAddonNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + rec.Status = AddonStatusDeleting + rec.ModifiedAt = time.Now().UTC() + out := &eks.DeleteAddonOutput{Addon: addonRecordToAWS(cluster, rec)} + if err := s.addonInstaller().Uninstall(accountID, cluster, addonName); err != nil { + return nil, err + } + if err := DeleteAddonRecord(acctKV, cluster, addonName); err != nil { + if errors.Is(err, ErrAddonNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return out, nil +} + +// addonRecordToAWS converts a persisted record to the SDK Addon shape. +func addonRecordToAWS(cluster string, rec *AddonRecord) *eks.Addon { + out := &eks.Addon{ + AddonArn: aws.String(rec.Arn), + AddonName: aws.String(rec.AddonName), + AddonVersion: aws.String(rec.AddonVersion), + ClusterName: aws.String(cluster), + Status: aws.String(string(rec.Status)), + CreatedAt: aws.Time(rec.CreatedAt), + ModifiedAt: aws.Time(rec.ModifiedAt), + } + if rec.ServiceAccountRoleArn != "" { + out.ServiceAccountRoleArn = aws.String(rec.ServiceAccountRoleArn) + } + if rec.ConfigurationValues != "" { + out.ConfigurationValues = aws.String(rec.ConfigurationValues) + } + if rec.Health != "" { + out.Health = &eks.AddonHealth{Issues: []*eks.AddonIssue{{ + Message: aws.String(rec.Health), + }}} + } + if len(rec.Tags) > 0 { + out.Tags = aws.StringMap(rec.Tags) + } + return out +} + +// addonSpecToAWS converts a catalog spec to the SDK AddonInfo shape, expanding +// each supported version into an AddonVersionInfo. +func addonSpecToAWS(spec AddonSpec) *eks.AddonInfo { + versions := make([]*eks.AddonVersionInfo, 0, len(spec.Versions)) + for _, v := range spec.Versions { + versions = append(versions, &eks.AddonVersionInfo{ + AddonVersion: aws.String(v), + RequiresIamPermissions: aws.Bool(spec.RequiresIRSA), + }) + } + return &eks.AddonInfo{ + AddonName: aws.String(spec.Name), + AddonVersions: versions, + } +} + +// addonInstaller returns the wired installer, defaulting to the staging +// installer bound to the daemon NATS connection when none was injected. +func (s *EKSServiceImpl) addonInstaller() AddonInstaller { + if s.deps.AddonInstaller != nil { + return s.deps.AddonInstaller + } + return newStagingInstaller(s.deps.NATSConn) +} + +// markAddonFailed best-effort flips a record to CREATE_FAILED with the reason +// after an installer error so a failed install is not left looking healthy. +func (s *EKSServiceImpl) markAddonFailed(acctKV nats.KeyValue, cluster, addon string, cause error) { + now := time.Now().UTC() + if _, err := casUpdateAddon(acctKV, cluster, addon, func(r *AddonRecord) bool { + r.Status = AddonStatusCreateFailed + r.Health = cause.Error() + r.ModifiedAt = now + return true + }); err != nil { + slog.Warn("markAddonFailed: CAS failed", "cluster", cluster, "addon", addon, "err", err) + } +} diff --git a/spinifex/handlers/eks/addons_test.go b/spinifex/handlers/eks/addons_test.go new file mode 100644 index 000000000..2a7d9c17d --- /dev/null +++ b/spinifex/handlers/eks/addons_test.go @@ -0,0 +1,241 @@ +package handlers_eks + +import ( + "errors" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// fakeAddonInstaller records calls so tests can assert the state machine drives +// the installer with the right record. +type fakeAddonInstaller struct { + installs []*AddonRecord + uninstalls []string + installErr error +} + +var _ AddonInstaller = (*fakeAddonInstaller)(nil) + +func (f *fakeAddonInstaller) Install(_, _ string, rec *AddonRecord) error { + f.installs = append(f.installs, rec) + return f.installErr +} + +func (f *fakeAddonInstaller) Uninstall(_, _, addon string) error { + f.uninstalls = append(f.uninstalls, addon) + return nil +} + +func setupAddonService(t *testing.T) (*EKSServiceImpl, *fakeAddonInstaller) { + t.Helper() + svc := setupTestService(t) + seedTestCluster(t, svc, "c1") + fake := &fakeAddonInstaller{} + svc.deps.AddonInstaller = fake + return svc, fake +} + +const albController = "aws-load-balancer-controller" + +func TestDescribeAddonVersions_ReturnsCatalog(t *testing.T) { + svc := setupTestService(t) + + out, err := svc.DescribeAddonVersions(&eks.DescribeAddonVersionsInput{}, testAccountID) + require.NoError(t, err) + assert.Len(t, out.Addons, len(addonCatalog)) + + // Filtered to one addon. + out, err = svc.DescribeAddonVersions(&eks.DescribeAddonVersionsInput{ + AddonName: aws.String(albController), + }, testAccountID) + require.NoError(t, err) + require.Len(t, out.Addons, 1) + assert.Equal(t, albController, aws.StringValue(out.Addons[0].AddonName)) + require.NotEmpty(t, out.Addons[0].AddonVersions) + assert.True(t, aws.BoolValue(out.Addons[0].AddonVersions[0].RequiresIamPermissions)) +} + +func TestCreateAddon_UnknownClusterIsNotFound(t *testing.T) { + svc := setupTestService(t) + _, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("missing"), AddonName: aws.String(albController), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +func TestCreateAddon_UnknownAddonRejected(t *testing.T) { + svc, _ := setupAddonService(t) + _, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String("not-a-real-addon"), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestCreateAddon_UnknownVersionRejected(t *testing.T) { + svc, _ := setupAddonService(t) + _, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + AddonVersion: aws.String("9.9.9-nope"), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestCreateAddon_CreatesStagingAndDefaultsVersion(t *testing.T) { + svc, fake := setupAddonService(t) + spec, _ := lookupAddon(albController) + + out, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.NoError(t, err) + require.NotNil(t, out.Addon) + // Version defaults to catalog default; status starts CREATING (honest until + // VM-side delivery confirms ACTIVE). + assert.Equal(t, spec.DefaultVersion, aws.StringValue(out.Addon.AddonVersion)) + assert.Equal(t, string(AddonStatusCreating), aws.StringValue(out.Addon.Status)) + assert.Contains(t, aws.StringValue(out.Addon.AddonArn), ":addon/c1/"+albController) + + // Installer was driven with the persisted record. + require.Len(t, fake.installs, 1) + assert.Equal(t, albController, fake.installs[0].AddonName) + + // Duplicate create → ResourceInUseException. + _, err = svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceInUse) +} + +func TestCreateAddon_InstallerFailureMarksCreateFailed(t *testing.T) { + svc, fake := setupAddonService(t) + fake.installErr = errors.New("delivery bus unreachable") + + _, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.Error(t, err) + + desc, err := svc.DescribeAddon(&eks.DescribeAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, string(AddonStatusCreateFailed), aws.StringValue(desc.Addon.Status)) +} + +func TestAddon_DescribeListDelete(t *testing.T) { + svc, fake := setupAddonService(t) + + _, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.NoError(t, err) + + desc, err := svc.DescribeAddon(&eks.DescribeAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, albController, aws.StringValue(desc.Addon.AddonName)) + + list, err := svc.ListAddons(&eks.ListAddonsInput{ClusterName: aws.String("c1")}, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{albController}, aws.StringValueSlice(list.Addons)) + + _, err = svc.DeleteAddon(&eks.DeleteAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{albController}, fake.uninstalls) + + _, err = svc.DescribeAddon(&eks.DescribeAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +func TestDescribeDeleteAddon_MissingIsNotFound(t *testing.T) { + svc, _ := setupAddonService(t) + _, err := svc.DescribeAddon(&eks.DescribeAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) + _, err = svc.DeleteAddon(&eks.DeleteAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +func TestUpdateAddon_ChangesVersionAndReinstalls(t *testing.T) { + svc, fake := setupAddonService(t) + spec, _ := lookupAddon("coredns") + + _, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String("coredns"), + }, testAccountID) + require.NoError(t, err) + require.Len(t, fake.installs, 1) + + out, err := svc.UpdateAddon(&eks.UpdateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String("coredns"), + ConfigurationValues: aws.String(`{"replicaCount":2}`), + }, testAccountID) + require.NoError(t, err) + require.NotNil(t, out.Update) + assert.Equal(t, eks.UpdateTypeAddonUpdate, aws.StringValue(out.Update.Type)) + require.Len(t, fake.installs, 2, "update must re-drive the installer") + + desc, err := svc.DescribeAddon(&eks.DescribeAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String("coredns"), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, `{"replicaCount":2}`, aws.StringValue(desc.Addon.ConfigurationValues)) + assert.Equal(t, spec.DefaultVersion, aws.StringValue(desc.Addon.AddonVersion)) + assert.Equal(t, string(AddonStatusUpdating), aws.StringValue(desc.Addon.Status)) +} + +func TestUpdateAddon_UnknownVersionRejected(t *testing.T) { + svc, _ := setupAddonService(t) + _, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String("coredns"), + }, testAccountID) + require.NoError(t, err) + + _, err = svc.UpdateAddon(&eks.UpdateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String("coredns"), + AddonVersion: aws.String("9.9.9-nope"), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestUpdateAddon_MissingIsNotFound(t *testing.T) { + svc, _ := setupAddonService(t) + _, err := svc.UpdateAddon(&eks.UpdateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String("coredns"), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +// The default (uninjected) installer stages a manifest in KV that the VM-side +// delivery slice consumes. +func TestStagingInstaller_StagesManifest(t *testing.T) { + svc := setupTestService(t) + seedTestCluster(t, svc, "c1") + + _, err := svc.CreateAddon(&eks.CreateAddonInput{ + ClusterName: aws.String("c1"), AddonName: aws.String(albController), + ServiceAccountRoleArn: aws.String("arn:aws:iam::111122223333:role/alb"), + }, testAccountID) + require.NoError(t, err) + + js, err := svc.deps.NATSConn.JetStream() + require.NoError(t, err) + kv, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + entry, err := kv.Get(AddonManifestKey("c1", albController)) + require.NoError(t, err, "installer must stage a manifest for VM-side delivery") + assert.Contains(t, string(entry.Value()), albController) +} diff --git a/spinifex/handlers/eks/awsgw_endpoint.go b/spinifex/handlers/eks/awsgw_endpoint.go new file mode 100644 index 000000000..4031512f3 --- /dev/null +++ b/spinifex/handlers/eks/awsgw_endpoint.go @@ -0,0 +1,47 @@ +package handlers_eks + +import ( + "errors" + "fmt" + "net/url" + "strings" +) + +// ClusterOIDCIssuer returns the public OIDC issuer URL for one cluster. This +// is the URL kube-apiserver advertises as service-account-issuer and the URL +// that AWS STS / external IRSA validators fetch /.well-known/openid-configuration +// against. Format per plan §10.6: +// +// {gatewayBaseURL}/oidc/eks/{region}/{accountID}/{clusterName} +// +// gatewayBaseURL is the awsgw HTTPS endpoint (e.g. "https://10.0.0.1:9999"), +// without a trailing /oidc. Validates that region/accountID/clusterName are +// non-empty and URL-safe. +func ClusterOIDCIssuer(gatewayBaseURL, region, accountID, clusterName string) (string, error) { + if gatewayBaseURL == "" { + return "", errors.New("eks: ClusterOIDCIssuer empty gatewayBaseURL") + } + if region == "" { + return "", errors.New("eks: ClusterOIDCIssuer empty region") + } + if accountID == "" { + return "", errors.New("eks: ClusterOIDCIssuer empty accountID") + } + if clusterName == "" { + return "", errors.New("eks: ClusterOIDCIssuer empty clusterName") + } + parsed, err := url.Parse(gatewayBaseURL) + if err != nil { + return "", fmt.Errorf("parse gatewayBaseURL %q: %w", gatewayBaseURL, err) + } + if parsed.Scheme == "" || parsed.Host == "" { + return "", fmt.Errorf("eks: gatewayBaseURL %q missing scheme or host", gatewayBaseURL) + } + for name, v := range map[string]string{"region": region, "accountID": accountID, "clusterName": clusterName} { + if strings.ContainsAny(v, "/?#") { + return "", fmt.Errorf("eks: %s %q contains URL-unsafe characters", name, v) + } + } + base := strings.TrimRight(gatewayBaseURL, "/") + return fmt.Sprintf("%s/oidc/eks/%s/%s/%s", base, region, accountID, clusterName), nil +} diff --git a/spinifex/handlers/eks/awsgw_endpoint_test.go b/spinifex/handlers/eks/awsgw_endpoint_test.go new file mode 100644 index 000000000..7fec30e97 --- /dev/null +++ b/spinifex/handlers/eks/awsgw_endpoint_test.go @@ -0,0 +1,62 @@ +package handlers_eks + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestClusterOIDCIssuer_HappyPath(t *testing.T) { + got, err := ClusterOIDCIssuer("https://gw.example:9999", "us-east-1", "111122223333", "alpha") + require.NoError(t, err) + assert.Equal(t, "https://gw.example:9999/oidc/eks/us-east-1/111122223333/alpha", got) +} + +func TestClusterOIDCIssuer_TrimsTrailingSlash(t *testing.T) { + got, err := ClusterOIDCIssuer("https://gw.example:9999/", "us-east-1", "111122223333", "alpha") + require.NoError(t, err) + assert.Equal(t, "https://gw.example:9999/oidc/eks/us-east-1/111122223333/alpha", got) +} + +func TestClusterOIDCIssuer_EmptyInputsRejected(t *testing.T) { + cases := []struct { + name string + base, region, accountID, clusterName, want string + }{ + {"empty base", "", "us-east-1", "111122223333", "alpha", "gatewayBaseURL"}, + {"empty region", "https://gw", "", "111122223333", "alpha", "region"}, + {"empty accountID", "https://gw", "us-east-1", "", "alpha", "accountID"}, + {"empty clusterName", "https://gw", "us-east-1", "111122223333", "", "clusterName"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, err := ClusterOIDCIssuer(tc.base, tc.region, tc.accountID, tc.clusterName) + require.Error(t, err) + assert.Contains(t, err.Error(), tc.want) + }) + } +} + +func TestClusterOIDCIssuer_MalformedBaseRejected(t *testing.T) { + _, err := ClusterOIDCIssuer("not-a-url", "us-east-1", "111122223333", "alpha") + require.Error(t, err) + assert.Contains(t, err.Error(), "missing scheme or host") +} + +func TestClusterOIDCIssuer_URLUnsafeInputsRejected(t *testing.T) { + cases := []struct { + field, region, accountID, clusterName string + }{ + {"region", "us/east", "111122223333", "alpha"}, + {"accountID", "us-east-1", "111?ab", "alpha"}, + {"clusterName", "us-east-1", "111122223333", "alpha#frag"}, + } + for _, tc := range cases { + t.Run(tc.field, func(t *testing.T) { + _, err := ClusterOIDCIssuer("https://gw", tc.region, tc.accountID, tc.clusterName) + require.Error(t, err) + assert.Contains(t, err.Error(), "URL-unsafe") + }) + } +} diff --git a/spinifex/handlers/eks/cluster_reconciler.go b/spinifex/handlers/eks/cluster_reconciler.go new file mode 100644 index 000000000..3f0d8edd2 --- /dev/null +++ b/spinifex/handlers/eks/cluster_reconciler.go @@ -0,0 +1,421 @@ +package handlers_eks + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "log/slog" + "net/http" + "sync/atomic" + "time" + + "github.com/nats-io/nats.go" +) + +const ( + defaultReconcileLeaseRefresh = 20 * time.Second + defaultReconcileInterval = 30 * time.Second + defaultHealthzTimeout = 5 * time.Second + // defaultCreateTimeout bounds how long a cluster may sit in CREATING before + // the reconciler gives up and marks it FAILED. Measured from meta.CreatedAt + // (absolute), so a VM that never boots reaches a terminal state even across + // daemon restarts. Plan Risk #1. + defaultCreateTimeout = 15 * time.Minute + // defaultStateStaleAfter bounds how old the control plane's last NATS state + // report may be before health is treated as unknown/failed. The CP publishes + // every ~30s; 3x that tolerates a missed tick without flapping. + defaultStateStaleAfter = 90 * time.Second +) + +// natsSubscriber is the minimum surface the reconciler needs to consume the +// control-plane state report; *nats.Conn satisfies it, tests stub it. +type natsSubscriber interface { + Subscribe(subject string, cb nats.MsgHandler) (*nats.Subscription, error) +} + +// ErrReconcilerLeaseLost is returned by Run when the leader lease was lost +// mid-loop. Callers can re-Acquire and re-Run if they want to keep driving +// reconcile from the same node. +var ErrReconcilerLeaseLost = errors.New("eks: reconciler lease lost") + +// ErrReconcilerClusterDeleting is returned by Run when meta.Status is +// observed as DELETING; the DeleteCluster service-impl path is responsible +// for teardown, so the reconciler exits without further work. +var ErrReconcilerClusterDeleting = errors.New("eks: cluster deleting") + +// ErrReconcilerClusterFailed is returned by Run when meta.Status is FAILED. +var ErrReconcilerClusterFailed = errors.New("eks: cluster failed") + +// HTTPDoer is the minimum http.Client surface the reconciler needs for +// /healthz probes; tests stub it without a TLS server. +type HTTPDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// ClusterReconciler drives one cluster's CREATING → ACTIVE → DELETING +// lifecycle under a leader lease. One instance per (accountID, clusterName); +// the leader lease arbitrates which spinifex node actually drives reconcile. +type ClusterReconciler struct { + leaderKV nats.KeyValue + acctKV nats.KeyValue + accountID string + clusterName string + holderID string + healthURL string + + leaseRefresh time.Duration + interval time.Duration + healthTimeout time.Duration + createTimeout time.Duration + httpClient HTTPDoer + + // State-report health source. When stateSub is non-nil the reconciler gates + // on the CP's NATS self-report (latest) rather than the HTTP /healthz probe — + // the apiserver is bound to the VPC node-ip and unreachable from the host. + stateSub natsSubscriber + stateSubject string + stateStaleAfter time.Duration + latest atomic.Pointer[ServerStateReport] +} + +// ReconcilerOption tunes a ClusterReconciler. Tests use the With* helpers to +// shrink tickers and inject a stub HTTP client. +type ReconcilerOption func(*ClusterReconciler) + +// WithReconcileInterval overrides the default reconcile loop period. +func WithReconcileInterval(d time.Duration) ReconcilerOption { + return func(r *ClusterReconciler) { r.interval = d } +} + +// WithLeaseRefresh overrides the default leader-lease refresh period. Should +// be less than KVBucketEKSLeaderTTL. +func WithLeaseRefresh(d time.Duration) ReconcilerOption { + return func(r *ClusterReconciler) { r.leaseRefresh = d } +} + +// WithHealthzTimeout overrides the per-probe HTTP timeout. +func WithHealthzTimeout(d time.Duration) ReconcilerOption { + return func(r *ClusterReconciler) { r.healthTimeout = d } +} + +// WithCreateTimeout overrides how long a cluster may remain CREATING before the +// reconciler marks it FAILED. +func WithCreateTimeout(d time.Duration) ReconcilerOption { + return func(r *ClusterReconciler) { r.createTimeout = d } +} + +// WithHTTPClient injects a stub HTTPDoer (tests). +func WithHTTPClient(c HTTPDoer) ReconcilerOption { + return func(r *ClusterReconciler) { r.httpClient = c } +} + +// WithStateSource configures the reconciler to gate health on the control +// plane's NATS state report (subject) consumed via sub, instead of the HTTP +// /healthz probe. The subscription is opened in Run and closed when it returns. +func WithStateSource(sub natsSubscriber, subject string) ReconcilerOption { + return func(r *ClusterReconciler) { + r.stateSub = sub + r.stateSubject = subject + } +} + +// WithStateStaleAfter overrides how old the last state report may be before +// health is treated as failing. +func WithStateStaleAfter(d time.Duration) ReconcilerOption { + return func(r *ClusterReconciler) { r.stateStaleAfter = d } +} + +// NewClusterReconciler constructs a ClusterReconciler. healthURL is the +// fully-qualified probe target (e.g. https://nlb-dns:443/healthz) or empty +// to skip /healthz probing (CREATING transitions on bootstrap state alone). +func NewClusterReconciler(leaderKV, acctKV nats.KeyValue, accountID, clusterName, holderID, healthURL string, opts ...ReconcilerOption) (*ClusterReconciler, error) { + if leaderKV == nil { + return nil, errors.New("eks: NewClusterReconciler nil leaderKV") + } + if acctKV == nil { + return nil, errors.New("eks: NewClusterReconciler nil acctKV") + } + if accountID == "" { + return nil, errors.New("eks: NewClusterReconciler empty accountID") + } + if clusterName == "" { + return nil, errors.New("eks: NewClusterReconciler empty clusterName") + } + if holderID == "" { + return nil, errors.New("eks: NewClusterReconciler empty holderID") + } + r := &ClusterReconciler{ + leaderKV: leaderKV, + acctKV: acctKV, + accountID: accountID, + clusterName: clusterName, + holderID: holderID, + healthURL: healthURL, + leaseRefresh: defaultReconcileLeaseRefresh, + interval: defaultReconcileInterval, + healthTimeout: defaultHealthzTimeout, + createTimeout: defaultCreateTimeout, + stateStaleAfter: defaultStateStaleAfter, + httpClient: &http.Client{ + Timeout: defaultHealthzTimeout, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, //nolint:gosec // K3s NLB is self-signed; CA pinning is a v2 hardening item + }, + }, + }, + } + for _, o := range opts { + o(r) + } + return r, nil +} + +func reconcilerLeaderKey(accountID, clusterName string) string { + return accountID + "/" + clusterName +} + +// AcquireLease attempts a CAS Create on the leader bucket. Returns +// (release, true) if this caller is now the leader; (nil, false) otherwise. +// The release closure performs a best-effort kv.Delete; the bucket's 60s +// TTL reaps stale leases when release does not run (crashed leader). +func (r *ClusterReconciler) AcquireLease() (func(), bool) { + key := reconcilerLeaderKey(r.accountID, r.clusterName) + if _, err := r.leaderKV.Create(key, []byte(r.holderID)); err != nil { + slog.Debug("ClusterReconciler: lease held by another holder", + "key", key, "holderID", r.holderID, "err", err) + return nil, false + } + slog.Info("ClusterReconciler: lease acquired", "key", key, "holderID", r.holderID) + return func() { + if err := r.leaderKV.Delete(key); err != nil { + slog.Warn("ClusterReconciler: lease release failed (TTL will reap)", + "key", key, "holderID", r.holderID, "err", err) + } + }, true +} + +// RefreshLease re-asserts ownership via a CAS update on the leader key. +// Returns false if the key was taken by another holder or the CAS conflicts. +func (r *ClusterReconciler) RefreshLease() bool { + key := reconcilerLeaderKey(r.accountID, r.clusterName) + entry, err := r.leaderKV.Get(key) + if err != nil { + slog.Warn("ClusterReconciler: lease get failed", "key", key, "err", err) + return false + } + if string(entry.Value()) != r.holderID { + slog.Info("ClusterReconciler: lease now held by another holder", + "key", key, "holderID", r.holderID, "got", string(entry.Value())) + return false + } + if _, err := r.leaderKV.Update(key, []byte(r.holderID), entry.Revision()); err != nil { + slog.Warn("ClusterReconciler: lease CAS update failed", + "key", key, "holderID", r.holderID, "err", err) + return false + } + return true +} + +// Run drives reconcile until ctx is cancelled, the lease is lost, or the +// cluster reaches a terminal state. Caller is expected to AcquireLease first; +// Run does not re-acquire. +func (r *ClusterReconciler) Run(ctx context.Context) error { + if r.stateSub != nil && r.stateSubject != "" { + sub, err := r.stateSub.Subscribe(r.stateSubject, func(m *nats.Msg) { + report, perr := unmarshalServerStateReport(m.Data) + if perr != nil { + slog.Warn("ClusterReconciler: bad state report", + "cluster", r.clusterName, "subject", m.Subject, "err", perr) + return + } + r.latest.Store(&report) + }) + if err != nil { + return fmt.Errorf("subscribe state report %s: %w", r.stateSubject, err) + } + defer func() { _ = sub.Unsubscribe() }() + } + + refreshT := time.NewTicker(r.leaseRefresh) + defer refreshT.Stop() + reconcileT := time.NewTicker(r.interval) + defer reconcileT.Stop() + + if err := r.reconcileOnce(ctx); err != nil { + if terminalReconcileErr(err) { + return err + } + slog.Warn("ClusterReconciler: initial reconcile failed", + "cluster", r.clusterName, "err", err) + } + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-refreshT.C: + if !r.RefreshLease() { + return ErrReconcilerLeaseLost + } + case <-reconcileT.C: + if err := r.reconcileOnce(ctx); err != nil { + if terminalReconcileErr(err) { + return err + } + slog.Warn("ClusterReconciler: reconcile failed", + "cluster", r.clusterName, "err", err) + } + } + } +} + +func terminalReconcileErr(err error) bool { + return errors.Is(err, ErrReconcilerClusterDeleting) || + errors.Is(err, ErrReconcilerClusterFailed) || + errors.Is(err, ErrClusterNotFound) +} + +func (r *ClusterReconciler) reconcileOnce(ctx context.Context) error { + meta, err := GetClusterMeta(r.acctKV, r.clusterName) + if err != nil { + return err + } + switch meta.Status { + case ClusterStatusCreating: + ready, reason := r.bootstrapReady(meta) + if !ready { + slog.Debug("ClusterReconciler: bootstrap not ready", + "cluster", r.clusterName, "reason", reason) + return r.failIfCreateTimedOut(meta, "bootstrap not ready: "+reason) + } + issue, nodeCount := r.observe(ctx) + if issue != "" { + slog.Debug("ClusterReconciler: health not ready", + "cluster", r.clusterName, "issue", issue) + return r.failIfCreateTimedOut(meta, "healthz not ready: "+issue) + } + if err := SetClusterStatus(r.acctKV, r.clusterName, ClusterStatusActive); err != nil { + return err + } + if err := SetClusterHealthState(r.acctKV, r.clusterName, "", nodeCount); err != nil { + if errors.Is(err, ErrClusterNotFound) { + return ErrClusterNotFound + } + return fmt.Errorf("record cluster health: %w", err) + } + slog.Info("ClusterReconciler: cluster transitioned to ACTIVE", + "cluster", r.clusterName, "nodes", nodeCount) + case ClusterStatusActive: + issue, nodeCount := r.observe(ctx) + if issue != "" { + slog.Warn("ClusterReconciler: health failing for ACTIVE cluster", + "cluster", r.clusterName, "issue", issue) + } + if err := SetClusterHealthState(r.acctKV, r.clusterName, issue, nodeCount); err != nil { + if errors.Is(err, ErrClusterNotFound) { + return ErrClusterNotFound + } + return fmt.Errorf("record cluster health: %w", err) + } + case ClusterStatusDeleting: + return ErrReconcilerClusterDeleting + case ClusterStatusFailed: + return ErrReconcilerClusterFailed + } + return nil +} + +// observe returns the current health issue ("" when healthy) and the reported +// node count. With a NATS state source it reads the control plane's latest +// self-report (rejecting a stale or unhealthy one); otherwise it falls back to +// the HTTP /healthz probe (node count is then 0, the probe carries no count). +func (r *ClusterReconciler) observe(ctx context.Context) (issue string, nodeCount int) { + if r.stateSub != nil && r.stateSubject != "" { + report := r.latest.Load() + if report == nil { + return "no control-plane state report received yet", 0 + } + age := time.Since(time.Unix(report.TS, 0)) + if age > r.stateStaleAfter { + return fmt.Sprintf("control-plane state report stale (%s old)", age.Round(time.Second)), report.NodeCount + } + if !report.Healthy() { + return fmt.Sprintf("apiserver healthz=%q", report.Healthz), report.NodeCount + } + return "", report.NodeCount + } + if err := r.probeHealthz(ctx); err != nil { + return err.Error(), 0 + } + return "", 0 +} + +// failIfCreateTimedOut transitions a still-CREATING cluster to FAILED once it +// has exceeded createTimeout since meta.CreatedAt, and returns +// ErrReconcilerClusterFailed so Run exits the loop. While still within the +// window it returns nil so the next tick retries. A zero or absent CreatedAt +// disables the deadline (no false-positive fail on a malformed record). +func (r *ClusterReconciler) failIfCreateTimedOut(meta *ClusterMeta, reason string) error { + if r.createTimeout <= 0 || meta.CreatedAt.IsZero() { + return nil + } + if time.Since(meta.CreatedAt) < r.createTimeout { + return nil + } + failReason := fmt.Sprintf("cluster did not become ACTIVE within %s: %s", r.createTimeout, reason) + if err := MarkClusterFailed(r.acctKV, r.clusterName, failReason); err != nil { + if errors.Is(err, ErrClusterNotFound) { + return ErrClusterNotFound + } + return fmt.Errorf("mark create-timeout failed: %w", err) + } + slog.Warn("ClusterReconciler: CREATING timed out, marked FAILED", + "cluster", r.clusterName, "timeout", r.createTimeout, "reason", reason) + return ErrReconcilerClusterFailed +} + +// bootstrapReady returns true once the four NATS bootstrap KV artifacts are +// present: node token, admin kubeconfig, the OIDC JWKS verified-marker, and the +// CA on meta. The JWKS gate is the verified-marker (written only after the VM's +// published JWKS passes the controller cross-check), NOT the pre-seeded +// OIDCJWKSKey — so a cluster never transitions to ACTIVE on an unverified or +// mismatched signing key. +func (r *ClusterReconciler) bootstrapReady(meta *ClusterMeta) (bool, string) { + if meta.CertificateAuthorityB64 == "" { + return false, "CA absent on meta" + } + keys := []string{ + NodeTokenKey(r.clusterName), + AdminKubeconfigKey(r.clusterName), + OIDCJWKSVerifiedKey(r.clusterName), + } + for _, k := range keys { + if _, err := r.acctKV.Get(k); err != nil { + return false, fmt.Sprintf("%s missing: %s", k, err) + } + } + return true, "" +} + +func (r *ClusterReconciler) probeHealthz(ctx context.Context) error { + if r.healthURL == "" { + return nil + } + probeCtx, cancel := context.WithTimeout(ctx, r.healthTimeout) + defer cancel() + req, err := http.NewRequestWithContext(probeCtx, http.MethodGet, r.healthURL, nil) + if err != nil { + return fmt.Errorf("new request: %w", err) + } + resp, err := r.httpClient.Do(req) + if err != nil { + return fmt.Errorf("healthz request: %w", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("eks: /healthz status %d", resp.StatusCode) + } + return nil +} diff --git a/spinifex/handlers/eks/cluster_reconciler_state_test.go b/spinifex/handlers/eks/cluster_reconciler_state_test.go new file mode 100644 index 000000000..49335d17a --- /dev/null +++ b/spinifex/handlers/eks/cluster_reconciler_state_test.go @@ -0,0 +1,154 @@ +package handlers_eks + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// newStateReconcilerHarness builds a reconciler wired to a real NATS conn as its +// state source (so observe() reads r.latest) and returns the conn + account KV. +// It does NOT start a subscription — tests drive r.latest directly for +// determinism; the integration test below exercises the real Subscribe path. +func newStateReconcilerHarness(t *testing.T, opts ...ReconcilerOption) (*ClusterReconciler, *nats.Conn, nats.KeyValue) { + t.Helper() + _, nc, js := testutil.StartTestJetStream(t) + leaderKV, err := InitLeaderBucket(js) + require.NoError(t, err) + acctKV, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + require.NoError(t, PutClusterMeta(acctKV, sampleClusterMeta("alpha"))) + + base := []ReconcilerOption{WithStateSource(nc, StateSubject(testAccountID, "alpha"))} + r, err := NewClusterReconciler(leaderKV, acctKV, testAccountID, "alpha", "holder-1", "", append(base, opts...)...) + require.NoError(t, err) + return r, nc, acctKV +} + +func freshReport(healthz string, nodes int) *ServerStateReport { + return &ServerStateReport{Healthz: healthz, NodeCount: nodes, TS: time.Now().Unix()} +} + +func TestClusterReconciler_CreatingTransitionsToActiveOnStateReport(t *testing.T) { + r, _, acctKV := newStateReconcilerHarness(t) + freshenClusterCreatedAt(t, acctKV) + seedBootstrapState(t, acctKV) + r.latest.Store(freshReport("ok", 3)) + + require.NoError(t, r.reconcileOnce(context.Background())) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusActive, meta.Status) + assert.Equal(t, 3, meta.NodeCount) + assert.Empty(t, meta.HealthIssue) +} + +func TestClusterReconciler_CreatingStaysWithoutStateReport(t *testing.T) { + r, _, acctKV := newStateReconcilerHarness(t) + freshenClusterCreatedAt(t, acctKV) + seedBootstrapState(t, acctKV) + // No report stored. + + require.NoError(t, r.reconcileOnce(context.Background())) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusCreating, meta.Status, "no state report yet → stays CREATING") +} + +func TestClusterReconciler_CreatingStaysOnUnhealthyReport(t *testing.T) { + r, _, acctKV := newStateReconcilerHarness(t) + freshenClusterCreatedAt(t, acctKV) + seedBootstrapState(t, acctKV) + r.latest.Store(freshReport("fail", 0)) + + require.NoError(t, r.reconcileOnce(context.Background())) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusCreating, meta.Status, "unhealthy apiserver → stays CREATING") +} + +func TestClusterReconciler_ActiveRecordsNodeCountAndClearsIssue(t *testing.T) { + r, _, acctKV := newStateReconcilerHarness(t) + require.NoError(t, SetClusterStatus(acctKV, "alpha", ClusterStatusActive)) + require.NoError(t, SetClusterHealthState(acctKV, "alpha", "stale", 0)) + r.latest.Store(freshReport("ok", 4)) + + require.NoError(t, r.reconcileOnce(context.Background())) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusActive, meta.Status) + assert.Equal(t, 4, meta.NodeCount) + assert.Empty(t, meta.HealthIssue, "healthy report clears the prior issue") +} + +func TestClusterReconciler_ActiveFlagsStaleReport(t *testing.T) { + r, _, acctKV := newStateReconcilerHarness(t, WithStateStaleAfter(90*time.Second)) + require.NoError(t, SetClusterStatus(acctKV, "alpha", ClusterStatusActive)) + r.latest.Store(&ServerStateReport{Healthz: "ok", NodeCount: 2, TS: time.Now().Add(-5 * time.Minute).Unix()}) + + require.NoError(t, r.reconcileOnce(context.Background())) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Contains(t, meta.HealthIssue, "stale", "report older than stale window flags an issue") + assert.Equal(t, 2, meta.NodeCount, "last known node count still recorded") +} + +func TestClusterReconciler_StateReportSubscriptionDrivesActive(t *testing.T) { + r, nc, acctKV := newStateReconcilerHarness(t, + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + freshenClusterCreatedAt(t, acctKV) + seedBootstrapState(t, acctKV) + + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + runErr := make(chan error, 1) + go func() { runErr <- r.Run(ctx) }() + + // Publish healthy reports until the cluster goes ACTIVE (core NATS only + // delivers post-subscribe, so keep emitting while Run's subscription opens). + stop := make(chan struct{}) + defer close(stop) + go func() { + payload, _ := json.Marshal(ServerStateReport{Healthz: "ok", NodeCount: 3, TS: time.Now().Unix()}) + tick := time.NewTicker(20 * time.Millisecond) + defer tick.Stop() + for { + select { + case <-stop: + return + case <-tick.C: + _ = nc.Publish(StateSubject(testAccountID, "alpha"), payload) + } + } + }() + + require.Eventually(t, func() bool { + meta, err := GetClusterMeta(acctKV, "alpha") + return err == nil && meta.Status == ClusterStatusActive && meta.NodeCount == 3 + }, 1500*time.Millisecond, 10*time.Millisecond, "state reports should drive CREATING → ACTIVE") + + cancel() + select { + case err := <-runErr: + assert.ErrorIs(t, err, context.Canceled) + case <-time.After(2 * time.Second): + t.Fatal("Run did not return after cancel") + } +} diff --git a/spinifex/handlers/eks/cluster_reconciler_test.go b/spinifex/handlers/eks/cluster_reconciler_test.go new file mode 100644 index 000000000..f327e5571 --- /dev/null +++ b/spinifex/handlers/eks/cluster_reconciler_test.go @@ -0,0 +1,469 @@ +package handlers_eks + +import ( + "context" + "errors" + "io" + "net/http" + "strings" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// stubHTTPDoer is a fake HTTPDoer that returns a configurable status code + +// optional error, and tracks the number of calls for assertion. +type stubHTTPDoer struct { + mu sync.Mutex + calls atomic.Int32 + status int + err error +} + +func (s *stubHTTPDoer) Do(_ *http.Request) (*http.Response, error) { + s.calls.Add(1) + s.mu.Lock() + err := s.err + status := s.status + s.mu.Unlock() + if err != nil { + return nil, err + } + return &http.Response{StatusCode: status, Body: io.NopCloser(strings.NewReader(""))}, nil +} + +func (s *stubHTTPDoer) setResponse(status int, err error) { + s.mu.Lock() + defer s.mu.Unlock() + s.status = status + s.err = err +} + +func newReconcilerHarness(t *testing.T, healthURL string, opts ...ReconcilerOption) (*ClusterReconciler, nats.KeyValue, nats.KeyValue) { + t.Helper() + _, _, js := testutil.StartTestJetStream(t) + leaderKV, err := InitLeaderBucket(js) + require.NoError(t, err) + acctKV, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + require.NoError(t, PutClusterMeta(acctKV, sampleClusterMeta("alpha"))) + + r, err := NewClusterReconciler(leaderKV, acctKV, testAccountID, "alpha", "holder-1", healthURL, opts...) + require.NoError(t, err) + return r, leaderKV, acctKV +} + +func TestNewClusterReconciler_EmptyInputsRejected(t *testing.T) { + _, _, js := testutil.StartTestJetStream(t) + leaderKV, err := InitLeaderBucket(js) + require.NoError(t, err) + acctKV, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + + _, err = NewClusterReconciler(nil, acctKV, testAccountID, "alpha", "h", "") + require.Error(t, err) + _, err = NewClusterReconciler(leaderKV, nil, testAccountID, "alpha", "h", "") + require.Error(t, err) + _, err = NewClusterReconciler(leaderKV, acctKV, "", "alpha", "h", "") + require.Error(t, err) + _, err = NewClusterReconciler(leaderKV, acctKV, testAccountID, "", "h", "") + require.Error(t, err) + _, err = NewClusterReconciler(leaderKV, acctKV, testAccountID, "alpha", "", "") + require.Error(t, err) +} + +func TestClusterReconciler_AcquireLeaseFirstHolderWins(t *testing.T) { + r, _, _ := newReconcilerHarness(t, "") + + release, ok := r.AcquireLease() + require.True(t, ok) + require.NotNil(t, release) + defer release() + + // Second AcquireLease from same holder must fail (Create returns KeyExists). + release2, ok2 := r.AcquireLease() + assert.False(t, ok2) + assert.Nil(t, release2) +} + +func TestClusterReconciler_AcquireLeaseSecondHolderLoses(t *testing.T) { + r1, leaderKV, acctKV := newReconcilerHarness(t, "") + + release, ok := r1.AcquireLease() + require.True(t, ok) + defer release() + + r2, err := NewClusterReconciler(leaderKV, acctKV, testAccountID, "alpha", "holder-2", "") + require.NoError(t, err) + release2, ok2 := r2.AcquireLease() + assert.False(t, ok2) + assert.Nil(t, release2) +} + +func TestClusterReconciler_RefreshLeaseFailsAfterStolen(t *testing.T) { + r, leaderKV, _ := newReconcilerHarness(t, "") + + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + assert.True(t, r.RefreshLease(), "refresh should succeed while we own the key") + + // Steal the key by overwriting its value via Put. + _, err := leaderKV.Put(reconcilerLeaderKey(testAccountID, "alpha"), []byte("holder-2")) + require.NoError(t, err) + + assert.False(t, r.RefreshLease(), "refresh should fail after another holder stole the key") +} + +func TestClusterReconciler_RefreshLeaseFailsWhenKeyDeleted(t *testing.T) { + r, leaderKV, _ := newReconcilerHarness(t, "") + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + require.NoError(t, leaderKV.Delete(reconcilerLeaderKey(testAccountID, "alpha"))) + assert.False(t, r.RefreshLease()) +} + +// freshenClusterCreatedAt re-stamps the cluster meta's CreatedAt to now so the +// reconciler's CREATING deadline is far from expiry. sampleClusterMeta uses a +// fixed past date, which would otherwise trip the create-timeout in tests that +// assert the cluster stays CREATING. +func freshenClusterCreatedAt(t *testing.T, kv nats.KeyValue) { + t.Helper() + meta, err := GetClusterMeta(kv, "alpha") + require.NoError(t, err) + meta.CreatedAt = time.Now().UTC() + require.NoError(t, PutClusterMeta(kv, meta)) +} + +func seedBootstrapState(t *testing.T, kv nats.KeyValue) { + t.Helper() + _, err := kv.Put(NodeTokenKey("alpha"), []byte("enc-token")) + require.NoError(t, err) + _, err = kv.Put(AdminKubeconfigKey("alpha"), []byte("enc-kc")) + require.NoError(t, err) + // Pre-seeded JWKS plus the verified-marker persistJWKS writes on a passing + // cross-check — bootstrapReady gates ACTIVE on the marker, not the seed. + _, err = kv.Put(OIDCJWKSKey("alpha"), []byte(`{"keys":[]}`)) + require.NoError(t, err) + _, err = kv.Put(OIDCJWKSVerifiedKey("alpha"), []byte("verified")) + require.NoError(t, err) + require.NoError(t, SetClusterCertificateAuthority(kv, "alpha", "ca-blob-b64")) +} + +func TestClusterReconciler_CreatingTransitionsToActiveOnReadyAndHealthz(t *testing.T) { + stub := &stubHTTPDoer{status: http.StatusOK} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + seedBootstrapState(t, acctKV) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + runErr := make(chan error, 1) + go func() { runErr <- r.Run(ctx) }() + + require.Eventually(t, func() bool { + meta, err := GetClusterMeta(acctKV, "alpha") + if err != nil { + return false + } + return meta.Status == ClusterStatusActive + }, 1500*time.Millisecond, 10*time.Millisecond, "CREATING should transition to ACTIVE") + + cancel() + select { + case err := <-runErr: + assert.ErrorIs(t, err, context.Canceled) + case <-time.After(2 * time.Second): + t.Fatal("Run did not return after cancel") + } + assert.Positive(t, stub.calls.Load(), "/healthz should have been probed at least once") +} + +func TestClusterReconciler_CreatingStaysWhenBootstrapMissing(t *testing.T) { + stub := &stubHTTPDoer{status: http.StatusOK} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + freshenClusterCreatedAt(t, acctKV) + + // Only seed two of the three KV keys (no CA, no JWKS). + _, err := acctKV.Put(NodeTokenKey("alpha"), []byte("enc-token")) + require.NoError(t, err) + _, err = acctKV.Put(AdminKubeconfigKey("alpha"), []byte("enc-kc")) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond) + defer cancel() + _ = r.Run(ctx) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusCreating, meta.Status, "must remain CREATING when bootstrap incomplete") +} + +func TestClusterReconciler_CreatingStaysWhenJWKSUnverified(t *testing.T) { + // Regression for the ACTIVE-on-pre-seeded-JWKS race: every artifact is + // present INCLUDING the controller-pre-seeded OIDCJWKSKey, but the VM's + // JWKS cross-check has not passed (no verified-marker). The cluster must + // stay CREATING — reaching ACTIVE here would mean signing IRSA tokens under + // a key the controller never confirmed. + stub := &stubHTTPDoer{status: http.StatusOK} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + freshenClusterCreatedAt(t, acctKV) + + _, err := acctKV.Put(NodeTokenKey("alpha"), []byte("enc-token")) + require.NoError(t, err) + _, err = acctKV.Put(AdminKubeconfigKey("alpha"), []byte("enc-kc")) + require.NoError(t, err) + require.NoError(t, SetClusterCertificateAuthority(acctKV, "alpha", "ca-blob-b64")) + // Pre-seeded JWKS present, but NO verified-marker. + _, err = acctKV.Put(OIDCJWKSKey("alpha"), []byte(`{"keys":[]}`)) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond) + defer cancel() + _ = r.Run(ctx) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusCreating, meta.Status, + "must remain CREATING until the JWKS cross-check positively passes") +} + +func TestClusterReconciler_CreatingStaysWhenHealthzFails(t *testing.T) { + stub := &stubHTTPDoer{err: errors.New("connection refused")} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + freshenClusterCreatedAt(t, acctKV) + seedBootstrapState(t, acctKV) + + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond) + defer cancel() + _ = r.Run(ctx) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusCreating, meta.Status, "must remain CREATING when /healthz fails") +} + +func TestClusterReconciler_CreatingTimesOutToFailed(t *testing.T) { + stub := &stubHTTPDoer{err: errors.New("connection refused")} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + WithCreateTimeout(1*time.Millisecond), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + // Stamp CreatedAt in the past so the 1ms deadline is already exceeded; the + // cluster never becomes ready, so the reconciler must mark it FAILED. + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + meta.CreatedAt = time.Now().Add(-time.Hour).UTC() + require.NoError(t, PutClusterMeta(acctKV, meta)) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err = r.Run(ctx) + assert.ErrorIs(t, err, ErrReconcilerClusterFailed) + + got, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusFailed, got.Status) + assert.Contains(t, got.StatusReason, "did not become ACTIVE") +} + +func TestClusterReconciler_CreatingWithinDeadlineStaysCreating(t *testing.T) { + stub := &stubHTTPDoer{err: errors.New("connection refused")} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + WithCreateTimeout(time.Hour), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + freshenClusterCreatedAt(t, acctKV) + + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + defer cancel() + _ = r.Run(ctx) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusCreating, meta.Status, "within deadline must stay CREATING") +} + +func TestClusterReconciler_DeletingExitsLoop(t *testing.T) { + stub := &stubHTTPDoer{status: http.StatusOK} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + require.NoError(t, SetClusterStatus(acctKV, "alpha", ClusterStatusDeleting)) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := r.Run(ctx) + assert.ErrorIs(t, err, ErrReconcilerClusterDeleting) +} + +func TestClusterReconciler_LostLeaseExitsLoop(t *testing.T) { + stub := &stubHTTPDoer{status: http.StatusOK} + r, leaderKV, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Second), + WithLeaseRefresh(20*time.Millisecond), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + seedBootstrapState(t, acctKV) + + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() + + runErr := make(chan error, 1) + go func() { runErr <- r.Run(ctx) }() + + // Steal the lease in another holder's name. The next refresh tick should + // notice and return ErrReconcilerLeaseLost. + time.Sleep(40 * time.Millisecond) + _, err := leaderKV.Put(reconcilerLeaderKey(testAccountID, "alpha"), []byte("holder-2")) + require.NoError(t, err) + + select { + case err := <-runErr: + assert.ErrorIs(t, err, ErrReconcilerLeaseLost) + case <-time.After(2 * time.Second): + t.Fatal("Run did not exit after lease loss") + } +} + +func TestClusterReconciler_ActiveProbesAndWarnsOnHealthzFail(t *testing.T) { + stub := &stubHTTPDoer{status: http.StatusOK} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + require.NoError(t, SetClusterStatus(acctKV, "alpha", ClusterStatusActive)) + stub.setResponse(http.StatusServiceUnavailable, nil) + + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + defer cancel() + _ = r.Run(ctx) + + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusActive, meta.Status, "ACTIVE must not flip on healthz fail in this PR") + assert.Positive(t, stub.calls.Load()) + assert.NotEmpty(t, meta.HealthIssue, "failing /healthz must be recorded on meta") + assert.False(t, meta.LastHealthProbe.IsZero(), "health probe time must be stamped") +} + +func TestClusterReconciler_ActiveHealthRecoversClearsIssue(t *testing.T) { + stub := &stubHTTPDoer{status: http.StatusServiceUnavailable} + r, _, acctKV := newReconcilerHarness(t, + "https://nlb.example/healthz", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + release, ok := r.AcquireLease() + require.True(t, ok) + defer release() + + require.NoError(t, SetClusterStatus(acctKV, "alpha", ClusterStatusActive)) + + // First probe fails and records the issue. + require.Error(t, r.probeHealthz(context.Background())) + require.NoError(t, r.reconcileOnce(context.Background())) + meta, err := GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + require.NotEmpty(t, meta.HealthIssue) + + // Probe recovers: the issue must be cleared. + stub.setResponse(http.StatusOK, nil) + require.NoError(t, r.reconcileOnce(context.Background())) + meta, err = GetClusterMeta(acctKV, "alpha") + require.NoError(t, err) + assert.Empty(t, meta.HealthIssue, "recovered /healthz must clear the recorded issue") +} + +func TestClusterReconciler_ProbeHealthzEmptyURLNoop(t *testing.T) { + stub := &stubHTTPDoer{} + r, _, _ := newReconcilerHarness(t, + "", + WithHTTPClient(stub), + WithReconcileInterval(10*time.Millisecond), + WithLeaseRefresh(10*time.Second), + ) + + require.NoError(t, r.probeHealthz(context.Background())) + assert.EqualValues(t, 0, stub.calls.Load(), "no HTTP call when URL empty") +} diff --git a/spinifex/handlers/eks/cluster_state.go b/spinifex/handlers/eks/cluster_state.go new file mode 100644 index 000000000..dcea217a6 --- /dev/null +++ b/spinifex/handlers/eks/cluster_state.go @@ -0,0 +1,303 @@ +package handlers_eks + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/nats-io/nats.go" +) + +// ClusterStatus is the persisted lifecycle state for a cluster. Values match +// the AWS EKS Cluster.status string set so DescribeCluster can pass them +// through verbatim. +type ClusterStatus string + +const ( + ClusterStatusCreating ClusterStatus = "CREATING" + ClusterStatusActive ClusterStatus = "ACTIVE" + ClusterStatusDeleting ClusterStatus = "DELETING" + ClusterStatusFailed ClusterStatus = "FAILED" +) + +// ClusterVpcConfig is the persisted subset of eks.VpcConfigResponse that the +// reconciler + DescribeCluster need at-rest. +type ClusterVpcConfig struct { + SubnetIds []string `json:"subnetIds"` + SecurityGroupIds []string `json:"securityGroupIds,omitempty"` + VpcId string `json:"vpcId,omitempty"` + // EndpointPublicAccess / EndpointPrivateAccess control apiserver endpoint + // exposure (AWS resourcesVpcConfig parity). Public ⇒ internet-facing cluster + // NLB (external-pool front-end IP, LAN-reachable); private ⇒ internal NLB + // (VPC-only). PublicAccessCidrs records the allowed source ranges for the + // public endpoint (stored for parity + Describe; ingress enforcement is a + // follow-up). + EndpointPublicAccess bool `json:"endpointPublicAccess"` + EndpointPrivateAccess bool `json:"endpointPrivateAccess"` + PublicAccessCidrs []string `json:"publicAccessCidrs,omitempty"` +} + +// ClusterMeta is the persisted control-plane record for one cluster. The +// blob lives at ClusterMetaKey(name) inside the per-account KV bucket and is +// the source of truth for DescribeCluster. +type ClusterMeta struct { + Name string `json:"name"` + Arn string `json:"arn"` + Status ClusterStatus `json:"status"` + StatusReason string `json:"statusReason,omitempty"` + Version string `json:"version"` + RoleArn string `json:"roleArn"` + Endpoint string `json:"endpoint,omitempty"` + // EndpointIP is the host/LAN-reachable front-end IP the cluster NLB exposes + // (public-access ⇒ external-pool IP; private ⇒ VPC IP). The apiserver serving + // cert SANs this IP, so kubectl reaches Endpoint with TLS verification on. + EndpointIP string `json:"endpointIp,omitempty"` + OIDCIssuer string `json:"oidcIssuer,omitempty"` + CertificateAuthorityB64 string `json:"certificateAuthorityB64,omitempty"` + ResourcesVpcConfig *ClusterVpcConfig `json:"resourcesVpcConfig,omitempty"` + ControlPlaneInstanceID string `json:"controlPlaneInstanceId,omitempty"` + ControlPlaneENIID string `json:"controlPlaneEniId,omitempty"` + ControlPlaneENIIP string `json:"controlPlaneEniIp,omitempty"` + // ControlPlaneMgmtIP is the control-plane VM's br-mgmt NIC address. Until + // authoritative DNS (Eclipso/Route53) lands, the host-side reconciler cannot + // resolve or route to the VPC-internal NLB DNS endpoint, so it probes + // /healthz on this host-reachable address instead. + ControlPlaneMgmtIP string `json:"controlPlaneMgmtIp,omitempty"` + // EgressEIPAllocationID / EgressEIPPublicIP track the hidden pool address + // SNAT'd to the control-plane VM for egress-only internet (image pulls). + // Released + the snat removed on DeleteCluster. + EgressEIPAllocationID string `json:"egressEipAllocationId,omitempty"` + EgressEIPPublicIP string `json:"egressEipPublicIp,omitempty"` + NLBArn string `json:"nlbArn,omitempty"` + NLBTargetGroupArn string `json:"nlbTargetGroupArn,omitempty"` + CreatedAt time.Time `json:"createdAt"` + // HealthIssue is the last /healthz probe failure reason for an ACTIVE + // cluster; empty means healthy. DescribeCluster surfaces a non-empty value + // as a ClusterHealth issue so a dead control plane is visible behind the + // still-ACTIVE status. LastHealthProbe stamps when the health state changed. + HealthIssue string `json:"healthIssue,omitempty"` + // No omitempty/omitzero: stdlib encoding/json ignores omitzero (json/v2 + // only) and never treats a time.Time struct as empty, so this field always + // serializes. The tag states that honestly rather than implying it elides. + LastHealthProbe time.Time `json:"lastHealthProbe"` + // NodeCount is the node total from the control plane's last NATS state + // report (server + joined agents). Surfaces nodegroup join/scale progress + // without the host needing apiserver reachability. + NodeCount int `json:"nodeCount,omitempty"` + // BuiltinIngress records whether the cluster opted into K3s' bundled + // traefik + servicelb (dev / interim in-VPC app exposure), derived from the + // managed-ingress tag at CreateCluster. Default false = AWS parity (built-ins + // disabled; ingress via the AWS Load Balancer Controller). + BuiltinIngress bool `json:"builtinIngress,omitempty"` +} + +// ErrClusterNotFound is returned by GetClusterMeta / SetClusterStatus / +// DeleteClusterPrefix when the cluster meta key is absent. Callers translate +// to the AWS shape (ResourceNotFoundException) at the service boundary. +var ErrClusterNotFound = errors.New("eks: cluster not found") + +const maxClusterStateCASRetries = 5 + +// PutClusterMeta writes the meta record unconditionally. Used at +// CreateCluster time to lay down the initial CREATING record. +func PutClusterMeta(kv nats.KeyValue, meta *ClusterMeta) error { + if meta == nil { + return errors.New("eks: PutClusterMeta nil meta") + } + if meta.Name == "" { + return errors.New("eks: PutClusterMeta meta.Name empty") + } + data, err := json.Marshal(meta) + if err != nil { + return fmt.Errorf("marshal cluster meta %s: %w", meta.Name, err) + } + if _, err := kv.Put(ClusterMetaKey(meta.Name), data); err != nil { + return fmt.Errorf("kv put %s: %w", ClusterMetaKey(meta.Name), err) + } + return nil +} + +// GetClusterMeta reads the meta record. Returns ErrClusterNotFound if the +// key is absent. +func GetClusterMeta(kv nats.KeyValue, name string) (*ClusterMeta, error) { + if name == "" { + return nil, errors.New("eks: GetClusterMeta empty name") + } + entry, err := kv.Get(ClusterMetaKey(name)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, ErrClusterNotFound + } + return nil, fmt.Errorf("kv get %s: %w", ClusterMetaKey(name), err) + } + var meta ClusterMeta + if err := json.Unmarshal(entry.Value(), &meta); err != nil { + return nil, fmt.Errorf("unmarshal cluster meta %s: %w", name, err) + } + return &meta, nil +} + +// casUpdateMeta does a revision-checked read-modify-write of the cluster meta +// record. mutate receives the decoded meta and returns true if it changed a +// field (false short-circuits to a no-op success). Retries on KV CAS conflict +// (concurrent reconciler write) up to maxClusterStateCASRetries. Returns +// ErrClusterNotFound if the meta record was deleted underneath us. +func casUpdateMeta(kv nats.KeyValue, name string, mutate func(*ClusterMeta) bool) error { + if name == "" { + return errors.New("eks: casUpdateMeta empty name") + } + for range maxClusterStateCASRetries { + entry, err := kv.Get(ClusterMetaKey(name)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return ErrClusterNotFound + } + return fmt.Errorf("kv get %s: %w", ClusterMetaKey(name), err) + } + var meta ClusterMeta + if err := json.Unmarshal(entry.Value(), &meta); err != nil { + return fmt.Errorf("unmarshal cluster meta %s: %w", name, err) + } + if !mutate(&meta) { + return nil + } + data, err := json.Marshal(&meta) + if err != nil { + return fmt.Errorf("marshal cluster meta %s: %w", name, err) + } + _, err = kv.Update(ClusterMetaKey(name), data, entry.Revision()) + if err == nil { + return nil + } + if errors.Is(err, nats.ErrKeyExists) { + continue + } + return fmt.Errorf("kv update %s: %w", ClusterMetaKey(name), err) + } + return fmt.Errorf("eks: casUpdateMeta %s exhausted CAS retries", name) +} + +// SetClusterStatus does a revision-checked update of the meta.Status field. +// Returns ErrClusterNotFound if the meta record was deleted underneath us. +func SetClusterStatus(kv nats.KeyValue, name string, status ClusterStatus) error { + if name == "" { + return errors.New("eks: SetClusterStatus empty name") + } + return casUpdateMeta(kv, name, func(m *ClusterMeta) bool { + if m.Status == status { + return false + } + m.Status = status + return true + }) +} + +// MarkClusterFailed transitions a cluster to FAILED with a human-readable +// reason, but only from CREATING — a cluster already DELETING/ACTIVE/FAILED is +// left untouched so a late bootstrap error cannot clobber a concurrent delete +// or an already-healthy cluster. Returns ErrClusterNotFound if the meta record +// was deleted underneath us. +func MarkClusterFailed(kv nats.KeyValue, name, reason string) error { + if name == "" { + return errors.New("eks: MarkClusterFailed empty name") + } + return casUpdateMeta(kv, name, func(m *ClusterMeta) bool { + if m.Status != ClusterStatusCreating { + return false + } + m.Status = ClusterStatusFailed + m.StatusReason = reason + return true + }) +} + +// SetClusterCertificateAuthority does a revision-checked update of the +// meta.CertificateAuthorityB64 field. The NATS bootstrap subscriber calls +// this once the K3s server VM publishes its CA on the bootstrap bus. Returns +// ErrClusterNotFound if the meta record was deleted underneath us. +func SetClusterCertificateAuthority(kv nats.KeyValue, name, caB64 string) error { + if name == "" { + return errors.New("eks: SetClusterCertificateAuthority empty name") + } + if caB64 == "" { + return errors.New("eks: SetClusterCertificateAuthority empty CA") + } + return casUpdateMeta(kv, name, func(m *ClusterMeta) bool { + if m.CertificateAuthorityB64 == caB64 { + return false + } + m.CertificateAuthorityB64 = caB64 + return true + }) +} + +// SetClusterHealth records the latest /healthz probe outcome for a cluster: +// issue is the failure reason (empty clears it / marks healthy). Writes only +// when the health state changes, stamping LastHealthProbe at the transition so +// a persistently-unhealthy cluster carries the time it first went bad rather +// than churning the KV every probe interval. Returns ErrClusterNotFound if the +// meta record was deleted underneath us. +func SetClusterHealth(kv nats.KeyValue, name, issue string) error { + if name == "" { + return errors.New("eks: SetClusterHealth empty name") + } + return casUpdateMeta(kv, name, func(m *ClusterMeta) bool { + if m.HealthIssue == issue { + return false + } + m.HealthIssue = issue + m.LastHealthProbe = time.Now().UTC() + return true + }) +} + +// SetClusterHealthState records the latest health outcome together with the +// node count from the control-plane state report. issue is the failure reason +// (empty clears it / marks healthy). LastHealthProbe is stamped whenever health +// or node count changes; a steady-state healthy cluster with an unchanging node +// count produces no KV write. Returns ErrClusterNotFound if the meta record was +// deleted underneath us. +func SetClusterHealthState(kv nats.KeyValue, name, issue string, nodeCount int) error { + if name == "" { + return errors.New("eks: SetClusterHealthState empty name") + } + return casUpdateMeta(kv, name, func(m *ClusterMeta) bool { + if m.HealthIssue == issue && m.NodeCount == nodeCount { + return false + } + m.HealthIssue = issue + m.NodeCount = nodeCount + m.LastHealthProbe = time.Now().UTC() + return true + }) +} + +// DeleteClusterPrefix removes every KV key under clusters/{name}/. Called +// from DeleteCluster after the OIDC private key has been zeroized. +// Returns the first delete error encountered but continues sweeping so +// best-effort cleanup proceeds. +func DeleteClusterPrefix(kv nats.KeyValue, name string) error { + if name == "" { + return errors.New("eks: DeleteClusterPrefix empty name") + } + prefix := fmt.Sprintf("clusters/%s/", name) + keys, err := kv.Keys() + if err != nil { + if errors.Is(err, nats.ErrNoKeysFound) { + return nil + } + return fmt.Errorf("kv keys: %w", err) + } + var firstErr error + for _, k := range keys { + if !strings.HasPrefix(k, prefix) { + continue + } + if err := kv.Delete(k); err != nil && firstErr == nil { + firstErr = fmt.Errorf("kv delete %s: %w", k, err) + } + } + return firstErr +} diff --git a/spinifex/handlers/eks/cluster_state_test.go b/spinifex/handlers/eks/cluster_state_test.go new file mode 100644 index 000000000..d25d2ca52 --- /dev/null +++ b/spinifex/handlers/eks/cluster_state_test.go @@ -0,0 +1,256 @@ +package handlers_eks + +import ( + "encoding/json" + "errors" + "testing" + "time" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newClusterStateTestKV(t *testing.T) nats.KeyValue { + t.Helper() + _, _, js := testutil.StartTestJetStream(t) + kv, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + return kv +} + +func sampleClusterMeta(name string) *ClusterMeta { + return &ClusterMeta{ + Name: name, + Arn: "arn:aws:eks:us-east-1:111122223333:cluster/" + name, + Status: ClusterStatusCreating, + Version: "1.32", + RoleArn: "arn:aws:iam::111122223333:role/eks-cluster", + ResourcesVpcConfig: &ClusterVpcConfig{ + SubnetIds: []string{"subnet-aaa"}, + SecurityGroupIds: []string{"sg-aaa"}, + }, + CreatedAt: time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC), + } +} + +func TestPutClusterMeta_NilOrEmptyNameRejected(t *testing.T) { + kv := newClusterStateTestKV(t) + + require.Error(t, PutClusterMeta(kv, nil)) + require.Error(t, PutClusterMeta(kv, &ClusterMeta{})) +} + +func TestPutClusterMeta_RoundTrip(t *testing.T) { + kv := newClusterStateTestKV(t) + + in := sampleClusterMeta("alpha") + require.NoError(t, PutClusterMeta(kv, in)) + + got, err := GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, in.Name, got.Name) + assert.Equal(t, in.Arn, got.Arn) + assert.Equal(t, in.Status, got.Status) + assert.Equal(t, in.Version, got.Version) + assert.Equal(t, in.RoleArn, got.RoleArn) + require.NotNil(t, got.ResourcesVpcConfig) + assert.Equal(t, []string{"subnet-aaa"}, got.ResourcesVpcConfig.SubnetIds) + assert.True(t, got.CreatedAt.Equal(in.CreatedAt)) +} + +func TestGetClusterMeta_MissingReturnsErrClusterNotFound(t *testing.T) { + kv := newClusterStateTestKV(t) + + _, err := GetClusterMeta(kv, "missing") + require.ErrorIs(t, err, ErrClusterNotFound) +} + +func TestGetClusterMeta_EmptyNameRejected(t *testing.T) { + kv := newClusterStateTestKV(t) + + _, err := GetClusterMeta(kv, "") + require.Error(t, err) + assert.NotErrorIs(t, err, ErrClusterNotFound) +} + +func TestGetClusterMeta_CorruptBlobRejected(t *testing.T) { + kv := newClusterStateTestKV(t) + + _, err := kv.Put(ClusterMetaKey("corrupt"), []byte("{not json")) + require.NoError(t, err) + + _, err = GetClusterMeta(kv, "corrupt") + require.Error(t, err) + assert.NotErrorIs(t, err, ErrClusterNotFound) +} + +func TestSetClusterStatus_TransitionsAndIsIdempotent(t *testing.T) { + kv := newClusterStateTestKV(t) + require.NoError(t, PutClusterMeta(kv, sampleClusterMeta("alpha"))) + + require.NoError(t, SetClusterStatus(kv, "alpha", ClusterStatusActive)) + got, err := GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusActive, got.Status) + + require.NoError(t, SetClusterStatus(kv, "alpha", ClusterStatusActive)) + got, err = GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusActive, got.Status) +} + +func TestSetClusterStatus_DeletingAlwaysAllowedFromAnyState(t *testing.T) { + kv := newClusterStateTestKV(t) + + for _, from := range []ClusterStatus{ClusterStatusCreating, ClusterStatusActive, ClusterStatusFailed} { + meta := sampleClusterMeta("alpha") + meta.Status = from + require.NoError(t, PutClusterMeta(kv, meta)) + require.NoError(t, SetClusterStatus(kv, "alpha", ClusterStatusDeleting)) + got, err := GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusDeleting, got.Status, "from=%s", from) + } +} + +func TestSetClusterStatus_MissingReturnsErrClusterNotFound(t *testing.T) { + kv := newClusterStateTestKV(t) + + err := SetClusterStatus(kv, "missing", ClusterStatusActive) + require.ErrorIs(t, err, ErrClusterNotFound) +} + +func TestSetClusterStatus_RecoversFromConcurrentRevisionBump(t *testing.T) { + kv := newClusterStateTestKV(t) + require.NoError(t, PutClusterMeta(kv, sampleClusterMeta("alpha"))) + + // Read once to get a stale revision, then bump the record from another + // goroutine via Put to force one CAS conflict on the next Update. + entry, err := kv.Get(ClusterMetaKey("alpha")) + require.NoError(t, err) + + bumped := sampleClusterMeta("alpha") + bumped.Version = "1.32-bumped" + data, err := json.Marshal(bumped) + require.NoError(t, err) + _, err = kv.Update(ClusterMetaKey("alpha"), data, entry.Revision()) + require.NoError(t, err) + + // SetClusterStatus must succeed even though there has been an intervening + // revision bump — its internal loop re-reads + retries. + require.NoError(t, SetClusterStatus(kv, "alpha", ClusterStatusActive)) + got, err := GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusActive, got.Status) + assert.Equal(t, "1.32-bumped", got.Version) +} + +func TestMarkClusterFailed_FromCreatingSetsStatusAndReason(t *testing.T) { + kv := newClusterStateTestKV(t) + require.NoError(t, PutClusterMeta(kv, sampleClusterMeta("alpha"))) + + require.NoError(t, MarkClusterFailed(kv, "alpha", "bootstrap failed: boom")) + got, err := GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusFailed, got.Status) + assert.Equal(t, "bootstrap failed: boom", got.StatusReason) +} + +func TestMarkClusterFailed_NoopFromNonCreating(t *testing.T) { + kv := newClusterStateTestKV(t) + + for _, from := range []ClusterStatus{ClusterStatusActive, ClusterStatusDeleting, ClusterStatusFailed} { + meta := sampleClusterMeta("alpha") + meta.Status = from + require.NoError(t, PutClusterMeta(kv, meta)) + + require.NoError(t, MarkClusterFailed(kv, "alpha", "late bootstrap error")) + got, err := GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, from, got.Status, "from=%s must be untouched", from) + assert.Empty(t, got.StatusReason, "from=%s reason must not be set", from) + } +} + +func TestMarkClusterFailed_MissingReturnsErrClusterNotFound(t *testing.T) { + kv := newClusterStateTestKV(t) + + err := MarkClusterFailed(kv, "ghost", "boom") + require.ErrorIs(t, err, ErrClusterNotFound) +} + +func TestSetClusterCertificateAuthority_WritesAndIsIdempotent(t *testing.T) { + kv := newClusterStateTestKV(t) + require.NoError(t, PutClusterMeta(kv, sampleClusterMeta("alpha"))) + + require.NoError(t, SetClusterCertificateAuthority(kv, "alpha", "ca-blob-b64")) + got, err := GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, "ca-blob-b64", got.CertificateAuthorityB64) + + require.NoError(t, SetClusterCertificateAuthority(kv, "alpha", "ca-blob-b64")) + got, err = GetClusterMeta(kv, "alpha") + require.NoError(t, err) + assert.Equal(t, "ca-blob-b64", got.CertificateAuthorityB64) +} + +func TestSetClusterCertificateAuthority_MissingReturnsErrClusterNotFound(t *testing.T) { + kv := newClusterStateTestKV(t) + + err := SetClusterCertificateAuthority(kv, "ghost", "ca-blob") + require.ErrorIs(t, err, ErrClusterNotFound) +} + +func TestSetClusterCertificateAuthority_EmptyInputsRejected(t *testing.T) { + kv := newClusterStateTestKV(t) + + require.Error(t, SetClusterCertificateAuthority(kv, "", "ca-blob")) + require.Error(t, SetClusterCertificateAuthority(kv, "alpha", "")) +} + +func TestDeleteClusterPrefix_SweepsEveryKey(t *testing.T) { + kv := newClusterStateTestKV(t) + + require.NoError(t, PutClusterMeta(kv, sampleClusterMeta("alpha"))) + _, err := kv.Put(NodegroupKey("alpha", "ng-1"), []byte(`{"name":"ng-1"}`)) + require.NoError(t, err) + _, err = kv.Put(OIDCSigningKeyKey("alpha"), []byte("enc-blob")) + require.NoError(t, err) + _, err = kv.Put(OIDCJWKSKey("alpha"), []byte(`{"keys":[]}`)) + require.NoError(t, err) + _, err = kv.Put(EventKey("alpha", "1700000000"), []byte(`{"ts":"1700000000"}`)) + require.NoError(t, err) + // Sibling cluster must survive the sweep. + require.NoError(t, PutClusterMeta(kv, sampleClusterMeta("beta"))) + + require.NoError(t, DeleteClusterPrefix(kv, "alpha")) + + for _, k := range []string{ + ClusterMetaKey("alpha"), + NodegroupKey("alpha", "ng-1"), + OIDCSigningKeyKey("alpha"), + OIDCJWKSKey("alpha"), + EventKey("alpha", "1700000000"), + } { + _, err := kv.Get(k) + assert.True(t, errors.Is(err, nats.ErrKeyNotFound), "key %s should be gone, got err=%v", k, err) + } + + got, err := GetClusterMeta(kv, "beta") + require.NoError(t, err) + assert.Equal(t, "beta", got.Name) +} + +func TestDeleteClusterPrefix_EmptyBucketIsNoop(t *testing.T) { + kv := newClusterStateTestKV(t) + + require.NoError(t, DeleteClusterPrefix(kv, "ghost")) +} + +func TestDeleteClusterPrefix_EmptyNameRejected(t *testing.T) { + kv := newClusterStateTestKV(t) + + require.Error(t, DeleteClusterPrefix(kv, "")) +} diff --git a/spinifex/handlers/eks/create_cluster_test.go b/spinifex/handlers/eks/create_cluster_test.go new file mode 100644 index 000000000..5d4be8481 --- /dev/null +++ b/spinifex/handlers/eks/create_cluster_test.go @@ -0,0 +1,280 @@ +package handlers_eks + +import ( + "errors" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/tags" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func createInput(name string) *eks.CreateClusterInput { + return &eks.CreateClusterInput{ + Name: aws.String(name), + RoleArn: aws.String("arn:aws:iam::111122223333:role/eks-cluster"), + Version: aws.String("1.32"), + ResourcesVpcConfig: &eks.VpcConfigRequest{ + SubnetIds: aws.StringSlice([]string{"subnet-aaa"}), + }, + } +} + +func TestCreateCluster_BuiltinIngressDefaultsOn(t *testing.T) { + f := newEKSServiceFixture(t) + + _, err := f.svc.CreateCluster(createInput("alpha"), testAccountID, "") + require.NoError(t, err) + + meta, err := GetClusterMeta(f.kv, "alpha") + require.NoError(t, err) + assert.True(t, meta.BuiltinIngress, "interim default: built-in ingress on until aws-lb-controller ships") +} + +func TestCreateCluster_BuiltinIngressOptOutFromTag(t *testing.T) { + f := newEKSServiceFixture(t) + + in := createInput("alpha") + in.Tags = map[string]*string{managedIngressTagKey: aws.String("false")} + _, err := f.svc.CreateCluster(in, testAccountID, "") + require.NoError(t, err) + + meta, err := GetClusterMeta(f.kv, "alpha") + require.NoError(t, err) + assert.False(t, meta.BuiltinIngress, "managed-ingress=false opts out for AWS parity") +} + +func TestCreateCluster_BuiltinIngressFromTag(t *testing.T) { + f := newEKSServiceFixture(t) + + in := createInput("alpha") + in.Tags = map[string]*string{managedIngressTagKey: aws.String("true")} + _, err := f.svc.CreateCluster(in, testAccountID, "") + require.NoError(t, err) + + meta, err := GetClusterMeta(f.kv, "alpha") + require.NoError(t, err) + assert.True(t, meta.BuiltinIngress, "managed-ingress=true keeps built-in ingress") +} + +// A create that fails after the NLB is provisioned must leave the NLB ARNs +// persisted on the (now FAILED) meta, otherwise the resources leak with no +// owning record and DeleteCluster cannot reclaim them (bead 165.3). +func TestCreateCluster_NLBArnPersistedBeforeLaterFailure(t *testing.T) { + f := newEKSServiceFixture(t) + + // Force the K3s VM launch to fail — this is after EnsureClusterNLB and the + // NLB-arn persist checkpoint, but before the final PutClusterMeta. + f.inst.launchErr = errors.New("no capacity") + + _, err := f.svc.CreateCluster(createInput("alpha"), testAccountID, "") + require.Error(t, err) + + meta, getErr := GetClusterMeta(f.kv, "alpha") + require.NoError(t, getErr, "meta must remain so the leaked NLB has an owning record") + assert.Equal(t, ClusterStatusFailed, meta.Status) + assert.NotEmpty(t, meta.NLBArn, "NLB ARN must be persisted before the VM-launch step") + assert.NotEmpty(t, meta.NLBTargetGroupArn) +} + +// End-to-end: a failed create followed by delete-cluster leaves zero orphaned +// NLB resources. The persisted ARNs from the partial create drive teardown. +func TestCreateCluster_FailedCreateThenDeleteReclaimsNLB(t *testing.T) { + f := newEKSServiceFixture(t) + f.inst.launchErr = errors.New("no capacity") + + _, err := f.svc.CreateCluster(createInput("alpha"), testAccountID, "") + require.Error(t, err) + + // The NLB the partial create provisioned exists in the fake. + require.NotEmpty(t, f.nlb.createLBCalls, "create provisioned an NLB") + + _, err = f.svc.DeleteCluster(deleteInput("alpha"), testAccountID) + require.NoError(t, err) + + assert.NotEmpty(t, f.nlb.deleteLBCalls, "DeleteCluster must tear down the NLB recorded by the failed create") + _, getErr := GetClusterMeta(f.kv, "alpha") + assert.ErrorIs(t, getErr, ErrClusterNotFound) +} + +// A live (CREATING/ACTIVE) cluster of the same name blocks create with a +// cluster-scoped ResourceInUseException, not the ELBv2 target-group message. +func TestCreateCluster_ExistingClusterReturnsResourceInUse(t *testing.T) { + f := newEKSServiceFixture(t) + + meta := sampleClusterMeta("alpha") + meta.Status = ClusterStatusActive + require.NoError(t, PutClusterMeta(f.kv, meta)) + + _, err := f.svc.CreateCluster(createInput("alpha"), testAccountID, "") + require.EqualError(t, err, awserrors.ErrorEKSResourceInUse) +} + +// A FAILED cluster from a prior attempt must not block a retry: create reclaims +// it (tearing down recorded resources) and proceeds to a fresh CREATING cluster. +func TestCreateCluster_FailedClusterIsReclaimedOnRetry(t *testing.T) { + f := newEKSServiceFixture(t) + + // First attempt fails at VM launch, leaving a FAILED meta with NLB ARNs. + f.inst.launchErr = errors.New("no capacity") + _, err := f.svc.CreateCluster(createInput("alpha"), testAccountID, "") + require.Error(t, err) + failed, err := GetClusterMeta(f.kv, "alpha") + require.NoError(t, err) + require.Equal(t, ClusterStatusFailed, failed.Status) + + // Retry now succeeds: the reclaim path purges the failed attempt's NLB and + // the create starts clean. + f.inst.launchErr = nil + _, err = f.svc.CreateCluster(createInput("alpha"), testAccountID, "") + require.NoError(t, err) + + got, err := GetClusterMeta(f.kv, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusCreating, got.Status) + assert.NotEmpty(t, f.nlb.deleteLBCalls, "reclaim must tear down the failed attempt's NLB") +} + +// A FAILED cluster is observable via Describe and List (bead 165.6) — no state +// where create blocks but describe/list show nothing. +func TestCreateCluster_FailedClusterVisibleInDescribeAndList(t *testing.T) { + f := newEKSServiceFixture(t) + + meta := sampleClusterMeta("alpha") + meta.Status = ClusterStatusFailed + meta.StatusReason = "bootstrap failed: boom" + require.NoError(t, PutClusterMeta(f.kv, meta)) + + desc, err := f.svc.DescribeCluster(&eks.DescribeClusterInput{Name: aws.String("alpha")}, testAccountID) + require.NoError(t, err) + assert.Equal(t, eks.ClusterStatusFailed, aws.StringValue(desc.Cluster.Status)) + + list, err := f.svc.ListClusters(&eks.ListClustersInput{}, testAccountID) + require.NoError(t, err) + assert.Contains(t, aws.StringValueSlice(list.Clusters), "alpha") +} + +// An ACTIVE cluster whose reconciler recorded a /healthz failure must surface +// that as a ClusterHealth issue in describe-cluster, so a dead control plane is +// visible behind the still-ACTIVE status (bead 165.13). +func TestDescribeCluster_SurfacesHealthIssueForActiveCluster(t *testing.T) { + f := newEKSServiceFixture(t) + + meta := sampleClusterMeta("alpha") + meta.Status = ClusterStatusActive + require.NoError(t, PutClusterMeta(f.kv, meta)) + require.NoError(t, SetClusterHealth(f.kv, "alpha", "healthz request: connection refused")) + + desc, err := f.svc.DescribeCluster(&eks.DescribeClusterInput{Name: aws.String("alpha")}, testAccountID) + require.NoError(t, err) + assert.Equal(t, eks.ClusterStatusActive, aws.StringValue(desc.Cluster.Status)) + require.NotNil(t, desc.Cluster.Health) + require.Len(t, desc.Cluster.Health.Issues, 1) + assert.Equal(t, eks.ClusterIssueCodeClusterUnreachable, aws.StringValue(desc.Cluster.Health.Issues[0].Code)) + assert.Contains(t, aws.StringValue(desc.Cluster.Health.Issues[0].Message), "connection refused") +} + +// A healthy ACTIVE cluster carries no health issue in describe-cluster. +func TestDescribeCluster_HealthyActiveClusterHasNoIssues(t *testing.T) { + f := newEKSServiceFixture(t) + + meta := sampleClusterMeta("alpha") + meta.Status = ClusterStatusActive + require.NoError(t, PutClusterMeta(f.kv, meta)) + + desc, err := f.svc.DescribeCluster(&eks.DescribeClusterInput{Name: aws.String("alpha")}, testAccountID) + require.NoError(t, err) + assert.Nil(t, desc.Cluster.Health, "healthy cluster must not report a ClusterHealth issue") +} + +// A missing eks-server AMI is an operator/config gap, not an unrecoverable +// internal fault: CreateCluster must surface ServiceUnavailable (not a generic +// ServerInternal) and mark the half-built cluster FAILED (bead 165.4). +func TestCreateCluster_MissingAMIReturnsServiceUnavailable(t *testing.T) { + f := newEKSServiceFixture(t) + f.ami.describeOut = &ec2.DescribeImagesOutput{} // no images tagged managed-by=eks + + _, err := f.svc.CreateCluster(createInput("alpha"), testAccountID, "") + require.EqualError(t, err, awserrors.ErrorServiceUnavailable) + + meta, getErr := GetClusterMeta(f.kv, "alpha") + require.NoError(t, getErr) + assert.Equal(t, ClusterStatusFailed, meta.Status) +} + +func TestCreateCluster_HappyPathPersistsActiveCreatingMeta(t *testing.T) { + f := newEKSServiceFixture(t) + + out, err := f.svc.CreateCluster(createInput("alpha"), testAccountID, "") + require.NoError(t, err) + require.NotNil(t, out) + + meta, err := GetClusterMeta(f.kv, "alpha") + require.NoError(t, err) + assert.Equal(t, ClusterStatusCreating, meta.Status) + assert.NotEmpty(t, meta.NLBArn) + assert.NotEmpty(t, meta.ControlPlaneInstanceID) + assert.NotEmpty(t, meta.ControlPlaneENIID) +} + +// bootstrapClusterCreatorAdminPermissions defaults true: a successful create +// with a known caller principal ARN mints a system:masters AccessEntry for the +// caller, keyed by their exact ARN (the token webhook looks it up by the same). +func TestCreateCluster_SeedsCreatorAdminAccessEntry(t *testing.T) { + f := newEKSServiceFixture(t) + const caller = "arn:aws:iam::111122223333:role/admin" + + _, err := f.svc.CreateCluster(createInput("alpha"), testAccountID, caller) + require.NoError(t, err) + + rec, err := GetAccessEntryRecord(f.kv, "alpha", caller) + require.NoError(t, err) + assert.Equal(t, []string{"system:masters"}, rec.KubernetesGroups) + assert.Equal(t, caller, rec.KubernetesUsername) + assert.Equal(t, AccessEntryTypeStandard, rec.Type) +} + +// With the bootstrap flag explicitly false, no creator-admin entry is minted. +func TestCreateCluster_SkipsCreatorAdminWhenDisabled(t *testing.T) { + f := newEKSServiceFixture(t) + const caller = "arn:aws:iam::111122223333:role/admin" + in := createInput("alpha") + in.AccessConfig = &eks.CreateAccessConfigRequest{ + BootstrapClusterCreatorAdminPermissions: aws.Bool(false), + } + + _, err := f.svc.CreateCluster(in, testAccountID, caller) + require.NoError(t, err) + + _, err = GetAccessEntryRecord(f.kv, "alpha", caller) + assert.ErrorIs(t, err, ErrAccessEntryNotFound) +} + +func TestCreateCluster_AllocatesHiddenEgressEIP(t *testing.T) { + f := newEKSServiceFixture(t) + + _, err := f.svc.CreateCluster(createInput("egress"), testAccountID, "") + require.NoError(t, err) + + meta, err := GetClusterMeta(f.kv, "egress") + require.NoError(t, err) + assert.Equal(t, f.eip.publicIP, meta.EgressEIPPublicIP) + assert.Equal(t, f.eip.allocationID, meta.EgressEIPAllocationID) + + require.Len(t, f.eip.allocateCalls, 1) + // The pool address is tagged ManagedBy=eks so it stays out of customer + // DescribeAddresses listings. + tagged := false + for _, spec := range f.eip.allocateCalls[0].TagSpecifications { + for _, tg := range spec.Tags { + if aws.StringValue(tg.Key) == tags.ManagedByKey && aws.StringValue(tg.Value) == tags.ManagedByEKS { + tagged = true + } + } + } + assert.True(t, tagged, "egress EIP must carry the ManagedBy=eks tag") +} diff --git a/spinifex/handlers/eks/delete_cluster_test.go b/spinifex/handlers/eks/delete_cluster_test.go new file mode 100644 index 000000000..66b18593f --- /dev/null +++ b/spinifex/handlers/eks/delete_cluster_test.go @@ -0,0 +1,156 @@ +package handlers_eks + +import ( + "errors" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type fakeSubnetResolver struct{} + +var _ SubnetVPCResolver = (*fakeSubnetResolver)(nil) + +func (fakeSubnetResolver) GetSubnetVPC(_, _ string) (string, error) { return "vpc-aaa", nil } +func (fakeSubnetResolver) GetVPCCIDR(_, _ string) (string, error) { return "10.0.0.0/16", nil } + +func deleteInput(name string) *eks.DeleteClusterInput { + return &eks.DeleteClusterInput{Name: aws.String(name)} +} + +// eksServiceFixture stands up an EKSServiceImpl with fake orchestration deps +// and an embedded JetStream KV. Tests poke the fakes' *Err fields to force +// failures at specific lifecycle steps. +type eksServiceFixture struct { + svc *EKSServiceImpl + kv nats.KeyValue + nlb *fakeNLBProvisioner + inst *fakeK3sInst + vpc *fakeK3sVPC + ami *fakeK3sAMI + eip *fakeEIPProvisioner + sg *fakeSGProvisioner + worker *fakeWorkerLauncher +} + +func newEKSServiceFixture(t *testing.T) *eksServiceFixture { + t.Helper() + _, nc, js := testutil.StartTestJetStream(t) + kv, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + + nlb := newFakeNLBProvisioner() + inst := &fakeK3sInst{} + vpc := &fakeK3sVPC{} + ami := &fakeK3sAMI{} + eip := newFakeEIPProvisioner() + sg := newFakeSGProvisioner() + worker := newFakeWorkerLauncher() + + svc, err := NewEKSServiceImpl(EKSServiceDeps{ + NATSConn: nc, + MasterKey: bootstrapTestMasterKey, + GatewayBaseURL: "https://gw.local:9999", + Region: "us-east-1", + HolderID: "node-1", + SystemGatewayURL: "https://gw.local:9999", + SystemAccessKey: "AKIAEXAMPLE", + SystemSecretKey: "s3cr3t-key", + GatewayCACert: "-----BEGIN CERTIFICATE-----\nFAKECA\n-----END CERTIFICATE-----\n", + VPCSG: sg, + VPCK3s: vpc, + VPCSubnet: fakeSubnetResolver{}, + NLB: nlb, + Instance: inst, + Image: ami, + EIP: eip, + Worker: worker, + }) + require.NoError(t, err) + t.Cleanup(svc.Shutdown) + + return &eksServiceFixture{svc: svc, kv: kv, nlb: nlb, inst: inst, vpc: vpc, ami: ami, eip: eip, sg: sg, worker: worker} +} + +// deleteClusterFixture is an eksServiceFixture pre-seeded with a CREATING +// cluster meta whose NLB/VM/SG resource references are all populated so +// DeleteCluster exercises every teardown branch. +type deleteClusterFixture struct { + svc *EKSServiceImpl + kv nats.KeyValue + nlb *fakeNLBProvisioner + inst *fakeK3sInst + vpc *fakeK3sVPC +} + +func newDeleteClusterFixture(t *testing.T, clusterName string) *deleteClusterFixture { + t.Helper() + f := newEKSServiceFixture(t) + kv := f.kv + + meta := sampleClusterMeta(clusterName) + meta.NLBArn = "arn:aws:elasticloadbalancing:us-east-1:111122223333:loadbalancer/net/" + ClusterNLBName(clusterName) + "/lb-001" + meta.NLBTargetGroupArn = "arn:aws:elasticloadbalancing:us-east-1:111122223333:targetgroup/" + ClusterTargetGroupName(clusterName) + "/tg-001" + meta.ControlPlaneENIIP = "10.0.1.42" + meta.ControlPlaneInstanceID = "i-aaa111" + meta.ControlPlaneENIID = "eni-aaa111" + meta.ResourcesVpcConfig.VpcId = "vpc-aaa" + require.NoError(t, PutClusterMeta(kv, meta)) + + // Real OIDC key so ZeroizeClusterOIDCKey has material to wipe. + _, _, err := GenerateClusterOIDCKeypair(kv, clusterName, bootstrapTestMasterKey) + require.NoError(t, err) + + return &deleteClusterFixture{svc: f.svc, kv: kv, nlb: f.nlb, inst: f.inst, vpc: f.vpc} +} + +func TestDeleteCluster_AllTeardownSucceedsSweepsKV(t *testing.T) { + f := newDeleteClusterFixture(t, "alpha") + + out, err := f.svc.DeleteCluster(deleteInput("alpha"), testAccountID) + require.NoError(t, err) + require.NotNil(t, out) + + _, err = GetClusterMeta(f.kv, "alpha") + assert.ErrorIs(t, err, ErrClusterNotFound, "meta must be swept after successful teardown") + assert.Len(t, f.inst.terminateCalls, 1) + assert.Len(t, f.vpc.deleteCalls, 1) +} + +func TestDeleteCluster_NLBFailureLeavesMetaAndDELETING(t *testing.T) { + f := newDeleteClusterFixture(t, "alpha") + + // Seed the LB so delete is attempted, then force it to fail. + lbName := ClusterNLBName("alpha") + f.nlb.lbByName[lbName] = &elbv2.LoadBalancer{ + LoadBalancerArn: aws.String("arn:lb"), + LoadBalancerName: aws.String(lbName), + } + f.nlb.deleteLBErr = errors.New("elbv2 unavailable") + + _, err := f.svc.DeleteCluster(deleteInput("alpha"), testAccountID) + require.Error(t, err, "teardown failure must surface, not be swallowed") + + meta, getErr := GetClusterMeta(f.kv, "alpha") + require.NoError(t, getErr, "meta must survive so a retry can find the resource ARNs") + assert.Equal(t, ClusterStatusDeleting, meta.Status) + assert.NotEmpty(t, meta.NLBArn, "NLB ARN must remain recorded for retry") +} + +func TestDeleteCluster_VMTerminateFailureLeavesMeta(t *testing.T) { + f := newDeleteClusterFixture(t, "alpha") + f.inst.terminateErr = errors.New("hypervisor unreachable") + + _, err := f.svc.DeleteCluster(deleteInput("alpha"), testAccountID) + require.Error(t, err) + + meta, getErr := GetClusterMeta(f.kv, "alpha") + require.NoError(t, getErr) + assert.Equal(t, ClusterStatusDeleting, meta.Status) +} diff --git a/spinifex/handlers/eks/eip_test.go b/spinifex/handlers/eks/eip_test.go new file mode 100644 index 000000000..209f6c0fc --- /dev/null +++ b/spinifex/handlers/eks/eip_test.go @@ -0,0 +1,44 @@ +package handlers_eks + +import ( + "github.com/aws/aws-sdk-go/aws" + ec2 "github.com/aws/aws-sdk-go/service/ec2" +) + +// fakeEIPProvisioner records allocate/release calls and hands back a +// deterministic pool address so CreateCluster/DeleteCluster egress wiring is +// exercised without a live EIP service. +type fakeEIPProvisioner struct { + allocateCalls []*ec2.AllocateAddressInput + releaseCalls []*ec2.ReleaseAddressInput + + publicIP string + allocationID string + allocateErr error + releaseErr error +} + +var _ eipProvisioner = (*fakeEIPProvisioner)(nil) + +func newFakeEIPProvisioner() *fakeEIPProvisioner { + return &fakeEIPProvisioner{publicIP: "203.0.113.50", allocationID: "eipalloc-fake01"} +} + +func (f *fakeEIPProvisioner) AllocateAddress(input *ec2.AllocateAddressInput, _ string) (*ec2.AllocateAddressOutput, error) { + f.allocateCalls = append(f.allocateCalls, input) + if f.allocateErr != nil { + return nil, f.allocateErr + } + return &ec2.AllocateAddressOutput{ + PublicIp: aws.String(f.publicIP), + AllocationId: aws.String(f.allocationID), + }, nil +} + +func (f *fakeEIPProvisioner) ReleaseAddress(input *ec2.ReleaseAddressInput, _ string) (*ec2.ReleaseAddressOutput, error) { + f.releaseCalls = append(f.releaseCalls, input) + if f.releaseErr != nil { + return nil, f.releaseErr + } + return &ec2.ReleaseAddressOutput{}, nil +} diff --git a/spinifex/handlers/eks/k3s_server_vm.go b/spinifex/handlers/eks/k3s_server_vm.go new file mode 100644 index 000000000..870a413d9 --- /dev/null +++ b/spinifex/handlers/eks/k3s_server_vm.go @@ -0,0 +1,553 @@ +package handlers_eks + +import ( + "errors" + "fmt" + "log/slog" + "net" + "strconv" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/handlers/sysinstance" + "github.com/mulgadc/spinifex/spinifex/tags" +) + +// imdsServerIP is the link-local IMDS address (mirror of +// handlers/imds.MetaDataServerIP, duplicated to avoid an import cycle: +// imds → sts → eks). +const imdsServerIP = "169.254.169.254" + +// ErrEKSServerAMINotFound is returned by the launcher when no AMI carrying the +// EKS managed-by tag exists in the account. CreateCluster maps it to a precise +// client error: it signals an operator/config gap (the eks-server image was +// never built/imported), not an unrecoverable internal fault. +var ErrEKSServerAMINotFound = errors.New("eks: eks-server AMI not found") + +// k3sVPCProvisioner is the subset of handlers_ec2_vpc.VPCService that the K3s +// server VM launcher needs. Narrow so tests can fake it without implementing +// the full VPC surface. +type k3sVPCProvisioner interface { + CreateNetworkInterface(input *ec2.CreateNetworkInterfaceInput, accountID string) (*ec2.CreateNetworkInterfaceOutput, error) + DeleteNetworkInterface(input *ec2.DeleteNetworkInterfaceInput, accountID string) (*ec2.DeleteNetworkInterfaceOutput, error) +} + +// k3sInstanceLauncher is the system-instance launch surface the K3s control-plane +// VM needs. The VM boots from the eks-server AMI (BootAMI) and must get a +// management-bridge NIC so it can reach the daemon's NATS endpoint off its +// tenant VPC subnet — which only the system-instance path provides. +// *daemon.Daemon satisfies this interface. +type k3sInstanceLauncher interface { + LaunchSystemInstance(input *sysinstance.SystemInstanceInput) (*sysinstance.SystemInstanceOutput, error) + TerminateSystemInstance(instanceID string) error +} + +// k3sAMIResolver is the subset of handlers_ec2_image.ImageService the launcher +// uses to resolve the eks-server AMI name to an AMI ID at launch time. +type k3sAMIResolver interface { + DescribeImages(input *ec2.DescribeImagesInput, accountID string) (*ec2.DescribeImagesOutput, error) +} + +const ( + // defaultK3sServerInstanceType is the spinifex instance type closest to + // the AWS EKS minimum control-plane footprint (2 vCPU / 8 GB / 40 GB). + // Callers can override via K3sServerInput.InstanceType. + defaultK3sServerInstanceType = "t3.medium" + + // k3sOIDCSigningKeyPath is the on-VM path where cloud-init drops the + // per-cluster OIDC private key PEM. K3s reads it via the + // service-account-signing-key-file kube-apiserver arg (signs SA tokens). + k3sOIDCSigningKeyPath = "/etc/rancher/k3s/oidc-signing-key.pem" + + // k3sOIDCPublicKeyPath is where cloud-init drops the matching public key + // PEM. kube-apiserver's service-account-key-file requires a PUBLIC key + // (it rejects a private-key PEM with "data does not contain any valid RSA + // or ECDSA public keys" and crash-loops), so it must be a separate file. + k3sOIDCPublicKeyPath = "/etc/rancher/k3s/oidc-signing-key.pub.pem" + + // k3sFirstBootEnvPath is the env file k3s-first-boot.sh sources at boot + // (see scripts/images/eks-server/k3s-first-boot.sh ENVFILE). + k3sFirstBootEnvPath = "/etc/spinifex-eks/first-boot.env" + + // agentEnvPath is the env file the k3s-agent OpenRC service sources for its + // K3S_URL/K3S_TOKEN/K3S_NODE_NAME/K3S_NODE_LABEL. Path matches the AGENT_ENVFILE + // in scripts/images/eks-node/eks-node-role.sh and k3s-agent.initd. + agentEnvPath = "/etc/spinifex-eks/agent.env" + + // k3sGatewayCAPath is the on-VM destination for the AWS gateway TLS CA cert + // PEM. The K3s VM uses it to verify the gateway's HTTPS cert when the + // eks-gateway-publish helper POSTs bootstrap envelopes + state reports. + // Path matches k3s-first-boot.sh EKS_GATEWAY_CA. + k3sGatewayCAPath = "/etc/spinifex-eks/gateway-ca.pem" + + // k3sTokenWebhookKubeconfigPath is the apiserver token-webhook config the + // eks-token-webhook service (ordered `before k3s`) writes before the + // apiserver starts. Wired via the authentication-token-webhook-config-file + // apiserver arg so bearer tokens minted by `aws eks get-token` resolve + // through the webhook. Must match the webhook's EKS_WEBHOOK_KUBECONFIG default. + k3sTokenWebhookKubeconfigPath = "/etc/spinifex-eks/token-webhook.kubeconfig" //nolint:gosec // file path, not a credential + + // k3sConfigPath is the K3s server config file cloud-init writes; K3s + // reads it at startup (overrides the AMI-baked config.yaml.skel). + k3sConfigPath = "/etc/rancher/k3s/config.yaml" + + // k3sResolvConfPath is the on-VM resolver file. The Alpine eks-server AMI + // runs dhcpcd, whose resolv.conf hook fails to persist the DHCP-supplied + // nameservers ("can't create /etc/resolv.conf: nonexistent directory"), so + // the VM boots with no resolver and containerd cannot resolve registry-1. + // docker.io to pull the system-pod images. cloud-init writes a static + // resolver here; the dhcpcd hook never clobbers it (it errors before it + // would). Reachable via the cluster's egress SNAT. + k3sResolvConfPath = "/etc/resolv.conf" + + // k3sResolvConf is the static resolver content. Public anycast resolvers + // reached over the control-plane VM's egress path. + k3sResolvConf = "nameserver 1.1.1.1\nnameserver 8.8.8.8" +) + +// K3sServerInput is the launcher's input shape. AccountID is the customer +// account; the ENI + VM both live there in v1 (SystemAccount-owned VM is +// deferred behind cross-account-ENI work). Region is carried for future +// region-aware AMI lookups but not consumed today. +type K3sServerInput struct { + AccountID string + ClusterName string + Region string + SubnetID string + ControlPlaneSGID string + NLBDNS string + // EndpointIP is the reachable cluster NLB front-end IP. When set it is added + // to the apiserver serving-cert SANs (tls-san) so kubectl reaching the + // cluster on this IP validates TLS. Empty for an internal endpoint with no + // front-end IP read back. + EndpointIP string + OIDCIssuer string + OIDCPrivateKeyPEM string + OIDCPublicKeyPEM string + // Gateway broker config: the control-plane VM publishes its bootstrap + // envelopes + state reports via SigV4-signed HTTPS POST to the AWS gateway + // (the ELBv2 lb-agent model), not by dialing core NATS. GatewayURL is the + // mgmt-reachable AWSGW endpoint; AccessKey/SecretKey are the system + // (Predastore) SigV4 creds; GatewayCACert is the PEM that signs the gateway + // server cert. + GatewayURL string + AccessKey string + SecretKey string + GatewayCACert string + InstanceType string + // BuiltinIngress keeps K3s' bundled traefik + servicelb enabled (interim + // in-VPC app exposure). When false the config disables them for AWS parity, + // where Service type=LoadBalancer / Ingress are satisfied by the AWS Load + // Balancer Controller instead. The CreateCluster default is ON until that + // controller ships (see managedIngressTagKey). + BuiltinIngress bool +} + +// K3sServerOutput carries the identifiers the caller needs to persist into +// ClusterMeta and to register the ENI IP with the cluster NLB target group. +type K3sServerOutput struct { + InstanceID string + ENIID string + ENIIP string + MgmtIP string +} + +// LaunchK3sServerVM provisions the K3s control-plane VM for an EKS cluster. +// Sequence: resolve the eks-server AMI, pre-create the customer-account ENI +// in the supplied subnet with the control-plane SG, render cloud-init user +// data (env vars, OIDC PEM, K3s config), then call RunInstances with +// NetworkInterfaces[0].NetworkInterfaceId pointing at the pre-created ENI. +// Returns instance/ENI identity so the caller can register the ENI IP with +// the cluster NLB target group and persist the IDs in ClusterMeta. On +// RunInstances failure the pre-created ENI is deleted best-effort so the +// caller does not leak a customer-account resource. +func LaunchK3sServerVM( + vpcSvc k3sVPCProvisioner, + instSvc k3sInstanceLauncher, + amiSvc k3sAMIResolver, + in K3sServerInput, +) (*K3sServerOutput, error) { + if err := validateK3sServerInput(in); err != nil { + return nil, err + } + instanceType := in.InstanceType + if instanceType == "" { + instanceType = defaultK3sServerInstanceType + } + + amiID, err := lookupEKSServerAMI(amiSvc, in.AccountID) + if err != nil { + return nil, err + } + + eniOut, err := vpcSvc.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{ + SubnetId: aws.String(in.SubnetID), + Description: aws.String("EKS K3s server ENI for " + in.ClusterName), + Groups: aws.StringSlice([]string{in.ControlPlaneSGID}), + TagSpecifications: []*ec2.TagSpecification{{ + ResourceType: aws.String("network-interface"), + Tags: []*ec2.Tag{ + {Key: aws.String(tags.ManagedByKey), Value: aws.String(tags.ManagedByEKS)}, + {Key: aws.String(clusterEKSClusterTagKey), Value: aws.String(in.ClusterName)}, + {Key: aws.String(clusterEKSRoleTagKey), Value: aws.String(clusterEKSRoleControlPlane)}, + }, + }}, + }, in.AccountID) + if err != nil { + return nil, fmt.Errorf("create K3s ENI in subnet %s: %w", in.SubnetID, err) + } + if eniOut == nil || eniOut.NetworkInterface == nil || + aws.StringValue(eniOut.NetworkInterface.NetworkInterfaceId) == "" || + aws.StringValue(eniOut.NetworkInterface.PrivateIpAddress) == "" { + return nil, errors.New("eks: CreateNetworkInterface returned incomplete ENI") + } + eniID := aws.StringValue(eniOut.NetworkInterface.NetworkInterfaceId) + eniIP := aws.StringValue(eniOut.NetworkInterface.PrivateIpAddress) + + userData := buildK3sUserData(in) + + sysOut, err := instSvc.LaunchSystemInstance(&sysinstance.SystemInstanceInput{ + BootMode: sysinstance.BootAMI, + ManagedBy: tags.ManagedByEKS, + InstanceType: instanceType, + ImageID: amiID, + AccountID: in.AccountID, + ENIID: eniID, + ENIMac: aws.StringValue(eniOut.NetworkInterface.MacAddress), + ENIIP: eniIP, + UserData: userData, + }) + if err != nil { + rollbackK3sENI(vpcSvc, in.AccountID, eniID) + return nil, fmt.Errorf("run K3s server instance for cluster %s: %w", in.ClusterName, err) + } + if sysOut == nil || sysOut.InstanceID == "" { + rollbackK3sENI(vpcSvc, in.AccountID, eniID) + return nil, fmt.Errorf("eks: LaunchSystemInstance returned no instance for cluster %s", in.ClusterName) + } + instanceID := sysOut.InstanceID + + slog.Info("LaunchK3sServerVM completed", + "clusterName", in.ClusterName, + "accountID", in.AccountID, + "instanceId", instanceID, + "eniId", eniID, + "eniIp", eniIP, + ) + + return &K3sServerOutput{ + InstanceID: instanceID, + ENIID: eniID, + ENIIP: eniIP, + MgmtIP: sysOut.MgmtIP, + }, nil +} + +// TerminateK3sServerVM tears down the K3s server VM for a cluster. The +// instance termination cascades ENI detach inside the customer instance path, +// then this helper deletes the ENI explicitly so DeleteCluster does not leak +// it. Missing instance/ENI is a no-op so retries stay safe. +func TerminateK3sServerVM( + vpcSvc k3sVPCProvisioner, + instSvc k3sInstanceLauncher, + accountID, instanceID, eniID string, +) error { + if instanceID == "" && eniID == "" { + return nil + } + var firstErr error + if instanceID != "" { + // A retried DeleteCluster reaches here after the VM already drained, so + // "instance not found" is the steady-state success case, not a failure — + // treat it as idempotent so teardown can proceed to the ENI/SG/KV sweep. + if err := instSvc.TerminateSystemInstance(instanceID); err != nil { + if errors.Is(err, sysinstance.ErrSystemInstanceNotFound) { + slog.Debug("TerminateK3sServerVM: instance already gone", "instanceId", instanceID) + } else { + slog.Warn("TerminateK3sServerVM: terminate failed", "instanceId", instanceID, "err", err) + firstErr = fmt.Errorf("terminate instance %s: %w", instanceID, err) + } + } + } + if eniID != "" { + if _, err := vpcSvc.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ + NetworkInterfaceId: aws.String(eniID), + }, accountID); err != nil { + switch { + case awserrors.IsErrorCode(err, awserrors.ErrorInvalidNetworkInterfaceIDNotFound), + awserrors.IsErrorCode(err, awserrors.ErrorInvalidNetworkInterfaceNotFound): + // The instance-terminate cascade (or a prior retry) already + // deleted the ENI. Idempotent success — must NOT block the SG + + // KV sweep, or the cluster wedges in DELETING permanently. + slog.Debug("TerminateK3sServerVM: ENI already gone", "eniId", eniID) + default: + // InvalidNetworkInterface.InUse (the VM is still terminating + // async and holds the ENI) and any other error are retryable: + // surface so the cluster stays DELETING and the reconciler + // retries once the instance releases the ENI. + slog.Warn("TerminateK3sServerVM: ENI delete failed", "eniId", eniID, "err", err) + if firstErr == nil { + firstErr = fmt.Errorf("delete ENI %s: %w", eniID, err) + } + } + } + } + return firstErr +} + +// lookupEKSServerAMI resolves the EKS control-plane AMI by the +// spinifex:managed-by=eks tag the build pipeline stamps on it, rather than a +// brittle exact name. This survives the planned server+agent → single EKS AMI +// unify untouched (the unified AMI keeps the tag; role is chosen per-instance +// at launch). If more than one AMI carries the tag (e.g. server + agent both +// imported pre-unify), the newest by CreationDate wins. +func lookupEKSServerAMI(amiSvc k3sAMIResolver, accountID string) (string, error) { + out, err := amiSvc.DescribeImages(&ec2.DescribeImagesInput{ + Filters: []*ec2.Filter{ + {Name: aws.String("tag:" + tags.ManagedByKey), Values: aws.StringSlice([]string{tags.ManagedByEKS})}, + }, + }, accountID) + if err != nil { + return "", fmt.Errorf("describe eks AMI (tag:%s=%s): %w", tags.ManagedByKey, tags.ManagedByEKS, err) + } + + var ( + newestID string + newestCreated string + matches int + ) + for _, img := range out.Images { + if img == nil || img.ImageId == nil || *img.ImageId == "" { + continue + } + matches++ + // CreationDate is a fixed-width RFC3339 timestamp, so lexicographic + // comparison orders it correctly without parsing. + if created := aws.StringValue(img.CreationDate); newestID == "" || created > newestCreated { + newestID, newestCreated = *img.ImageId, created + } + } + if newestID == "" { + return "", fmt.Errorf("%w (tag:%s=%s, account %s)", ErrEKSServerAMINotFound, tags.ManagedByKey, tags.ManagedByEKS, accountID) + } + if matches > 1 { + slog.Warn("eks: multiple AMIs match managed-by=eks; using newest", + "count", matches, "imageId", newestID, "created", newestCreated) + } + return newestID, nil +} + +func rollbackK3sENI(vpcSvc k3sVPCProvisioner, accountID, eniID string) { + if _, err := vpcSvc.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ + NetworkInterfaceId: aws.String(eniID), + }, accountID); err != nil { + slog.Warn("LaunchK3sServerVM: rollback ENI delete failed", "eniId", eniID, "err", err) + } +} + +func validateK3sServerInput(in K3sServerInput) error { + switch { + case in.AccountID == "": + return errors.New("eks: K3sServerInput empty AccountID") + case in.ClusterName == "": + return errors.New("eks: K3sServerInput empty ClusterName") + case in.SubnetID == "": + return errors.New("eks: K3sServerInput empty SubnetID") + case in.ControlPlaneSGID == "": + return errors.New("eks: K3sServerInput empty ControlPlaneSGID") + case in.NLBDNS == "": + return errors.New("eks: K3sServerInput empty NLBDNS") + case in.OIDCIssuer == "": + return errors.New("eks: K3sServerInput empty OIDCIssuer") + case strings.TrimSpace(in.OIDCPrivateKeyPEM) == "": + return errors.New("eks: K3sServerInput empty OIDCPrivateKeyPEM") + case strings.TrimSpace(in.OIDCPublicKeyPEM) == "": + return errors.New("eks: K3sServerInput empty OIDCPublicKeyPEM") + case in.GatewayURL == "": + return errors.New("eks: K3sServerInput empty GatewayURL") + case in.AccessKey == "": + return errors.New("eks: K3sServerInput empty AccessKey") + case in.SecretKey == "": + return errors.New("eks: K3sServerInput empty SecretKey") + case strings.TrimSpace(in.GatewayCACert) == "": + return errors.New("eks: K3sServerInput empty GatewayCACert") + } + return nil +} + +// boolFlag renders a bool as the "1"/"0" string the shell first-boot env reads. +func boolFlag(b bool) string { + if b { + return "1" + } + return "0" +} + +// userDataFile is one entry in the cloud-config write_files block. +type userDataFile struct { + Path string + Perms string + Body string +} + +// buildK3sUserData renders the cloud-config YAML the K3s server VM consumes +// at first boot. Output is unencoded YAML; the caller base64-wraps it for +// the RunInstances UserData field. +func buildK3sUserData(in K3sServerInput) string { + nlbEndpoint := "https://" + net.JoinHostPort(in.NLBDNS, strconv.FormatInt(clusterNLBListenPort, 10)) + + envBody := strings.Join([]string{ + // Role the eks-node-role first-boot selector reads to enable the + // control-plane services (k3s server + token webhook + bootstrap + // publisher). Nodegroup workers seed "agent" through their own path. + "SPINIFEX_K3S_ROLE=server", + "EKS_GATEWAY_URL=" + in.GatewayURL, + "EKS_GATEWAY_CA=" + k3sGatewayCAPath, + "EKS_ACCESS_KEY=" + in.AccessKey, + "EKS_SECRET_KEY=" + in.SecretKey, + "EKS_REGION=" + in.Region, + "EKS_ACCOUNT_ID=" + in.AccountID, + "EKS_CLUSTER_NAME=" + in.ClusterName, + "EKS_NLB_ENDPOINT=" + nlbEndpoint, + "EKS_OIDC_ISSUER=" + in.OIDCIssuer, + // Gate deferred built-in ingress: k3s.initd stages a traefik .skip marker + // before boot and the state-reporter removes it once the apiserver is + // stable. Set only for clusters that want built-in ingress. + "EKS_DEFER_TRAEFIK=" + boolFlag(in.BuiltinIngress), + }, "\n") + + // cluster-init selects the embedded etcd datastore (required for multi-server + // HA). etcd-expose-metrics surfaces etcd's own wal_fsync_duration_seconds and + // backend_commit_duration_seconds on 127.0.0.1:2381/metrics so control-plane + // commit latency is measurable directly, not inferred. + // + // anonymous-auth=true: k3s hardens it off by default, which makes the + // apiserver answer 401 to an unauthenticated /healthz. The cluster reconciler + // probes https:///healthz anonymously to gate ACTIVE, so it must be + // reachable; the default RBAC binds only the health/version non-resource + // paths to system:unauthenticated, so this exposes nothing else. + // In real EKS neither traefik nor servicelb exists; Service type=LoadBalancer + // and Ingress are reconciled by the AWS Load Balancer Controller. When a + // cluster does not opt into built-in ingress (BuiltinIngress=false) the K3s + // built-ins are disabled outright for parity. + // + // When a cluster DOES want built-in ingress (dev / interim, until the AWS LB + // controller add-on lands) traefik stays ENABLED in config so k3s writes its + // manifest — but its helm-install Job (klipper-helm image pull + chart + // install) hammers the embedded etcd during the fragile bootstrap window, + // which can tip etcd fsync latency past the apiserver / leaderelection + // deadlines and crash the control plane. So traefik is deferred, NOT disabled: + // k3s.initd stages a deploy-controller .skip marker before the apiserver + // starts (EKS_DEFER_TRAEFIK gates it), and the state-reporter removes that + // marker once the apiserver is sustainedly healthy — traefik then installs + // with etcd headroom. Disabling traefik (`disable: traefik`) is wrong for this: + // k3s never writes traefik.yaml, leaving no manifest to un-skip. servicelb + // (klipper) is lazy — it acts only when a LoadBalancer Service exists — so it + // carries no bootstrap cost and stays enabled whenever built-in ingress is on. + configLines := []string{ + "cluster-init: true", + "etcd-expose-metrics: true", + // Keep user workloads off the control plane (EKS parity — the CP is never + // a worker). Critical here because the server node is schedulable by + // default: a workload landing on the CP pulls its images onto the same + // NBD/viperblock disk the embedded etcd fsyncs to, starving etcd until the + // apiserver crashes and the VM OOMs. k3s' packaged addons tolerate + // CriticalAddonsOnly, so they still run (here, draining to the nodegroup); + // NoExecute also evicts anything already scheduled, not just future pods. + "node-taint:", + " - CriticalAddonsOnly=true:NoExecute", + } + if !in.BuiltinIngress { + configLines = append(configLines, + "disable:", + " - traefik", + " - servicelb", + ) + } + configLines = append(configLines, "tls-san:", " - "+in.NLBDNS) + // The reachable front-end IP must be a cert SAN so kubectl reaching the + // cluster on https://:443 validates TLS. k3s accepts IP literals + // in tls-san and classifies them as IP SANs. + if in.EndpointIP != "" { + configLines = append(configLines, " - "+in.EndpointIP) + } + configLines = append(configLines, + "kube-apiserver-arg:", + " - service-account-key-file="+k3sOIDCPublicKeyPath, + " - service-account-signing-key-file="+k3sOIDCSigningKeyPath, + " - service-account-issuer="+in.OIDCIssuer, + " - api-audiences=sts.amazonaws.com", + " - anonymous-auth=true", + " - authentication-token-webhook-config-file="+k3sTokenWebhookKubeconfigPath, + " - authentication-token-webhook-cache-ttl=5m", + // Pin v1 so the apiserver decodes the webhook's authentication.k8s.io/v1 + // TokenReview response; the default v1beta1 rejects the GVK mismatch (401). + " - authentication-token-webhook-version=v1", + ) + k3sConfig := strings.Join(configLines, "\n") + + files := []userDataFile{ + // first-boot.env carries the system SigV4 secret key, so keep it + // root-only (0600). + {Path: k3sFirstBootEnvPath, Perms: "0600", Body: envBody}, + {Path: k3sOIDCSigningKeyPath, Perms: "0600", Body: strings.TrimRight(in.OIDCPrivateKeyPEM, "\n")}, + {Path: k3sOIDCPublicKeyPath, Perms: "0644", Body: strings.TrimRight(in.OIDCPublicKeyPEM, "\n")}, + {Path: k3sConfigPath, Perms: "0644", Body: k3sConfig}, + {Path: k3sGatewayCAPath, Perms: "0644", Body: strings.TrimRight(in.GatewayCACert, "\n")}, + // IMDS on-link route. Alpine's cloud-init eni renderer crashes on a + // gateway-less route in network-config, so it's delivered out-of-band: + // a persistent local.d script (re-applied every boot by the OpenRC + // `local` service, enabled via runcmd below) ARPs 169.254.169.254 + // directly on the VPC subnet. This block owns the only write_files key, + // so it must carry the route itself — appending a second write_files via + // the generic cloud-init template would collide (last key wins, silently + // dropping the k3s config). The device is resolved via the default route + // (the VPC egress NIC), not a name: the persistent-net rename to vpc0 + // doesn't apply on the Alpine AMI, so the kernel name is eth0 at boot. + { + Path: "/etc/local.d/imds-onlink-route.start", + Perms: "0755", + Body: "#!/bin/sh\n" + + "dev=$(ip route show default | awk '{print $5; exit}')\n" + + "[ -n \"$dev\" ] && ip route replace " + imdsServerIP + "/32 dev \"$dev\" scope link", + }, + } + + var buf strings.Builder + buf.WriteString("#cloud-config\n") + + // Resolver via bootcmd, NOT write_files: on the Alpine AMI /etc/resolv.conf + // is a dangling symlink (its target dir does not exist — which is why the + // dhcpcd hook cannot persist DHCP DNS), and pointing write_files at it makes + // cloud-init follow the dead link, fail, and abort the WHOLE write_files + // block (dropping first-boot.env + the k3s config). bootcmd runs before + // write_files in the init stage and as a shell, so it can drop the symlink + // and write a real file. Containerd needs this to resolve registry-1.docker. + // io for system-pod image pulls; reachable over the cluster egress SNAT. + buf.WriteString("bootcmd:\n") + buf.WriteString(" - rm -f " + k3sResolvConfPath + "\n") + fmt.Fprintf(&buf, " - printf '%s\\n' > %s\n", + strings.ReplaceAll(k3sResolvConf, "\n", "\\n"), k3sResolvConfPath) + + buf.WriteString("write_files:\n") + for _, f := range files { + fmt.Fprintf(&buf, " - path: %s\n", f.Path) + fmt.Fprintf(&buf, " permissions: '%s'\n", f.Perms) + buf.WriteString(" content: |\n") + for line := range strings.SplitSeq(f.Body, "\n") { + buf.WriteString(" ") + buf.WriteString(line) + buf.WriteString("\n") + } + } + + // Enable + start the OpenRC `local` service so the IMDS route script runs on + // first boot and is re-applied on every subsequent boot. + buf.WriteString("runcmd:\n") + buf.WriteString(" - [ rc-update, add, local, default ]\n") + buf.WriteString(" - [ rc-service, local, start ]\n") + + return buf.String() +} diff --git a/spinifex/handlers/eks/k3s_server_vm_test.go b/spinifex/handlers/eks/k3s_server_vm_test.go new file mode 100644 index 000000000..77f7bb407 --- /dev/null +++ b/spinifex/handlers/eks/k3s_server_vm_test.go @@ -0,0 +1,471 @@ +package handlers_eks + +import ( + "errors" + "fmt" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/handlers/sysinstance" + "github.com/mulgadc/spinifex/spinifex/tags" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type fakeK3sVPC struct { + createCalls []*ec2.CreateNetworkInterfaceInput + deleteCalls []*ec2.DeleteNetworkInterfaceInput + + createOut *ec2.CreateNetworkInterfaceOutput + createErr error + deleteErr error +} + +var _ k3sVPCProvisioner = (*fakeK3sVPC)(nil) + +func (f *fakeK3sVPC) CreateNetworkInterface(input *ec2.CreateNetworkInterfaceInput, _ string) (*ec2.CreateNetworkInterfaceOutput, error) { + f.createCalls = append(f.createCalls, input) + if f.createErr != nil { + return nil, f.createErr + } + if f.createOut != nil { + return f.createOut, nil + } + return &ec2.CreateNetworkInterfaceOutput{ + NetworkInterface: &ec2.NetworkInterface{ + NetworkInterfaceId: aws.String("eni-aaa111"), + PrivateIpAddress: aws.String("10.0.1.42"), + SubnetId: input.SubnetId, + }, + }, nil +} + +func (f *fakeK3sVPC) DeleteNetworkInterface(input *ec2.DeleteNetworkInterfaceInput, _ string) (*ec2.DeleteNetworkInterfaceOutput, error) { + f.deleteCalls = append(f.deleteCalls, input) + if f.deleteErr != nil { + return nil, f.deleteErr + } + return &ec2.DeleteNetworkInterfaceOutput{}, nil +} + +type fakeK3sInst struct { + launchCalls []*sysinstance.SystemInstanceInput + terminateCalls []string + + launchOut *sysinstance.SystemInstanceOutput + launchErr error + terminateErr error +} + +var _ k3sInstanceLauncher = (*fakeK3sInst)(nil) + +func (f *fakeK3sInst) LaunchSystemInstance(input *sysinstance.SystemInstanceInput) (*sysinstance.SystemInstanceOutput, error) { + f.launchCalls = append(f.launchCalls, input) + if f.launchErr != nil { + return nil, f.launchErr + } + if f.launchOut != nil { + return f.launchOut, nil + } + return &sysinstance.SystemInstanceOutput{InstanceID: "i-aaa111"}, nil +} + +func (f *fakeK3sInst) TerminateSystemInstance(instanceID string) error { + f.terminateCalls = append(f.terminateCalls, instanceID) + return f.terminateErr +} + +type fakeK3sAMI struct { + describeCalls []*ec2.DescribeImagesInput + + describeOut *ec2.DescribeImagesOutput + describeErr error +} + +var _ k3sAMIResolver = (*fakeK3sAMI)(nil) + +func (f *fakeK3sAMI) DescribeImages(input *ec2.DescribeImagesInput, _ string) (*ec2.DescribeImagesOutput, error) { + f.describeCalls = append(f.describeCalls, input) + if f.describeErr != nil { + return nil, f.describeErr + } + if f.describeOut != nil { + return f.describeOut, nil + } + return &ec2.DescribeImagesOutput{ + Images: []*ec2.Image{{ + ImageId: aws.String("ami-eks-server-001"), + Name: aws.String("spinifex-eks-server"), + CreationDate: aws.String("2026-06-01T00:00:00.000Z"), + Tags: []*ec2.Tag{ + {Key: aws.String(tags.ManagedByKey), Value: aws.String(tags.ManagedByEKS)}, + }, + }}, + }, nil +} + +func validK3sInput() K3sServerInput { + return K3sServerInput{ + AccountID: "111122223333", + ClusterName: "alpha", + Region: "us-east-1", + SubnetID: "subnet-aaa", + ControlPlaneSGID: "sg-cp-aaa", + NLBDNS: "eks-alpha-lb-001.us-east-1.elb.spinifex.local", + OIDCIssuer: "https://oidc.spinifex.local/clusters/111122223333/alpha", + OIDCPrivateKeyPEM: "-----BEGIN PRIVATE KEY-----\nFAKEKEY\n-----END PRIVATE KEY-----\n", + OIDCPublicKeyPEM: "-----BEGIN PUBLIC KEY-----\nFAKEPUB\n-----END PUBLIC KEY-----\n", + GatewayURL: "https://10.15.8.1:9999", + AccessKey: "AKIAEXAMPLE", + SecretKey: "s3cr3t-key", + GatewayCACert: "-----BEGIN CERTIFICATE-----\nFAKECA\n-----END CERTIFICATE-----\n", + } +} + +func TestLaunchK3sServerVM_EmptyInputsRejected(t *testing.T) { + mk := func(mutate func(*K3sServerInput)) K3sServerInput { + in := validK3sInput() + mutate(&in) + return in + } + cases := []struct { + name string + in K3sServerInput + }{ + {"empty AccountID", mk(func(in *K3sServerInput) { in.AccountID = "" })}, + {"empty ClusterName", mk(func(in *K3sServerInput) { in.ClusterName = "" })}, + {"empty SubnetID", mk(func(in *K3sServerInput) { in.SubnetID = "" })}, + {"empty ControlPlaneSGID", mk(func(in *K3sServerInput) { in.ControlPlaneSGID = "" })}, + {"empty NLBDNS", mk(func(in *K3sServerInput) { in.NLBDNS = "" })}, + {"empty OIDCIssuer", mk(func(in *K3sServerInput) { in.OIDCIssuer = "" })}, + {"empty OIDCPrivateKeyPEM", mk(func(in *K3sServerInput) { in.OIDCPrivateKeyPEM = " \n " })}, + {"empty OIDCPublicKeyPEM", mk(func(in *K3sServerInput) { in.OIDCPublicKeyPEM = " \n " })}, + {"empty GatewayURL", mk(func(in *K3sServerInput) { in.GatewayURL = "" })}, + {"empty AccessKey", mk(func(in *K3sServerInput) { in.AccessKey = "" })}, + {"empty SecretKey", mk(func(in *K3sServerInput) { in.SecretKey = "" })}, + {"empty GatewayCACert", mk(func(in *K3sServerInput) { in.GatewayCACert = " \n" })}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + vpc, inst, ami := &fakeK3sVPC{}, &fakeK3sInst{}, &fakeK3sAMI{} + _, err := LaunchK3sServerVM(vpc, inst, ami, tc.in) + require.Error(t, err) + assert.Empty(t, vpc.createCalls) + assert.Empty(t, inst.launchCalls) + assert.Empty(t, ami.describeCalls) + }) + } +} + +func TestLaunchK3sServerVM_AMINotFound(t *testing.T) { + vpc, inst := &fakeK3sVPC{}, &fakeK3sInst{} + ami := &fakeK3sAMI{describeOut: &ec2.DescribeImagesOutput{}} + + _, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.ErrorIs(t, err, ErrEKSServerAMINotFound) + assert.Contains(t, err.Error(), "spinifex:managed-by=eks") + assert.Empty(t, vpc.createCalls, "no ENI created when AMI lookup fails") + assert.Empty(t, inst.launchCalls) +} + +func TestLaunchK3sServerVM_AMILookupErrorPropagated(t *testing.T) { + vpc, inst := &fakeK3sVPC{}, &fakeK3sInst{} + ami := &fakeK3sAMI{describeErr: errors.New("DescribeImages backend down")} + + _, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.Error(t, err) + assert.Contains(t, err.Error(), "describe eks AMI") + assert.Empty(t, vpc.createCalls) + assert.Empty(t, inst.launchCalls) +} + +func TestLaunchK3sServerVM_ENICreateFailureNoRunInstances(t *testing.T) { + vpc := &fakeK3sVPC{createErr: errors.New("InsufficientFreeAddressesInSubnet")} + inst, ami := &fakeK3sInst{}, &fakeK3sAMI{} + + _, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.Error(t, err) + assert.Contains(t, err.Error(), "create K3s ENI in subnet subnet-aaa") + assert.Empty(t, inst.launchCalls) + assert.Empty(t, vpc.deleteCalls, "no ENI to roll back when create itself failed") +} + +func TestLaunchK3sServerVM_RunInstancesFailureRollsBackENI(t *testing.T) { + vpc := &fakeK3sVPC{} + inst := &fakeK3sInst{launchErr: errors.New("InsufficientInstanceCapacity")} + ami := &fakeK3sAMI{} + + _, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.Error(t, err) + require.Len(t, vpc.createCalls, 1) + require.Len(t, vpc.deleteCalls, 1, "ENI must roll back when RunInstances fails") + assert.Equal(t, "eni-aaa111", aws.StringValue(vpc.deleteCalls[0].NetworkInterfaceId)) +} + +func TestLaunchK3sServerVM_HappyPath(t *testing.T) { + vpc, inst, ami := &fakeK3sVPC{}, &fakeK3sInst{}, &fakeK3sAMI{} + + out, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.NoError(t, err) + require.NotNil(t, out) + assert.Equal(t, "i-aaa111", out.InstanceID) + assert.Equal(t, "eni-aaa111", out.ENIID) + assert.Equal(t, "10.0.1.42", out.ENIIP) + + require.Len(t, vpc.createCalls, 1) + eniIn := vpc.createCalls[0] + assert.Equal(t, "subnet-aaa", aws.StringValue(eniIn.SubnetId)) + assert.Equal(t, []string{"sg-cp-aaa"}, aws.StringValueSlice(eniIn.Groups)) + require.Len(t, eniIn.TagSpecifications, 1) + assertEC2TaggedAsEKSControlPlane(t, eniIn.TagSpecifications[0].Tags, "alpha") + + require.Len(t, inst.launchCalls, 1) + runIn := inst.launchCalls[0] + assert.Equal(t, sysinstance.BootAMI, runIn.BootMode) + assert.Equal(t, tags.ManagedByEKS, runIn.ManagedBy) + assert.Equal(t, "ami-eks-server-001", runIn.ImageID) + assert.Equal(t, defaultK3sServerInstanceType, runIn.InstanceType) + assert.Equal(t, "111122223333", runIn.AccountID) + assert.Equal(t, "eni-aaa111", runIn.ENIID) + assert.Equal(t, "10.0.1.42", runIn.ENIIP) + + assert.Empty(t, vpc.deleteCalls, "no rollback on success") +} + +func TestLaunchK3sServerVM_HonorsCustomInstanceType(t *testing.T) { + vpc, inst, ami := &fakeK3sVPC{}, &fakeK3sInst{}, &fakeK3sAMI{} + in := validK3sInput() + in.InstanceType = "t3.large" + + _, err := LaunchK3sServerVM(vpc, inst, ami, in) + require.NoError(t, err) + require.Len(t, inst.launchCalls, 1) + assert.Equal(t, "t3.large", inst.launchCalls[0].InstanceType) +} + +func TestLaunchK3sServerVM_UserDataContainsAllArtifacts(t *testing.T) { + vpc, inst, ami := &fakeK3sVPC{}, &fakeK3sInst{}, &fakeK3sAMI{} + + _, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.NoError(t, err) + require.Len(t, inst.launchCalls, 1) + + udata := inst.launchCalls[0].UserData + + assert.True(t, strings.HasPrefix(udata, "#cloud-config\n")) + assert.Contains(t, udata, "write_files:") + + // Static resolver via bootcmd (NOT write_files — /etc/resolv.conf is a + // dangling symlink on the AMI; write_files would follow it, fail, and abort + // the whole block). Without it containerd image pulls cannot resolve. + assert.Contains(t, udata, "bootcmd:") + assert.Contains(t, udata, "rm -f "+k3sResolvConfPath) + assert.Contains(t, udata, "nameserver 1.1.1.1") + // Resolver must NOT be a write_files entry (that path is what broke seeding). + assert.NotContains(t, udata, "path: "+k3sResolvConfPath) + + assert.Contains(t, udata, "path: "+k3sFirstBootEnvPath) + // The eks-node-role first-boot selector keys off this to start control-plane + // services; without it the unified AMI boots into no role. + assert.Contains(t, udata, "SPINIFEX_K3S_ROLE=server") + assert.Contains(t, udata, "EKS_GATEWAY_URL=https://10.15.8.1:9999") + assert.Contains(t, udata, "EKS_GATEWAY_CA="+k3sGatewayCAPath) + assert.Contains(t, udata, "EKS_ACCESS_KEY=AKIAEXAMPLE") + assert.Contains(t, udata, "EKS_SECRET_KEY=s3cr3t-key") + assert.Contains(t, udata, "EKS_REGION=us-east-1") + assert.Contains(t, udata, "EKS_ACCOUNT_ID=111122223333") + assert.Contains(t, udata, "EKS_CLUSTER_NAME=alpha") + assert.Contains(t, udata, "EKS_NLB_ENDPOINT=https://eks-alpha-lb-001.us-east-1.elb.spinifex.local:443") + assert.Contains(t, udata, "EKS_OIDC_ISSUER=https://oidc.spinifex.local/clusters/111122223333/alpha") + + assert.Contains(t, udata, "path: "+k3sOIDCSigningKeyPath) + assert.Contains(t, udata, "permissions: '0600'") + assert.Contains(t, udata, "-----BEGIN PRIVATE KEY-----") + assert.Contains(t, udata, "FAKEKEY") + + assert.Contains(t, udata, "path: "+k3sOIDCPublicKeyPath) + assert.Contains(t, udata, "-----BEGIN PUBLIC KEY-----") + assert.Contains(t, udata, "FAKEPUB") + + assert.Contains(t, udata, "path: "+k3sConfigPath) + // The control plane is tainted so user workloads never schedule on it (EKS + // parity, and it keeps image pulls off the etcd disk). + assert.Contains(t, udata, "node-taint:") + assert.Contains(t, udata, " - CriticalAddonsOnly=true:NoExecute") + // Parity default (BuiltinIngress=false): K3s' bundled traefik + servicelb are + // disabled; Service type=LoadBalancer / Ingress are the AWS LB Controller's job. + // With built-in ingress off there is nothing to defer, so EKS_DEFER_TRAEFIK=0. + assert.Contains(t, udata, "disable:") + assert.Contains(t, udata, " - traefik") + assert.Contains(t, udata, " - servicelb") + assert.Contains(t, udata, "EKS_DEFER_TRAEFIK=0") + assert.Contains(t, udata, "tls-san:") + assert.Contains(t, udata, " - eks-alpha-lb-001.us-east-1.elb.spinifex.local") + // service-account-key-file must point at the PUBLIC key; the signing key + // (private) is a separate file — pointing key-file at the private key + // crash-loops kube-apiserver. + assert.Contains(t, udata, "service-account-key-file="+k3sOIDCPublicKeyPath) + assert.Contains(t, udata, "service-account-signing-key-file="+k3sOIDCSigningKeyPath) + assert.NotContains(t, udata, "service-account-key-file="+k3sOIDCSigningKeyPath) + assert.Contains(t, udata, "service-account-issuer=https://oidc.spinifex.local/clusters/111122223333/alpha") + assert.Contains(t, udata, "api-audiences=sts.amazonaws.com") + // IAM bearer-token auth: the generated config overrides the AMI skel, so the + // token-webhook arg MUST be emitted here or `aws eks get-token` 401s with the + // webhook never invoked. + assert.Contains(t, udata, "authentication-token-webhook-config-file="+k3sTokenWebhookKubeconfigPath) + + assert.Contains(t, udata, "path: "+k3sGatewayCAPath) + assert.Contains(t, udata, "-----BEGIN CERTIFICATE-----") + assert.Contains(t, udata, "FAKECA") + + // IMDS on-link route delivered out-of-band (Alpine's eni renderer can't emit + // the gateway-less route in network-config). It rides this payload's own + // write_files/runcmd, enabled via the OpenRC local service. + assert.Contains(t, udata, "path: /etc/local.d/imds-onlink-route.start") + assert.Contains(t, udata, "ip route show default") + assert.Contains(t, udata, `ip route replace 169.254.169.254/32 dev "$dev" scope link`) + assert.Contains(t, udata, "runcmd:") + assert.Contains(t, udata, "[ rc-update, add, local, default ]") + + // Exactly one write_files / runcmd key — a second would collide and silently + // drop a block under yaml.safe_load (last key wins). + assert.Equal(t, 1, strings.Count(udata, "\nwrite_files:")) + assert.Equal(t, 1, strings.Count(udata, "\nruncmd:")) +} + +func TestLaunchK3sServerVM_BuiltinIngressOptInDefersTraefik(t *testing.T) { + vpc, inst, ami := &fakeK3sVPC{}, &fakeK3sInst{}, &fakeK3sAMI{} + + in := validK3sInput() + in.BuiltinIngress = true + _, err := LaunchK3sServerVM(vpc, inst, ami, in) + require.NoError(t, err) + require.Len(t, inst.launchCalls, 1) + + udata := inst.launchCalls[0].UserData + // Opted in: traefik stays ENABLED in config so k3s writes traefik.yaml, but + // it is deferred — k3s.initd stages a .skip marker and the state-reporter + // removes it once the apiserver is stable, gated by EKS_DEFER_TRAEFIK=1. + // Disabling traefik would leave no manifest to un-skip, so the config must + // carry no disable block at all. servicelb is lazy and likewise enabled. + assert.NotContains(t, udata, "disable:") + assert.NotContains(t, udata, " - traefik") + assert.NotContains(t, udata, " - servicelb") + assert.Contains(t, udata, "EKS_DEFER_TRAEFIK=1") +} + +func TestLaunchK3sServerVM_UsesEmbeddedEtcd(t *testing.T) { + vpc, inst, ami := &fakeK3sVPC{}, &fakeK3sInst{}, &fakeK3sAMI{} + + _, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.NoError(t, err) + require.Len(t, inst.launchCalls, 1) + + udata := inst.launchCalls[0].UserData + // cluster-init selects the embedded etcd datastore (not SQLite/kine); + // etcd-expose-metrics surfaces wal_fsync/backend_commit on 127.0.0.1:2381. + assert.Contains(t, udata, "cluster-init: true") + assert.Contains(t, udata, "etcd-expose-metrics: true") +} + +func TestLaunchK3sServerVM_RunInstancesEmptyReservationRollsBack(t *testing.T) { + vpc, ami := &fakeK3sVPC{}, &fakeK3sAMI{} + inst := &fakeK3sInst{launchOut: &sysinstance.SystemInstanceOutput{}} + + _, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.Error(t, err) + assert.Contains(t, err.Error(), "LaunchSystemInstance returned no instance") + require.Len(t, vpc.deleteCalls, 1, "must roll back ENI when reservation empty") +} + +func TestLaunchK3sServerVM_AMIFilterShape(t *testing.T) { + vpc, inst, ami := &fakeK3sVPC{}, &fakeK3sInst{}, &fakeK3sAMI{} + + _, err := LaunchK3sServerVM(vpc, inst, ami, validK3sInput()) + require.NoError(t, err) + require.Len(t, ami.describeCalls, 1) + filters := ami.describeCalls[0].Filters + require.Len(t, filters, 1) + assert.Equal(t, "tag:"+tags.ManagedByKey, aws.StringValue(filters[0].Name)) + assert.Equal(t, []string{tags.ManagedByEKS}, aws.StringValueSlice(filters[0].Values)) +} + +func TestTerminateK3sServerVM_BothNoopOnEmpty(t *testing.T) { + vpc, inst := &fakeK3sVPC{}, &fakeK3sInst{} + + require.NoError(t, TerminateK3sServerVM(vpc, inst, "111122223333", "", "")) + assert.Empty(t, inst.terminateCalls) + assert.Empty(t, vpc.deleteCalls) +} + +func TestTerminateK3sServerVM_TerminatesInstanceAndDeletesENI(t *testing.T) { + vpc, inst := &fakeK3sVPC{}, &fakeK3sInst{} + + require.NoError(t, TerminateK3sServerVM(vpc, inst, "111122223333", "i-aaa111", "eni-aaa111")) + require.Len(t, inst.terminateCalls, 1) + assert.Equal(t, "i-aaa111", inst.terminateCalls[0]) + require.Len(t, vpc.deleteCalls, 1) + assert.Equal(t, "eni-aaa111", aws.StringValue(vpc.deleteCalls[0].NetworkInterfaceId)) +} + +func TestTerminateK3sServerVM_InstanceErrorReturnedENIStillDeleted(t *testing.T) { + vpc := &fakeK3sVPC{} + inst := &fakeK3sInst{terminateErr: errors.New("IncorrectInstanceState")} + + err := TerminateK3sServerVM(vpc, inst, "111122223333", "i-aaa111", "eni-aaa111") + require.Error(t, err) + assert.Contains(t, err.Error(), "terminate instance i-aaa111") + require.Len(t, vpc.deleteCalls, 1, "ENI delete should still run after instance terminate fails") +} + +func TestTerminateK3sServerVM_ENINotFoundIsIdempotent(t *testing.T) { + // A retried delete-cluster after the instance-terminate cascade already + // removed the ENI: DeleteNetworkInterface returns NotFound. This must be + // treated as success so the SG + KV sweep downstream is not blocked. + vpc := &fakeK3sVPC{deleteErr: errors.New(awserrors.ErrorInvalidNetworkInterfaceIDNotFound)} + inst := &fakeK3sInst{} + + err := TerminateK3sServerVM(vpc, inst, "111122223333", "i-aaa111", "eni-aaa111") + require.NoError(t, err, "ENI already gone must be idempotent success, not a blocking error") + require.Len(t, vpc.deleteCalls, 1) +} + +func TestTerminateK3sServerVM_ENIInUseIsRetryable(t *testing.T) { + // The VM is still terminating async and holds the ENI. InUse must surface + // as an error so the cluster stays DELETING and the reconciler retries. + vpc := &fakeK3sVPC{deleteErr: errors.New(awserrors.ErrorInvalidNetworkInterfaceInUse)} + inst := &fakeK3sInst{} + + err := TerminateK3sServerVM(vpc, inst, "111122223333", "i-aaa111", "eni-aaa111") + require.Error(t, err, "ENI still in use must be retryable, not swallowed") + assert.Contains(t, err.Error(), "delete ENI eni-aaa111") +} + +func TestTerminateK3sServerVM_InstanceAlreadyGoneIsIdempotent(t *testing.T) { + // On a reconciler retry the VM already drained, so TerminateSystemInstance + // returns ErrSystemInstanceNotFound. This must not block teardown; the ENI + // delete still runs. + vpc := &fakeK3sVPC{} + inst := &fakeK3sInst{terminateErr: fmt.Errorf("%w: i-aaa111", sysinstance.ErrSystemInstanceNotFound)} + + err := TerminateK3sServerVM(vpc, inst, "111122223333", "i-aaa111", "eni-aaa111") + require.NoError(t, err, "instance already gone must be idempotent success") + require.Len(t, vpc.deleteCalls, 1, "ENI delete still runs after a tolerated instance-gone") +} + +func assertEC2TaggedAsEKSControlPlane(t *testing.T, ec2Tags []*ec2.Tag, clusterName string) { + t.Helper() + got := map[string]string{} + for _, tg := range ec2Tags { + if tg == nil || tg.Key == nil || tg.Value == nil { + continue + } + got[*tg.Key] = *tg.Value + } + assert.Equal(t, tags.ManagedByEKS, got[tags.ManagedByKey]) + assert.Equal(t, clusterName, got[clusterEKSClusterTagKey]) + assert.Equal(t, clusterEKSRoleControlPlane, got[clusterEKSRoleTagKey]) +} diff --git a/spinifex/handlers/eks/list_clusters_test.go b/spinifex/handlers/eks/list_clusters_test.go new file mode 100644 index 000000000..a2ff78ea1 --- /dev/null +++ b/spinifex/handlers/eks/list_clusters_test.go @@ -0,0 +1,82 @@ +package handlers_eks + +import ( + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestEKSServiceImpl_ListClustersPagination walks the NextToken/MaxResults +// paging path end-to-end: each page honours MaxResults, hands back a NextToken +// pointing at the next name, and the final page carries no token. +func TestEKSServiceImpl_ListClustersPagination(t *testing.T) { + f := newEKSServiceFixture(t) + for _, n := range []string{"c01", "c02", "c03", "c04", "c05"} { + require.NoError(t, PutClusterMeta(f.kv, sampleClusterMeta(n))) + } + + p1, err := f.svc.ListClusters(&eks.ListClustersInput{MaxResults: aws.Int64(2)}, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{"c01", "c02"}, aws.StringValueSlice(p1.Clusters)) + require.NotNil(t, p1.NextToken) + assert.Equal(t, "c03", aws.StringValue(p1.NextToken)) + + p2, err := f.svc.ListClusters(&eks.ListClustersInput{MaxResults: aws.Int64(2), NextToken: p1.NextToken}, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{"c03", "c04"}, aws.StringValueSlice(p2.Clusters)) + require.NotNil(t, p2.NextToken) + assert.Equal(t, "c05", aws.StringValue(p2.NextToken)) + + p3, err := f.svc.ListClusters(&eks.ListClustersInput{MaxResults: aws.Int64(2), NextToken: p2.NextToken}, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{"c05"}, aws.StringValueSlice(p3.Clusters)) + assert.Nil(t, p3.NextToken, "final page must not advertise a NextToken") +} + +// TestEKSServiceImpl_ListClustersMaxResultsClamped confirms an out-of-range +// MaxResults (zero, negative, or above the 100 ceiling) clamps to a single full +// page rather than truncating or erroring. +func TestEKSServiceImpl_ListClustersMaxResultsClamped(t *testing.T) { + f := newEKSServiceFixture(t) + want := []string{"c01", "c02", "c03"} + for _, n := range want { + require.NoError(t, PutClusterMeta(f.kv, sampleClusterMeta(n))) + } + + for _, mr := range []int64{0, -5, 250} { + out, err := f.svc.ListClusters(&eks.ListClustersInput{MaxResults: aws.Int64(mr)}, testAccountID) + require.NoError(t, err) + assert.Equal(t, want, aws.StringValueSlice(out.Clusters), "MaxResults=%d must clamp to one full page", mr) + assert.Nil(t, out.NextToken, "MaxResults=%d single page must not advertise a NextToken", mr) + } +} + +// TestEKSReadUnavailableClassification pins the post-restart behaviour: a +// transient JetStream error (no leader yet) surfaces as a retryable +// ServiceUnavailable, while a genuine fault stays wrapped (the daemon maps it +// to InternalError). Regression guard for list-clusters returning a misleading +// 500 during the KV warmup window. +func TestEKSReadUnavailableClassification(t *testing.T) { + transient := []error{ + nats.ErrNoResponders, + nats.ErrTimeout, + nats.ErrNoStreamResponse, + nats.ErrConnectionClosed, + fmt.Errorf("kv keys: %w", nats.ErrNoResponders), // wrapped, as ListClusters wraps it + } + for _, e := range transient { + assert.True(t, natsTransient(e), "%v must be classified transient", e) + assert.EqualError(t, eksReadUnavailableOr(e, "op"), awserrors.ErrorServiceUnavailable) + } + + genuine := errors.New("corrupt cluster meta") + assert.False(t, natsTransient(genuine)) + assert.ErrorContains(t, eksReadUnavailableOr(genuine, "list cluster names"), "list cluster names: corrupt cluster meta") +} diff --git a/spinifex/handlers/eks/nats_bootstrap.go b/spinifex/handlers/eks/nats_bootstrap.go new file mode 100644 index 000000000..fd7a64dbe --- /dev/null +++ b/spinifex/handlers/eks/nats_bootstrap.go @@ -0,0 +1,386 @@ +package handlers_eks + +import ( + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "log/slog" + "sync" + "time" + + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/nats-io/nats.go" +) + +// errBootstrapPermanent marks a bootstrap persist failure that no amount of +// retrying will fix — a malformed envelope, a non-base64 CA, or a JWKS whose +// kid does not match the controller-generated keypair. Transient failures +// (KV write blips, JWKS-not-yet-propagated) are NOT wrapped with this, so +// persistWithRetry keeps retrying them. A permanent error fails the cluster. +var errBootstrapPermanent = errors.New("bootstrap permanent error") + +const ( + // bootstrapPersistAttempts bounds inline retries of a single subject's + // persist op on transient errors. A core-NATS publication is delivered + // once, so a transient KV blip must be ridden out in-handler rather than + // by tearing down the subscription and waiting for a re-publish that + // never comes. + bootstrapPersistAttempts = 3 + bootstrapPersistBackoff = 200 * time.Millisecond +) + +// persistWithRetry runs op, retrying on transient errors up to +// bootstrapPersistAttempts. A nil result or an errBootstrapPermanent result +// returns immediately (no retry). +func persistWithRetry(op func() error) error { + var err error + for attempt := 1; attempt <= bootstrapPersistAttempts; attempt++ { + err = op() + if err == nil || errors.Is(err, errBootstrapPermanent) { + return err + } + if attempt < bootstrapPersistAttempts { + time.Sleep(bootstrapPersistBackoff) + } + } + return err +} + +// Bootstrap subject kinds. Full subject is +// "eks.bus.{accountID}.{clusterName}.{kind}" — see BootstrapSubject. +const ( + BootstrapSubjectToken = "k3s-bootstrap-token" //nolint:gosec // subject suffix, not a credential + BootstrapSubjectKubeconfig = "k3s-admin-kubeconfig" + BootstrapSubjectJWKS = "k3s-oidc-jwks" + BootstrapSubjectCA = "k3s-ca" +) + +// BootstrapSubject returns the per-cluster bootstrap subject for one kind. +func BootstrapSubject(accountID, clusterName, kind string) string { + return fmt.Sprintf("eks.bus.%s.%s.%s", accountID, clusterName, kind) +} + +// BootstrapEnvelope is the JSON wire shape every k3s-first-boot.sh +// publication uses. Each subject populates the field matching its kind; the +// rest stay empty. Versioned implicitly by field shape — adding new optional +// fields is backwards-compatible. +type BootstrapEnvelope struct { + // Token is the K3s bootstrap node-token for the cluster (k3s-bootstrap-token). + Token string `json:"token,omitempty"` + // AdminKubeconfig is the cluster-admin kubeconfig YAML + // (k3s-admin-kubeconfig). + AdminKubeconfig string `json:"adminKubeconfig,omitempty"` + // JWKS is the K3s-published RFC 7517 JWKS document. The bootstrap + // subscriber cross-checks it against the controller-generated keypair + // (k3s-oidc-jwks). + JWKS string `json:"jwks,omitempty"` + // CertificateAuthority is the base64-encoded K3s server CA PEM + // (k3s-ca). Written onto meta.CertificateAuthorityB64. + CertificateAuthority string `json:"certificateAuthority,omitempty"` +} + +// NATSBootstrap collects the four single-shot bootstrap publications from a +// K3s server VM, validates the OIDC JWKS against what the controller wrote +// pre-VM, persists each artifact into the per-cluster KV bucket, and writes +// the CA onto meta.CertificateAuthorityB64. The reconciler observes +// completion via the four KV keys + meta.CertificateAuthorityB64. +type NATSBootstrap struct { + nc *nats.Conn + kv nats.KeyValue + masterKey []byte + accountID string + clusterName string +} + +// NewNATSBootstrap validates inputs and returns a ready subscriber. Run must +// be called to actually subscribe; constructor is side-effect-free. +func NewNATSBootstrap(nc *nats.Conn, kv nats.KeyValue, masterKey []byte, accountID, clusterName string) (*NATSBootstrap, error) { + if nc == nil { + return nil, errors.New("eks: NewNATSBootstrap nil nats conn") + } + if kv == nil { + return nil, errors.New("eks: NewNATSBootstrap nil kv") + } + if len(masterKey) == 0 { + return nil, errors.New("eks: NewNATSBootstrap empty master key") + } + if accountID == "" { + return nil, errors.New("eks: NewNATSBootstrap empty accountID") + } + if clusterName == "" { + return nil, errors.New("eks: NewNATSBootstrap empty clusterName") + } + return &NATSBootstrap{ + nc: nc, + kv: kv, + masterKey: masterKey, + accountID: accountID, + clusterName: clusterName, + }, nil +} + +// BootstrapPendingKinds reports which bootstrap subjects still lack their +// persisted artifact for a cluster, so a daemon-restart resume re-subscribes +// only to what is missing. The K3s VM publishes each subject once over core +// NATS and never republishes, so re-waiting on an already-arrived subject +// would deadlock. +// +// JWKS is tracked by its verified-marker (OIDCJWKSVerifiedKey), written by +// persistJWKS only after the cross-check passes — NOT by OIDCJWKSKey, which the +// controller pre-seeds at create time. Until the marker exists the cross-check +// has not positively passed, so resume re-subscribes to JWKS like any other +// pending subject. (If the VM published JWKS during the restart window the +// re-subscribe waits for a republish that never comes and the cluster fails on +// the create timeout — the same one-shot limitation every bootstrap subject +// shares, now applied uniformly so ACTIVE always implies a verified key.) +func BootstrapPendingKinds(kv nats.KeyValue, clusterName string, meta *ClusterMeta) []string { + var pending []string + if _, err := kv.Get(NodeTokenKey(clusterName)); err != nil { + pending = append(pending, BootstrapSubjectToken) + } + if _, err := kv.Get(AdminKubeconfigKey(clusterName)); err != nil { + pending = append(pending, BootstrapSubjectKubeconfig) + } + if _, err := kv.Get(OIDCJWKSVerifiedKey(clusterName)); err != nil { + pending = append(pending, BootstrapSubjectJWKS) + } + if meta == nil || meta.CertificateAuthorityB64 == "" { + pending = append(pending, BootstrapSubjectCA) + } + return pending +} + +type bootstrapSubjectHandler struct { + subject string + kind string + handler func([]byte) error +} + +// subjectHandlers returns the subject/handler entry for each requested kind. +// An unknown kind is an error — callers pass kinds from BootstrapSubject* +// constants. A nil/empty kinds slice means all four. +func (b *NATSBootstrap) subjectHandlers(kinds []string) ([]bootstrapSubjectHandler, error) { + all := map[string]func([]byte) error{ + BootstrapSubjectToken: b.persistToken, + BootstrapSubjectKubeconfig: b.persistKubeconfig, + BootstrapSubjectJWKS: b.persistJWKS, + BootstrapSubjectCA: b.persistCA, + } + if len(kinds) == 0 { + kinds = []string{BootstrapSubjectToken, BootstrapSubjectKubeconfig, BootstrapSubjectJWKS, BootstrapSubjectCA} + } + out := make([]bootstrapSubjectHandler, 0, len(kinds)) + for _, kind := range kinds { + handler, ok := all[kind] + if !ok { + return nil, fmt.Errorf("eks: unknown bootstrap kind %q", kind) + } + out = append(out, bootstrapSubjectHandler{ + subject: BootstrapSubject(b.accountID, b.clusterName, kind), + kind: kind, + handler: handler, + }) + } + return out, nil +} + +// Run subscribes to all four bootstrap subjects and returns when all four have +// arrived, ctx is cancelled, or a handler errors. See RunForKinds. +func (b *NATSBootstrap) Run(ctx context.Context) error { + return b.RunForKinds(ctx, nil) +} + +// RunForKinds subscribes only to the requested bootstrap subjects (nil = all +// four) and returns when: +// - every requested subject has arrived and persisted successfully (nil), +// - ctx is cancelled (ctx.Err()), +// - one of the persistence handlers returns an error (first error). +// +// The subset form drives daemon-restart recovery: a cluster that already has +// some artifacts in KV re-subscribes only to the missing ones, since the K3s +// VM published each subject once over core NATS and will not republish. +// +// Each subject is consumed at most once; subsequent publications on the same +// subject are dropped. Subscriptions are always cleaned up before return. +func (b *NATSBootstrap) RunForKinds(ctx context.Context, kinds []string) error { + subs, err := b.subjectHandlers(kinds) + if err != nil { + return err + } + want := len(subs) + + errCh := make(chan error, want) + doneCh := make(chan struct{}, 1) + var ( + mu sync.Mutex + done = make(map[string]struct{}, want) + ) + + var subscriptions []*nats.Subscription + defer func() { + for _, sub := range subscriptions { + _ = sub.Unsubscribe() + } + }() + + for _, s := range subs { + sub, err := b.nc.Subscribe(s.subject, func(m *nats.Msg) { + mu.Lock() + if _, ok := done[s.kind]; ok { + mu.Unlock() + return + } + mu.Unlock() + if err := persistWithRetry(func() error { return s.handler(m.Data) }); err != nil { + errCh <- fmt.Errorf("persist %s: %w", s.kind, err) + return + } + mu.Lock() + done[s.kind] = struct{}{} + full := len(done) == want + mu.Unlock() + if full { + select { + case doneCh <- struct{}{}: + default: + } + } + }) + if err != nil { + return fmt.Errorf("subscribe %s: %w", s.subject, err) + } + subscriptions = append(subscriptions, sub) + } + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errCh: + return err + case <-doneCh: + slog.Info("NATSBootstrap: all requested bootstrap messages received", + "accountID", b.accountID, "cluster", b.clusterName, "kinds", want) + return nil + } + } +} + +func (b *NATSBootstrap) persistToken(data []byte) error { + env, err := unmarshalBootstrapEnvelope(data) + if err != nil { + return err + } + if env.Token == "" { + return fmt.Errorf("%w: token envelope empty", errBootstrapPermanent) + } + ct, err := handlers_iam.EncryptSecret(env.Token, b.masterKey) + if err != nil { + return fmt.Errorf("encrypt token: %w", err) + } + if _, err := b.kv.Put(NodeTokenKey(b.clusterName), []byte(ct)); err != nil { + return fmt.Errorf("kv put %s: %w", NodeTokenKey(b.clusterName), err) + } + return nil +} + +func (b *NATSBootstrap) persistKubeconfig(data []byte) error { + env, err := unmarshalBootstrapEnvelope(data) + if err != nil { + return err + } + if env.AdminKubeconfig == "" { + return fmt.Errorf("%w: adminKubeconfig envelope empty", errBootstrapPermanent) + } + ct, err := handlers_iam.EncryptSecret(env.AdminKubeconfig, b.masterKey) + if err != nil { + return fmt.Errorf("encrypt kubeconfig: %w", err) + } + if _, err := b.kv.Put(AdminKubeconfigKey(b.clusterName), []byte(ct)); err != nil { + return fmt.Errorf("kv put %s: %w", AdminKubeconfigKey(b.clusterName), err) + } + return nil +} + +func (b *NATSBootstrap) persistJWKS(data []byte) error { + env, err := unmarshalBootstrapEnvelope(data) + if err != nil { + return err + } + if env.JWKS == "" { + return fmt.Errorf("%w: jwks envelope empty", errBootstrapPermanent) + } + existing, err := b.kv.Get(OIDCJWKSKey(b.clusterName)) + if err != nil { + return fmt.Errorf("kv get %s: %w", OIDCJWKSKey(b.clusterName), err) + } + if err := assertJWKSMatch(existing.Value(), []byte(env.JWKS)); err != nil { + return err + } + // Record the positive cross-check as a distinct marker. The reconciler + // gates ACTIVE on this key — never on OIDCJWKSKey, which the controller + // pre-seeds at create time — so a cluster cannot reach ACTIVE on an + // unverified or mismatched signing key. + if _, err := b.kv.Put(OIDCJWKSVerifiedKey(b.clusterName), []byte("verified")); err != nil { + return fmt.Errorf("kv put %s: %w", OIDCJWKSVerifiedKey(b.clusterName), err) + } + return nil +} + +func (b *NATSBootstrap) persistCA(data []byte) error { + env, err := unmarshalBootstrapEnvelope(data) + if err != nil { + return err + } + if env.CertificateAuthority == "" { + return fmt.Errorf("%w: CA envelope empty", errBootstrapPermanent) + } + if _, err := base64.StdEncoding.DecodeString(env.CertificateAuthority); err != nil { + return fmt.Errorf("%w: CA not base64: %v", errBootstrapPermanent, err) + } + return SetClusterCertificateAuthority(b.kv, b.clusterName, env.CertificateAuthority) +} + +func unmarshalBootstrapEnvelope(data []byte) (BootstrapEnvelope, error) { + var env BootstrapEnvelope + if err := json.Unmarshal(data, &env); err != nil { + return env, fmt.Errorf("%w: unmarshal envelope: %v", errBootstrapPermanent, err) + } + return env, nil +} + +// assertJWKSMatch verifies that every key in the incoming JWKS matches a +// controller-generated key by both key id (kid) and key type (kty). A kid +// mismatch means the VM generated its own signing key instead of consuming our +// PEM; a kty mismatch (e.g. an RSA key carrying our EC key's kid) means the +// published key cannot be the one the controller distributed even though the +// kid collides. Either rejects the publication. +func assertJWKSMatch(existing, incoming []byte) error { + var ex, in oidcJWKS + if err := json.Unmarshal(existing, &ex); err != nil { + return fmt.Errorf("unmarshal existing JWKS: %w", err) + } + if err := json.Unmarshal(incoming, &in); err != nil { + return fmt.Errorf("%w: unmarshal incoming JWKS: %v", errBootstrapPermanent, err) + } + if len(in.Keys) == 0 { + return fmt.Errorf("%w: incoming JWKS has no keys", errBootstrapPermanent) + } + have := make(map[string]oidcJWK, len(ex.Keys)) + for _, k := range ex.Keys { + have[k.Kid] = k + } + for _, k := range in.Keys { + exKey, ok := have[k.Kid] + if !ok { + return fmt.Errorf("%w: bootstrap JWKS kid %q does not match generated keypair", errBootstrapPermanent, k.Kid) + } + if k.Kty != exKey.Kty { + return fmt.Errorf("%w: bootstrap JWKS kid %q key type %q does not match generated %q", + errBootstrapPermanent, k.Kid, k.Kty, exKey.Kty) + } + } + return nil +} diff --git a/spinifex/handlers/eks/nats_bootstrap_test.go b/spinifex/handlers/eks/nats_bootstrap_test.go new file mode 100644 index 000000000..51518e3bc --- /dev/null +++ b/spinifex/handlers/eks/nats_bootstrap_test.go @@ -0,0 +1,410 @@ +package handlers_eks + +import ( + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "strings" + "testing" + "time" + + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + bootstrapTestCluster = "alpha" +) + +var bootstrapTestMasterKey = []byte("0123456789abcdef0123456789abcdef") + +// natsBootstrapHarness wires an embedded JetStream NATS server, a per-account +// KV bucket, a published controller JWKS (so persistJWKS has something to +// cross-check against), and a NATSBootstrap subscriber. +type natsBootstrapHarness struct { + t *testing.T + nc *nats.Conn + kv nats.KeyValue + + subscriber *NATSBootstrap +} + +func newBootstrapHarness(t *testing.T) *natsBootstrapHarness { + t.Helper() + _, nc, js := testutil.StartTestJetStream(t) + kv, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + + require.NoError(t, PutClusterMeta(kv, sampleClusterMeta(bootstrapTestCluster))) + + // Lay down the controller-generated JWKS so persistJWKS has something + // to cross-check against. + _, _, err = GenerateClusterOIDCKeypair(kv, bootstrapTestCluster, bootstrapTestMasterKey) + require.NoError(t, err) + + sub, err := NewNATSBootstrap(nc, kv, bootstrapTestMasterKey, testAccountID, bootstrapTestCluster) + require.NoError(t, err) + + return &natsBootstrapHarness{t: t, nc: nc, kv: kv, subscriber: sub} +} + +func (h *natsBootstrapHarness) publish(kind string, env BootstrapEnvelope) { + h.t.Helper() + data, err := json.Marshal(env) + require.NoError(h.t, err) + subject := BootstrapSubject(testAccountID, bootstrapTestCluster, kind) + require.NoError(h.t, h.nc.Publish(subject, data)) + require.NoError(h.t, h.nc.Flush()) +} + +func (h *natsBootstrapHarness) loadControllerJWKS() []byte { + h.t.Helper() + entry, err := h.kv.Get(OIDCJWKSKey(bootstrapTestCluster)) + require.NoError(h.t, err) + return entry.Value() +} + +// waitSubsBound blocks until the subscriber goroutine has bound `want` new +// subscriptions on the connection (base = NumSubscriptions captured before the +// goroutine started). Polling the real subscription count replaces a fixed +// sleep that raced the goroutine and went flaky under CI load. +func (h *natsBootstrapHarness) waitSubsBound(base, want int) { + h.t.Helper() + require.Eventually(h.t, func() bool { + return h.nc.NumSubscriptions() >= base+want + }, 2*time.Second, 2*time.Millisecond, "bootstrap subscriptions never bound") +} + +func TestNewNATSBootstrap_RejectsBadInputs(t *testing.T) { + _, nc, js := testutil.StartTestJetStream(t) + kv, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + + _, err = NewNATSBootstrap(nil, kv, bootstrapTestMasterKey, testAccountID, "alpha") + require.Error(t, err) + _, err = NewNATSBootstrap(nc, nil, bootstrapTestMasterKey, testAccountID, "alpha") + require.Error(t, err) + _, err = NewNATSBootstrap(nc, kv, nil, testAccountID, "alpha") + require.Error(t, err) + _, err = NewNATSBootstrap(nc, kv, bootstrapTestMasterKey, "", "alpha") + require.Error(t, err) + _, err = NewNATSBootstrap(nc, kv, bootstrapTestMasterKey, testAccountID, "") + require.Error(t, err) +} + +func TestNATSBootstrap_HappyPath(t *testing.T) { + h := newBootstrapHarness(t) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + jwks := string(h.loadControllerJWKS()) + caB64 := base64.StdEncoding.EncodeToString([]byte("-----BEGIN CERTIFICATE-----\nfakeCA\n-----END CERTIFICATE-----\n")) + + base := h.nc.NumSubscriptions() + runErr := make(chan error, 1) + go func() { runErr <- h.subscriber.Run(ctx) }() + h.waitSubsBound(base, 4) + + h.publish(BootstrapSubjectToken, BootstrapEnvelope{Token: "k3s::server-node-token::v1"}) + h.publish(BootstrapSubjectKubeconfig, BootstrapEnvelope{AdminKubeconfig: "apiVersion: v1\nkind: Config\n"}) + h.publish(BootstrapSubjectJWKS, BootstrapEnvelope{JWKS: jwks}) + h.publish(BootstrapSubjectCA, BootstrapEnvelope{CertificateAuthority: caB64}) + + select { + case err := <-runErr: + require.NoError(t, err) + case <-time.After(3 * time.Second): + t.Fatal("Run did not return after all four publications") + } + + tokEntry, err := h.kv.Get(NodeTokenKey(bootstrapTestCluster)) + require.NoError(t, err) + dec, err := handlers_iam.DecryptSecret(string(tokEntry.Value()), bootstrapTestMasterKey) + require.NoError(t, err) + assert.Equal(t, "k3s::server-node-token::v1", dec) + + kcEntry, err := h.kv.Get(AdminKubeconfigKey(bootstrapTestCluster)) + require.NoError(t, err) + dec, err = handlers_iam.DecryptSecret(string(kcEntry.Value()), bootstrapTestMasterKey) + require.NoError(t, err) + assert.Equal(t, "apiVersion: v1\nkind: Config\n", dec) + + meta, err := GetClusterMeta(h.kv, bootstrapTestCluster) + require.NoError(t, err) + assert.Equal(t, caB64, meta.CertificateAuthorityB64) + + // The passing JWKS cross-check must record the verified-marker the + // reconciler gates ACTIVE on. + _, err = h.kv.Get(OIDCJWKSVerifiedKey(bootstrapTestCluster)) + require.NoError(t, err, "persistJWKS must write the verified-marker on a passing cross-check") + + // Run's deferred cleanup must tear down every subscription it bound, so the + // connection is back to its pre-Run count once Run returns. + assert.Equal(t, base, h.nc.NumSubscriptions(), "Run must unsubscribe all bootstrap subs on return") +} + +func TestNATSBootstrap_ContextCancelReturnsErr(t *testing.T) { + h := newBootstrapHarness(t) + ctx, cancel := context.WithCancel(context.Background()) + + base := h.nc.NumSubscriptions() + runErr := make(chan error, 1) + go func() { runErr <- h.subscriber.Run(ctx) }() + h.waitSubsBound(base, 4) + + cancel() + + select { + case err := <-runErr: + require.ErrorIs(t, err, context.Canceled) + case <-time.After(2 * time.Second): + t.Fatal("Run did not return after ctx cancel") + } +} + +func TestNATSBootstrap_JWKSMismatchReturnsErr(t *testing.T) { + h := newBootstrapHarness(t) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + base := h.nc.NumSubscriptions() + runErr := make(chan error, 1) + go func() { runErr <- h.subscriber.Run(ctx) }() + h.waitSubsBound(base, 4) + + // kid mismatch — controller generated a real kid, we send a bogus one. + badJWKS := `{"keys":[{"kty":"EC","kid":"not-the-controller-kid","crv":"P-256","x":"AAA","y":"BBB"}]}` + h.publish(BootstrapSubjectJWKS, BootstrapEnvelope{JWKS: badJWKS}) + + select { + case err := <-runErr: + require.Error(t, err) + assert.Contains(t, err.Error(), "JWKS kid") + case <-time.After(3 * time.Second): + t.Fatal("Run did not return on JWKS mismatch") + } +} + +func TestAssertJWKSMatch_RejectsKtyMismatch(t *testing.T) { + // Same kid as the controller key, but RSA instead of EC — a published key + // that cannot be the one the controller distributed despite the kid + // collision. Must be rejected. + existing := []byte(`{"keys":[{"kty":"EC","kid":"abc","crv":"P-256","x":"AAA","y":"BBB"}]}`) + incoming := []byte(`{"keys":[{"kty":"RSA","kid":"abc","n":"AAA","e":"AQAB"}]}`) + + err := assertJWKSMatch(existing, incoming) + require.Error(t, err) + require.ErrorIs(t, err, errBootstrapPermanent) + assert.Contains(t, err.Error(), "key type") +} + +func TestAssertJWKSMatch_AcceptsMatchingKidAndKty(t *testing.T) { + jwks := []byte(`{"keys":[{"kty":"EC","kid":"abc","crv":"P-256","x":"AAA","y":"BBB"}]}`) + require.NoError(t, assertJWKSMatch(jwks, jwks)) +} + +func TestNATSBootstrap_EmptyEnvelopeFieldsRejected(t *testing.T) { + h := newBootstrapHarness(t) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + base := h.nc.NumSubscriptions() + runErr := make(chan error, 1) + go func() { runErr <- h.subscriber.Run(ctx) }() + h.waitSubsBound(base, 4) + + h.publish(BootstrapSubjectToken, BootstrapEnvelope{}) + + select { + case err := <-runErr: + require.Error(t, err) + assert.Contains(t, err.Error(), "token envelope empty") + case <-time.After(3 * time.Second): + t.Fatal("Run did not return on empty envelope") + } +} + +func TestNATSBootstrap_OneShotIgnoresSubsequentMessages(t *testing.T) { + h := newBootstrapHarness(t) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + jwks := string(h.loadControllerJWKS()) + caB64 := base64.StdEncoding.EncodeToString([]byte("ca-pem")) + + base := h.nc.NumSubscriptions() + runErr := make(chan error, 1) + go func() { runErr <- h.subscriber.Run(ctx) }() + h.waitSubsBound(base, 4) + + // First, send only token so subsequent token publications are dropped. + h.publish(BootstrapSubjectToken, BootstrapEnvelope{Token: "first-token"}) + // Wait until the first token is persisted before racing in a second, so the + // one-shot drop is what's under test rather than publish ordering. + require.Eventually(t, func() bool { + _, err := h.kv.Get(NodeTokenKey(bootstrapTestCluster)) + return err == nil + }, 2*time.Second, 5*time.Millisecond, "first token never persisted") + // Second token publication has a different value; persistToken should not + // re-encrypt/overwrite because the one-shot map prevents reprocess. + h.publish(BootstrapSubjectToken, BootstrapEnvelope{Token: "second-token"}) + + h.publish(BootstrapSubjectKubeconfig, BootstrapEnvelope{AdminKubeconfig: "kc"}) + h.publish(BootstrapSubjectJWKS, BootstrapEnvelope{JWKS: jwks}) + h.publish(BootstrapSubjectCA, BootstrapEnvelope{CertificateAuthority: caB64}) + + select { + case err := <-runErr: + require.NoError(t, err) + case <-time.After(3 * time.Second): + t.Fatal("Run did not return") + } + + entry, err := h.kv.Get(NodeTokenKey(bootstrapTestCluster)) + require.NoError(t, err) + dec, err := handlers_iam.DecryptSecret(string(entry.Value()), bootstrapTestMasterKey) + require.NoError(t, err) + assert.Equal(t, "first-token", dec, "second token publication must be dropped") +} + +func TestNATSBootstrap_CANotBase64Rejected(t *testing.T) { + h := newBootstrapHarness(t) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + base := h.nc.NumSubscriptions() + runErr := make(chan error, 1) + go func() { runErr <- h.subscriber.Run(ctx) }() + h.waitSubsBound(base, 4) + + h.publish(BootstrapSubjectCA, BootstrapEnvelope{CertificateAuthority: "not!base64@@@"}) + + select { + case err := <-runErr: + require.Error(t, err) + assert.Contains(t, err.Error(), "CA not base64") + case <-time.After(3 * time.Second): + t.Fatal("Run did not return") + } +} + +func TestPersistWithRetry(t *testing.T) { + t.Run("succeeds first try", func(t *testing.T) { + calls := 0 + err := persistWithRetry(func() error { calls++; return nil }) + require.NoError(t, err) + assert.Equal(t, 1, calls) + }) + + t.Run("retries transient then succeeds", func(t *testing.T) { + calls := 0 + err := persistWithRetry(func() error { + calls++ + if calls < 2 { + return errors.New("transient kv blip") + } + return nil + }) + require.NoError(t, err) + assert.Equal(t, 2, calls) + }) + + t.Run("permanent error returns immediately without retry", func(t *testing.T) { + calls := 0 + err := persistWithRetry(func() error { + calls++ + return fmt.Errorf("%w: malformed envelope", errBootstrapPermanent) + }) + require.ErrorIs(t, err, errBootstrapPermanent) + assert.Equal(t, 1, calls, "permanent error must not be retried") + }) + + t.Run("transient error exhausts attempts", func(t *testing.T) { + calls := 0 + err := persistWithRetry(func() error { calls++; return errors.New("always transient") }) + require.Error(t, err) + assert.Equal(t, bootstrapPersistAttempts, calls) + }) +} + +func TestBootstrapPendingKinds(t *testing.T) { + h := newBootstrapHarness(t) + + meta, err := GetClusterMeta(h.kv, bootstrapTestCluster) + require.NoError(t, err) + + // Fresh cluster: all four pending. The JWKS verified-marker is absent even + // though the controller pre-seeded OIDCJWKSKey, so JWKS is still pending. + assert.Equal(t, + []string{BootstrapSubjectToken, BootstrapSubjectKubeconfig, BootstrapSubjectJWKS, BootstrapSubjectCA}, + BootstrapPendingKinds(h.kv, bootstrapTestCluster, meta)) + + // Persist token only -> token drops out of pending. + ct, err := handlers_iam.EncryptSecret("tok", bootstrapTestMasterKey) + require.NoError(t, err) + _, err = h.kv.Put(NodeTokenKey(bootstrapTestCluster), []byte(ct)) + require.NoError(t, err) + assert.Equal(t, + []string{BootstrapSubjectKubeconfig, BootstrapSubjectJWKS, BootstrapSubjectCA}, + BootstrapPendingKinds(h.kv, bootstrapTestCluster, meta)) + + // Persist kubeconfig + CA-on-meta -> only JWKS (unverified) still pending. + _, err = h.kv.Put(AdminKubeconfigKey(bootstrapTestCluster), []byte(ct)) + require.NoError(t, err) + meta.CertificateAuthorityB64 = "ca-b64" + assert.Equal(t, + []string{BootstrapSubjectJWKS}, + BootstrapPendingKinds(h.kv, bootstrapTestCluster, meta)) + + // Write the JWKS verified-marker (persistJWKS does this on a passing + // cross-check) -> nothing pending. + _, err = h.kv.Put(OIDCJWKSVerifiedKey(bootstrapTestCluster), []byte("verified")) + require.NoError(t, err) + assert.Empty(t, BootstrapPendingKinds(h.kv, bootstrapTestCluster, meta)) +} + +func TestNATSBootstrap_RunForKindsWaitsOnlyForSubset(t *testing.T) { + h := newBootstrapHarness(t) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + caB64 := base64.StdEncoding.EncodeToString([]byte("ca-pem")) + + base := h.nc.NumSubscriptions() + runErr := make(chan error, 1) + go func() { runErr <- h.subscriber.RunForKinds(ctx, []string{BootstrapSubjectCA}) }() + h.waitSubsBound(base, 1) + + // Only publish CA; the subset subscriber must complete without token, + // kubeconfig, or JWKS ever arriving (they were consumed pre-restart). + h.publish(BootstrapSubjectCA, BootstrapEnvelope{CertificateAuthority: caB64}) + + select { + case err := <-runErr: + require.NoError(t, err) + case <-time.After(3 * time.Second): + t.Fatal("RunForKinds did not return after CA-only publication") + } + + meta, err := GetClusterMeta(h.kv, bootstrapTestCluster) + require.NoError(t, err) + assert.Equal(t, caB64, meta.CertificateAuthorityB64) +} + +func TestNATSBootstrap_RunForKindsRejectsUnknownKind(t *testing.T) { + h := newBootstrapHarness(t) + err := h.subscriber.RunForKinds(context.Background(), []string{"bogus-kind"}) + require.Error(t, err) + assert.Contains(t, err.Error(), "unknown bootstrap kind") +} + +func TestBootstrapSubject_Shape(t *testing.T) { + got := BootstrapSubject("111122223333", "alpha", BootstrapSubjectToken) + assert.Equal(t, "eks.bus.111122223333.alpha.k3s-bootstrap-token", got) + assert.True(t, strings.HasPrefix(got, "eks.bus.")) +} diff --git a/spinifex/handlers/eks/nlb.go b/spinifex/handlers/eks/nlb.go new file mode 100644 index 000000000..1aa6b04d2 --- /dev/null +++ b/spinifex/handlers/eks/nlb.go @@ -0,0 +1,428 @@ +package handlers_eks + +import ( + "errors" + "fmt" + "log/slog" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/tags" +) + +// nlbProvisioner is the subset of handlers_elbv2.ELBv2Service that the +// cluster NLB helpers need. Narrow so tests can fake with hand-rolled +// recorders without implementing the full ELBv2 surface. +type nlbProvisioner interface { + CreateLoadBalancer(input *elbv2.CreateLoadBalancerInput, accountID string) (*elbv2.CreateLoadBalancerOutput, error) + DescribeLoadBalancers(input *elbv2.DescribeLoadBalancersInput, accountID string) (*elbv2.DescribeLoadBalancersOutput, error) + DeleteLoadBalancer(input *elbv2.DeleteLoadBalancerInput, accountID string) (*elbv2.DeleteLoadBalancerOutput, error) + + CreateTargetGroup(input *elbv2.CreateTargetGroupInput, accountID string) (*elbv2.CreateTargetGroupOutput, error) + DescribeTargetGroups(input *elbv2.DescribeTargetGroupsInput, accountID string) (*elbv2.DescribeTargetGroupsOutput, error) + DeleteTargetGroup(input *elbv2.DeleteTargetGroupInput, accountID string) (*elbv2.DeleteTargetGroupOutput, error) + + CreateListener(input *elbv2.CreateListenerInput, accountID string) (*elbv2.CreateListenerOutput, error) + DescribeListeners(input *elbv2.DescribeListenersInput, accountID string) (*elbv2.DescribeListenersOutput, error) + + RegisterTargets(input *elbv2.RegisterTargetsInput, accountID string) (*elbv2.RegisterTargetsOutput, error) + DeregisterTargets(input *elbv2.DeregisterTargetsInput, accountID string) (*elbv2.DeregisterTargetsOutput, error) + + SetLoadBalancerIngressCIDRs(lbArn string, cidrs []string, accountID string) error +} + +// AWS load-balancer + target-group names are bounded to 32 alphanumeric or +// hyphen characters. EnsureClusterNLB rejects clusters whose derived name +// would exceed this before issuing the create call so callers see a clear +// error instead of a generic ELBv2 InvalidParameterValue. +const maxELBv2NameLen = 32 + +// k3sAPIServerPort is the in-VM K3s api-server bind port. The NLB target +// group sends traffic to this port on the K3s server VM's ENI. +const k3sAPIServerPort int64 = 6443 + +// clusterNLBListenPort is the customer-facing port the cluster NLB exposes. +// kubectl + the AWS SDK both expect TLS on 443. +const clusterNLBListenPort int64 = 443 + +// ClusterNLB is the in-process tuple produced by EnsureClusterNLB. Each ARN +// is persisted into ClusterMeta so the reconciler + DeleteCluster can act on +// the resources without re-resolving by name. +type ClusterNLB struct { + LoadBalancerArn string + TargetGroupArn string + ListenerArn string + DNSName string + // FrontendIP is the NLB's reachable front-end address: the external-pool + // public IP for an internet-facing cluster NLB, or the VPC private IP for an + // internal one. Used as the kubeconfig endpoint host + apiserver cert SAN. + // Empty if the backing LB record has no address yet. + FrontendIP string +} + +// ClusterNLBName returns the deterministic NLB name for a cluster. Stable so +// DeleteCluster can locate the NLB from clusterName alone after a daemon +// restart that lost the in-memory ARN. +func ClusterNLBName(clusterName string) string { + return "eks-" + clusterName +} + +// ClusterTargetGroupName returns the deterministic target-group name for a +// cluster's control-plane TG. +func ClusterTargetGroupName(clusterName string) string { + return "eks-" + clusterName + "-cp" +} + +// EnsureClusterNLB provisions (or returns) the cluster's NLB + target group + +// :443→:6443 TCP listener. Idempotent on clusterName: existing resources are +// reused so DeleteCluster failure-retry and reconciler crash-recovery both +// converge without duplicate creates. +// +// The K3s server VM ENI IP is NOT registered here — Stage 4 +// (k3s_server_vm.go) calls RegisterClusterTarget once the VM has its ENI IP. +// +// internetFacing selects the NLB scheme: true ⇒ internet-facing (external-pool +// front-end IP, LAN-reachable, for a public cluster endpoint); false ⇒ internal +// (VPC-only). After ensuring the LB, the reachable FrontendIP is read back from +// the backing LB record so the caller can use it as the endpoint host + cert SAN. +// +// publicAccessCidrs narrows who may reach an internet-facing front-end below the +// scheme default (0.0.0.0/0). It maps the cluster's publicAccessCidrs onto the +// NLB managed-SG ingress; ignored for an internal NLB (its ingress already +// tracks the VPC CIDR) and for the wide-open default, which the LB carries out +// of the box. +func EnsureClusterNLB(nlbp nlbProvisioner, accountID, clusterName string, subnetIDs []string, internetFacing bool, publicAccessCidrs []string) (*ClusterNLB, error) { + if clusterName == "" { + return nil, errors.New("eks: EnsureClusterNLB empty cluster name") + } + if len(subnetIDs) == 0 { + return nil, errors.New("eks: EnsureClusterNLB empty subnet list") + } + lbName := ClusterNLBName(clusterName) + tgName := ClusterTargetGroupName(clusterName) + if len(lbName) > maxELBv2NameLen { + return nil, fmt.Errorf("eks: NLB name %q exceeds %d chars (cluster name too long)", lbName, maxELBv2NameLen) + } + if len(tgName) > maxELBv2NameLen { + return nil, fmt.Errorf("eks: TG name %q exceeds %d chars (cluster name too long)", tgName, maxELBv2NameLen) + } + + out := &ClusterNLB{} + + if err := ensureClusterLB(nlbp, accountID, clusterName, lbName, subnetIDs, internetFacing, out); err != nil { + return nil, err + } + if err := ensureClusterTG(nlbp, accountID, clusterName, tgName, out); err != nil { + return nil, err + } + if err := ensureClusterListener(nlbp, accountID, lbName, out); err != nil { + return nil, err + } + // Narrow the internet-facing front-end to the cluster's publicAccessCidrs + // when they restrict below the wide-open scheme default the LB already + // carries. The override is idempotent, so re-running on retry converges. + if internetFacing && narrowsPublicAccess(publicAccessCidrs) { + if err := nlbp.SetLoadBalancerIngressCIDRs(out.LoadBalancerArn, publicAccessCidrs, accountID); err != nil { + return nil, fmt.Errorf("eks: set NLB ingress CIDRs for %s: %w", lbName, err) + } + } + // Read the front-end IP back from the LB record (DescribeLoadBalancers → + // populated LoadBalancerAddresses). Best-effort: an empty IP is not fatal + // here — the caller falls back to the DNS-name endpoint. + if lb, err := lookupLBByName(nlbp, accountID, lbName); err == nil && lb != nil { + out.FrontendIP = frontendIPFromLB(lb, internetFacing) + } else if err != nil { + slog.Warn("EnsureClusterNLB: front-end IP read-back failed", "name", lbName, "err", err) + } + return out, nil +} + +// narrowsPublicAccess reports whether publicAccessCidrs restrict the front-end +// below the wide-open default an internet-facing NLB already carries. Empty (no +// override) or the lone 0.0.0.0/0 default are no-ops and skip the extra ELBv2 +// round-trip. +func narrowsPublicAccess(cidrs []string) bool { + if len(cidrs) == 0 { + return false + } + if len(cidrs) == 1 && cidrs[0] == defaultPublicAccessCidr { + return false + } + return true +} + +// frontendIPFromLB extracts the reachable front-end address from a described +// load balancer: the public IpAddress for an internet-facing LB, else the VPC +// PrivateIPv4Address. Returns the first non-empty match across AZs. +func frontendIPFromLB(lb *elbv2.LoadBalancer, internetFacing bool) string { + for _, az := range lb.AvailabilityZones { + for _, addr := range az.LoadBalancerAddresses { + if addr == nil { + continue + } + if internetFacing { + if ip := aws.StringValue(addr.IpAddress); ip != "" { + return ip + } + continue + } + if ip := aws.StringValue(addr.PrivateIPv4Address); ip != "" { + return ip + } + } + } + return "" +} + +// RegisterClusterTarget attaches the K3s server ENI IP to the cluster TG so +// the NLB starts forwarding :443 traffic to :6443 on the VM. The ELBv2 +// RegisterTargets impl already dedupes on (id, port), so re-invocation by +// the reconciler is idempotent. +func RegisterClusterTarget(nlbp nlbProvisioner, accountID, tgArn, eniIP string) error { + if tgArn == "" { + return errors.New("eks: RegisterClusterTarget empty TG arn") + } + if eniIP == "" { + return errors.New("eks: RegisterClusterTarget empty ENI IP") + } + if _, err := nlbp.RegisterTargets(&elbv2.RegisterTargetsInput{ + TargetGroupArn: aws.String(tgArn), + Targets: []*elbv2.TargetDescription{{ + Id: aws.String(eniIP), + Port: aws.Int64(k3sAPIServerPort), + }}, + }, accountID); err != nil { + return fmt.Errorf("register target %s on TG %s: %w", eniIP, tgArn, err) + } + return nil +} + +// DeregisterClusterTarget removes the K3s server ENI IP from the cluster TG. +// Called by DeleteCluster before VM termination so health checks stop hitting +// a dying target. +func DeregisterClusterTarget(nlbp nlbProvisioner, accountID, tgArn, eniIP string) error { + if tgArn == "" { + return errors.New("eks: DeregisterClusterTarget empty TG arn") + } + if eniIP == "" { + return errors.New("eks: DeregisterClusterTarget empty ENI IP") + } + if _, err := nlbp.DeregisterTargets(&elbv2.DeregisterTargetsInput{ + TargetGroupArn: aws.String(tgArn), + Targets: []*elbv2.TargetDescription{{ + Id: aws.String(eniIP), + Port: aws.Int64(k3sAPIServerPort), + }}, + }, accountID); err != nil { + return fmt.Errorf("deregister target %s on TG %s: %w", eniIP, tgArn, err) + } + return nil +} + +// DeleteClusterNLB tears down the cluster NLB + TG. DeleteLoadBalancer +// cascades listener cleanup inside the ELBv2 impl, so listeners do not need +// an explicit delete pass. Missing resources are no-ops to keep DeleteCluster +// retries safe. Both sub-sweeps always run; LB delete error takes precedence +// over TG delete error when both fail. +func DeleteClusterNLB(nlbp nlbProvisioner, accountID, clusterName string) error { + if clusterName == "" { + return errors.New("eks: DeleteClusterNLB empty cluster name") + } + lbErr := deleteClusterLB(nlbp, accountID, ClusterNLBName(clusterName)) + tgErr := deleteClusterTG(nlbp, accountID, ClusterTargetGroupName(clusterName)) + if lbErr != nil { + return lbErr + } + return tgErr +} + +func deleteClusterLB(nlbp nlbProvisioner, accountID, lbName string) error { + lb, err := lookupLBByName(nlbp, accountID, lbName) + if err != nil { + slog.Warn("DeleteClusterNLB: LB lookup failed", "name", lbName, "err", err) + return err + } + if lb == nil || lb.LoadBalancerArn == nil { + return nil + } + if _, err := nlbp.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{LoadBalancerArn: lb.LoadBalancerArn}, accountID); err != nil { + slog.Warn("DeleteClusterNLB: delete NLB failed", "name", lbName, "err", err) + return fmt.Errorf("delete NLB %s: %w", lbName, err) + } + return nil +} + +func deleteClusterTG(nlbp nlbProvisioner, accountID, tgName string) error { + tg, err := lookupTGByName(nlbp, accountID, tgName) + if err != nil { + slog.Warn("DeleteClusterNLB: TG lookup failed", "name", tgName, "err", err) + return err + } + if tg == nil || tg.TargetGroupArn == nil { + return nil + } + if _, err := nlbp.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{TargetGroupArn: tg.TargetGroupArn}, accountID); err != nil { + slog.Warn("DeleteClusterNLB: delete TG failed", "name", tgName, "err", err) + return fmt.Errorf("delete TG %s: %w", tgName, err) + } + return nil +} + +func ensureClusterLB(nlbp nlbProvisioner, accountID, clusterName, lbName string, subnetIDs []string, internetFacing bool, out *ClusterNLB) error { + if lb, err := lookupLBByName(nlbp, accountID, lbName); err != nil { + return err + } else if lb != nil { + out.LoadBalancerArn = aws.StringValue(lb.LoadBalancerArn) + out.DNSName = aws.StringValue(lb.DNSName) + if out.LoadBalancerArn == "" || out.DNSName == "" { + return fmt.Errorf("eks: existing NLB %s missing arn or DNS name", lbName) + } + return nil + } + + scheme := elbv2.LoadBalancerSchemeEnumInternal + if internetFacing { + scheme = elbv2.LoadBalancerSchemeEnumInternetFacing + } + created, err := nlbp.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String(lbName), + Type: aws.String(elbv2.LoadBalancerTypeEnumNetwork), + Scheme: aws.String(scheme), + Subnets: aws.StringSlice(subnetIDs), + Tags: []*elbv2.Tag{ + {Key: aws.String(tags.ManagedByKey), Value: aws.String(tags.ManagedByEKS)}, + {Key: aws.String(clusterEKSClusterTagKey), Value: aws.String(clusterName)}, + }, + }, accountID) + if err != nil { + return fmt.Errorf("create NLB %s: %w", lbName, err) + } + if created == nil || len(created.LoadBalancers) == 0 || created.LoadBalancers[0] == nil { + return fmt.Errorf("eks: CreateLoadBalancer returned no LB for %s", lbName) + } + lb := created.LoadBalancers[0] + out.LoadBalancerArn = aws.StringValue(lb.LoadBalancerArn) + out.DNSName = aws.StringValue(lb.DNSName) + if out.LoadBalancerArn == "" || out.DNSName == "" { + return fmt.Errorf("eks: CreateLoadBalancer returned empty arn or DNS name for %s", lbName) + } + return nil +} + +func ensureClusterTG(nlbp nlbProvisioner, accountID, clusterName, tgName string, out *ClusterNLB) error { + if tg, err := lookupTGByName(nlbp, accountID, tgName); err != nil { + return err + } else if tg != nil { + out.TargetGroupArn = aws.StringValue(tg.TargetGroupArn) + if out.TargetGroupArn == "" { + return fmt.Errorf("eks: existing TG %s missing arn", tgName) + } + return nil + } + + created, err := nlbp.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String(tgName), + Protocol: aws.String(elbv2.ProtocolEnumTcp), + Port: aws.Int64(k3sAPIServerPort), + TargetType: aws.String(elbv2.TargetTypeEnumIp), + Tags: []*elbv2.Tag{ + {Key: aws.String(tags.ManagedByKey), Value: aws.String(tags.ManagedByEKS)}, + {Key: aws.String(clusterEKSClusterTagKey), Value: aws.String(clusterName)}, + }, + }, accountID) + if err != nil { + return fmt.Errorf("create TG %s: %w", tgName, err) + } + if created == nil || len(created.TargetGroups) == 0 || created.TargetGroups[0] == nil { + return fmt.Errorf("eks: CreateTargetGroup returned no TG for %s", tgName) + } + out.TargetGroupArn = aws.StringValue(created.TargetGroups[0].TargetGroupArn) + if out.TargetGroupArn == "" { + return fmt.Errorf("eks: CreateTargetGroup returned empty arn for %s", tgName) + } + return nil +} + +func ensureClusterListener(nlbp nlbProvisioner, accountID, lbName string, out *ClusterNLB) error { + if l, err := lookupListenerByPort(nlbp, accountID, out.LoadBalancerArn, clusterNLBListenPort); err != nil { + return err + } else if l != nil { + out.ListenerArn = aws.StringValue(l.ListenerArn) + if out.ListenerArn == "" { + return fmt.Errorf("eks: existing listener for %s:%d missing arn", lbName, clusterNLBListenPort) + } + return nil + } + + created, err := nlbp.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: aws.String(out.LoadBalancerArn), + Protocol: aws.String(elbv2.ProtocolEnumTcp), + Port: aws.Int64(clusterNLBListenPort), + DefaultActions: []*elbv2.Action{{ + Type: aws.String(elbv2.ActionTypeEnumForward), + TargetGroupArn: aws.String(out.TargetGroupArn), + }}, + }, accountID) + if err != nil { + return fmt.Errorf("create listener %s:%d: %w", lbName, clusterNLBListenPort, err) + } + if created == nil || len(created.Listeners) == 0 || created.Listeners[0] == nil { + return fmt.Errorf("eks: CreateListener returned no listener for %s:%d", lbName, clusterNLBListenPort) + } + out.ListenerArn = aws.StringValue(created.Listeners[0].ListenerArn) + if out.ListenerArn == "" { + return fmt.Errorf("eks: CreateListener returned empty arn for %s:%d", lbName, clusterNLBListenPort) + } + return nil +} + +func lookupLBByName(nlbp nlbProvisioner, accountID, name string) (*elbv2.LoadBalancer, error) { + out, err := nlbp.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{ + Names: aws.StringSlice([]string{name}), + }, accountID) + if err != nil { + return nil, fmt.Errorf("describe NLB %s: %w", name, err) + } + for _, lb := range out.LoadBalancers { + if lb != nil { + return lb, nil + } + } + return nil, nil +} + +func lookupTGByName(nlbp nlbProvisioner, accountID, name string) (*elbv2.TargetGroup, error) { + out, err := nlbp.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{ + Names: aws.StringSlice([]string{name}), + }, accountID) + if err != nil { + return nil, fmt.Errorf("describe TG %s: %w", name, err) + } + for _, tg := range out.TargetGroups { + if tg != nil { + return tg, nil + } + } + return nil, nil +} + +func lookupListenerByPort(nlbp nlbProvisioner, accountID, lbArn string, port int64) (*elbv2.Listener, error) { + if lbArn == "" { + return nil, errors.New("eks: lookupListenerByPort empty LB arn") + } + out, err := nlbp.DescribeListeners(&elbv2.DescribeListenersInput{ + LoadBalancerArn: aws.String(lbArn), + }, accountID) + if err != nil { + return nil, fmt.Errorf("describe listeners on %s: %w", lbArn, err) + } + for _, l := range out.Listeners { + if l == nil || l.Port == nil { + continue + } + if *l.Port == port { + return l, nil + } + } + return nil, nil +} diff --git a/spinifex/handlers/eks/nlb_test.go b/spinifex/handlers/eks/nlb_test.go new file mode 100644 index 000000000..632c4a4e2 --- /dev/null +++ b/spinifex/handlers/eks/nlb_test.go @@ -0,0 +1,501 @@ +package handlers_eks + +import ( + "errors" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/tags" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type fakeNLBProvisioner struct { + createLBCalls []*elbv2.CreateLoadBalancerInput + describeLBCalls []*elbv2.DescribeLoadBalancersInput + deleteLBCalls []*elbv2.DeleteLoadBalancerInput + createTGCalls []*elbv2.CreateTargetGroupInput + describeTGCalls []*elbv2.DescribeTargetGroupsInput + deleteTGCalls []*elbv2.DeleteTargetGroupInput + createListenerCalls []*elbv2.CreateListenerInput + describeListeners []*elbv2.DescribeListenersInput + registerCalls []*elbv2.RegisterTargetsInput + deregisterCalls []*elbv2.DeregisterTargetsInput + setIngressCalls []setIngressCIDRsCall + + lbByName map[string]*elbv2.LoadBalancer + tgByName map[string]*elbv2.TargetGroup + listenerByPort map[string]map[int64]*elbv2.Listener // lbArn → port → listener + + createLBOut *elbv2.CreateLoadBalancerOutput + createTGOut *elbv2.CreateTargetGroupOutput + createListenerOut *elbv2.CreateListenerOutput + createLBErr error + createTGErr error + createListenerErr error + deleteLBErr error + deleteTGErr error + registerErr error + deregisterErr error + setIngressErr error +} + +type setIngressCIDRsCall struct { + lbArn string + cidrs []string +} + +var _ nlbProvisioner = (*fakeNLBProvisioner)(nil) + +func newFakeNLBProvisioner() *fakeNLBProvisioner { + return &fakeNLBProvisioner{ + lbByName: map[string]*elbv2.LoadBalancer{}, + tgByName: map[string]*elbv2.TargetGroup{}, + listenerByPort: map[string]map[int64]*elbv2.Listener{}, + } +} + +func (f *fakeNLBProvisioner) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInput, _ string) (*elbv2.CreateLoadBalancerOutput, error) { + f.createLBCalls = append(f.createLBCalls, input) + if f.createLBErr != nil { + return nil, f.createLBErr + } + if f.createLBOut == nil { + name := aws.StringValue(input.Name) + arn := "arn:aws:elasticloadbalancing:us-east-1:111122223333:loadbalancer/net/" + name + "/lb-001" + dns := name + "-lb-001.us-east-1.elb.spinifex.local" + f.createLBOut = &elbv2.CreateLoadBalancerOutput{ + LoadBalancers: []*elbv2.LoadBalancer{{ + LoadBalancerArn: aws.String(arn), + LoadBalancerName: aws.String(name), + DNSName: aws.String(dns), + Type: aws.String(elbv2.LoadBalancerTypeEnumNetwork), + Scheme: aws.String(elbv2.LoadBalancerSchemeEnumInternal), + }}, + } + } + out := f.createLBOut + if len(out.LoadBalancers) > 0 { + name := aws.StringValue(out.LoadBalancers[0].LoadBalancerName) + f.lbByName[name] = out.LoadBalancers[0] + } + return out, nil +} + +func (f *fakeNLBProvisioner) DescribeLoadBalancers(input *elbv2.DescribeLoadBalancersInput, _ string) (*elbv2.DescribeLoadBalancersOutput, error) { + f.describeLBCalls = append(f.describeLBCalls, input) + out := &elbv2.DescribeLoadBalancersOutput{} + for _, n := range input.Names { + if n == nil { + continue + } + if lb, ok := f.lbByName[*n]; ok { + out.LoadBalancers = append(out.LoadBalancers, lb) + } + } + return out, nil +} + +func (f *fakeNLBProvisioner) DeleteLoadBalancer(input *elbv2.DeleteLoadBalancerInput, _ string) (*elbv2.DeleteLoadBalancerOutput, error) { + f.deleteLBCalls = append(f.deleteLBCalls, input) + if f.deleteLBErr != nil { + return nil, f.deleteLBErr + } + if input.LoadBalancerArn != nil { + for name, lb := range f.lbByName { + if aws.StringValue(lb.LoadBalancerArn) == *input.LoadBalancerArn { + delete(f.lbByName, name) + } + } + } + return &elbv2.DeleteLoadBalancerOutput{}, nil +} + +func (f *fakeNLBProvisioner) CreateTargetGroup(input *elbv2.CreateTargetGroupInput, _ string) (*elbv2.CreateTargetGroupOutput, error) { + f.createTGCalls = append(f.createTGCalls, input) + if f.createTGErr != nil { + return nil, f.createTGErr + } + if f.createTGOut == nil { + name := aws.StringValue(input.Name) + arn := "arn:aws:elasticloadbalancing:us-east-1:111122223333:targetgroup/" + name + "/tg-001" + f.createTGOut = &elbv2.CreateTargetGroupOutput{ + TargetGroups: []*elbv2.TargetGroup{{ + TargetGroupArn: aws.String(arn), + TargetGroupName: aws.String(name), + Protocol: input.Protocol, + Port: input.Port, + TargetType: input.TargetType, + }}, + } + } + out := f.createTGOut + if len(out.TargetGroups) > 0 { + name := aws.StringValue(out.TargetGroups[0].TargetGroupName) + f.tgByName[name] = out.TargetGroups[0] + } + return out, nil +} + +func (f *fakeNLBProvisioner) DescribeTargetGroups(input *elbv2.DescribeTargetGroupsInput, _ string) (*elbv2.DescribeTargetGroupsOutput, error) { + f.describeTGCalls = append(f.describeTGCalls, input) + out := &elbv2.DescribeTargetGroupsOutput{} + for _, n := range input.Names { + if n == nil { + continue + } + if tg, ok := f.tgByName[*n]; ok { + out.TargetGroups = append(out.TargetGroups, tg) + } + } + return out, nil +} + +func (f *fakeNLBProvisioner) DeleteTargetGroup(input *elbv2.DeleteTargetGroupInput, _ string) (*elbv2.DeleteTargetGroupOutput, error) { + f.deleteTGCalls = append(f.deleteTGCalls, input) + if f.deleteTGErr != nil { + return nil, f.deleteTGErr + } + if input.TargetGroupArn != nil { + for name, tg := range f.tgByName { + if aws.StringValue(tg.TargetGroupArn) == *input.TargetGroupArn { + delete(f.tgByName, name) + } + } + } + return &elbv2.DeleteTargetGroupOutput{}, nil +} + +func (f *fakeNLBProvisioner) CreateListener(input *elbv2.CreateListenerInput, _ string) (*elbv2.CreateListenerOutput, error) { + f.createListenerCalls = append(f.createListenerCalls, input) + if f.createListenerErr != nil { + return nil, f.createListenerErr + } + if f.createListenerOut == nil { + lbArn := aws.StringValue(input.LoadBalancerArn) + port := aws.Int64Value(input.Port) + listenerArn := lbArn + "/listener/lst-001" + f.createListenerOut = &elbv2.CreateListenerOutput{ + Listeners: []*elbv2.Listener{{ + ListenerArn: aws.String(listenerArn), + LoadBalancerArn: input.LoadBalancerArn, + Port: input.Port, + Protocol: input.Protocol, + DefaultActions: input.DefaultActions, + }}, + } + if f.listenerByPort[lbArn] == nil { + f.listenerByPort[lbArn] = map[int64]*elbv2.Listener{} + } + f.listenerByPort[lbArn][port] = f.createListenerOut.Listeners[0] + } + return f.createListenerOut, nil +} + +func (f *fakeNLBProvisioner) DescribeListeners(input *elbv2.DescribeListenersInput, _ string) (*elbv2.DescribeListenersOutput, error) { + f.describeListeners = append(f.describeListeners, input) + out := &elbv2.DescribeListenersOutput{} + if input.LoadBalancerArn != nil { + for _, l := range f.listenerByPort[*input.LoadBalancerArn] { + out.Listeners = append(out.Listeners, l) + } + } + return out, nil +} + +func (f *fakeNLBProvisioner) RegisterTargets(input *elbv2.RegisterTargetsInput, _ string) (*elbv2.RegisterTargetsOutput, error) { + f.registerCalls = append(f.registerCalls, input) + if f.registerErr != nil { + return nil, f.registerErr + } + return &elbv2.RegisterTargetsOutput{}, nil +} + +func (f *fakeNLBProvisioner) DeregisterTargets(input *elbv2.DeregisterTargetsInput, _ string) (*elbv2.DeregisterTargetsOutput, error) { + f.deregisterCalls = append(f.deregisterCalls, input) + if f.deregisterErr != nil { + return nil, f.deregisterErr + } + return &elbv2.DeregisterTargetsOutput{}, nil +} + +func (f *fakeNLBProvisioner) SetLoadBalancerIngressCIDRs(lbArn string, cidrs []string, _ string) error { + f.setIngressCalls = append(f.setIngressCalls, setIngressCIDRsCall{lbArn: lbArn, cidrs: cidrs}) + return f.setIngressErr +} + +func TestEnsureClusterNLB_EmptyInputsRejected(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + _, err := EnsureClusterNLB(nlbp, "111122223333", "", []string{"subnet-aaa"}, false, nil) + require.Error(t, err) + + _, err = EnsureClusterNLB(nlbp, "111122223333", "alpha", nil, false, nil) + require.Error(t, err) + + assert.Empty(t, nlbp.createLBCalls) +} + +func TestEnsureClusterNLB_NameTooLongRejected(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + longName := strings.Repeat("x", maxELBv2NameLen) // "eks-" + 32x = 36 chars + _, err := EnsureClusterNLB(nlbp, "111122223333", longName, []string{"subnet-aaa"}, false, nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "exceeds") + assert.Empty(t, nlbp.createLBCalls) +} + +func TestEnsureClusterNLB_FreshCreatesAllThree(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + out, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa", "subnet-bbb"}, false, nil) + require.NoError(t, err) + require.NotNil(t, out) + assert.NotEmpty(t, out.LoadBalancerArn) + assert.NotEmpty(t, out.TargetGroupArn) + assert.NotEmpty(t, out.ListenerArn) + assert.Contains(t, out.DNSName, "eks-alpha") + + require.Len(t, nlbp.createLBCalls, 1) + lbIn := nlbp.createLBCalls[0] + assert.Equal(t, "eks-alpha", aws.StringValue(lbIn.Name)) + assert.Equal(t, elbv2.LoadBalancerTypeEnumNetwork, aws.StringValue(lbIn.Type)) + assert.Equal(t, elbv2.LoadBalancerSchemeEnumInternal, aws.StringValue(lbIn.Scheme)) + assert.Equal(t, []string{"subnet-aaa", "subnet-bbb"}, aws.StringValueSlice(lbIn.Subnets)) + assertELBv2TaggedAsEKS(t, lbIn.Tags, "alpha") + + require.Len(t, nlbp.createTGCalls, 1) + tgIn := nlbp.createTGCalls[0] + assert.Equal(t, "eks-alpha-cp", aws.StringValue(tgIn.Name)) + assert.Equal(t, elbv2.ProtocolEnumTcp, aws.StringValue(tgIn.Protocol)) + assert.Equal(t, k3sAPIServerPort, aws.Int64Value(tgIn.Port)) + assert.Equal(t, elbv2.TargetTypeEnumIp, aws.StringValue(tgIn.TargetType)) + assertELBv2TaggedAsEKS(t, tgIn.Tags, "alpha") + + require.Len(t, nlbp.createListenerCalls, 1) + lstIn := nlbp.createListenerCalls[0] + assert.Equal(t, out.LoadBalancerArn, aws.StringValue(lstIn.LoadBalancerArn)) + assert.Equal(t, elbv2.ProtocolEnumTcp, aws.StringValue(lstIn.Protocol)) + assert.Equal(t, clusterNLBListenPort, aws.Int64Value(lstIn.Port)) + require.Len(t, lstIn.DefaultActions, 1) + assert.Equal(t, elbv2.ActionTypeEnumForward, aws.StringValue(lstIn.DefaultActions[0].Type)) + assert.Equal(t, out.TargetGroupArn, aws.StringValue(lstIn.DefaultActions[0].TargetGroupArn)) +} + +func TestEnsureClusterNLB_IdempotentReusesExisting(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + first, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, false, nil) + require.NoError(t, err) + + createLBCount := len(nlbp.createLBCalls) + createTGCount := len(nlbp.createTGCalls) + createListenerCount := len(nlbp.createListenerCalls) + + second, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, false, nil) + require.NoError(t, err) + + assert.Equal(t, first.LoadBalancerArn, second.LoadBalancerArn) + assert.Equal(t, first.TargetGroupArn, second.TargetGroupArn) + assert.Equal(t, first.ListenerArn, second.ListenerArn) + assert.Equal(t, first.DNSName, second.DNSName) + + assert.Equal(t, createLBCount, len(nlbp.createLBCalls), "no new LB create on idempotent call") + assert.Equal(t, createTGCount, len(nlbp.createTGCalls), "no new TG create on idempotent call") + assert.Equal(t, createListenerCount, len(nlbp.createListenerCalls), "no new listener create on idempotent call") +} + +func TestEnsureClusterNLB_LBCreateErrorSurfaced(t *testing.T) { + nlbp := newFakeNLBProvisioner() + nlbp.createLBErr = errors.New("InsufficientCapacity") + + _, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, false, nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "create NLB eks-alpha") + assert.Empty(t, nlbp.createTGCalls, "TG create should not run when LB create fails") +} + +func TestEnsureClusterNLB_InternetFacingSchemeAndPublicFrontendIP(t *testing.T) { + nlbp := newFakeNLBProvisioner() + // Internet-facing LB record exposes a public IpAddress in its AZ addresses. + arn := "arn:aws:elasticloadbalancing:us-east-1:111122223333:loadbalancer/net/eks-alpha/lb-001" + nlbp.createLBOut = &elbv2.CreateLoadBalancerOutput{ + LoadBalancers: []*elbv2.LoadBalancer{{ + LoadBalancerArn: aws.String(arn), + LoadBalancerName: aws.String("eks-alpha"), + DNSName: aws.String("eks-alpha-lb-001.us-east-1.elb.spinifex.local"), + Type: aws.String(elbv2.LoadBalancerTypeEnumNetwork), + Scheme: aws.String(elbv2.LoadBalancerSchemeEnumInternetFacing), + AvailabilityZones: []*elbv2.AvailabilityZone{{ + LoadBalancerAddresses: []*elbv2.LoadBalancerAddress{ + {PrivateIPv4Address: aws.String("10.0.1.5")}, + {IpAddress: aws.String("203.0.113.10")}, + }, + }}, + }}, + } + + out, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, true, nil) + require.NoError(t, err) + require.Len(t, nlbp.createLBCalls, 1) + assert.Equal(t, elbv2.LoadBalancerSchemeEnumInternetFacing, aws.StringValue(nlbp.createLBCalls[0].Scheme)) + assert.Equal(t, "203.0.113.10", out.FrontendIP, "internet-facing front-end uses the public IpAddress") +} + +func TestEnsureClusterNLB_InternalSchemeUsesPrivateFrontendIP(t *testing.T) { + nlbp := newFakeNLBProvisioner() + arn := "arn:aws:elasticloadbalancing:us-east-1:111122223333:loadbalancer/net/eks-alpha/lb-001" + nlbp.createLBOut = &elbv2.CreateLoadBalancerOutput{ + LoadBalancers: []*elbv2.LoadBalancer{{ + LoadBalancerArn: aws.String(arn), + LoadBalancerName: aws.String("eks-alpha"), + DNSName: aws.String("eks-alpha-lb-001.us-east-1.elb.spinifex.local"), + Type: aws.String(elbv2.LoadBalancerTypeEnumNetwork), + Scheme: aws.String(elbv2.LoadBalancerSchemeEnumInternal), + AvailabilityZones: []*elbv2.AvailabilityZone{{ + LoadBalancerAddresses: []*elbv2.LoadBalancerAddress{ + {PrivateIPv4Address: aws.String("10.0.1.5")}, + }, + }}, + }}, + } + + out, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, false, nil) + require.NoError(t, err) + require.Len(t, nlbp.createLBCalls, 1) + assert.Equal(t, elbv2.LoadBalancerSchemeEnumInternal, aws.StringValue(nlbp.createLBCalls[0].Scheme)) + assert.Equal(t, "10.0.1.5", out.FrontendIP, "internal front-end uses the private IPv4 address") +} + +func TestEnsureClusterNLB_NarrowedPublicAccessSetsIngressCIDRs(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + cidrs := []string{"203.0.113.0/24", "198.51.100.7/32"} + out, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, true, cidrs) + require.NoError(t, err) + + require.Len(t, nlbp.setIngressCalls, 1, "narrowed public access should drive SetLoadBalancerIngressCIDRs") + assert.Equal(t, out.LoadBalancerArn, nlbp.setIngressCalls[0].lbArn) + assert.Equal(t, cidrs, nlbp.setIngressCalls[0].cidrs) +} + +func TestEnsureClusterNLB_DefaultPublicAccessSkipsIngressCIDRs(t *testing.T) { + for _, tc := range []struct { + name string + cidrs []string + }{ + {"nil", nil}, + {"empty", []string{}}, + {"wide-open default", []string{defaultPublicAccessCidr}}, + } { + t.Run(tc.name, func(t *testing.T) { + nlbp := newFakeNLBProvisioner() + _, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, true, tc.cidrs) + require.NoError(t, err) + assert.Empty(t, nlbp.setIngressCalls, "wide-open front-end needs no ingress override") + }) + } +} + +func TestEnsureClusterNLB_InternalSkipsIngressCIDRs(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + // Even with narrowed CIDRs, an internal NLB ignores them — its ingress + // already tracks the VPC CIDR, not a public front-end. + _, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, false, []string{"203.0.113.0/24"}) + require.NoError(t, err) + assert.Empty(t, nlbp.setIngressCalls) +} + +func TestEnsureClusterNLB_SetIngressErrorSurfaced(t *testing.T) { + nlbp := newFakeNLBProvisioner() + nlbp.setIngressErr = errors.New("InvalidLoadBalancer") + + _, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, true, []string{"203.0.113.0/24"}) + require.Error(t, err) + assert.Contains(t, err.Error(), "set NLB ingress CIDRs for eks-alpha") +} + +func TestRegisterClusterTarget_PostsENIIPAndAPIPort(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + err := RegisterClusterTarget(nlbp, "111122223333", "arn:tg/alpha", "10.0.1.42") + require.NoError(t, err) + require.Len(t, nlbp.registerCalls, 1) + + in := nlbp.registerCalls[0] + assert.Equal(t, "arn:tg/alpha", aws.StringValue(in.TargetGroupArn)) + require.Len(t, in.Targets, 1) + assert.Equal(t, "10.0.1.42", aws.StringValue(in.Targets[0].Id)) + assert.Equal(t, k3sAPIServerPort, aws.Int64Value(in.Targets[0].Port)) +} + +func TestRegisterClusterTarget_EmptyInputsRejected(t *testing.T) { + nlbp := newFakeNLBProvisioner() + require.Error(t, RegisterClusterTarget(nlbp, "111122223333", "", "10.0.1.42")) + require.Error(t, RegisterClusterTarget(nlbp, "111122223333", "arn:tg/alpha", "")) + assert.Empty(t, nlbp.registerCalls) +} + +func TestDeregisterClusterTarget_PostsENIIPAndAPIPort(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + err := DeregisterClusterTarget(nlbp, "111122223333", "arn:tg/alpha", "10.0.1.42") + require.NoError(t, err) + require.Len(t, nlbp.deregisterCalls, 1) + + in := nlbp.deregisterCalls[0] + assert.Equal(t, "arn:tg/alpha", aws.StringValue(in.TargetGroupArn)) + require.Len(t, in.Targets, 1) + assert.Equal(t, "10.0.1.42", aws.StringValue(in.Targets[0].Id)) + assert.Equal(t, k3sAPIServerPort, aws.Int64Value(in.Targets[0].Port)) +} + +func TestDeleteClusterNLB_DeletesBoth(t *testing.T) { + nlbp := newFakeNLBProvisioner() + out, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, false, nil) + require.NoError(t, err) + + require.NoError(t, DeleteClusterNLB(nlbp, "111122223333", "alpha")) + require.Len(t, nlbp.deleteLBCalls, 1) + assert.Equal(t, out.LoadBalancerArn, aws.StringValue(nlbp.deleteLBCalls[0].LoadBalancerArn)) + require.Len(t, nlbp.deleteTGCalls, 1) + assert.Equal(t, out.TargetGroupArn, aws.StringValue(nlbp.deleteTGCalls[0].TargetGroupArn)) +} + +func TestDeleteClusterNLB_MissingResourcesNoOp(t *testing.T) { + nlbp := newFakeNLBProvisioner() + + require.NoError(t, DeleteClusterNLB(nlbp, "111122223333", "alpha")) + assert.Empty(t, nlbp.deleteLBCalls) + assert.Empty(t, nlbp.deleteTGCalls) +} + +func TestDeleteClusterNLB_FirstErrorSurfacedSweepContinues(t *testing.T) { + nlbp := newFakeNLBProvisioner() + _, err := EnsureClusterNLB(nlbp, "111122223333", "alpha", []string{"subnet-aaa"}, false, nil) + require.NoError(t, err) + nlbp.deleteLBErr = errors.New("LoadBalancerInUse") + + err = DeleteClusterNLB(nlbp, "111122223333", "alpha") + require.Error(t, err) + assert.Contains(t, err.Error(), "delete NLB eks-alpha") + assert.Len(t, nlbp.deleteLBCalls, 1) + assert.Len(t, nlbp.deleteTGCalls, 1, "TG delete should still be attempted after LB delete fails") +} + +func assertELBv2TaggedAsEKS(t *testing.T, tgs []*elbv2.Tag, clusterName string) { + t.Helper() + got := map[string]string{} + for _, tg := range tgs { + if tg == nil || tg.Key == nil || tg.Value == nil { + continue + } + got[*tg.Key] = *tg.Value + } + assert.Equal(t, tags.ManagedByEKS, got[tags.ManagedByKey]) + assert.Equal(t, clusterName, got[clusterEKSClusterTagKey]) +} diff --git a/spinifex/handlers/eks/nodegroup.go b/spinifex/handlers/eks/nodegroup.go new file mode 100644 index 000000000..38e4cf081 --- /dev/null +++ b/spinifex/handlers/eks/nodegroup.go @@ -0,0 +1,605 @@ +package handlers_eks + +import ( + "encoding/json" + "errors" + "fmt" + "log/slog" + "maps" + "sort" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/google/uuid" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/nats-io/nats.go" +) + +// defaultNodegroupInstanceType mirrors the AWS EKS managed-nodegroup default +// when the caller omits instanceTypes. +const defaultNodegroupInstanceType = "t3.medium" + +// ErrNodegroupNotFound is returned by GetNodegroupRecord when the record key is +// absent. Callers translate to the AWS shape (ResourceNotFoundException) at the +// service boundary. +var ErrNodegroupNotFound = errors.New("eks: nodegroup not found") + +// NodegroupARN composes a nodegroup ARN matching the AWS shape +// (.../nodegroup/{cluster}/{ng}/{uuid}). The trailing UUID is the per-nodegroup +// discriminator AWS appends; it is generated once at create time and persisted. +func NodegroupARN(region, accountID, cluster, ng, id string) string { + return fmt.Sprintf("arn:aws:eks:%s:%s:nodegroup/%s/%s/%s", region, accountID, cluster, ng, id) +} + +// PutNodegroupRecord writes the record unconditionally. +func PutNodegroupRecord(kv nats.KeyValue, rec *NodegroupRecord) error { + if rec == nil { + return errors.New("eks: PutNodegroupRecord nil record") + } + if rec.ClusterName == "" || rec.Name == "" { + return errors.New("eks: PutNodegroupRecord missing cluster or name") + } + data, err := json.Marshal(rec) + if err != nil { + return fmt.Errorf("marshal nodegroup %s: %w", rec.Name, err) + } + key := NodegroupKey(rec.ClusterName, rec.Name) + if _, err := kv.Put(key, data); err != nil { + return fmt.Errorf("kv put %s: %w", key, err) + } + return nil +} + +// GetNodegroupRecord reads one record. Returns ErrNodegroupNotFound if absent. +func GetNodegroupRecord(kv nats.KeyValue, cluster, ng string) (*NodegroupRecord, error) { + if cluster == "" || ng == "" { + return nil, errors.New("eks: GetNodegroupRecord empty cluster or name") + } + entry, err := kv.Get(NodegroupKey(cluster, ng)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, ErrNodegroupNotFound + } + return nil, fmt.Errorf("kv get nodegroup: %w", err) + } + var rec NodegroupRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal nodegroup %s: %w", ng, err) + } + return &rec, nil +} + +// ListNodegroupRecords returns every nodegroup record under a cluster, sorted +// by name for stable output. +func ListNodegroupRecords(kv nats.KeyValue, cluster string) ([]*NodegroupRecord, error) { + if cluster == "" { + return nil, errors.New("eks: ListNodegroupRecords empty cluster") + } + keys, err := kv.Keys() + if err != nil { + if errors.Is(err, nats.ErrNoKeysFound) { + return nil, nil + } + return nil, fmt.Errorf("kv keys: %w", err) + } + prefix := NodegroupsPrefix(cluster) + out := make([]*NodegroupRecord, 0) + for _, k := range keys { + if !strings.HasPrefix(k, prefix) { + continue + } + entry, err := kv.Get(k) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + continue + } + return nil, fmt.Errorf("kv get %s: %w", k, err) + } + var rec NodegroupRecord + if err := json.Unmarshal(entry.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal nodegroup %s: %w", k, err) + } + out = append(out, &rec) + } + sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name }) + return out, nil +} + +// DeleteNodegroupRecord removes one record. A missing key is a no-op so +// DeleteNodegroup stays idempotent. +func DeleteNodegroupRecord(kv nats.KeyValue, cluster, ng string) error { + key := NodegroupKey(cluster, ng) + if err := kv.Delete(key); err != nil && !errors.Is(err, nats.ErrKeyNotFound) { + return fmt.Errorf("kv delete %s: %w", key, err) + } + return nil +} + +// nodegroupAcctKV opens the per-account bucket for nodegroup handlers. +func (s *EKSServiceImpl) nodegroupAcctKV(accountID string) (nats.KeyValue, error) { + js, err := s.deps.NATSConn.JetStream() + if err != nil { + return nil, fmt.Errorf("jetstream: %w", err) + } + acctKV, err := GetOrCreateAccountBucket(js, accountID) + if err != nil { + return nil, fmt.Errorf("get account bucket: %w", err) + } + return acctKV, nil +} + +func (s *EKSServiceImpl) createNodegroup(acctKV nats.KeyValue, input *eks.CreateNodegroupInput, accountID string) (*eks.CreateNodegroupOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + ng := aws.StringValue(input.NodegroupName) + if cluster == "" || ng == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + subnets := aws.StringValueSlice(input.Subnets) + if len(subnets) == 0 { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + meta, err := GetClusterMeta(acctKV, cluster) + if err != nil { + if errors.Is(err, ErrClusterNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + if meta.Status != ClusterStatusActive { + slog.Warn("createNodegroup: cluster not ACTIVE", "cluster", cluster, "status", meta.Status) + return nil, errors.New(awserrors.ErrorInvalidRequest) + } + if meta.ControlPlaneENIIP == "" { + slog.Error("createNodegroup: cluster has no control-plane ENI IP", "cluster", cluster) + return nil, errors.New(awserrors.ErrorInvalidRequest) + } + if meta.ResourcesVpcConfig == nil || meta.ResourcesVpcConfig.VpcId == "" { + slog.Error("createNodegroup: cluster has no VPC", "cluster", cluster) + return nil, errors.New(awserrors.ErrorInvalidRequest) + } + + if _, err := GetNodegroupRecord(acctKV, cluster, ng); err == nil { + return nil, errors.New(awserrors.ErrorEKSResourceInUse) + } else if !errors.Is(err, ErrNodegroupNotFound) { + return nil, err + } + + instanceTypes := aws.StringValueSlice(input.InstanceTypes) + if len(instanceTypes) == 0 { + instanceTypes = []string{defaultNodegroupInstanceType} + } + minSize, maxSize, desired := scalingFromInput(input.ScalingConfig) + + amiType := aws.StringValue(input.AmiType) + if amiType == "" { + amiType = eks.AMITypesAl2X8664 + } + version := aws.StringValue(input.Version) + if version == "" { + version = meta.Version + } + + now := time.Now().UTC() + rec := &NodegroupRecord{ + ClusterName: cluster, + Name: ng, + Arn: NodegroupARN(s.deps.Region, accountID, cluster, ng, uuid.NewString()), + Status: eks.NodegroupStatusCreating, + Subnets: subnets, + InstanceTypes: instanceTypes, + AMIType: amiType, + DiskSize: aws.Int64Value(input.DiskSize), + ScalingMin: minSize, + ScalingMax: maxSize, + ScalingDesired: desired, + Version: version, + NodeRole: aws.StringValue(input.NodeRole), + Labels: aws.StringValueMap(input.Labels), + CreatedAt: now, + ModifiedAt: now, + } + + cpSGID, ngSGID, err := EnsureClusterSGs(s.deps.VPCSG, accountID, cluster, meta.ResourcesVpcConfig.VpcId) + if err != nil { + return nil, fmt.Errorf("ensure cluster SGs: %w", err) + } + if err := EnsureNodegroupSGRules(s.deps.VPCSG, accountID, cluster, cpSGID, ngSGID); err != nil { + return nil, fmt.Errorf("ensure nodegroup SG rules: %w", err) + } + + if err := PutNodegroupRecord(acctKV, rec); err != nil { + return nil, err + } + + token, err := s.decryptNodeToken(acctKV, cluster) + if err != nil { + s.markNodegroupFailed(acctKV, cluster, ng, "decrypt node token: "+err.Error()) + return nil, fmt.Errorf("decrypt node token: %w", err) + } + + amiID, err := lookupEKSServerAMI(s.deps.Image, accountID) + if err != nil { + s.markNodegroupFailed(acctKV, cluster, ng, "resolve eks-node AMI: "+err.Error()) + if errors.Is(err, ErrEKSServerAMINotFound) { + return nil, errors.New(awserrors.ErrorServiceUnavailable) + } + return nil, fmt.Errorf("resolve eks-node AMI: %w", err) + } + + instanceIDs, err := s.launchWorkers(accountID, rec, meta, ngSGID, amiID, token, int(desired)) + rec.InstanceIDs = instanceIDs + if err != nil { + // Persist whatever launched so DeleteNodegroup can reclaim it, then fail. + rec.Status = eks.NodegroupStatusCreateFailed + rec.StatusReason = "launch workers: " + err.Error() + rec.ModifiedAt = time.Now().UTC() + if perr := PutNodegroupRecord(acctKV, rec); perr != nil { + slog.Error("createNodegroup: persist CREATE_FAILED record", "cluster", cluster, "nodegroup", ng, "err", perr) + } + return nil, fmt.Errorf("launch workers: %w", err) + } + + // v1 marks ACTIVE on successful RunInstances (instance-level), not on + // k8s node-Ready — node-Ready gating needs a kubectl probe from the + // reconciler and lands in a follow-up. + rec.Status = eks.NodegroupStatusActive + rec.ModifiedAt = time.Now().UTC() + if err := PutNodegroupRecord(acctKV, rec); err != nil { + return nil, err + } + + return &eks.CreateNodegroupOutput{Nodegroup: nodegroupRecordToAWS(rec)}, nil +} + +// launchWorkers issues count customer-owned RunInstances calls (one per worker +// so each gets a distinct node name + node label in its user-data) and returns +// the launched instance IDs. On a partial failure it returns the IDs that did +// launch plus the error so the caller can persist them for teardown. +func (s *EKSServiceImpl) launchWorkers(accountID string, rec *NodegroupRecord, meta *ClusterMeta, ngSGID, amiID, token string, count int) ([]string, error) { + instanceType := defaultNodegroupInstanceType + if len(rec.InstanceTypes) > 0 { + instanceType = rec.InstanceTypes[0] + } + subnet := rec.Subnets[0] + + ids := make([]string, 0, count) + for i := range count { + shortID := uuid.NewString()[:8] + userData := buildAgentUserData(agentUserDataInput{ + ClusterName: rec.ClusterName, + NodegroupName: rec.Name, + ControlPlaneENIIP: meta.ControlPlaneENIIP, + JoinToken: token, + NodeName: fmt.Sprintf("%s-%s-%s", rec.ClusterName, rec.Name, shortID), + }) + runInput := &ec2.RunInstancesInput{ + ImageId: aws.String(amiID), + InstanceType: aws.String(instanceType), + MinCount: aws.Int64(1), + MaxCount: aws.Int64(1), + SubnetId: aws.String(subnet), + SecurityGroupIds: aws.StringSlice([]string{ngSGID}), + UserData: aws.String(userData), + TagSpecifications: []*ec2.TagSpecification{{ + ResourceType: aws.String("instance"), + Tags: []*ec2.Tag{ + {Key: aws.String(clusterEKSClusterTagKey), Value: aws.String(rec.ClusterName)}, + {Key: aws.String(clusterEKSNodegroupTagKey), Value: aws.String(rec.Name)}, + }, + }}, + } + res, err := s.deps.Worker.RunWorkerInstance(runInput, accountID) + if err != nil { + return ids, fmt.Errorf("run worker %d/%d: %w", i+1, count, err) + } + for _, inst := range res.Instances { + if id := aws.StringValue(inst.InstanceId); id != "" { + ids = append(ids, id) + } + } + } + return ids, nil +} + +func (s *EKSServiceImpl) describeNodegroup(acctKV nats.KeyValue, input *eks.DescribeNodegroupInput) (*eks.DescribeNodegroupOutput, error) { + cluster := aws.StringValue(input.ClusterName) + ng := aws.StringValue(input.NodegroupName) + if cluster == "" || ng == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + rec, err := GetNodegroupRecord(acctKV, cluster, ng) + if err != nil { + if errors.Is(err, ErrNodegroupNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return &eks.DescribeNodegroupOutput{Nodegroup: nodegroupRecordToAWS(rec)}, nil +} + +func (s *EKSServiceImpl) listNodegroups(acctKV nats.KeyValue, input *eks.ListNodegroupsInput) (*eks.ListNodegroupsOutput, error) { + cluster := aws.StringValue(input.ClusterName) + if cluster == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + if _, err := GetClusterMeta(acctKV, cluster); err != nil { + if errors.Is(err, ErrClusterNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + recs, err := ListNodegroupRecords(acctKV, cluster) + if err != nil { + return nil, err + } + names := make([]string, 0, len(recs)) + for _, rec := range recs { + names = append(names, rec.Name) + } + return &eks.ListNodegroupsOutput{Nodegroups: aws.StringSlice(names)}, nil +} + +func (s *EKSServiceImpl) updateNodegroupConfig(acctKV nats.KeyValue, input *eks.UpdateNodegroupConfigInput, accountID string) (*eks.UpdateNodegroupConfigOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + ng := aws.StringValue(input.NodegroupName) + if cluster == "" || ng == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + meta, err := GetClusterMeta(acctKV, cluster) + if err != nil { + if errors.Is(err, ErrClusterNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + rec, err := GetNodegroupRecord(acctKV, cluster, ng) + if err != nil { + if errors.Is(err, ErrNodegroupNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + + if input.ScalingConfig != nil { + if v := input.ScalingConfig.MinSize; v != nil { + rec.ScalingMin = *v + } + if v := input.ScalingConfig.MaxSize; v != nil { + rec.ScalingMax = *v + } + if v := input.ScalingConfig.DesiredSize; v != nil { + rec.ScalingDesired = *v + } + } + if input.Labels != nil { + rec.Labels = applyLabelUpdate(rec.Labels, input.Labels) + } + + if err := s.reconcileWorkerCount(acctKV, accountID, rec, meta); err != nil { + // reconcileWorkerCount may have launched some workers before failing; their + // IDs are already on rec. Persist so DeleteNodegroup can reclaim them rather + // than leaking the partial scale-up. + rec.ModifiedAt = time.Now().UTC() + if perr := PutNodegroupRecord(acctKV, rec); perr != nil { + slog.Error("updateNodegroupConfig: persist after reconcile failure", + "cluster", cluster, "nodegroup", ng, "err", perr) + } + return nil, err + } + + rec.ModifiedAt = time.Now().UTC() + if err := PutNodegroupRecord(acctKV, rec); err != nil { + return nil, err + } + + return &eks.UpdateNodegroupConfigOutput{Update: &eks.Update{ + Id: aws.String(uuid.NewString()), + Status: aws.String(eks.UpdateStatusSuccessful), + Type: aws.String(eks.UpdateTypeConfigUpdate), + CreatedAt: aws.Time(rec.ModifiedAt), + }}, nil +} + +// reconcileWorkerCount launches or terminates workers so len(InstanceIDs) +// matches ScalingDesired. Surplus removal terminates the last (highest-index) +// instance IDs so scale-down is deterministic. +func (s *EKSServiceImpl) reconcileWorkerCount(acctKV nats.KeyValue, accountID string, rec *NodegroupRecord, meta *ClusterMeta) error { + current := len(rec.InstanceIDs) + desired := int(rec.ScalingDesired) + + switch { + case desired > current: + token, err := s.decryptNodeToken(acctKV, rec.ClusterName) + if err != nil { + return fmt.Errorf("decrypt node token: %w", err) + } + amiID, err := lookupEKSServerAMI(s.deps.Image, accountID) + if err != nil { + if errors.Is(err, ErrEKSServerAMINotFound) { + return errors.New(awserrors.ErrorServiceUnavailable) + } + return fmt.Errorf("resolve eks-node AMI: %w", err) + } + _, ngSGID, err := EnsureClusterSGs(s.deps.VPCSG, accountID, rec.ClusterName, meta.ResourcesVpcConfig.VpcId) + if err != nil { + return fmt.Errorf("ensure cluster SGs: %w", err) + } + newIDs, err := s.launchWorkers(accountID, rec, meta, ngSGID, amiID, token, desired-current) + rec.InstanceIDs = append(rec.InstanceIDs, newIDs...) + if err != nil { + return fmt.Errorf("launch workers: %w", err) + } + case desired < current: + surplus := rec.InstanceIDs[desired:] + if err := s.deps.Worker.TerminateWorkerInstances(surplus, accountID); err != nil { + return fmt.Errorf("terminate surplus workers: %w", err) + } + rec.InstanceIDs = rec.InstanceIDs[:desired] + } + return nil +} + +func (s *EKSServiceImpl) updateNodegroupVersion(input *eks.UpdateNodegroupVersionInput) (*eks.UpdateNodegroupVersionOutput, error) { + // Worker AMI version upgrades (drain + replace) land in a follow-up; v1 + // nodegroups are fixed at their create-time version. + if input != nil { + slog.Info("UpdateNodegroupVersion not implemented in v1", + "cluster", aws.StringValue(input.ClusterName), "nodegroup", aws.StringValue(input.NodegroupName)) + } + return nil, notImpl() +} + +func (s *EKSServiceImpl) deleteNodegroup(acctKV nats.KeyValue, input *eks.DeleteNodegroupInput, accountID string) (*eks.DeleteNodegroupOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + ng := aws.StringValue(input.NodegroupName) + if cluster == "" || ng == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + rec, err := GetNodegroupRecord(acctKV, cluster, ng) + if err != nil { + if errors.Is(err, ErrNodegroupNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + + if len(rec.InstanceIDs) > 0 { + if err := s.deps.Worker.TerminateWorkerInstances(rec.InstanceIDs, accountID); err != nil { + return nil, fmt.Errorf("terminate workers: %w", err) + } + } + if err := DeleteNodegroupRecord(acctKV, cluster, ng); err != nil { + return nil, err + } + + rec.Status = eks.NodegroupStatusDeleting + return &eks.DeleteNodegroupOutput{Nodegroup: nodegroupRecordToAWS(rec)}, nil +} + +// decryptNodeToken reads + decrypts the cluster's K3s join token from KV. +func (s *EKSServiceImpl) decryptNodeToken(kv nats.KeyValue, cluster string) (string, error) { + if kv == nil { + return "", errors.New("eks: nil KV for node token") + } + entry, err := kv.Get(NodeTokenKey(cluster)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return "", errors.New("eks: cluster join token not yet provisioned") + } + return "", fmt.Errorf("kv get node token: %w", err) + } + token, err := handlers_iam.DecryptSecret(string(entry.Value()), s.deps.MasterKey) + if err != nil { + return "", fmt.Errorf("decrypt node token: %w", err) + } + if token == "" { + return "", errors.New("eks: decrypted node token is empty") + } + return token, nil +} + +func (s *EKSServiceImpl) markNodegroupFailed(kv nats.KeyValue, cluster, ng, reason string) { + rec, err := GetNodegroupRecord(kv, cluster, ng) + if err != nil { + return + } + rec.Status = eks.NodegroupStatusCreateFailed + rec.StatusReason = reason + rec.ModifiedAt = time.Now().UTC() + if err := PutNodegroupRecord(kv, rec); err != nil { + slog.Warn("markNodegroupFailed: persist failed", "cluster", cluster, "nodegroup", ng, "err", err) + } +} + +// scalingFromInput derives min/max/desired from a NodegroupScalingConfig, +// defaulting to a single node when the caller omits the config or fields. +func scalingFromInput(cfg *eks.NodegroupScalingConfig) (minSize, maxSize, desired int64) { + minSize, maxSize, desired = 1, 1, 1 + if cfg == nil { + return minSize, maxSize, desired + } + if cfg.MinSize != nil { + minSize = *cfg.MinSize + } + if cfg.DesiredSize != nil { + desired = *cfg.DesiredSize + } else { + desired = minSize + } + if cfg.MaxSize != nil { + maxSize = *cfg.MaxSize + } else if desired > maxSize { + maxSize = desired + } + return minSize, maxSize, desired +} + +// applyLabelUpdate applies an UpdateLabelsPayload (addOrUpdate + removeLabels) +// onto the record's label map. +func applyLabelUpdate(existing map[string]string, payload *eks.UpdateLabelsPayload) map[string]string { + if payload == nil { + return existing + } + out := map[string]string{} + maps.Copy(out, existing) + for k, v := range payload.AddOrUpdateLabels { + out[k] = aws.StringValue(v) + } + for _, k := range payload.RemoveLabels { + delete(out, aws.StringValue(k)) + } + return out +} + +func nodegroupRecordToAWS(rec *NodegroupRecord) *eks.Nodegroup { + if rec == nil { + return nil + } + out := &eks.Nodegroup{ + ClusterName: aws.String(rec.ClusterName), + NodegroupName: aws.String(rec.Name), + NodegroupArn: aws.String(rec.Arn), + Status: aws.String(rec.Status), + Subnets: aws.StringSlice(rec.Subnets), + InstanceTypes: aws.StringSlice(rec.InstanceTypes), + AmiType: aws.String(rec.AMIType), + NodeRole: aws.String(rec.NodeRole), + Version: aws.String(rec.Version), + CreatedAt: aws.Time(rec.CreatedAt), + ModifiedAt: aws.Time(rec.ModifiedAt), + ScalingConfig: &eks.NodegroupScalingConfig{ + MinSize: aws.Int64(rec.ScalingMin), + MaxSize: aws.Int64(rec.ScalingMax), + DesiredSize: aws.Int64(rec.ScalingDesired), + }, + Resources: &eks.NodegroupResources{}, + } + if rec.DiskSize > 0 { + out.DiskSize = aws.Int64(rec.DiskSize) + } + if len(rec.Labels) > 0 { + out.Labels = aws.StringMap(rec.Labels) + } + if rec.StatusReason != "" { + out.Health = &eks.NodegroupHealth{Issues: []*eks.Issue{{ + Code: aws.String(eks.NodegroupIssueCodeNodeCreationFailure), + Message: aws.String(rec.StatusReason), + ResourceIds: aws.StringSlice(rec.InstanceIDs), + }}} + } + return out +} diff --git a/spinifex/handlers/eks/nodegroup_test.go b/spinifex/handlers/eks/nodegroup_test.go new file mode 100644 index 000000000..d3432f9f3 --- /dev/null +++ b/spinifex/handlers/eks/nodegroup_test.go @@ -0,0 +1,321 @@ +package handlers_eks + +import ( + "errors" + "strconv" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// fakeWorkerLauncher records RunWorkerInstance / TerminateWorkerInstances calls +// and hands back sequential instance IDs so tests can assert on the IDs a +// nodegroup tracks. +type fakeWorkerLauncher struct { + runCalls []*ec2.RunInstancesInput + terminateCalls [][]string + + nextID int + runErr error + termErr error +} + +var _ WorkerLauncher = (*fakeWorkerLauncher)(nil) + +func newFakeWorkerLauncher() *fakeWorkerLauncher { + return &fakeWorkerLauncher{} +} + +func (f *fakeWorkerLauncher) RunWorkerInstance(input *ec2.RunInstancesInput, _ string) (*ec2.Reservation, error) { + f.runCalls = append(f.runCalls, input) + if f.runErr != nil { + return nil, f.runErr + } + f.nextID++ + id := "i-worker" + strconv.Itoa(f.nextID) + return &ec2.Reservation{Instances: []*ec2.Instance{{InstanceId: aws.String(id)}}}, nil +} + +func (f *fakeWorkerLauncher) TerminateWorkerInstances(ids []string, _ string) error { + f.terminateCalls = append(f.terminateCalls, ids) + return f.termErr +} + +// seedActiveClusterWithToken lays down an ACTIVE cluster meta with the control- +// plane ENI IP + VPC populated and an encrypted K3s join token, so the +// nodegroup path can run end-to-end. +func seedActiveClusterWithToken(t *testing.T, f *eksServiceFixture, cluster string) { + t.Helper() + meta := &ClusterMeta{ + Name: cluster, + Status: ClusterStatusActive, + Version: "1.32", + ControlPlaneENIIP: "10.0.1.42", + ResourcesVpcConfig: &ClusterVpcConfig{ + SubnetIds: []string{"subnet-aaa"}, + VpcId: "vpc-aaa", + }, + } + require.NoError(t, PutClusterMeta(f.kv, meta)) + + ct, err := handlers_iam.EncryptSecret("K10node-join-token::server:abc", bootstrapTestMasterKey) + require.NoError(t, err) + _, err = f.kv.Put(NodeTokenKey(cluster), []byte(ct)) + require.NoError(t, err) +} + +func createNGInput(cluster, ng string, desired int64) *eks.CreateNodegroupInput { + return &eks.CreateNodegroupInput{ + ClusterName: aws.String(cluster), + NodegroupName: aws.String(ng), + NodeRole: aws.String("arn:aws:iam::111122223333:role/eks-node"), + Subnets: aws.StringSlice([]string{"subnet-aaa"}), + ScalingConfig: &eks.NodegroupScalingConfig{ + MinSize: aws.Int64(1), + MaxSize: aws.Int64(3), + DesiredSize: aws.Int64(desired), + }, + } +} + +func TestNodegroupRecord_CRUDRoundTrip(t *testing.T) { + _, _, js := testutil.StartTestJetStream(t) + kv, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + + rec := &NodegroupRecord{ + ClusterName: "c1", + Name: "ng1", + Arn: "arn:aws:eks:us-east-1:111122223333:nodegroup/c1/ng1/abc", + Status: eks.NodegroupStatusActive, + Subnets: []string{"subnet-aaa"}, + InstanceTypes: []string{"t3.medium"}, + ScalingMin: 1, + ScalingMax: 3, + ScalingDesired: 2, + InstanceIDs: []string{"i-1", "i-2"}, + } + require.NoError(t, PutNodegroupRecord(kv, rec)) + + got, err := GetNodegroupRecord(kv, "c1", "ng1") + require.NoError(t, err) + assert.Equal(t, rec.InstanceIDs, got.InstanceIDs) + assert.Equal(t, int64(2), got.ScalingDesired) + + list, err := ListNodegroupRecords(kv, "c1") + require.NoError(t, err) + require.Len(t, list, 1) + assert.Equal(t, "ng1", list[0].Name) + + require.NoError(t, DeleteNodegroupRecord(kv, "c1", "ng1")) + _, err = GetNodegroupRecord(kv, "c1", "ng1") + assert.ErrorIs(t, err, ErrNodegroupNotFound) + + // Delete of an absent record is idempotent. + require.NoError(t, DeleteNodegroupRecord(kv, "c1", "ng1")) +} + +func TestBuildAgentUserData_Shape(t *testing.T) { + ud := buildAgentUserData(agentUserDataInput{ + ClusterName: "c1", + NodegroupName: "ng1", + ControlPlaneENIIP: "10.0.1.42", + JoinToken: "K10secret::server:xyz", + NodeName: "c1-ng1-abc123de", + }) + + assert.True(t, strings.HasPrefix(ud, "#cloud-config\n")) + assert.Contains(t, ud, "SPINIFEX_K3S_ROLE=agent") + assert.Contains(t, ud, "K3S_URL=https://10.0.1.42:6443") + assert.Contains(t, ud, "K3S_TOKEN=K10secret::server:xyz") + assert.Contains(t, ud, "K3S_NODE_NAME=c1-ng1-abc123de") + assert.Contains(t, ud, "eks.amazonaws.com/nodegroup=ng1") + assert.Contains(t, ud, agentEnvPath) + // IMDS on-link route line present. + assert.Contains(t, ud, "ip route replace "+imdsServerIP+"/32") + // Exactly one write_files block (single "write_files:" key). + assert.Equal(t, 1, strings.Count(ud, "write_files:")) +} + +func TestEnsureNodegroupSGRules_AuthorizesExpectedRules(t *testing.T) { + sg := newFakeSGProvisioner() + + require.NoError(t, EnsureNodegroupSGRules(sg, testAccountID, "c1", "sg-cp", "sg-ng")) + + require.Len(t, sg.authorizeCalls, 6) + + // Verify the apiserver rule: 6443/tcp on cpSG sourced from ngSG. + var found6443 bool + for _, call := range sg.authorizeCalls { + require.Len(t, call.IpPermissions, 1) + perm := call.IpPermissions[0] + if aws.StringValue(call.GroupId) == "sg-cp" && + aws.StringValue(perm.IpProtocol) == "tcp" && + aws.Int64Value(perm.FromPort) == 6443 { + require.Len(t, perm.UserIdGroupPairs, 1) + assert.Equal(t, "sg-ng", aws.StringValue(perm.UserIdGroupPairs[0].GroupId)) + assert.Empty(t, perm.IpRanges, "rules must be group-to-group, not CIDR") + found6443 = true + } + } + assert.True(t, found6443, "expected agent→apiserver 6443/tcp rule") +} + +func TestEnsureNodegroupSGRules_DuplicateRuleTolerated(t *testing.T) { + sg := newFakeSGProvisioner() + sg.authorizeErr = errors.New(awserrors.ErrorInvalidPermissionDuplicate) + + require.NoError(t, EnsureNodegroupSGRules(sg, testAccountID, "c1", "sg-cp", "sg-ng"), + "duplicate-rule error must be treated as success") +} + +func TestCreateNodegroup_HappyPath(t *testing.T) { + f := newEKSServiceFixture(t) + seedActiveClusterWithToken(t, f, "c1") + + out, err := f.svc.CreateNodegroup(createNGInput("c1", "ng1", 2), testAccountID) + require.NoError(t, err) + require.NotNil(t, out.Nodegroup) + assert.Equal(t, eks.NodegroupStatusActive, aws.StringValue(out.Nodegroup.Status)) + assert.Equal(t, int64(2), aws.Int64Value(out.Nodegroup.ScalingConfig.DesiredSize)) + assert.Contains(t, aws.StringValue(out.Nodegroup.NodegroupArn), ":nodegroup/c1/ng1/") + + // Two workers launched and tracked. + assert.Len(t, f.worker.runCalls, 2) + rec, err := GetNodegroupRecord(f.kv, "c1", "ng1") + require.NoError(t, err) + assert.Len(t, rec.InstanceIDs, 2) + + // Workers go through the customer path: no ManagedBy tag, SG = nodegroup SG. + for _, call := range f.worker.runCalls { + require.NotEmpty(t, call.SecurityGroupIds) + assert.Equal(t, "subnet-aaa", aws.StringValue(call.SubnetId)) + for _, ts := range call.TagSpecifications { + for _, tg := range ts.Tags { + assert.NotEqual(t, "spinifex:managed-by", aws.StringValue(tg.Key)) + } + } + } + + // SG ingress rules were authorized. + assert.NotEmpty(t, f.sg.authorizeCalls) + + // Duplicate create → ResourceInUseException. + _, err = f.svc.CreateNodegroup(createNGInput("c1", "ng1", 2), testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceInUse) +} + +func TestCreateNodegroup_ClusterNotActive(t *testing.T) { + f := newEKSServiceFixture(t) + require.NoError(t, PutClusterMeta(f.kv, &ClusterMeta{ + Name: "c1", + Status: ClusterStatusCreating, + ControlPlaneENIIP: "10.0.1.42", + ResourcesVpcConfig: &ClusterVpcConfig{VpcId: "vpc-aaa"}, + })) + + _, err := f.svc.CreateNodegroup(createNGInput("c1", "ng1", 1), testAccountID) + require.EqualError(t, err, awserrors.ErrorInvalidRequest) +} + +func TestCreateNodegroup_UnknownCluster(t *testing.T) { + f := newEKSServiceFixture(t) + _, err := f.svc.CreateNodegroup(createNGInput("ghost", "ng1", 1), testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +func TestDescribeAndListNodegroups(t *testing.T) { + f := newEKSServiceFixture(t) + seedActiveClusterWithToken(t, f, "c1") + _, err := f.svc.CreateNodegroup(createNGInput("c1", "ng1", 1), testAccountID) + require.NoError(t, err) + + desc, err := f.svc.DescribeNodegroup(&eks.DescribeNodegroupInput{ + ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1"), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, "ng1", aws.StringValue(desc.Nodegroup.NodegroupName)) + assert.Equal(t, "c1", aws.StringValue(desc.Nodegroup.ClusterName)) + + list, err := f.svc.ListNodegroups(&eks.ListNodegroupsInput{ClusterName: aws.String("c1")}, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{"ng1"}, aws.StringValueSlice(list.Nodegroups)) + + _, err = f.svc.DescribeNodegroup(&eks.DescribeNodegroupInput{ + ClusterName: aws.String("c1"), NodegroupName: aws.String("ghost"), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +func TestUpdateNodegroupConfig_ScaleUpAndDown(t *testing.T) { + f := newEKSServiceFixture(t) + seedActiveClusterWithToken(t, f, "c1") + _, err := f.svc.CreateNodegroup(createNGInput("c1", "ng1", 1), testAccountID) + require.NoError(t, err) + require.Len(t, f.worker.runCalls, 1) + + // Scale up 1 → 3: two new workers launched. + upOut, err := f.svc.UpdateNodegroupConfig(&eks.UpdateNodegroupConfigInput{ + ClusterName: aws.String("c1"), + NodegroupName: aws.String("ng1"), + ScalingConfig: &eks.NodegroupScalingConfig{DesiredSize: aws.Int64(3)}, + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, eks.UpdateStatusSuccessful, aws.StringValue(upOut.Update.Status)) + assert.Len(t, f.worker.runCalls, 3) + rec, err := GetNodegroupRecord(f.kv, "c1", "ng1") + require.NoError(t, err) + assert.Len(t, rec.InstanceIDs, 3) + + // Scale down 3 → 1: surplus (last two) terminated. + _, err = f.svc.UpdateNodegroupConfig(&eks.UpdateNodegroupConfigInput{ + ClusterName: aws.String("c1"), + NodegroupName: aws.String("ng1"), + ScalingConfig: &eks.NodegroupScalingConfig{DesiredSize: aws.Int64(1)}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, f.worker.terminateCalls, 1) + assert.Len(t, f.worker.terminateCalls[0], 2) + rec, err = GetNodegroupRecord(f.kv, "c1", "ng1") + require.NoError(t, err) + assert.Len(t, rec.InstanceIDs, 1) +} + +func TestDeleteNodegroup_TerminatesAndIsIdempotent(t *testing.T) { + f := newEKSServiceFixture(t) + seedActiveClusterWithToken(t, f, "c1") + _, err := f.svc.CreateNodegroup(createNGInput("c1", "ng1", 2), testAccountID) + require.NoError(t, err) + + _, err = f.svc.DeleteNodegroup(&eks.DeleteNodegroupInput{ + ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1"), + }, testAccountID) + require.NoError(t, err) + require.Len(t, f.worker.terminateCalls, 1) + assert.Len(t, f.worker.terminateCalls[0], 2) + + _, err = GetNodegroupRecord(f.kv, "c1", "ng1") + assert.ErrorIs(t, err, ErrNodegroupNotFound) + + // Second delete → ResourceNotFoundException (record already gone). + _, err = f.svc.DeleteNodegroup(&eks.DeleteNodegroupInput{ + ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1"), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +func TestUpdateNodegroupVersion_NotImplemented(t *testing.T) { + f := newEKSServiceFixture(t) + _, err := f.svc.UpdateNodegroupVersion(&eks.UpdateNodegroupVersionInput{ + ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1"), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorNotImplemented) +} diff --git a/spinifex/handlers/eks/nodegroup_userdata.go b/spinifex/handlers/eks/nodegroup_userdata.go new file mode 100644 index 000000000..40329d2bd --- /dev/null +++ b/spinifex/handlers/eks/nodegroup_userdata.go @@ -0,0 +1,90 @@ +package handlers_eks + +import ( + "fmt" + "net" + "strings" +) + +// agentUserDataInput is the input shape for buildAgentUserData. ControlPlaneENIIP +// is the cluster's in-VPC control-plane ENI private IP (the K3s join target); +// JoinToken is the decrypted K3s node token. +type agentUserDataInput struct { + ClusterName string + NodegroupName string + ControlPlaneENIIP string + JoinToken string + NodeName string +} + +// buildAgentUserData renders the cloud-config YAML a nodegroup worker consumes +// at first boot. It mirrors buildK3sUserData: a bootcmd resolver fix, a single +// write_files block (first-boot.env + agent.env + the IMDS on-link route +// script), and a runcmd that enables the OpenRC `local` service. Output is +// unencoded YAML; the caller base64-wraps it for the RunInstances UserData +// field. The eks-node-role first-boot selector reads SPINIFEX_K3S_ROLE=agent +// (and the presence of agent.env) to enable the k3s-agent service, which sources +// agent.env for K3S_URL/K3S_TOKEN. +func buildAgentUserData(in agentUserDataInput) string { + // K3S_URL targets the control-plane ENI's in-VPC private IP on :6443 + // because the cluster NLB DNS endpoint is not resolvable in-VPC yet. + // Workers share the VPC with the server, so this is directly reachable and + // TLS-pinned by the join token's CA hash (SAN/hostname irrelevant for the + // k3s agent join). Switch to the NLB endpoint on :443 once authoritative + // in-VPC DNS is available. + k3sURL := "https://" + net.JoinHostPort(in.ControlPlaneENIIP, "6443") + nodeLabel := "eks.amazonaws.com/nodegroup=" + in.NodegroupName + + envBody := strings.Join([]string{ + "SPINIFEX_K3S_ROLE=agent", + }, "\n") + + agentBody := strings.Join([]string{ + "K3S_URL=" + k3sURL, + "K3S_TOKEN=" + in.JoinToken, + "K3S_NODE_NAME=" + in.NodeName, + "K3S_NODE_LABEL=" + nodeLabel, + }, "\n") + + files := []userDataFile{ + {Path: k3sFirstBootEnvPath, Perms: "0600", Body: envBody}, + {Path: agentEnvPath, Perms: "0600", Body: agentBody}, + { + Path: "/etc/local.d/imds-onlink-route.start", + Perms: "0755", + Body: "#!/bin/sh\n" + + "dev=$(ip route show default | awk '{print $5; exit}')\n" + + "[ -n \"$dev\" ] && ip route replace " + imdsServerIP + "/32 dev \"$dev\" scope link", + }, + } + + var buf strings.Builder + buf.WriteString("#cloud-config\n") + + // Resolver fix via bootcmd (not write_files) for the same reason as the + // server path: /etc/resolv.conf is a dangling symlink on the Alpine AMI, so + // write_files would follow the dead link and abort the whole block. + // Containerd needs a working resolver to pull the worker's system images. + buf.WriteString("bootcmd:\n") + buf.WriteString(" - rm -f " + k3sResolvConfPath + "\n") + fmt.Fprintf(&buf, " - printf '%s\\n' > %s\n", + strings.ReplaceAll(k3sResolvConf, "\n", "\\n"), k3sResolvConfPath) + + buf.WriteString("write_files:\n") + for _, f := range files { + fmt.Fprintf(&buf, " - path: %s\n", f.Path) + fmt.Fprintf(&buf, " permissions: '%s'\n", f.Perms) + buf.WriteString(" content: |\n") + for line := range strings.SplitSeq(f.Body, "\n") { + buf.WriteString(" ") + buf.WriteString(line) + buf.WriteString("\n") + } + } + + buf.WriteString("runcmd:\n") + buf.WriteString(" - [ rc-update, add, local, default ]\n") + buf.WriteString(" - [ rc-service, local, start ]\n") + + return buf.String() +} diff --git a/spinifex/handlers/eks/oidc_keypair.go b/spinifex/handlers/eks/oidc_keypair.go new file mode 100644 index 000000000..fc81999ea --- /dev/null +++ b/spinifex/handlers/eks/oidc_keypair.go @@ -0,0 +1,214 @@ +package handlers_eks + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/nats-io/nats.go" +) + +// PublicKeyPEMFromPrivate parses a PKCS#8 private-key PEM and returns the PKIX +// public-key PEM. kube-apiserver's --service-account-key-file requires a PUBLIC +// key (ParsePublicKeysPEM rejects private-key blocks), while the matching +// --service-account-signing-key-file takes the private key; the K3s server VM +// needs both, seeded to separate files. +func PublicKeyPEMFromPrivate(privPEM string) (string, error) { + block, _ := pem.Decode([]byte(privPEM)) + if block == nil { + return "", errors.New("eks: PublicKeyPEMFromPrivate: no PEM block in private key") + } + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return "", fmt.Errorf("parse pkcs8 private key: %w", err) + } + signer, ok := key.(crypto.Signer) + if !ok { + return "", errors.New("eks: PublicKeyPEMFromPrivate: private key is not a crypto.Signer") + } + pubDER, err := x509.MarshalPKIXPublicKey(signer.Public()) + if err != nil { + return "", fmt.Errorf("marshal pkix public key: %w", err) + } + return string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubDER})), nil +} + +// oidcJWK and oidcJWKS are the RFC 7517 wire shapes the per-cluster JWKS +// document uses. handlers_sts decodes the same JSON via its own JWK / JWKS +// types — wire compatibility (not Go type identity) is the contract. +type oidcJWK struct { + Kty string `json:"kty"` + Kid string `json:"kid"` + Use string `json:"use,omitempty"` + Alg string `json:"alg,omitempty"` + Crv string `json:"crv,omitempty"` + X string `json:"x,omitempty"` + Y string `json:"y,omitempty"` +} + +type oidcJWKS struct { + Keys []oidcJWK `json:"keys"` +} + +// P-256 coordinate width per SEC1 — JWK requires fixed-length base64url, not +// the variable-length big.Int byte representation. +const p256CoordLen = 32 + +// GenerateClusterOIDCKeypair creates a fresh ECDSA-P256 keypair for the +// cluster, PEM-marshals the private key as PKCS8, envelope-encrypts the PEM +// with masterKey, writes the ciphertext to OIDCSigningKeyKey and the JWKS +// document to OIDCJWKSKey. Returns the plaintext private-key PEM and the JWKS +// bytes so the caller (CreateCluster) can pass them straight to user-data +// without a follow-up KV read + decrypt. +func GenerateClusterOIDCKeypair(kv nats.KeyValue, clusterName string, masterKey []byte) (privPEM string, jwksBytes []byte, err error) { + if clusterName == "" { + return "", nil, errors.New("eks: GenerateClusterOIDCKeypair empty cluster name") + } + if len(masterKey) == 0 { + return "", nil, errors.New("eks: GenerateClusterOIDCKeypair empty master key") + } + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return "", nil, fmt.Errorf("generate ECDSA-P256 key: %w", err) + } + + pkcs8, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + return "", nil, fmt.Errorf("marshal pkcs8 private key: %w", err) + } + pemBlock := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: pkcs8}) + + ciphertext, err := handlers_iam.EncryptSecret(string(pemBlock), masterKey) + if err != nil { + return "", nil, fmt.Errorf("encrypt OIDC private key: %w", err) + } + if _, err := kv.Put(OIDCSigningKeyKey(clusterName), []byte(ciphertext)); err != nil { + return "", nil, fmt.Errorf("kv put %s: %w", OIDCSigningKeyKey(clusterName), err) + } + + jwksBytes, err = marshalJWKS(&priv.PublicKey) + if err != nil { + return "", nil, err + } + if _, err := kv.Put(OIDCJWKSKey(clusterName), jwksBytes); err != nil { + return "", nil, fmt.Errorf("kv put %s: %w", OIDCJWKSKey(clusterName), err) + } + return string(pemBlock), jwksBytes, nil +} + +// LoadClusterOIDCPrivateKey reads the encrypted PEM from KV, decrypts with +// masterKey, parses it to *ecdsa.PrivateKey. Returns ErrClusterNotFound when +// the key is absent so DeleteCluster idempotency works. +func LoadClusterOIDCPrivateKey(kv nats.KeyValue, clusterName string, masterKey []byte) (*ecdsa.PrivateKey, error) { + if clusterName == "" { + return nil, errors.New("eks: LoadClusterOIDCPrivateKey empty cluster name") + } + if len(masterKey) == 0 { + return nil, errors.New("eks: LoadClusterOIDCPrivateKey empty master key") + } + entry, err := kv.Get(OIDCSigningKeyKey(clusterName)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, ErrClusterNotFound + } + return nil, fmt.Errorf("kv get %s: %w", OIDCSigningKeyKey(clusterName), err) + } + pemStr, err := handlers_iam.DecryptSecret(string(entry.Value()), masterKey) + if err != nil { + return nil, fmt.Errorf("decrypt OIDC private key: %w", err) + } + block, _ := pem.Decode([]byte(pemStr)) + if block == nil { + return nil, errors.New("eks: PEM decode produced no block") + } + keyAny, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("parse pkcs8 private key: %w", err) + } + priv, ok := keyAny.(*ecdsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("eks: unexpected key type %T (want *ecdsa.PrivateKey)", keyAny) + } + if priv.Curve != elliptic.P256() { + return nil, fmt.Errorf("eks: unexpected curve %s (want P-256)", priv.Curve.Params().Name) + } + return priv, nil +} + +// ZeroizeClusterOIDCKey overwrites the encrypted private-key blob with zeros +// of the same length, then purges the key. DeleteCluster calls this first so +// a crash after this point leaves no recoverable key material. Purge (not +// Delete) drops the key's entire revision history — a Delete leaves a tombstone +// over prior revisions, so the original encrypted PEM would survive in history +// until age/limit rolls it off. The account bucket is History=1 regardless +// (see store.go), so this is defense-in-depth against a widened history. +// Absent key is a no-op — supports idempotent DeleteCluster retries. +func ZeroizeClusterOIDCKey(kv nats.KeyValue, clusterName string) error { + if clusterName == "" { + return errors.New("eks: ZeroizeClusterOIDCKey empty cluster name") + } + key := OIDCSigningKeyKey(clusterName) + entry, err := kv.Get(key) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil + } + return fmt.Errorf("kv get %s: %w", key, err) + } + zero := make([]byte, len(entry.Value())) + if _, err := kv.Put(key, zero); err != nil { + return fmt.Errorf("kv put zero %s: %w", key, err) + } + if err := kv.Purge(key); err != nil { + return fmt.Errorf("kv purge %s: %w", key, err) + } + return nil +} + +// marshalJWKS builds the single-key JWKS document for an ECDSA P-256 +// public key. kid is the base64url SHA-256 of the SPKI DER. +func marshalJWKS(pub *ecdsa.PublicKey) ([]byte, error) { + pubDER, err := x509.MarshalPKIXPublicKey(pub) + if err != nil { + return nil, fmt.Errorf("marshal public key DER: %w", err) + } + kidHash := sha256.Sum256(pubDER) + kid := base64.RawURLEncoding.EncodeToString(kidHash[:]) + + xb := padCoord(pub.X.Bytes()) + yb := padCoord(pub.Y.Bytes()) + + doc := oidcJWKS{Keys: []oidcJWK{{ + Kty: "EC", + Kid: kid, + Use: "sig", + Alg: "ES256", + Crv: "P-256", + X: base64.RawURLEncoding.EncodeToString(xb), + Y: base64.RawURLEncoding.EncodeToString(yb), + }}} + out, err := json.Marshal(&doc) + if err != nil { + return nil, fmt.Errorf("marshal JWKS: %w", err) + } + return out, nil +} + +func padCoord(b []byte) []byte { + if len(b) >= p256CoordLen { + return b + } + out := make([]byte, p256CoordLen) + copy(out[p256CoordLen-len(b):], b) + return out +} diff --git a/spinifex/handlers/eks/oidc_keypair_test.go b/spinifex/handlers/eks/oidc_keypair_test.go new file mode 100644 index 000000000..341e612f1 --- /dev/null +++ b/spinifex/handlers/eks/oidc_keypair_test.go @@ -0,0 +1,228 @@ +package handlers_eks + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "errors" + "testing" + + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var testMasterKey = func() []byte { + k := make([]byte, 32) + if _, err := rand.Read(k); err != nil { + panic(err) + } + return k +}() + +func TestGenerateClusterOIDCKeypair_PersistsJWKSAndEncryptedPrivateKey(t *testing.T) { + kv := newClusterStateTestKV(t) + + _, jwksBytes, err := GenerateClusterOIDCKeypair(kv, "alpha", testMasterKey) + require.NoError(t, err) + require.NotEmpty(t, jwksBytes) + + jwksEntry, err := kv.Get(OIDCJWKSKey("alpha")) + require.NoError(t, err) + assert.Equal(t, jwksBytes, jwksEntry.Value()) + + keyEntry, err := kv.Get(OIDCSigningKeyKey("alpha")) + require.NoError(t, err) + assert.NotEmpty(t, keyEntry.Value()) + assert.NotContains(t, string(keyEntry.Value()), "BEGIN PRIVATE KEY", + "encrypted blob must not contain a PEM header") +} + +// PublicKeyPEMFromPrivate must yield a PKIX PUBLIC KEY PEM matching the private +// key's public part — kube-apiserver's --service-account-key-file requires a +// public key and crash-loops on a private-key PEM. +func TestPublicKeyPEMFromPrivate_RoundTrips(t *testing.T) { + kv := newClusterStateTestKV(t) + privPEM, _, err := GenerateClusterOIDCKeypair(kv, "alpha", testMasterKey) + require.NoError(t, err) + + pubPEM, err := PublicKeyPEMFromPrivate(privPEM) + require.NoError(t, err) + require.Contains(t, pubPEM, "BEGIN PUBLIC KEY") + assert.NotContains(t, pubPEM, "PRIVATE KEY") + + block, _ := pem.Decode([]byte(pubPEM)) + require.NotNil(t, block) + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + require.NoError(t, err) + pubEC, ok := pub.(*ecdsa.PublicKey) + require.True(t, ok) + + stored, err := LoadClusterOIDCPrivateKey(kv, "alpha", testMasterKey) + require.NoError(t, err) + assert.True(t, stored.PublicKey.Equal(pubEC), "derived public key must match the private key") +} + +func TestPublicKeyPEMFromPrivate_RejectsGarbage(t *testing.T) { + _, err := PublicKeyPEMFromPrivate("not a pem") + require.Error(t, err) + + _, err = PublicKeyPEMFromPrivate("-----BEGIN PRIVATE KEY-----\nbm90LWtleQ==\n-----END PRIVATE KEY-----\n") + require.Error(t, err) +} + +// The generator returns the plaintext private-key PEM directly so CreateCluster +// avoids a second KV read + decrypt; it must match the key persisted in KV. +func TestGenerateClusterOIDCKeypair_ReturnsPrivateKeyPEMMatchingStored(t *testing.T) { + kv := newClusterStateTestKV(t) + + privPEM, _, err := GenerateClusterOIDCKeypair(kv, "alpha", testMasterKey) + require.NoError(t, err) + require.Contains(t, privPEM, "BEGIN PRIVATE KEY", "returned PEM must be plaintext") + + block, _ := pem.Decode([]byte(privPEM)) + require.NotNil(t, block) + returned, err := x509.ParsePKCS8PrivateKey(block.Bytes) + require.NoError(t, err) + returnedEC, ok := returned.(*ecdsa.PrivateKey) + require.True(t, ok) + + stored, err := LoadClusterOIDCPrivateKey(kv, "alpha", testMasterKey) + require.NoError(t, err) + assert.True(t, stored.Equal(returnedEC), "returned PEM must match the persisted key") +} + +func TestGenerateClusterOIDCKeypair_JWKSShapeIsRFC7517EC_P256(t *testing.T) { + kv := newClusterStateTestKV(t) + + _, jwksBytes, err := GenerateClusterOIDCKeypair(kv, "alpha", testMasterKey) + require.NoError(t, err) + + var doc oidcJWKS + require.NoError(t, json.Unmarshal(jwksBytes, &doc)) + require.Len(t, doc.Keys, 1) + + k := doc.Keys[0] + assert.Equal(t, "EC", k.Kty) + assert.Equal(t, "P-256", k.Crv) + assert.Equal(t, "ES256", k.Alg) + assert.Equal(t, "sig", k.Use) + assert.NotEmpty(t, k.Kid) + + xBytes, err := base64.RawURLEncoding.DecodeString(k.X) + require.NoError(t, err) + assert.Len(t, xBytes, p256CoordLen, "JWK x must be 32-byte SEC1 coordinate") + yBytes, err := base64.RawURLEncoding.DecodeString(k.Y) + require.NoError(t, err) + assert.Len(t, yBytes, p256CoordLen, "JWK y must be 32-byte SEC1 coordinate") +} + +func TestGenerateClusterOIDCKeypair_TwoCallsProduceDistinctKeys(t *testing.T) { + kv := newClusterStateTestKV(t) + + _, a, err := GenerateClusterOIDCKeypair(kv, "alpha", testMasterKey) + require.NoError(t, err) + _, b, err := GenerateClusterOIDCKeypair(kv, "beta", testMasterKey) + require.NoError(t, err) + assert.NotEqual(t, a, b, "JWKS for distinct clusters must differ") +} + +func TestGenerateClusterOIDCKeypair_EmptyArgsRejected(t *testing.T) { + kv := newClusterStateTestKV(t) + + _, _, err := GenerateClusterOIDCKeypair(kv, "", testMasterKey) + require.Error(t, err) + _, _, err = GenerateClusterOIDCKeypair(kv, "alpha", nil) + require.Error(t, err) +} + +func TestLoadClusterOIDCPrivateKey_RoundTripMatchesJWKSPublic(t *testing.T) { + kv := newClusterStateTestKV(t) + + _, jwksBytes, err := GenerateClusterOIDCKeypair(kv, "alpha", testMasterKey) + require.NoError(t, err) + + priv, err := LoadClusterOIDCPrivateKey(kv, "alpha", testMasterKey) + require.NoError(t, err) + require.NotNil(t, priv) + assert.Equal(t, elliptic.P256(), priv.Curve) + + var doc oidcJWKS + require.NoError(t, json.Unmarshal(jwksBytes, &doc)) + require.Len(t, doc.Keys, 1) + + xExpected, err := base64.RawURLEncoding.DecodeString(doc.Keys[0].X) + require.NoError(t, err) + yExpected, err := base64.RawURLEncoding.DecodeString(doc.Keys[0].Y) + require.NoError(t, err) + + assert.Equal(t, padCoord(priv.X.Bytes()), xExpected) + assert.Equal(t, padCoord(priv.Y.Bytes()), yExpected) +} + +func TestLoadClusterOIDCPrivateKey_WrongMasterKeyFails(t *testing.T) { + kv := newClusterStateTestKV(t) + _, _, err := GenerateClusterOIDCKeypair(kv, "alpha", testMasterKey) + require.NoError(t, err) + + other := make([]byte, 32) + other[0] = 0xff + _, err = LoadClusterOIDCPrivateKey(kv, "alpha", other) + require.Error(t, err) +} + +func TestLoadClusterOIDCPrivateKey_MissingReturnsErrClusterNotFound(t *testing.T) { + kv := newClusterStateTestKV(t) + _, err := LoadClusterOIDCPrivateKey(kv, "ghost", testMasterKey) + require.ErrorIs(t, err, ErrClusterNotFound) +} + +func TestZeroizeClusterOIDCKey_DeletesKeyAndLeavesSiblingsIntact(t *testing.T) { + kv := newClusterStateTestKV(t) + _, _, err := GenerateClusterOIDCKeypair(kv, "alpha", testMasterKey) + require.NoError(t, err) + + require.NoError(t, ZeroizeClusterOIDCKey(kv, "alpha")) + + _, err = kv.Get(OIDCSigningKeyKey("alpha")) + assert.True(t, errors.Is(err, nats.ErrKeyNotFound)) + + jwks, err := kv.Get(OIDCJWKSKey("alpha")) + require.NoError(t, err, "JWKS must survive zeroize (it carries no secret material)") + assert.NotEmpty(t, jwks.Value()) +} + +func TestZeroizeClusterOIDCKey_MissingIsNoop(t *testing.T) { + kv := newClusterStateTestKV(t) + require.NoError(t, ZeroizeClusterOIDCKey(kv, "ghost")) +} + +func TestZeroizeClusterOIDCKey_EmptyNameRejected(t *testing.T) { + kv := newClusterStateTestKV(t) + require.Error(t, ZeroizeClusterOIDCKey(kv, "")) +} + +func TestMarshalJWKS_DeterministicForSameKey(t *testing.T) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + a, err := marshalJWKS(&priv.PublicKey) + require.NoError(t, err) + b, err := marshalJWKS(&priv.PublicKey) + require.NoError(t, err) + assert.Equal(t, a, b) +} + +func TestPadCoord_LeftPadsShortBigIntBytes(t *testing.T) { + got := padCoord([]byte{0x01, 0x02}) + require.Len(t, got, p256CoordLen) + assert.Equal(t, byte(0x01), got[p256CoordLen-2]) + assert.Equal(t, byte(0x02), got[p256CoordLen-1]) + for _, b := range got[:p256CoordLen-2] { + assert.Zero(t, b) + } +} diff --git a/spinifex/handlers/eks/reconciler_registry.go b/spinifex/handlers/eks/reconciler_registry.go new file mode 100644 index 000000000..5fc72d5a0 --- /dev/null +++ b/spinifex/handlers/eks/reconciler_registry.go @@ -0,0 +1,178 @@ +package handlers_eks + +import ( + "context" + "errors" + "log/slog" + "sync" + + "github.com/nats-io/nats.go" +) + +// ErrLeaseHeld signals that a reconciler was not spawned because another node +// holds the leader lease. Spawn treats it as benign: it drops the registry +// entry (rather than recording a phantom holder) so a later Spawn re-attempts +// once the holder's lease TTL lapses. +var ErrLeaseHeld = errors.New("eks: reconciler lease held by another node") + +// ReconcilerRegistry tracks active per-cluster reconciler goroutines so the +// daemon can spawn, stop, and graceful-shutdown them in unison. One entry per +// (accountID, clusterName) key. +type ReconcilerRegistry struct { + mu sync.Mutex + holders map[string]*reconcilerHandle +} + +type reconcilerHandle struct { + cancel context.CancelFunc + release func() + done chan struct{} +} + +// NewReconcilerRegistry returns an empty registry. +func NewReconcilerRegistry() *ReconcilerRegistry { + return &ReconcilerRegistry{holders: map[string]*reconcilerHandle{}} +} + +// SpawnReconcilerFn is the per-cluster goroutine starter the registry uses. +// Tests inject a fake; production wires a real ClusterReconciler under the +// hood (acquires lease, then loops via Run). +type SpawnReconcilerFn func(ctx context.Context, accountID, clusterName string) (release func(), err error) + +// Spawn launches one reconciler goroutine for (accountID, clusterName). If +// one is already running for that pair, returns nil without spawning a +// duplicate. SpawnFn is responsible for acquiring the leader lease + invoking +// Run; the registry only owns the ctx and the release. +func (r *ReconcilerRegistry) Spawn(parent context.Context, accountID, clusterName string, spawnFn SpawnReconcilerFn) error { + if accountID == "" || clusterName == "" { + return errors.New("eks: ReconcilerRegistry.Spawn empty ids") + } + if spawnFn == nil { + return errors.New("eks: ReconcilerRegistry.Spawn nil SpawnFn") + } + key := registryKey(accountID, clusterName) + + r.mu.Lock() + if _, ok := r.holders[key]; ok { + r.mu.Unlock() + return nil + } + ctx, cancel := context.WithCancel(parent) + done := make(chan struct{}) + h := &reconcilerHandle{cancel: cancel, done: done} + r.holders[key] = h + r.mu.Unlock() + + release, err := spawnFn(ctx, accountID, clusterName) + if err != nil { + r.mu.Lock() + delete(r.holders, key) + r.mu.Unlock() + cancel() + close(done) + if errors.Is(err, ErrLeaseHeld) { + // Lease held elsewhere — not a failure. The entry is already + // dropped above, so a later Spawn retries after the holder's TTL. + return nil + } + return err + } + + r.mu.Lock() + h.release = release + r.mu.Unlock() + + go func() { + <-ctx.Done() + r.mu.Lock() + rel := h.release + r.mu.Unlock() + if rel != nil { + rel() + } + close(done) + }() + + slog.Info("ReconcilerRegistry: spawned", "accountID", accountID, "cluster", clusterName) + return nil +} + +// Stop cancels a single reconciler. Blocks until the goroutine has exited. +// No-op if no reconciler is registered for the key. +func (r *ReconcilerRegistry) Stop(accountID, clusterName string) { + key := registryKey(accountID, clusterName) + r.mu.Lock() + h, ok := r.holders[key] + if ok { + delete(r.holders, key) + } + r.mu.Unlock() + if !ok { + return + } + h.cancel() + <-h.done + slog.Info("ReconcilerRegistry: stopped", "accountID", accountID, "cluster", clusterName) +} + +// StopAll cancels every active reconciler and blocks until all goroutines +// have exited. Called from daemon graceful-shutdown. +func (r *ReconcilerRegistry) StopAll() { + r.mu.Lock() + handles := make([]*reconcilerHandle, 0, len(r.holders)) + for k, h := range r.holders { + handles = append(handles, h) + delete(r.holders, k) + } + r.mu.Unlock() + + for _, h := range handles { + h.cancel() + } + for _, h := range handles { + <-h.done + } +} + +// Has reports whether a reconciler is currently registered for the key. +func (r *ReconcilerRegistry) Has(accountID, clusterName string) bool { + r.mu.Lock() + defer r.mu.Unlock() + _, ok := r.holders[registryKey(accountID, clusterName)] + return ok +} + +func registryKey(accountID, clusterName string) string { + return accountID + "/" + clusterName +} + +// RunClusterReconciler is the production SpawnReconcilerFn: it constructs a +// ClusterReconciler, acquires the leader lease, then drives Run in a +// goroutine. Returns (release, nil) on lease acquired, or (nil, error) on +// constructor failure. If another node holds the lease, returns +// (nil, ErrLeaseHeld) so the registry drops the entry and re-attempts on a +// later tick instead of recording a phantom holder. +func RunClusterReconciler( + ctx context.Context, + leaderKV, acctKV nats.KeyValue, + accountID, clusterName, holderID, healthURL string, + opts ...ReconcilerOption, +) (func(), error) { + r, err := NewClusterReconciler(leaderKV, acctKV, accountID, clusterName, holderID, healthURL, opts...) + if err != nil { + return nil, err + } + release, ok := r.AcquireLease() + if !ok { + slog.Info("RunClusterReconciler: lease held elsewhere, skipping spawn", + "accountID", accountID, "cluster", clusterName) + return nil, ErrLeaseHeld + } + go func() { + if runErr := r.Run(ctx); runErr != nil && !errors.Is(runErr, context.Canceled) { + slog.Info("RunClusterReconciler: Run exited", + "accountID", accountID, "cluster", clusterName, "err", runErr) + } + }() + return release, nil +} diff --git a/spinifex/handlers/eks/reconciler_registry_test.go b/spinifex/handlers/eks/reconciler_registry_test.go new file mode 100644 index 000000000..2494f764f --- /dev/null +++ b/spinifex/handlers/eks/reconciler_registry_test.go @@ -0,0 +1,122 @@ +package handlers_eks + +import ( + "context" + "errors" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestReconcilerRegistry_SpawnAndStop(t *testing.T) { + reg := NewReconcilerRegistry() + + var ( + ran atomic.Bool + released atomic.Bool + ) + fn := func(ctx context.Context, accountID, clusterName string) (func(), error) { + ran.Store(true) + go func() { <-ctx.Done() }() + return func() { released.Store(true) }, nil + } + + require.NoError(t, reg.Spawn(t.Context(), "111122223333", "alpha", fn)) + assert.True(t, reg.Has("111122223333", "alpha")) + assert.True(t, ran.Load()) + + reg.Stop("111122223333", "alpha") + assert.False(t, reg.Has("111122223333", "alpha")) + require.Eventually(t, released.Load, 500*time.Millisecond, 5*time.Millisecond) +} + +func TestReconcilerRegistry_SpawnIdempotent(t *testing.T) { + reg := NewReconcilerRegistry() + var spawnCalls atomic.Int32 + fn := func(ctx context.Context, _, _ string) (func(), error) { + spawnCalls.Add(1) + go func() { <-ctx.Done() }() + return func() {}, nil + } + + require.NoError(t, reg.Spawn(t.Context(), "111122223333", "alpha", fn)) + require.NoError(t, reg.Spawn(t.Context(), "111122223333", "alpha", fn)) + + assert.EqualValues(t, 1, spawnCalls.Load(), "second Spawn must not re-invoke fn") + reg.StopAll() +} + +func TestReconcilerRegistry_SpawnFnErrorRemovesEntry(t *testing.T) { + reg := NewReconcilerRegistry() + fn := func(_ context.Context, _, _ string) (func(), error) { + return nil, errors.New("acquire lease failed") + } + + err := reg.Spawn(t.Context(), "111122223333", "alpha", fn) + require.Error(t, err) + assert.False(t, reg.Has("111122223333", "alpha"), "entry must not linger after spawn failure") +} + +func TestReconcilerRegistry_LeaseHeldDropsEntryAndRetries(t *testing.T) { + reg := NewReconcilerRegistry() + var spawnCalls atomic.Int32 + leaseHeld := atomic.Bool{} + leaseHeld.Store(true) + fn := func(ctx context.Context, _, _ string) (func(), error) { + spawnCalls.Add(1) + if leaseHeld.Load() { + return nil, ErrLeaseHeld + } + go func() { <-ctx.Done() }() + return func() {}, nil + } + + // Lease held elsewhere: Spawn is benign (nil), records no phantom holder. + require.NoError(t, reg.Spawn(t.Context(), "111122223333", "alpha", fn)) + assert.False(t, reg.Has("111122223333", "alpha"), "lease-held must not leave a holder") + + // Holder's TTL lapses; a later Spawn re-invokes fn and now takes over — + // no daemon restart needed. + leaseHeld.Store(false) + require.NoError(t, reg.Spawn(t.Context(), "111122223333", "alpha", fn)) + assert.True(t, reg.Has("111122223333", "alpha")) + assert.EqualValues(t, 2, spawnCalls.Load(), "second Spawn must re-attempt after lease-held") + reg.StopAll() +} + +func TestReconcilerRegistry_StopAllCancelsEvery(t *testing.T) { + reg := NewReconcilerRegistry() + var released atomic.Int32 + fn := func(ctx context.Context, _, _ string) (func(), error) { + go func() { <-ctx.Done() }() + return func() { released.Add(1) }, nil + } + + require.NoError(t, reg.Spawn(t.Context(), "111122223333", "alpha", fn)) + require.NoError(t, reg.Spawn(t.Context(), "111122223333", "beta", fn)) + require.NoError(t, reg.Spawn(t.Context(), "444455556666", "gamma", fn)) + + reg.StopAll() + require.Eventually(t, func() bool { return released.Load() == 3 }, 500*time.Millisecond, 5*time.Millisecond) + assert.False(t, reg.Has("111122223333", "alpha")) + assert.False(t, reg.Has("111122223333", "beta")) + assert.False(t, reg.Has("444455556666", "gamma")) +} + +func TestReconcilerRegistry_StopUnknownKeyNoop(t *testing.T) { + reg := NewReconcilerRegistry() + reg.Stop("111122223333", "ghost") + assert.False(t, reg.Has("111122223333", "ghost")) +} + +func TestReconcilerRegistry_SpawnRejectsBadArgs(t *testing.T) { + reg := NewReconcilerRegistry() + fn := func(_ context.Context, _, _ string) (func(), error) { return func() {}, nil } + + require.Error(t, reg.Spawn(t.Context(), "", "alpha", fn)) + require.Error(t, reg.Spawn(t.Context(), "111122223333", "", fn)) + require.Error(t, reg.Spawn(t.Context(), "111122223333", "alpha", nil)) +} diff --git a/spinifex/handlers/eks/security_groups.go b/spinifex/handlers/eks/security_groups.go new file mode 100644 index 000000000..fd0f964a9 --- /dev/null +++ b/spinifex/handlers/eks/security_groups.go @@ -0,0 +1,262 @@ +package handlers_eks + +import ( + "errors" + "fmt" + "log/slog" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/tags" +) + +// sgProvisioner is the subset of handlers_ec2_vpc.VPCService that the cluster +// SG helpers need. Narrow so tests can fake with hand-rolled recorders without +// implementing the full VPCService surface. +type sgProvisioner interface { + CreateSecurityGroup(input *ec2.CreateSecurityGroupInput, accountID string) (*ec2.CreateSecurityGroupOutput, error) + DescribeSecurityGroups(input *ec2.DescribeSecurityGroupsInput, accountID string) (*ec2.DescribeSecurityGroupsOutput, error) + DeleteSecurityGroup(input *ec2.DeleteSecurityGroupInput, accountID string) (*ec2.DeleteSecurityGroupOutput, error) + AuthorizeSecurityGroupIngress(input *ec2.AuthorizeSecurityGroupIngressInput, accountID string) (*ec2.AuthorizeSecurityGroupIngressOutput, error) +} + +// clusterEKSClusterTagKey is stamped on every cluster-scoped resource so the +// DeleteCluster sweep + the UI hide-from-customer filter can group them. +const clusterEKSClusterTagKey = "spinifex:eks-cluster" + +// clusterEKSRoleTagKey distinguishes the control-plane SG from the nodegroup +// SG when both live in the same VPC. +const clusterEKSRoleTagKey = "spinifex:eks-role" + +// clusterEKSNodegroupTagKey is stamped on worker instances so the nodegroup +// they belong to is recoverable from EC2 tags alone. +const clusterEKSNodegroupTagKey = "spinifex:eks-nodegroup" + +const ( + clusterEKSRoleControlPlane = "control-plane" + clusterEKSRoleNodegroup = "nodegroup" +) + +// ClusterControlPlaneSGName returns the deterministic SG name used for the +// cluster's control-plane SG. Stable so Stage-6 DeleteCluster can locate it +// from clusterName alone. +func ClusterControlPlaneSGName(clusterName string) string { + return fmt.Sprintf("eks-cluster-%s-control-plane-sg", clusterName) +} + +// ClusterNodegroupSGName returns the deterministic SG name used for the +// cluster's nodegroup SG. +func ClusterNodegroupSGName(clusterName string) string { + return fmt.Sprintf("eks-cluster-%s-nodegroup-sg", clusterName) +} + +// EnsureClusterSGs creates (or returns existing) control-plane + nodegroup +// security groups in the customer VPC. Both are tagged +// spinifex:managed-by=eks + spinifex:eks-cluster=. Idempotent: an SG +// whose group-name + vpc-id match the expected pair is reused without a +// second create call. The two creates are independent — a failure on the +// nodegroup SG does not unwind the control-plane SG (DeleteCluster cleans up). +func EnsureClusterSGs(sgp sgProvisioner, accountID, clusterName, vpcID string) (controlPlaneSGID, nodegroupSGID string, err error) { + if clusterName == "" { + return "", "", errors.New("eks: EnsureClusterSGs empty cluster name") + } + if vpcID == "" { + return "", "", errors.New("eks: EnsureClusterSGs empty vpc id") + } + + controlPlaneSGID, err = ensureClusterSG(sgp, accountID, vpcID, clusterName, + ClusterControlPlaneSGName(clusterName), + fmt.Sprintf("EKS control-plane SG for cluster %s", clusterName), + clusterEKSRoleControlPlane) + if err != nil { + return "", "", err + } + + nodegroupSGID, err = ensureClusterSG(sgp, accountID, vpcID, clusterName, + ClusterNodegroupSGName(clusterName), + fmt.Sprintf("EKS nodegroup SG for cluster %s", clusterName), + clusterEKSRoleNodegroup) + if err != nil { + return "", "", err + } + + return controlPlaneSGID, nodegroupSGID, nil +} + +// DeleteClusterSGs removes both cluster SGs in the customer VPC. Missing SGs +// are no-ops so DeleteCluster can retry safely. Delete errors are logged and +// the sweep continues; the first error is returned so the caller surfaces it +// without halting the broader teardown. +func DeleteClusterSGs(sgp sgProvisioner, accountID, clusterName, vpcID string) error { + if clusterName == "" { + return errors.New("eks: DeleteClusterSGs empty cluster name") + } + if vpcID == "" { + return errors.New("eks: DeleteClusterSGs empty vpc id") + } + + var firstErr error + for _, name := range []string{ClusterControlPlaneSGName(clusterName), ClusterNodegroupSGName(clusterName)} { + id, err := lookupSGByName(sgp, accountID, vpcID, name) + if err != nil { + slog.Warn("DeleteClusterSGs: SG lookup failed", "sg", name, "err", err) + if firstErr == nil { + firstErr = err + } + continue + } + if id == "" { + continue + } + if _, err := sgp.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{GroupId: aws.String(id)}, accountID); err != nil { + slog.Warn("DeleteClusterSGs: SG delete failed", "sg", id, "name", name, "err", err) + if firstErr == nil { + firstErr = fmt.Errorf("delete SG %s: %w", id, err) + } + } + } + return firstErr +} + +func ensureClusterSG(sgp sgProvisioner, accountID, vpcID, clusterName, name, description, role string) (string, error) { + if id, err := lookupSGByName(sgp, accountID, vpcID, name); err != nil { + return "", err + } else if id != "" { + return id, nil + } + + out, err := sgp.CreateSecurityGroup(&ec2.CreateSecurityGroupInput{ + GroupName: aws.String(name), + Description: aws.String(description), + VpcId: aws.String(vpcID), + TagSpecifications: []*ec2.TagSpecification{{ + ResourceType: aws.String("security-group"), + Tags: []*ec2.Tag{ + {Key: aws.String(tags.ManagedByKey), Value: aws.String(tags.ManagedByEKS)}, + {Key: aws.String(clusterEKSClusterTagKey), Value: aws.String(clusterName)}, + {Key: aws.String(clusterEKSRoleTagKey), Value: aws.String(role)}, + }, + }}, + }, accountID) + if err != nil { + return "", fmt.Errorf("create SG %s: %w", name, err) + } + if out == nil || out.GroupId == nil || *out.GroupId == "" { + return "", fmt.Errorf("eks: CreateSecurityGroup returned empty GroupId for %s", name) + } + return *out.GroupId, nil +} + +// EnsureNodegroupSGRules authorizes the ingress rules a nodegroup's workers +// need to join the K3s control plane and form the flannel overlay. All rules +// are group-to-group (UserIdGroupPairs) so they stay correct as instances come +// and go. Idempotent: the InvalidPermission.Duplicate error a re-run triggers +// for an already-authorized rule is treated as success. +// +// Rules: +// - agent → server apiserver/supervisor: 6443/tcp (ngSG → cpSG) +// - flannel VXLAN: 8472/udp both directions between cpSG and ngSG, and +// ngSG ↔ ngSG +// - kubelet: 10250/tcp (cpSG → ngSG) +// - intra-nodegroup all traffic: ngSG self-reference +func EnsureNodegroupSGRules(sgp sgProvisioner, accountID, clusterName, cpSGID, ngSGID string) error { + if cpSGID == "" || ngSGID == "" { + return errors.New("eks: EnsureNodegroupSGRules empty SG id") + } + + type rule struct { + targetSG string + sourceSG string + proto string + from int64 + to int64 + desc string + } + rules := []rule{ + {cpSGID, ngSGID, "tcp", 6443, 6443, "EKS agent to apiserver/supervisor"}, + {cpSGID, ngSGID, "udp", 8472, 8472, "EKS flannel VXLAN node to control-plane"}, + {ngSGID, cpSGID, "udp", 8472, 8472, "EKS flannel VXLAN control-plane to node"}, + {ngSGID, ngSGID, "udp", 8472, 8472, "EKS flannel VXLAN node to node"}, + {ngSGID, cpSGID, "tcp", 10250, 10250, "EKS kubelet control-plane to node"}, + {ngSGID, ngSGID, "-1", -1, -1, "EKS intra-nodegroup all traffic"}, + } + + for _, r := range rules { + perm := &ec2.IpPermission{ + IpProtocol: aws.String(r.proto), + UserIdGroupPairs: []*ec2.UserIdGroupPair{{ + GroupId: aws.String(r.sourceSG), + Description: aws.String(r.desc), + }}, + } + if r.proto != "-1" { + perm.FromPort = aws.Int64(r.from) + perm.ToPort = aws.Int64(r.to) + } + _, err := sgp.AuthorizeSecurityGroupIngress(&ec2.AuthorizeSecurityGroupIngressInput{ + GroupId: aws.String(r.targetSG), + IpPermissions: []*ec2.IpPermission{perm}, + }, accountID) + if err != nil { + if awserrors.IsErrorCode(err, awserrors.ErrorInvalidPermissionDuplicate) { + continue + } + return fmt.Errorf("authorize %s ingress on %s: %w", r.desc, r.targetSG, err) + } + } + return nil +} + +// EnsureControlPlaneIngress authorizes the ingress the cluster NLB needs to +// reach the K3s apiserver. The NLB's backing LB VM forwards the front-end +// listener to the apiserver on k3sAPIServerPort from inside the VPC, so the +// control-plane SG must admit that port from the VPC CIDR. Public exposure is +// gated separately at the NLB front-end by publicAccessCidrs; this rule only +// governs the in-VPC LB→apiserver hop. Idempotent: the +// InvalidPermission.Duplicate error a re-run triggers is treated as success. +func EnsureControlPlaneIngress(sgp sgProvisioner, accountID, cpSGID, vpcCIDR string) error { + if cpSGID == "" { + return errors.New("eks: EnsureControlPlaneIngress empty control-plane SG id") + } + if vpcCIDR == "" { + return errors.New("eks: EnsureControlPlaneIngress empty vpc cidr") + } + + perm := &ec2.IpPermission{ + IpProtocol: aws.String("tcp"), + FromPort: aws.Int64(k3sAPIServerPort), + ToPort: aws.Int64(k3sAPIServerPort), + IpRanges: []*ec2.IpRange{{ + CidrIp: aws.String(vpcCIDR), + Description: aws.String("EKS NLB to apiserver"), + }}, + } + _, err := sgp.AuthorizeSecurityGroupIngress(&ec2.AuthorizeSecurityGroupIngressInput{ + GroupId: aws.String(cpSGID), + IpPermissions: []*ec2.IpPermission{perm}, + }, accountID) + if err != nil && !awserrors.IsErrorCode(err, awserrors.ErrorInvalidPermissionDuplicate) { + return fmt.Errorf("authorize control-plane apiserver ingress on %s: %w", cpSGID, err) + } + return nil +} + +func lookupSGByName(sgp sgProvisioner, accountID, vpcID, name string) (string, error) { + out, err := sgp.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ + Filters: []*ec2.Filter{ + {Name: aws.String("group-name"), Values: aws.StringSlice([]string{name})}, + {Name: aws.String("vpc-id"), Values: aws.StringSlice([]string{vpcID})}, + }, + }, accountID) + if err != nil { + return "", fmt.Errorf("describe SG %s: %w", name, err) + } + for _, g := range out.SecurityGroups { + if g == nil || g.GroupId == nil { + continue + } + return *g.GroupId, nil + } + return "", nil +} diff --git a/spinifex/handlers/eks/security_groups_test.go b/spinifex/handlers/eks/security_groups_test.go new file mode 100644 index 000000000..41ff851f2 --- /dev/null +++ b/spinifex/handlers/eks/security_groups_test.go @@ -0,0 +1,257 @@ +package handlers_eks + +import ( + "errors" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/tags" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type fakeSGProvisioner struct { + createCalls []*ec2.CreateSecurityGroupInput + describeCalls []*ec2.DescribeSecurityGroupsInput + deleteCalls []*ec2.DeleteSecurityGroupInput + authorizeCalls []*ec2.AuthorizeSecurityGroupIngressInput + + // existing maps "name|vpcId" → groupId for DescribeSecurityGroups lookup. + existing map[string]string + + // nextCreateID is returned by the next CreateSecurityGroup call. Tests can + // pre-seed a queue via createIDs to differentiate the control-plane vs + // nodegroup SG IDs. + createIDs []string + + createErr error + describeErr error + deleteErr error + authorizeErr error +} + +var _ sgProvisioner = (*fakeSGProvisioner)(nil) + +func newFakeSGProvisioner() *fakeSGProvisioner { + return &fakeSGProvisioner{existing: map[string]string{}} +} + +func (f *fakeSGProvisioner) CreateSecurityGroup(input *ec2.CreateSecurityGroupInput, _ string) (*ec2.CreateSecurityGroupOutput, error) { + f.createCalls = append(f.createCalls, input) + if f.createErr != nil { + return nil, f.createErr + } + id := "sg-default" + if len(f.createIDs) > 0 { + id = f.createIDs[0] + f.createIDs = f.createIDs[1:] + } + if input.GroupName != nil && input.VpcId != nil { + f.existing[*input.GroupName+"|"+*input.VpcId] = id + } + return &ec2.CreateSecurityGroupOutput{GroupId: aws.String(id)}, nil +} + +func (f *fakeSGProvisioner) DescribeSecurityGroups(input *ec2.DescribeSecurityGroupsInput, _ string) (*ec2.DescribeSecurityGroupsOutput, error) { + f.describeCalls = append(f.describeCalls, input) + if f.describeErr != nil { + return nil, f.describeErr + } + var name, vpc string + for _, filt := range input.Filters { + if filt == nil || filt.Name == nil || len(filt.Values) == 0 || filt.Values[0] == nil { + continue + } + switch *filt.Name { + case "group-name": + name = *filt.Values[0] + case "vpc-id": + vpc = *filt.Values[0] + } + } + id, ok := f.existing[name+"|"+vpc] + out := &ec2.DescribeSecurityGroupsOutput{} + if ok { + out.SecurityGroups = []*ec2.SecurityGroup{{GroupId: aws.String(id), GroupName: aws.String(name), VpcId: aws.String(vpc)}} + } + return out, nil +} + +func (f *fakeSGProvisioner) DeleteSecurityGroup(input *ec2.DeleteSecurityGroupInput, _ string) (*ec2.DeleteSecurityGroupOutput, error) { + f.deleteCalls = append(f.deleteCalls, input) + if f.deleteErr != nil { + return nil, f.deleteErr + } + return &ec2.DeleteSecurityGroupOutput{}, nil +} + +func (f *fakeSGProvisioner) AuthorizeSecurityGroupIngress(input *ec2.AuthorizeSecurityGroupIngressInput, _ string) (*ec2.AuthorizeSecurityGroupIngressOutput, error) { + f.authorizeCalls = append(f.authorizeCalls, input) + if f.authorizeErr != nil { + return nil, f.authorizeErr + } + return &ec2.AuthorizeSecurityGroupIngressOutput{}, nil +} + +func TestEnsureClusterSGs_EmptyInputsRejected(t *testing.T) { + sgp := newFakeSGProvisioner() + + _, _, err := EnsureClusterSGs(sgp, "111122223333", "", "vpc-aaa") + require.Error(t, err) + + _, _, err = EnsureClusterSGs(sgp, "111122223333", "alpha", "") + require.Error(t, err) + + assert.Empty(t, sgp.createCalls) + assert.Empty(t, sgp.describeCalls) +} + +func TestEnsureClusterSGs_FreshCreatesBoth(t *testing.T) { + sgp := newFakeSGProvisioner() + sgp.createIDs = []string{"sg-cp-001", "sg-ng-002"} + + cpID, ngID, err := EnsureClusterSGs(sgp, "111122223333", "alpha", "vpc-aaa") + require.NoError(t, err) + assert.Equal(t, "sg-cp-001", cpID) + assert.Equal(t, "sg-ng-002", ngID) + + require.Len(t, sgp.createCalls, 2) + + assertSGCreateTagged(t, sgp.createCalls[0], "eks-cluster-alpha-control-plane-sg", "vpc-aaa", "alpha", clusterEKSRoleControlPlane) + assertSGCreateTagged(t, sgp.createCalls[1], "eks-cluster-alpha-nodegroup-sg", "vpc-aaa", "alpha", clusterEKSRoleNodegroup) +} + +func TestEnsureClusterSGs_IdempotentReusesExisting(t *testing.T) { + sgp := newFakeSGProvisioner() + sgp.existing["eks-cluster-alpha-control-plane-sg|vpc-aaa"] = "sg-existing-cp" + sgp.existing["eks-cluster-alpha-nodegroup-sg|vpc-aaa"] = "sg-existing-ng" + + cpID, ngID, err := EnsureClusterSGs(sgp, "111122223333", "alpha", "vpc-aaa") + require.NoError(t, err) + assert.Equal(t, "sg-existing-cp", cpID) + assert.Equal(t, "sg-existing-ng", ngID) + assert.Empty(t, sgp.createCalls, "no create calls expected when SGs already exist") +} + +func TestEnsureClusterSGs_MixedExistenceCreatesMissing(t *testing.T) { + sgp := newFakeSGProvisioner() + sgp.existing["eks-cluster-alpha-control-plane-sg|vpc-aaa"] = "sg-existing-cp" + sgp.createIDs = []string{"sg-new-ng"} + + cpID, ngID, err := EnsureClusterSGs(sgp, "111122223333", "alpha", "vpc-aaa") + require.NoError(t, err) + assert.Equal(t, "sg-existing-cp", cpID) + assert.Equal(t, "sg-new-ng", ngID) + + require.Len(t, sgp.createCalls, 1, "only nodegroup SG should be created") + assert.Equal(t, "eks-cluster-alpha-nodegroup-sg", aws.StringValue(sgp.createCalls[0].GroupName)) +} + +func TestEnsureClusterSGs_CreateErrorSurfacedFromControlPlane(t *testing.T) { + sgp := newFakeSGProvisioner() + sgp.createErr = errors.New("vpcd unavailable") + + _, _, err := EnsureClusterSGs(sgp, "111122223333", "alpha", "vpc-aaa") + require.Error(t, err) + assert.Contains(t, err.Error(), "create SG eks-cluster-alpha-control-plane-sg") +} + +func TestDeleteClusterSGs_DeletesBothExisting(t *testing.T) { + sgp := newFakeSGProvisioner() + sgp.existing["eks-cluster-alpha-control-plane-sg|vpc-aaa"] = "sg-existing-cp" + sgp.existing["eks-cluster-alpha-nodegroup-sg|vpc-aaa"] = "sg-existing-ng" + + err := DeleteClusterSGs(sgp, "111122223333", "alpha", "vpc-aaa") + require.NoError(t, err) + + require.Len(t, sgp.deleteCalls, 2) + assert.Equal(t, "sg-existing-cp", aws.StringValue(sgp.deleteCalls[0].GroupId)) + assert.Equal(t, "sg-existing-ng", aws.StringValue(sgp.deleteCalls[1].GroupId)) +} + +func TestDeleteClusterSGs_MissingSGsNoOp(t *testing.T) { + sgp := newFakeSGProvisioner() + + err := DeleteClusterSGs(sgp, "111122223333", "alpha", "vpc-aaa") + require.NoError(t, err) + assert.Empty(t, sgp.deleteCalls, "no delete calls expected when SGs already absent") +} + +func TestDeleteClusterSGs_FirstErrorSurfacedSweepContinues(t *testing.T) { + sgp := newFakeSGProvisioner() + sgp.existing["eks-cluster-alpha-control-plane-sg|vpc-aaa"] = "sg-existing-cp" + sgp.existing["eks-cluster-alpha-nodegroup-sg|vpc-aaa"] = "sg-existing-ng" + sgp.deleteErr = errors.New("DependencyViolation") + + err := DeleteClusterSGs(sgp, "111122223333", "alpha", "vpc-aaa") + require.Error(t, err) + assert.Contains(t, err.Error(), "sg-existing-cp") + assert.Len(t, sgp.deleteCalls, 2, "both delete calls should be attempted despite first error") +} + +func TestEnsureControlPlaneIngress_AuthorizesAPIServerFromVPCCIDR(t *testing.T) { + sgp := newFakeSGProvisioner() + + err := EnsureControlPlaneIngress(sgp, "111122223333", "sg-cp-001", "10.0.0.0/16") + require.NoError(t, err) + + require.Len(t, sgp.authorizeCalls, 1) + in := sgp.authorizeCalls[0] + assert.Equal(t, "sg-cp-001", aws.StringValue(in.GroupId)) + require.Len(t, in.IpPermissions, 1) + perm := in.IpPermissions[0] + assert.Equal(t, "tcp", aws.StringValue(perm.IpProtocol)) + assert.Equal(t, k3sAPIServerPort, aws.Int64Value(perm.FromPort)) + assert.Equal(t, k3sAPIServerPort, aws.Int64Value(perm.ToPort)) + require.Len(t, perm.IpRanges, 1) + assert.Equal(t, "10.0.0.0/16", aws.StringValue(perm.IpRanges[0].CidrIp)) +} + +func TestEnsureControlPlaneIngress_EmptyInputsRejected(t *testing.T) { + sgp := newFakeSGProvisioner() + + require.Error(t, EnsureControlPlaneIngress(sgp, "111122223333", "", "10.0.0.0/16")) + require.Error(t, EnsureControlPlaneIngress(sgp, "111122223333", "sg-cp-001", "")) + assert.Empty(t, sgp.authorizeCalls) +} + +func TestEnsureControlPlaneIngress_DuplicateTolerated(t *testing.T) { + sgp := newFakeSGProvisioner() + sgp.authorizeErr = errors.New(awserrors.ErrorInvalidPermissionDuplicate) + + err := EnsureControlPlaneIngress(sgp, "111122223333", "sg-cp-001", "10.0.0.0/16") + require.NoError(t, err, "a duplicate rule on re-run must be treated as success") +} + +func TestEnsureControlPlaneIngress_AuthorizeErrorSurfaced(t *testing.T) { + sgp := newFakeSGProvisioner() + sgp.authorizeErr = errors.New("vpcd unavailable") + + err := EnsureControlPlaneIngress(sgp, "111122223333", "sg-cp-001", "10.0.0.0/16") + require.Error(t, err) + assert.Contains(t, err.Error(), "sg-cp-001") +} + +func assertSGCreateTagged(t *testing.T, in *ec2.CreateSecurityGroupInput, name, vpcID, clusterName, role string) { + t.Helper() + require.NotNil(t, in) + assert.Equal(t, name, aws.StringValue(in.GroupName)) + assert.Equal(t, vpcID, aws.StringValue(in.VpcId)) + require.Len(t, in.TagSpecifications, 1) + spec := in.TagSpecifications[0] + require.NotNil(t, spec) + assert.Equal(t, "security-group", aws.StringValue(spec.ResourceType)) + + got := map[string]string{} + for _, tg := range spec.Tags { + if tg == nil || tg.Key == nil || tg.Value == nil { + continue + } + got[*tg.Key] = *tg.Value + } + assert.Equal(t, tags.ManagedByEKS, got[tags.ManagedByKey]) + assert.Equal(t, clusterName, got[clusterEKSClusterTagKey]) + assert.Equal(t, role, got[clusterEKSRoleTagKey]) +} diff --git a/spinifex/handlers/eks/service.go b/spinifex/handlers/eks/service.go index 96f793e39..aaf17ede8 100644 --- a/spinifex/handlers/eks/service.go +++ b/spinifex/handlers/eks/service.go @@ -11,8 +11,10 @@ import "github.com/aws/aws-sdk-go/service/eks" // (PascalCase). Signatures use the aws-sdk-go input/output types verbatim so // downstream callers can pass straight through from generated SDK code. type EKSService interface { - // Cluster - CreateCluster(input *eks.CreateClusterInput, accountID string) (*eks.CreateClusterOutput, error) + // Cluster. CreateCluster also takes the caller's resolved IAM principal ARN + // (from the X-Principal-ARN header) so it can mint the bootstrap + // cluster-creator-admin AccessEntry; "" skips that step. + CreateCluster(input *eks.CreateClusterInput, accountID, callerPrincipalARN string) (*eks.CreateClusterOutput, error) DescribeCluster(input *eks.DescribeClusterInput, accountID string) (*eks.DescribeClusterOutput, error) ListClusters(input *eks.ListClustersInput, accountID string) (*eks.ListClustersOutput, error) UpdateClusterConfig(input *eks.UpdateClusterConfigInput, accountID string) (*eks.UpdateClusterConfigOutput, error) diff --git a/spinifex/handlers/eks/service_impl.go b/spinifex/handlers/eks/service_impl.go index 1ff9827e6..899463b94 100644 --- a/spinifex/handlers/eks/service_impl.go +++ b/spinifex/handlers/eks/service_impl.go @@ -1,35 +1,123 @@ package handlers_eks import ( + "context" "errors" "fmt" + "log/slog" + "net" + "sort" + "strconv" + "strings" + "sync" + "time" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/eks" "github.com/mulgadc/spinifex/spinifex/awserrors" "github.com/mulgadc/spinifex/spinifex/config" + "github.com/mulgadc/spinifex/spinifex/tags" + "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" ) -// EKSServiceImpl is the daemon-side EKSService. All methods currently return -// NotImplemented; bodies land alongside the K3s control-plane work. +// SubnetVPCResolver resolves a subnet ID to its parent VPC ID and the VPC's +// CIDR block. Narrow so EKSServiceImpl can stay free of the wider +// handlers/ec2/vpc surface; the daemon adapts VPCServiceImpl onto this +// contract. +type SubnetVPCResolver interface { + GetSubnetVPC(accountID, subnetID string) (vpcID string, err error) + GetVPCCIDR(accountID, vpcID string) (cidr string, err error) +} + +// EKSServiceDeps wires every external collaborator EKSServiceImpl needs. +// The narrow per-area interfaces (sgProvisioner, nlbProvisioner, etc.) are +// already defined alongside their consumers; the daemon adapts each +// service's concrete type onto the interface. +type EKSServiceDeps struct { + Config *config.Config + NATSConn *nats.Conn + MasterKey []byte + GatewayBaseURL string + Region string + HolderID string + + // Gateway broker config handed to the K3s server VM so it can publish its + // bootstrap envelopes + state reports via SigV4-signed HTTPS POST to the AWS + // gateway (the ELBv2 lb-agent model) instead of dialing core NATS. + // SystemGatewayURL is the mgmt-reachable AWSGW endpoint (distinct from + // GatewayBaseURL, which is the OIDC issuer host); SystemAccessKey/SecretKey + // are the system (Predastore) SigV4 creds; GatewayCACert is the PEM that + // signs the gateway server cert. + SystemGatewayURL string + SystemAccessKey string + SystemSecretKey string + GatewayCACert string + + VPCSG sgProvisioner + VPCK3s k3sVPCProvisioner + VPCSubnet SubnetVPCResolver + NLB nlbProvisioner + Instance k3sInstanceLauncher + Image k3sAMIResolver + EIP eipProvisioner + Worker WorkerLauncher + + // AddonInstaller delivers managed-addon manifests to clusters. Optional: + // when nil the service defaults to the KV staging installer + // (newStagingInstaller) bound to NATSConn. + AddonInstaller AddonInstaller +} + +// WorkerLauncher is the narrow EC2 surface CreateNodegroup needs to launch and +// terminate nodegroup worker instances. Workers go through the normal +// customer-owned RunInstances path (no ManagedBy tag, no mgmt NIC) so they are +// visible in DescribeInstances and reclaimed by TerminateInstances like any +// other customer EC2. The daemon adapts its concrete instance service onto this. +// Exported so the daemon can assert *Daemon satisfies it with a compile-time +// check. +type WorkerLauncher interface { + RunWorkerInstance(input *ec2.RunInstancesInput, accountID string) (*ec2.Reservation, error) + TerminateWorkerInstances(instanceIDs []string, accountID string) error +} + +// eipProvisioner is the narrow EIP surface CreateCluster needs to give the +// hidden K3s control-plane VM an egress-only public IP (the distributed-NAT +// dev/edge topology has no shared masquerade address). The daemon adapts the +// concrete EIP service onto this. +type eipProvisioner interface { + AllocateAddress(input *ec2.AllocateAddressInput, accountID string) (*ec2.AllocateAddressOutput, error) + ReleaseAddress(input *ec2.ReleaseAddressInput, accountID string) (*ec2.ReleaseAddressOutput, error) +} + +// EKSServiceImpl is the daemon-side EKSService. CreateCluster / +// DescribeCluster / ListClusters / DeleteCluster have real bodies; every +// other action is still NotImplemented pending follow-up sprints. type EKSServiceImpl struct { - config *config.Config - nc *nats.Conn - store *Store + deps EKSServiceDeps leaderKV nats.KeyValue + registry *ReconcilerRegistry + + mu sync.Mutex + bgCtx context.Context + bgCancel context.CancelFunc } var _ EKSService = (*EKSServiceImpl)(nil) -// NewEKSServiceImplWithNATS wires the Store handle and creates the shared -// spinifex-eks-leader bucket so the leader-lease CAS loop has a ready bucket -// to CAS against without a chicken-and-egg bootstrap. -func NewEKSServiceImplWithNATS(cfg *config.Config, nc *nats.Conn) (*EKSServiceImpl, error) { - store, err := NewStore(nc) - if err != nil { - return nil, fmt.Errorf("failed to create EKS store: %w", err) +const defaultK8sVersion = "1.32" + +// NewEKSServiceImpl wires the Store, leader bucket, reconciler registry, and +// per-cluster background-goroutine context. Validates that MasterKey, +// GatewayBaseURL, Region, and HolderID are set when called from the live +// daemon path; the legacy NewEKSServiceImplWithNATS shim leaves them empty +// and bodies that need them return ServerInternal. +func NewEKSServiceImpl(deps EKSServiceDeps) (*EKSServiceImpl, error) { + if deps.NATSConn == nil { + return nil, errors.New("eks: NewEKSServiceImpl nil NATSConn") } - js, err := nc.JetStream() + js, err := deps.NATSConn.JetStream() if err != nil { return nil, fmt.Errorf("failed to get JetStream context: %w", err) } @@ -37,28 +125,626 @@ func NewEKSServiceImplWithNATS(cfg *config.Config, nc *nats.Conn) (*EKSServiceIm if err != nil { return nil, err } + ctx, cancel := context.WithCancel(context.Background()) return &EKSServiceImpl{ - config: cfg, - nc: nc, - store: store, + deps: deps, leaderKV: leaderKV, + registry: NewReconcilerRegistry(), + bgCtx: ctx, + bgCancel: cancel, }, nil } -func notImpl() error { return errors.New(awserrors.ErrorNotImplemented) } +// NewEKSServiceImplWithNATS is a back-compat shim. Bodies that need +// orchestration deps return ServerInternal when invoked through this shim; +// the daemon-handler routing test calls it to verify NATS subject wiring +// without standing up the full dependency graph. +func NewEKSServiceImplWithNATS(cfg *config.Config, nc *nats.Conn) (*EKSServiceImpl, error) { + return NewEKSServiceImpl(EKSServiceDeps{Config: cfg, NATSConn: nc}) +} + +// Shutdown stops the per-cluster reconciler goroutines and cancels every +// background bootstrap goroutine. Safe to call multiple times. +func (s *EKSServiceImpl) Shutdown() { + s.mu.Lock() + cancel := s.bgCancel + s.mu.Unlock() + if cancel != nil { + cancel() + } + s.registry.StopAll() +} + +// SpawnRegisteredReconcilers enumerates every cluster in every per-account +// bucket and registers a reconciler goroutine for each cluster in +// CREATING or ACTIVE state. Called by the daemon on boot so a node restart +// resumes lifecycle reconcile without waiting for the next CreateCluster. +func (s *EKSServiceImpl) SpawnRegisteredReconcilers() error { + if !s.depsReadyForOrchestration() { + slog.Debug("SpawnRegisteredReconcilers: deps not ready, skipping") + return nil + } + js, err := s.deps.NATSConn.JetStream() + if err != nil { + return fmt.Errorf("jetstream: %w", err) + } + buckets := js.KeyValueStoreNames() + for name := range buckets { + if !strings.HasPrefix(name, KVBucketEKSAccountPrefix) { + continue + } + accountID := strings.TrimPrefix(name, KVBucketEKSAccountPrefix) + acctKV, err := js.KeyValue(name) + if err != nil { + slog.Warn("SpawnRegisteredReconcilers: open bucket failed", "bucket", name, "err", err) + continue + } + clusters, err := listClusterNames(acctKV) + if err != nil { + slog.Warn("SpawnRegisteredReconcilers: list clusters failed", "bucket", name, "err", err) + continue + } + for _, cluster := range clusters { + meta, err := GetClusterMeta(acctKV, cluster) + if err != nil { + continue + } + if meta.Status != ClusterStatusCreating && meta.Status != ClusterStatusActive { + continue + } + // A restart mid-CREATING lost the bootstrap subscriptions; the K3s + // VM publishes its one-shot core-NATS messages once and to nobody. + // Re-subscribe to whichever artifacts are still missing so the + // cluster can still reach ACTIVE instead of hanging in CREATING. + if meta.Status == ClusterStatusCreating { + if pending := BootstrapPendingKinds(acctKV, cluster, meta); len(pending) > 0 { + slog.Info("SpawnRegisteredReconcilers: resuming bootstrap", + "cluster", cluster, "pending", pending) + s.spawnBootstrap(accountID, cluster, acctKV, pending) + } + } + s.spawnReconciler(accountID, cluster, meta) + } + } + return nil +} + +func (s *EKSServiceImpl) depsReadyForOrchestration() bool { + return len(s.missingOrchestrationDeps()) == 0 +} + +// requireOrchestrationDeps returns a client-facing ServiceUnavailable (with the +// specific unmet deps logged at ERROR) when the daemon is missing orchestration +// wiring, so the failure is diagnosable instead of a bare ServerInternal. +func (s *EKSServiceImpl) requireOrchestrationDeps(op string) error { + missing := s.missingOrchestrationDeps() + if len(missing) == 0 { + return nil + } + slog.Error("EKS orchestration deps not ready", "op", op, "missing", missing) + return errors.New(awserrors.ErrorServiceUnavailable) +} + +// missingOrchestrationDeps names the deps required for cluster orchestration +// that are unset, so a "deps not ready" rejection can log the precise cause +// instead of an opaque ServerInternal whose only hint is a one-time boot WARN. +func (s *EKSServiceImpl) missingOrchestrationDeps() []string { + var missing []string + if s.deps.VPCSG == nil { + missing = append(missing, "VPCSG") + } + if s.deps.VPCK3s == nil { + missing = append(missing, "VPCK3s") + } + if s.deps.VPCSubnet == nil { + missing = append(missing, "VPCSubnet") + } + if s.deps.NLB == nil { + missing = append(missing, "NLB") + } + if s.deps.Instance == nil { + missing = append(missing, "Instance") + } + if s.deps.Image == nil { + missing = append(missing, "Image") + } + if s.deps.EIP == nil { + missing = append(missing, "EIP") + } + if s.deps.Worker == nil { + missing = append(missing, "Worker") + } + if len(s.deps.MasterKey) == 0 { + missing = append(missing, "MasterKey") + } + if s.deps.GatewayBaseURL == "" { + missing = append(missing, "GatewayBaseURL") + } + if s.deps.Region == "" { + missing = append(missing, "Region") + } + if s.deps.HolderID == "" { + missing = append(missing, "HolderID") + } + return missing +} // --- Cluster --- -func (s *EKSServiceImpl) CreateCluster(_ *eks.CreateClusterInput, _ string) (*eks.CreateClusterOutput, error) { - return nil, notImpl() +// logCreateErr records the precise cause of a CreateCluster step failure at +// ERROR. The request surface collapses these wrapped errors to an opaque +// ServerInternal ("An internal error has occurred... contact AWS re:Post"), so +// without this the wrapped error never reaches the operator. Returns the error +// wrapped with the stage for the caller to surface. +func logCreateErr(name, accountID, stage string, err error) error { + slog.Error("CreateCluster: "+stage+" failed", "cluster", name, "accountID", accountID, "err", err) + return fmt.Errorf("%s: %w", stage, err) } -func (s *EKSServiceImpl) DescribeCluster(_ *eks.DescribeClusterInput, _ string) (*eks.DescribeClusterOutput, error) { - return nil, notImpl() +// managedIngressTagKey is the CreateCluster tag that controls K3s' bundled +// traefik + servicelb (interim in-VPC app exposure). They are ON by default so +// workloads are reachable before the AWS Load Balancer Controller add-on ships; +// value "false" (case-insensitive) opts out for AWS parity. TODO: flip the +// default back off (parity) once the aws-lb-controller add-on lands — see +// docs/development/feature/eks-dataplane-ingress.md. +const managedIngressTagKey = "spinifex.io/managed-ingress" + +// builtinIngressEnabled reports whether a cluster keeps the K3s built-in ingress +// stack. Default ON; the tag opts out with "false". +func builtinIngressEnabled(tags map[string]*string) bool { + return !strings.EqualFold(aws.StringValue(tags[managedIngressTagKey]), "false") } -func (s *EKSServiceImpl) ListClusters(_ *eks.ListClustersInput, _ string) (*eks.ListClustersOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) CreateCluster(input *eks.CreateClusterInput, accountID, callerPrincipalARN string) (*eks.CreateClusterOutput, error) { + if err := s.requireOrchestrationDeps("CreateCluster"); err != nil { + return nil, err + } + if err := validateCreateClusterInput(input); err != nil { + return nil, err + } + name := aws.StringValue(input.Name) + subnetIDs := aws.StringValueSlice(input.ResourcesVpcConfig.SubnetIds) + + js, err := s.deps.NATSConn.JetStream() + if err != nil { + return nil, logCreateErr(name, accountID, "jetstream", err) + } + acctKV, err := GetOrCreateAccountBucket(js, accountID) + if err != nil { + return nil, logCreateErr(name, accountID, "get account bucket", err) + } + + if existing, err := GetClusterMeta(acctKV, name); err == nil { + if existing.Status != ClusterStatusFailed { + slog.Info("CreateCluster: cluster already exists", + "name", name, "accountID", accountID, "status", existing.Status) + return nil, errors.New(awserrors.ErrorEKSResourceInUse) + } + // A prior attempt failed. Reclaim whatever it left behind and clear its + // state so this create starts clean — makes a retry (including the + // SDK's own auto-retry) idempotent instead of masking the original + // failure with a spurious "already exists" error. If teardown of the + // failed attempt's resources fails, surface that rather than recreating + // on top of orphans. + slog.Info("CreateCluster: reclaiming FAILED cluster before recreate", + "name", name, "accountID", accountID) + if perr := s.purgeClusterInfra(accountID, name, existing, acctKV); perr != nil { + return nil, logCreateErr(name, accountID, "reclaim failed cluster", perr) + } + } else if !errors.Is(err, ErrClusterNotFound) { + return nil, logCreateErr(name, accountID, "preflight get meta", err) + } + + vpcID, err := s.deps.VPCSubnet.GetSubnetVPC(accountID, subnetIDs[0]) + if err != nil { + // Subnet is a caller-supplied parameter — a resolve failure is a client + // fault (bad/foreign subnet), not an internal one. Surface it as such + // instead of collapsing to a retryable ServerInternal the SDK retries. + slog.Error("CreateCluster: resolve subnet VPC failed", + "cluster", name, "accountID", accountID, "subnet", subnetIDs[0], "err", err) + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + region := s.deps.Region + arn := fmt.Sprintf("arn:aws:eks:%s:%s:cluster/%s", region, accountID, name) + + publicAccess, privateAccess := endpointAccess(input.ResourcesVpcConfig) + publicCidrs := publicAccessCidrs(input.ResourcesVpcConfig, publicAccess) + + meta := &ClusterMeta{ + Name: name, + Arn: arn, + Status: ClusterStatusCreating, + Version: deref(input.Version, defaultK8sVersion), + RoleArn: aws.StringValue(input.RoleArn), + ResourcesVpcConfig: &ClusterVpcConfig{ + SubnetIds: subnetIDs, + VpcId: vpcID, + EndpointPublicAccess: publicAccess, + EndpointPrivateAccess: privateAccess, + PublicAccessCidrs: publicCidrs, + }, + BuiltinIngress: builtinIngressEnabled(input.Tags), + CreatedAt: time.Now().UTC(), + } + if err := PutClusterMeta(acctKV, meta); err != nil { + return nil, logCreateErr(name, accountID, "persist initial meta", err) + } + + cpSG, ngSG, err := EnsureClusterSGs(s.deps.VPCSG, accountID, name, vpcID) + if err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "ensure cluster SGs", err) + } + meta.ResourcesVpcConfig.SecurityGroupIds = []string{cpSG, ngSG} + + // The NLB's backing LB VM forwards the published endpoint to the apiserver + // from inside the VPC, so the control-plane SG must admit that hop or the + // NLB target stays unhealthy and the endpoint never serves. + vpcCIDR, err := s.deps.VPCSubnet.GetVPCCIDR(accountID, vpcID) + if err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "resolve vpc cidr", err) + } + if err := EnsureControlPlaneIngress(s.deps.VPCSG, accountID, cpSG, vpcCIDR); err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "ensure control-plane ingress", err) + } + + // Public access ⇒ internet-facing NLB (external-pool front-end IP, reachable + // on the LAN/edge network); private-only ⇒ internal NLB (VPC-only). + nlb, err := EnsureClusterNLB(s.deps.NLB, accountID, name, subnetIDs, publicAccess, publicCidrs) + if err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "ensure cluster NLB", err) + } + // Prefer the reachable front-end IP as the kubeconfig endpoint host so the + // apiserver serving cert (which SANs this IP) validates with TLS verification + // on. Fall back to the synthetic NLB DNS name when no IP was read back. + endpointHost := nlb.FrontendIP + if endpointHost == "" { + endpointHost = nlb.DNSName + } + meta.Endpoint = "https://" + net.JoinHostPort(endpointHost, strconv.FormatInt(clusterNLBListenPort, 10)) + meta.EndpointIP = nlb.FrontendIP + meta.NLBArn = nlb.LoadBalancerArn + meta.NLBTargetGroupArn = nlb.TargetGroupArn + + // Persist the NLB ARNs now, before any further fallible step. Without this + // a failure between here and the final PutClusterMeta would leave the NLB + + // target group + listener + backing LB VM created but unrecorded, so + // DeleteCluster (which keys teardown off the persisted ARNs) could not + // reclaim them. + if err := PutClusterMeta(acctKV, meta); err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "persist NLB arns", err) + } + + oidcIssuer, err := ClusterOIDCIssuer(s.deps.GatewayBaseURL, region, accountID, name) + if err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "build OIDC issuer", err) + } + meta.OIDCIssuer = oidcIssuer + + privPEM, _, err := GenerateClusterOIDCKeypair(acctKV, name, s.deps.MasterKey) + if err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "generate OIDC keypair", err) + } + pubPEM, err := PublicKeyPEMFromPrivate(privPEM) + if err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "derive OIDC public key", err) + } + + k3sOut, err := LaunchK3sServerVM(s.deps.VPCK3s, s.deps.Instance, s.deps.Image, K3sServerInput{ + AccountID: accountID, + ClusterName: name, + Region: region, + SubnetID: subnetIDs[0], + ControlPlaneSGID: cpSG, + NLBDNS: nlb.DNSName, + EndpointIP: nlb.FrontendIP, + OIDCIssuer: oidcIssuer, + OIDCPrivateKeyPEM: privPEM, + OIDCPublicKeyPEM: pubPEM, + GatewayURL: s.deps.SystemGatewayURL, + AccessKey: s.deps.SystemAccessKey, + SecretKey: s.deps.SystemSecretKey, + GatewayCACert: s.deps.GatewayCACert, + BuiltinIngress: meta.BuiltinIngress, + }) + if err != nil { + s.markFailed(acctKV, name) + if errors.Is(err, ErrEKSServerAMINotFound) { + slog.Error("CreateCluster: eks-server AMI not found; cannot launch control plane", + "cluster", name, "accountID", accountID, "err", err) + return nil, errors.New(awserrors.ErrorServiceUnavailable) + } + slog.Error("CreateCluster: K3s control-plane VM launch failed", + "cluster", name, "accountID", accountID, "err", err) + return nil, fmt.Errorf("launch K3s VM: %w", err) + } + meta.ControlPlaneInstanceID = k3sOut.InstanceID + meta.ControlPlaneENIID = k3sOut.ENIID + meta.ControlPlaneENIIP = k3sOut.ENIIP + meta.ControlPlaneMgmtIP = k3sOut.MgmtIP + + // Egress: the control-plane VM must pull container images. Allocate a + // hidden pool address and wire an egress-only SNAT for its /32 (no DNAT — + // the VM stays unreachable inbound; the NLB is its only front door). + egressIP, egressAllocID, err := s.allocateClusterEgressIP(accountID, name) + if err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "allocate cluster egress IP", err) + } + meta.EgressEIPAllocationID = egressAllocID + meta.EgressEIPPublicIP = egressIP + utils.PublishEvent(s.deps.NATSConn, "vpc.add-system-egress", systemEgressEvent{ + VpcId: vpcID, + SubnetId: subnetIDs[0], + InstanceIp: k3sOut.ENIIP, + ExternalIp: egressIP, + }) + + // Record the VM + ENI + egress IP before registering the target so a failed + // register (or anything after) leaves them recoverable by DeleteCluster. + if err := PutClusterMeta(acctKV, meta); err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "persist control-plane ids", err) + } + + if err := RegisterClusterTarget(s.deps.NLB, accountID, nlb.TargetGroupArn, k3sOut.ENIIP); err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "register NLB target", err) + } + + if err := PutClusterMeta(acctKV, meta); err != nil { + s.markFailed(acctKV, name) + return nil, logCreateErr(name, accountID, "persist final meta", err) + } + + // bootstrapClusterCreatorAdminPermissions (default true): grant the caller + // system:masters via an AccessEntry so the cluster creator can immediately + // authenticate through the token webhook (Q9). Keyed by the caller's exact + // principal ARN — the webhook looks the entry up by the same ARN STS returns. + if bootstrapCreatorAdmin(input) && callerPrincipalARN != "" { + rec := newAccessEntryRecord(region, accountID, name, callerPrincipalARN, "", + []string{"system:masters"}, AccessEntryTypeStandard, nil, time.Now().UTC()) + if err := PutAccessEntryRecord(acctKV, rec); err != nil { + s.markFailed(acctKV, name) + return nil, fmt.Errorf("seed cluster-creator admin access entry: %w", err) + } + } else if bootstrapCreatorAdmin(input) { + slog.Warn("CreateCluster: bootstrapClusterCreatorAdminPermissions set but caller principal ARN unknown; skipping creator-admin AccessEntry", + "cluster", name, "accountID", accountID) + } + + s.spawnBootstrap(accountID, name, acctKV, nil) + s.spawnReconciler(accountID, name, meta) + + return &eks.CreateClusterOutput{Cluster: clusterMetaToAWS(meta)}, nil +} + +// bootstrapCreatorAdmin reports whether the cluster-creator-admin AccessEntry +// should be minted. AWS defaults this to true when unspecified. +func bootstrapCreatorAdmin(input *eks.CreateClusterInput) bool { + if input.AccessConfig == nil || input.AccessConfig.BootstrapClusterCreatorAdminPermissions == nil { + return true + } + return *input.AccessConfig.BootstrapClusterCreatorAdminPermissions +} + +// systemEgressEvent is the wire shape for vpc.add-system-egress / +// vpc.delete-system-egress (mirrors network/subscribers.SystemEgressEvent; +// kept local to avoid importing the network layer from a handler). +type systemEgressEvent struct { + VpcId string `json:"vpc_id"` + SubnetId string `json:"subnet_id"` + InstanceIp string `json:"instance_ip"` + ExternalIp string `json:"external_ip"` +} + +// allocateClusterEgressIP allocates a hidden pool address for the cluster's +// control-plane VM egress, tagged ManagedBy=eks so it stays out of customer +// DescribeAddresses listings. +func (s *EKSServiceImpl) allocateClusterEgressIP(accountID, name string) (publicIP, allocationID string, err error) { + out, err := s.deps.EIP.AllocateAddress(&ec2.AllocateAddressInput{ + Domain: aws.String("vpc"), + TagSpecifications: []*ec2.TagSpecification{{ + ResourceType: aws.String("elastic-ip"), + Tags: []*ec2.Tag{{Key: aws.String(tags.ManagedByKey), Value: aws.String(tags.ManagedByEKS)}}, + }}, + }, accountID) + if err != nil { + return "", "", err + } + if out == nil || aws.StringValue(out.PublicIp) == "" || aws.StringValue(out.AllocationId) == "" { + return "", "", errors.New("eks: AllocateAddress returned incomplete EIP") + } + return aws.StringValue(out.PublicIp), aws.StringValue(out.AllocationId), nil +} + +func (s *EKSServiceImpl) DescribeCluster(input *eks.DescribeClusterInput, accountID string) (*eks.DescribeClusterOutput, error) { + name := aws.StringValue(input.Name) + if name == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + js, err := s.deps.NATSConn.JetStream() + if err != nil { + return nil, fmt.Errorf("jetstream: %w", err) + } + acctKV, err := GetOrCreateAccountBucket(js, accountID) + if err != nil { + return nil, fmt.Errorf("get account bucket: %w", err) + } + meta, err := GetClusterMeta(acctKV, name) + if err != nil { + if errors.Is(err, ErrClusterNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return &eks.DescribeClusterOutput{Cluster: clusterMetaToAWS(meta)}, nil +} + +func (s *EKSServiceImpl) ListClusters(input *eks.ListClustersInput, accountID string) (*eks.ListClustersOutput, error) { + js, err := s.deps.NATSConn.JetStream() + if err != nil { + return nil, eksReadUnavailableOr(err, "jetstream") + } + acctKV, err := GetOrCreateAccountBucket(js, accountID) + if err != nil { + return nil, eksReadUnavailableOr(err, "get account bucket") + } + names, err := listClusterNames(acctKV) + if err != nil { + return nil, eksReadUnavailableOr(err, "list cluster names") + } + sort.Strings(names) + + maxResults := int(aws.Int64Value(input.MaxResults)) + if maxResults <= 0 || maxResults > 100 { + maxResults = 100 + } + startToken := aws.StringValue(input.NextToken) + + page := make([]string, 0, maxResults) + var nextToken string + startIdx := 0 + if startToken != "" { + startIdx = sort.SearchStrings(names, startToken) + } + endIdx := min(startIdx+maxResults, len(names)) + page = append(page, names[startIdx:endIdx]...) + if endIdx < len(names) { + nextToken = names[endIdx] + } + + out := &eks.ListClustersOutput{Clusters: aws.StringSlice(page)} + if nextToken != "" { + out.NextToken = aws.String(nextToken) + } + return out, nil +} + +func (s *EKSServiceImpl) DeleteCluster(input *eks.DeleteClusterInput, accountID string) (*eks.DeleteClusterOutput, error) { + if err := s.requireOrchestrationDeps("DeleteCluster"); err != nil { + return nil, err + } + name := aws.StringValue(input.Name) + if name == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + js, err := s.deps.NATSConn.JetStream() + if err != nil { + return nil, fmt.Errorf("jetstream: %w", err) + } + acctKV, err := GetOrCreateAccountBucket(js, accountID) + if err != nil { + return nil, fmt.Errorf("get account bucket: %w", err) + } + + meta, err := GetClusterMeta(acctKV, name) + if err != nil { + if errors.Is(err, ErrClusterNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + + if err := SetClusterStatus(acctKV, name, ClusterStatusDeleting); err != nil { + return nil, fmt.Errorf("set DELETING: %w", err) + } + meta.Status = ClusterStatusDeleting + + if err := s.purgeClusterInfra(accountID, name, meta, acctKV); err != nil { + slog.Error("DeleteCluster: teardown incomplete; leaving cluster DELETING for retry", + "cluster", name, "err", err) + return nil, fmt.Errorf("eks: DeleteCluster %s: %w", name, err) + } + + return &eks.DeleteClusterOutput{Cluster: clusterMetaToAWS(meta)}, nil +} + +// purgeClusterInfra tears down a cluster's live AWS resources and erases its +// per-cluster KV state. Zeroize (a security guarantee — no recoverable key +// material) plus NLB and VM teardown are blocking: their failures are joined +// and returned BEFORE the KV sweep, so a billable resource is never orphaned +// without the meta record that owns its ARNs/IDs. SG cleanup is best-effort +// (a leaked SG strands no owning record). Shared by DeleteCluster and the +// FAILED-cluster reclaim path in CreateCluster. +func (s *EKSServiceImpl) purgeClusterInfra(accountID, name string, meta *ClusterMeta, acctKV nats.KeyValue) error { + s.registry.Stop(accountID, name) + + var teardownErrs []error + + if err := ZeroizeClusterOIDCKey(acctKV, name); err != nil { + teardownErrs = append(teardownErrs, fmt.Errorf("zeroize OIDC key: %w", err)) + } + + if meta.NLBArn != "" { + // Deregister is best-effort: DeleteClusterNLB tears down the whole NLB + // + target group, so a stale target registration cannot leak past it. + if meta.NLBTargetGroupArn != "" && meta.ControlPlaneENIIP != "" { + if err := DeregisterClusterTarget(s.deps.NLB, accountID, meta.NLBTargetGroupArn, meta.ControlPlaneENIIP); err != nil { + slog.Warn("purgeClusterInfra: deregister NLB target failed", "cluster", name, "err", err) + } + } + if err := DeleteClusterNLB(s.deps.NLB, accountID, name); err != nil { + teardownErrs = append(teardownErrs, fmt.Errorf("delete NLB: %w", err)) + } + } + + if meta.ControlPlaneInstanceID != "" || meta.ControlPlaneENIID != "" { + if err := TerminateK3sServerVM(s.deps.VPCK3s, s.deps.Instance, + accountID, meta.ControlPlaneInstanceID, meta.ControlPlaneENIID); err != nil { + teardownErrs = append(teardownErrs, fmt.Errorf("terminate K3s VM: %w", err)) + } + } + + // Egress: drop the OVN reroute+snat (fire-and-forget event, prune-safe) and + // release the hidden pool address. ReleaseAddress is blocking — the EIP is + // billable and its allocation ID lives in the meta we are about to erase, so + // a release failure must not let the KV sweep orphan it. + if meta.EgressEIPPublicIP != "" || meta.EgressEIPAllocationID != "" { + if meta.ResourcesVpcConfig != nil && meta.ResourcesVpcConfig.VpcId != "" && meta.ControlPlaneENIIP != "" { + subnetID := "" + if len(meta.ResourcesVpcConfig.SubnetIds) > 0 { + subnetID = meta.ResourcesVpcConfig.SubnetIds[0] + } + utils.PublishEvent(s.deps.NATSConn, "vpc.delete-system-egress", systemEgressEvent{ + VpcId: meta.ResourcesVpcConfig.VpcId, + SubnetId: subnetID, + InstanceIp: meta.ControlPlaneENIIP, + ExternalIp: meta.EgressEIPPublicIP, + }) + } + if meta.EgressEIPAllocationID != "" { + if _, err := s.deps.EIP.ReleaseAddress(&ec2.ReleaseAddressInput{ + AllocationId: aws.String(meta.EgressEIPAllocationID), + }, accountID); err != nil { + teardownErrs = append(teardownErrs, fmt.Errorf("release egress EIP: %w", err)) + } + } + } + + if len(teardownErrs) > 0 { + return errors.Join(teardownErrs...) + } + + if meta.ResourcesVpcConfig != nil && meta.ResourcesVpcConfig.VpcId != "" { + if err := DeleteClusterSGs(s.deps.VPCSG, accountID, name, meta.ResourcesVpcConfig.VpcId); err != nil { + slog.Warn("purgeClusterInfra: DeleteClusterSGs failed", "cluster", name, "err", err) + } + } + + // Only now, with NLB + VM confirmed gone, erase the meta + per-cluster KV. + if err := DeleteClusterPrefix(acctKV, name); err != nil { + return fmt.Errorf("delete cluster prefix: %w", err) + } + return nil } func (s *EKSServiceImpl) UpdateClusterConfig(_ *eks.UpdateClusterConfigInput, _ string) (*eks.UpdateClusterConfigOutput, error) { @@ -69,99 +755,364 @@ func (s *EKSServiceImpl) UpdateClusterVersion(_ *eks.UpdateClusterVersionInput, return nil, notImpl() } -func (s *EKSServiceImpl) DeleteCluster(_ *eks.DeleteClusterInput, _ string) (*eks.DeleteClusterOutput, error) { - return nil, notImpl() -} - // --- Nodegroup --- -func (s *EKSServiceImpl) CreateNodegroup(_ *eks.CreateNodegroupInput, _ string) (*eks.CreateNodegroupOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) CreateNodegroup(input *eks.CreateNodegroupInput, accountID string) (*eks.CreateNodegroupOutput, error) { + if err := s.requireOrchestrationDeps("CreateNodegroup"); err != nil { + return nil, err + } + acctKV, err := s.nodegroupAcctKV(accountID) + if err != nil { + return nil, err + } + return s.createNodegroup(acctKV, input, accountID) } -func (s *EKSServiceImpl) DescribeNodegroup(_ *eks.DescribeNodegroupInput, _ string) (*eks.DescribeNodegroupOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) DescribeNodegroup(input *eks.DescribeNodegroupInput, accountID string) (*eks.DescribeNodegroupOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + acctKV, err := s.nodegroupAcctKV(accountID) + if err != nil { + return nil, err + } + return s.describeNodegroup(acctKV, input) } -func (s *EKSServiceImpl) ListNodegroups(_ *eks.ListNodegroupsInput, _ string) (*eks.ListNodegroupsOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) ListNodegroups(input *eks.ListNodegroupsInput, accountID string) (*eks.ListNodegroupsOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + acctKV, err := s.nodegroupAcctKV(accountID) + if err != nil { + return nil, err + } + return s.listNodegroups(acctKV, input) } -func (s *EKSServiceImpl) UpdateNodegroupConfig(_ *eks.UpdateNodegroupConfigInput, _ string) (*eks.UpdateNodegroupConfigOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) UpdateNodegroupConfig(input *eks.UpdateNodegroupConfigInput, accountID string) (*eks.UpdateNodegroupConfigOutput, error) { + if err := s.requireOrchestrationDeps("UpdateNodegroupConfig"); err != nil { + return nil, err + } + acctKV, err := s.nodegroupAcctKV(accountID) + if err != nil { + return nil, err + } + return s.updateNodegroupConfig(acctKV, input, accountID) } -func (s *EKSServiceImpl) UpdateNodegroupVersion(_ *eks.UpdateNodegroupVersionInput, _ string) (*eks.UpdateNodegroupVersionOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) UpdateNodegroupVersion(input *eks.UpdateNodegroupVersionInput, accountID string) (*eks.UpdateNodegroupVersionOutput, error) { + return s.updateNodegroupVersion(input) } -func (s *EKSServiceImpl) DeleteNodegroup(_ *eks.DeleteNodegroupInput, _ string) (*eks.DeleteNodegroupOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) DeleteNodegroup(input *eks.DeleteNodegroupInput, accountID string) (*eks.DeleteNodegroupOutput, error) { + if err := s.requireOrchestrationDeps("DeleteNodegroup"); err != nil { + return nil, err + } + acctKV, err := s.nodegroupAcctKV(accountID) + if err != nil { + return nil, err + } + return s.deleteNodegroup(acctKV, input, accountID) } // --- AccessEntry + AccessPolicy --- -func (s *EKSServiceImpl) CreateAccessEntry(_ *eks.CreateAccessEntryInput, _ string) (*eks.CreateAccessEntryOutput, error) { - return nil, notImpl() +// acctKVForCluster opens the per-account bucket and verifies the cluster exists, +// translating absence to the AWS ResourceNotFoundException shape. The +// AccessEntry handlers all need both. +func (s *EKSServiceImpl) acctKVForCluster(accountID, cluster string) (nats.KeyValue, error) { + if cluster == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + js, err := s.deps.NATSConn.JetStream() + if err != nil { + return nil, fmt.Errorf("jetstream: %w", err) + } + acctKV, err := GetOrCreateAccountBucket(js, accountID) + if err != nil { + return nil, fmt.Errorf("get account bucket: %w", err) + } + if _, err := GetClusterMeta(acctKV, cluster); err != nil { + if errors.Is(err, ErrClusterNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return acctKV, nil } -func (s *EKSServiceImpl) DescribeAccessEntry(_ *eks.DescribeAccessEntryInput, _ string) (*eks.DescribeAccessEntryOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) CreateAccessEntry(input *eks.CreateAccessEntryInput, accountID string) (*eks.CreateAccessEntryOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + principalARN := aws.StringValue(input.PrincipalArn) + if principalARN == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + entryType := aws.StringValue(input.Type) + if entryType == "" { + entryType = AccessEntryTypeStandard + } + if entryType != AccessEntryTypeStandard { + // Node-type entries (EC2_LINUX/etc.) need the nodegroup join path; reject + // until Sprint 6d wires them. + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + if _, err := GetAccessEntryRecord(acctKV, cluster, principalARN); err == nil { + return nil, errors.New(awserrors.ErrorEKSResourceInUse) + } else if !errors.Is(err, ErrAccessEntryNotFound) { + return nil, err + } + rec := newAccessEntryRecord(s.deps.Region, accountID, cluster, principalARN, + aws.StringValue(input.Username), aws.StringValueSlice(input.KubernetesGroups), + entryType, aws.StringValueMap(input.Tags), time.Now().UTC()) + if err := PutAccessEntryRecord(acctKV, rec); err != nil { + return nil, err + } + return &eks.CreateAccessEntryOutput{AccessEntry: accessEntryRecordToAWS(rec)}, nil } -func (s *EKSServiceImpl) ListAccessEntries(_ *eks.ListAccessEntriesInput, _ string) (*eks.ListAccessEntriesOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) DescribeAccessEntry(input *eks.DescribeAccessEntryInput, accountID string) (*eks.DescribeAccessEntryOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + principalARN := aws.StringValue(input.PrincipalArn) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + rec, err := GetAccessEntryRecord(acctKV, cluster, principalARN) + if err != nil { + if errors.Is(err, ErrAccessEntryNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return &eks.DescribeAccessEntryOutput{AccessEntry: accessEntryRecordToAWS(rec)}, nil } -func (s *EKSServiceImpl) UpdateAccessEntry(_ *eks.UpdateAccessEntryInput, _ string) (*eks.UpdateAccessEntryOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) ListAccessEntries(input *eks.ListAccessEntriesInput, accountID string) (*eks.ListAccessEntriesOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + recs, err := ListAccessEntryRecords(acctKV, cluster) + if err != nil { + return nil, err + } + filter := aws.StringValue(input.AssociatedPolicyArn) + arns := make([]string, 0, len(recs)) + for _, rec := range recs { + if filter != "" && !hasAssociatedPolicy(rec, filter) { + continue + } + arns = append(arns, rec.PrincipalARN) + } + return &eks.ListAccessEntriesOutput{AccessEntries: aws.StringSlice(arns)}, nil } -func (s *EKSServiceImpl) DeleteAccessEntry(_ *eks.DeleteAccessEntryInput, _ string) (*eks.DeleteAccessEntryOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) UpdateAccessEntry(input *eks.UpdateAccessEntryInput, accountID string) (*eks.UpdateAccessEntryOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + principalARN := aws.StringValue(input.PrincipalArn) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + now := time.Now().UTC() + rec, err := casUpdateAccessEntry(acctKV, cluster, principalARN, func(r *AccessEntryRecord) bool { + if input.KubernetesGroups != nil { + r.KubernetesGroups = aws.StringValueSlice(input.KubernetesGroups) + } + if u := aws.StringValue(input.Username); u != "" { + r.KubernetesUsername = u + } + r.ModifiedAt = now + return true + }) + if err != nil { + if errors.Is(err, ErrAccessEntryNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return &eks.UpdateAccessEntryOutput{AccessEntry: accessEntryRecordToAWS(rec)}, nil } -func (s *EKSServiceImpl) AssociateAccessPolicy(_ *eks.AssociateAccessPolicyInput, _ string) (*eks.AssociateAccessPolicyOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) DeleteAccessEntry(input *eks.DeleteAccessEntryInput, accountID string) (*eks.DeleteAccessEntryOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + principalARN := aws.StringValue(input.PrincipalArn) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + if err := DeleteAccessEntryRecord(acctKV, cluster, principalARN); err != nil { + if errors.Is(err, ErrAccessEntryNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return &eks.DeleteAccessEntryOutput{}, nil } -func (s *EKSServiceImpl) DisassociateAccessPolicy(_ *eks.DisassociateAccessPolicyInput, _ string) (*eks.DisassociateAccessPolicyOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) AssociateAccessPolicy(input *eks.AssociateAccessPolicyInput, accountID string) (*eks.AssociateAccessPolicyOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + principalARN := aws.StringValue(input.PrincipalArn) + policyARN := aws.StringValue(input.PolicyArn) + if _, ok := supportedAccessPolicies[policyARN]; !ok { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + scope, err := validateAccessScope(input.AccessScope) + if err != nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + now := time.Now().UTC() + var assoc AssociatedAccessPolicy + _, err = casUpdateAccessEntry(acctKV, cluster, principalARN, func(r *AccessEntryRecord) bool { + assoc = AssociatedAccessPolicy{PolicyARN: policyARN, AccessScope: scope, AssociatedAt: now, ModifiedAt: now} + for i := range r.AssociatedPolicies { + if r.AssociatedPolicies[i].PolicyARN == policyARN { + assoc.AssociatedAt = r.AssociatedPolicies[i].AssociatedAt + r.AssociatedPolicies[i] = assoc + r.ModifiedAt = now + return true + } + } + r.AssociatedPolicies = append(r.AssociatedPolicies, assoc) + r.ModifiedAt = now + return true + }) + if err != nil { + if errors.Is(err, ErrAccessEntryNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return &eks.AssociateAccessPolicyOutput{ + ClusterName: aws.String(cluster), + PrincipalArn: aws.String(principalARN), + AssociatedAccessPolicy: associatedPolicyToAWS(assoc), + }, nil } -func (s *EKSServiceImpl) ListAssociatedAccessPolicies(_ *eks.ListAssociatedAccessPoliciesInput, _ string) (*eks.ListAssociatedAccessPoliciesOutput, error) { - return nil, notImpl() +func (s *EKSServiceImpl) DisassociateAccessPolicy(input *eks.DisassociateAccessPolicyInput, accountID string) (*eks.DisassociateAccessPolicyOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + principalARN := aws.StringValue(input.PrincipalArn) + policyARN := aws.StringValue(input.PolicyArn) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + now := time.Now().UTC() + _, err = casUpdateAccessEntry(acctKV, cluster, principalARN, func(r *AccessEntryRecord) bool { + for i := range r.AssociatedPolicies { + if r.AssociatedPolicies[i].PolicyARN == policyARN { + r.AssociatedPolicies = append(r.AssociatedPolicies[:i], r.AssociatedPolicies[i+1:]...) + r.ModifiedAt = now + return true + } + } + return false + }) + if err != nil { + if errors.Is(err, ErrAccessEntryNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + return &eks.DisassociateAccessPolicyOutput{}, nil +} + +func (s *EKSServiceImpl) ListAssociatedAccessPolicies(input *eks.ListAssociatedAccessPoliciesInput, accountID string) (*eks.ListAssociatedAccessPoliciesOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + cluster := aws.StringValue(input.ClusterName) + principalARN := aws.StringValue(input.PrincipalArn) + acctKV, err := s.acctKVForCluster(accountID, cluster) + if err != nil { + return nil, err + } + rec, err := GetAccessEntryRecord(acctKV, cluster, principalARN) + if err != nil { + if errors.Is(err, ErrAccessEntryNotFound) { + return nil, errors.New(awserrors.ErrorEKSResourceNotFound) + } + return nil, err + } + policies := make([]*eks.AssociatedAccessPolicy, 0, len(rec.AssociatedPolicies)) + for _, p := range rec.AssociatedPolicies { + policies = append(policies, associatedPolicyToAWS(p)) + } + return &eks.ListAssociatedAccessPoliciesOutput{ + ClusterName: aws.String(cluster), + PrincipalArn: aws.String(principalARN), + AssociatedAccessPolicies: policies, + }, nil } func (s *EKSServiceImpl) ListAccessPolicies(_ *eks.ListAccessPoliciesInput, _ string) (*eks.ListAccessPoliciesOutput, error) { - return nil, notImpl() + arns := make([]string, 0, len(supportedAccessPolicies)) + for arn := range supportedAccessPolicies { + arns = append(arns, arn) + } + sort.Strings(arns) + policies := make([]*eks.AccessPolicy, 0, len(arns)) + for _, arn := range arns { + policies = append(policies, &eks.AccessPolicy{ + Arn: aws.String(arn), + Name: aws.String(accessPolicyName(arn)), + }) + } + return &eks.ListAccessPoliciesOutput{AccessPolicies: policies}, nil } -// --- Addons --- - -func (s *EKSServiceImpl) ListAddons(_ *eks.ListAddonsInput, _ string) (*eks.ListAddonsOutput, error) { - return nil, notImpl() +// hasAssociatedPolicy reports whether the entry has the given policy ARN bound. +func hasAssociatedPolicy(rec *AccessEntryRecord, policyARN string) bool { + for _, p := range rec.AssociatedPolicies { + if p.PolicyARN == policyARN { + return true + } + } + return false } -func (s *EKSServiceImpl) DescribeAddonVersions(_ *eks.DescribeAddonVersionsInput, _ string) (*eks.DescribeAddonVersionsOutput, error) { - return nil, notImpl() +// accessPolicyName extracts the policy short name from its ARN +// (…/cluster-access-policy/AmazonEKSViewPolicy → AmazonEKSViewPolicy). +func accessPolicyName(arn string) string { + if i := strings.LastIndex(arn, "/"); i >= 0 { + return arn[i+1:] + } + return arn } -func (s *EKSServiceImpl) CreateAddon(_ *eks.CreateAddonInput, _ string) (*eks.CreateAddonOutput, error) { - return nil, notImpl() -} - -func (s *EKSServiceImpl) DeleteAddon(_ *eks.DeleteAddonInput, _ string) (*eks.DeleteAddonOutput, error) { - return nil, notImpl() -} - -func (s *EKSServiceImpl) DescribeAddon(_ *eks.DescribeAddonInput, _ string) (*eks.DescribeAddonOutput, error) { - return nil, notImpl() -} - -func (s *EKSServiceImpl) UpdateAddon(_ *eks.UpdateAddonInput, _ string) (*eks.UpdateAddonOutput, error) { - return nil, notImpl() -} +// --- Addons (see addons.go) --- // --- OIDC identity-provider configs --- @@ -194,3 +1145,228 @@ func (s *EKSServiceImpl) UntagResource(_ *eks.UntagResourceInput, _ string) (*ek func (s *EKSServiceImpl) ListTagsForResource(_ *eks.ListTagsForResourceInput, _ string) (*eks.ListTagsForResourceOutput, error) { return nil, notImpl() } + +// --- helpers --- + +func notImpl() error { return errors.New(awserrors.ErrorNotImplemented) } + +func validateCreateClusterInput(input *eks.CreateClusterInput) error { + if input == nil || input.Name == nil || *input.Name == "" { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + if input.RoleArn == nil || !strings.HasPrefix(*input.RoleArn, "arn:aws:iam:") { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + if input.ResourcesVpcConfig == nil || len(input.ResourcesVpcConfig.SubnetIds) == 0 { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + for _, sn := range input.ResourcesVpcConfig.SubnetIds { + if sn == nil || *sn == "" { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + } + if input.AccessConfig != nil && input.AccessConfig.AuthenticationMode != nil { + mode := *input.AccessConfig.AuthenticationMode + if mode != "" && mode != eks.AuthenticationModeApi { + return errors.New(awserrors.ErrorInvalidParameter) + } + } + // AWS rejects disabling both public and private endpoint access — the + // control plane would be unreachable. + if v := input.ResourcesVpcConfig; v != nil && + v.EndpointPublicAccess != nil && !*v.EndpointPublicAccess && + v.EndpointPrivateAccess != nil && !*v.EndpointPrivateAccess { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return nil +} + +// defaultPublicAccessCidr is the AWS-default allowed source range for a public +// cluster endpoint when the caller specifies none. +const defaultPublicAccessCidr = "0.0.0.0/0" + +// endpointAccess resolves apiserver endpoint exposure from the request, +// applying AWS defaults (public on, private off) to omitted fields. +func endpointAccess(vpc *eks.VpcConfigRequest) (public, private bool) { + public, private = true, false + if vpc == nil { + return public, private + } + if vpc.EndpointPublicAccess != nil { + public = *vpc.EndpointPublicAccess + } + if vpc.EndpointPrivateAccess != nil { + private = *vpc.EndpointPrivateAccess + } + return public, private +} + +// publicAccessCidrs returns the allowed source ranges for a public endpoint, +// defaulting to 0.0.0.0/0 (AWS parity) when public access is on and none given. +// Nil for a private-only endpoint. +func publicAccessCidrs(vpc *eks.VpcConfigRequest, public bool) []string { + if !public { + return nil + } + if vpc != nil && len(vpc.PublicAccessCidrs) > 0 { + return aws.StringValueSlice(vpc.PublicAccessCidrs) + } + return []string{defaultPublicAccessCidr} +} + +func (s *EKSServiceImpl) markFailed(kv nats.KeyValue, name string) { + if err := SetClusterStatus(kv, name, ClusterStatusFailed); err != nil { + slog.Warn("CreateCluster: SetClusterStatus(FAILED) failed", "cluster", name, "err", err) + } +} + +// spawnBootstrap launches the one-shot NATS bootstrap subscriber for a cluster. +// kinds nil means a fresh CreateCluster (wait on all four subjects); a non-nil +// subset is a daemon-restart resume that waits only on the missing artifacts. +func (s *EKSServiceImpl) spawnBootstrap(accountID, clusterName string, kv nats.KeyValue, kinds []string) { + boot, err := NewNATSBootstrap(s.deps.NATSConn, kv, s.deps.MasterKey, accountID, clusterName) + if err != nil { + slog.Error("spawnBootstrap: NewNATSBootstrap failed", "cluster", clusterName, "err", err) + return + } + go func() { + err := boot.RunForKinds(s.bgCtx, kinds) + if err == nil || errors.Is(err, context.Canceled) { + return + } + // Bootstrap died without collecting all requested artifacts. Without + // this the cluster sits in CREATING forever — fail it so + // DescribeCluster surfaces the cause and DeleteCluster can reclaim the + // resources. + slog.Warn("NATSBootstrap exited; marking cluster FAILED", "cluster", clusterName, "err", err) + if markErr := MarkClusterFailed(kv, clusterName, "bootstrap failed: "+err.Error()); markErr != nil && + !errors.Is(markErr, ErrClusterNotFound) { + slog.Error("spawnBootstrap: MarkClusterFailed", "cluster", clusterName, "err", markErr) + } + }() +} + +func (s *EKSServiceImpl) spawnReconciler(accountID, clusterName string, _ *ClusterMeta) { + js, err := s.deps.NATSConn.JetStream() + if err != nil { + slog.Error("spawnReconciler: jetstream", "err", err) + return + } + acctKV, err := GetOrCreateAccountBucket(js, accountID) + if err != nil { + slog.Error("spawnReconciler: account bucket", "err", err) + return + } + // Health gates on the control plane's NATS self-report, not an HTTP probe: + // k3s binds the apiserver to the VPC node-ip, unreachable from the host. The + // CP publishes {healthz,node_count} on the mgmt bus the daemon already shares. + stateSubject := StateSubject(accountID, clusterName) + spawn := func(ctx context.Context, _, _ string) (func(), error) { + return RunClusterReconciler(ctx, s.leaderKV, acctKV, accountID, clusterName, s.deps.HolderID, "", + WithStateSource(s.deps.NATSConn, stateSubject)) + } + if err := s.registry.Spawn(s.bgCtx, accountID, clusterName, spawn); err != nil { + slog.Error("spawnReconciler: registry spawn", "cluster", clusterName, "err", err) + } +} + +func clusterMetaToAWS(meta *ClusterMeta) *eks.Cluster { + if meta == nil { + return nil + } + out := &eks.Cluster{ + Name: aws.String(meta.Name), + Arn: aws.String(meta.Arn), + Status: aws.String(string(meta.Status)), + Version: aws.String(meta.Version), + RoleArn: aws.String(meta.RoleArn), + CreatedAt: aws.Time(meta.CreatedAt), + } + if meta.Endpoint != "" { + out.Endpoint = aws.String(meta.Endpoint) + } + if meta.OIDCIssuer != "" { + out.Identity = &eks.Identity{Oidc: &eks.OIDC{Issuer: aws.String(meta.OIDCIssuer)}} + } + if meta.CertificateAuthorityB64 != "" { + out.CertificateAuthority = &eks.Certificate{Data: aws.String(meta.CertificateAuthorityB64)} + } + if meta.ResourcesVpcConfig != nil { + out.ResourcesVpcConfig = &eks.VpcConfigResponse{ + SubnetIds: aws.StringSlice(meta.ResourcesVpcConfig.SubnetIds), + SecurityGroupIds: aws.StringSlice(meta.ResourcesVpcConfig.SecurityGroupIds), + VpcId: aws.String(meta.ResourcesVpcConfig.VpcId), + EndpointPublicAccess: aws.Bool(meta.ResourcesVpcConfig.EndpointPublicAccess), + EndpointPrivateAccess: aws.Bool(meta.ResourcesVpcConfig.EndpointPrivateAccess), + PublicAccessCidrs: aws.StringSlice(meta.ResourcesVpcConfig.PublicAccessCidrs), + } + } + if meta.HealthIssue != "" { + out.Health = &eks.ClusterHealth{ + Issues: []*eks.ClusterIssue{{ + Code: aws.String(eks.ClusterIssueCodeClusterUnreachable), + Message: aws.String(meta.HealthIssue), + }}, + } + } + return out +} + +// natsTransient reports whether err is a transient NATS/JetStream condition +// rather than a genuine fault. These occur in the post-restart window before +// the KV stream's Raft group has elected a leader: JetStream requests get no +// responder, time out, or the stream is briefly unreachable. +func natsTransient(err error) bool { + return err != nil && (errors.Is(err, nats.ErrNoResponders) || + errors.Is(err, nats.ErrTimeout) || + errors.Is(err, nats.ErrNoStreamResponse) || + errors.Is(err, nats.ErrConnectionClosed)) +} + +// eksReadUnavailableOr maps a transient NATS/JetStream failure to a retryable +// ServiceUnavailable (503) so AWS clients back off and retry, instead of the +// daemon sanitizing an unrecognised wrapped error to a misleading 500 +// InternalError. Non-transient errors pass through wrapped so real faults stay +// visible. +func eksReadUnavailableOr(err error, op string) error { + if natsTransient(err) { + slog.Warn("EKS read temporarily unavailable", "op", op, "err", err) + return errors.New(awserrors.ErrorServiceUnavailable) + } + return fmt.Errorf("%s: %w", op, err) +} + +func listClusterNames(kv nats.KeyValue) ([]string, error) { + keys, err := kv.Keys() + if err != nil { + if errors.Is(err, nats.ErrNoKeysFound) { + return nil, nil + } + return nil, fmt.Errorf("kv keys: %w", err) + } + suffix := "/meta" + names := make([]string, 0, len(keys)) + seen := make(map[string]struct{}, len(keys)) + for _, k := range keys { + if !strings.HasPrefix(k, "clusters/") || !strings.HasSuffix(k, suffix) { + continue + } + name := strings.TrimSuffix(strings.TrimPrefix(k, "clusters/"), suffix) + if name == "" || strings.Contains(name, "/") { + continue + } + if _, ok := seen[name]; ok { + continue + } + seen[name] = struct{}{} + names = append(names, name) + } + return names, nil +} + +func deref(p *string, fallback string) string { + if p == nil || *p == "" { + return fallback + } + return *p +} diff --git a/spinifex/handlers/eks/service_impl_test.go b/spinifex/handlers/eks/service_impl_test.go index fce46a0a8..963884c51 100644 --- a/spinifex/handlers/eks/service_impl_test.go +++ b/spinifex/handlers/eks/service_impl_test.go @@ -1,12 +1,15 @@ package handlers_eks import ( + "errors" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/eks" "github.com/mulgadc/spinifex/spinifex/awserrors" "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -18,17 +21,27 @@ func setupTestService(t *testing.T) *EKSServiceImpl { return svc } -func TestEKSServiceImpl_ClusterMethodsReturnNotImplemented(t *testing.T) { +// TestEKSServiceImpl_ClusterLifecycleShimMode covers the four lifecycle +// methods when the service is constructed via NewEKSServiceImplWithNATS +// (the shim path the daemon-handler routing test uses): orchestration deps +// are absent so CreateCluster/DeleteCluster short-circuit to ServiceUnavailable +// (the missing deps are logged at ERROR), DescribeCluster hits an empty +// per-account bucket and surfaces ResourceNotFoundException, and ListClusters +// returns an empty list. The UpdateClusterConfig + UpdateClusterVersion paths +// stay NotImplemented. +func TestEKSServiceImpl_ClusterLifecycleShimMode(t *testing.T) { svc := setupTestService(t) - _, err := svc.CreateCluster(&eks.CreateClusterInput{Name: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + _, err := svc.CreateCluster(&eks.CreateClusterInput{Name: aws.String("c1")}, testAccountID, "") + require.EqualError(t, err, awserrors.ErrorServiceUnavailable) _, err = svc.DescribeCluster(&eks.DescribeClusterInput{Name: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) - _, err = svc.ListClusters(&eks.ListClustersInput{}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + out, err := svc.ListClusters(&eks.ListClustersInput{}, testAccountID) + require.NoError(t, err) + require.NotNil(t, out) + require.Empty(t, out.Clusters) _, err = svc.UpdateClusterConfig(&eks.UpdateClusterConfigInput{Name: aws.String("c1")}, testAccountID) require.EqualError(t, err, awserrors.ErrorNotImplemented) @@ -37,82 +50,277 @@ func TestEKSServiceImpl_ClusterMethodsReturnNotImplemented(t *testing.T) { require.EqualError(t, err, awserrors.ErrorNotImplemented) _, err = svc.DeleteCluster(&eks.DeleteClusterInput{Name: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + require.EqualError(t, err, awserrors.ErrorServiceUnavailable) } -func TestEKSServiceImpl_NodegroupMethodsReturnNotImplemented(t *testing.T) { +// EKS only supports the API authentication mode here; CONFIG_MAP (and the +// API_AND_CONFIG_MAP hybrid) must be rejected with InvalidParameterException. +func TestValidateCreateClusterInput_RejectsConfigMapAuthMode(t *testing.T) { + in := createInput("alpha") + in.AccessConfig = &eks.CreateAccessConfigRequest{ + AuthenticationMode: aws.String(eks.AuthenticationModeConfigMap), + } + err := validateCreateClusterInput(in) + require.EqualError(t, err, awserrors.ErrorInvalidParameter) +} + +// The API_AND_CONFIG_MAP hybrid still enables the unsupported aws-auth ConfigMap +// path, so it must be rejected the same as plain CONFIG_MAP — the sibling test +// only covers the CONFIG_MAP value. +func TestValidateCreateClusterInput_RejectsAPIAndConfigMapAuthMode(t *testing.T) { + in := createInput("alpha") + in.AccessConfig = &eks.CreateAccessConfigRequest{ + AuthenticationMode: aws.String(eks.AuthenticationModeApiAndConfigMap), + } + require.EqualError(t, validateCreateClusterInput(in), awserrors.ErrorInvalidParameter) +} + +func TestValidateCreateClusterInput_AcceptsAPIAuthMode(t *testing.T) { + in := createInput("alpha") + in.AccessConfig = &eks.CreateAccessConfigRequest{ + AuthenticationMode: aws.String(eks.AuthenticationModeApi), + } + require.NoError(t, validateCreateClusterInput(in)) +} + +// A create request with no subnets is malformed and must be rejected before any +// orchestration work (InvalidParameterValue → 400). +func TestValidateCreateClusterInput_RejectsMissingSubnetIds(t *testing.T) { + in := createInput("alpha") + in.ResourcesVpcConfig = &eks.VpcConfigRequest{} + require.EqualError(t, validateCreateClusterInput(in), awserrors.ErrorInvalidParameterValue) + + in.ResourcesVpcConfig = nil + require.EqualError(t, validateCreateClusterInput(in), awserrors.ErrorInvalidParameterValue) +} + +// DescribeCluster on an absent cluster must reach the KV lookup (full deps +// wired, unlike the shim short-circuit) and surface ResourceNotFoundException. +func TestDescribeCluster_NotFoundWithFullDeps(t *testing.T) { + f := newEKSServiceFixture(t) + + _, err := f.svc.DescribeCluster(&eks.DescribeClusterInput{Name: aws.String("ghost")}, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +// DeleteCluster on an absent cluster must reach the KV lookup with full deps +// wired (the shim path short-circuits to ServiceUnavailable before the meta +// read) and surface ResourceNotFoundException, not a teardown of nothing. +func TestDeleteCluster_NotFoundWithFullDeps(t *testing.T) { + f := newEKSServiceFixture(t) + + _, err := f.svc.DeleteCluster(deleteInput("ghost"), testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +// Security guarantee: the OIDC signing key must be zeroized BEFORE infra +// teardown, so a teardown failure (which leaves the meta for retry) can never +// leave recoverable key material behind. Force VM terminate to fail and assert +// the key is already gone while the meta survives. +func TestDeleteCluster_ZeroizesOIDCKeyBeforeTeardown(t *testing.T) { + f := newDeleteClusterFixture(t, "alpha") + f.inst.terminateErr = errors.New("hypervisor unreachable") + + _, err := f.svc.DeleteCluster(deleteInput("alpha"), testAccountID) + require.Error(t, err, "teardown failure must surface") + + _, getErr := f.kv.Get(OIDCSigningKeyKey("alpha")) + assert.ErrorIs(t, getErr, nats.ErrKeyNotFound, "OIDC key must be zeroized before teardown, even when teardown fails") + + meta, metaErr := GetClusterMeta(f.kv, "alpha") + require.NoError(t, metaErr, "meta must survive a failed teardown for retry") + assert.Equal(t, ClusterStatusDeleting, meta.Status) +} + +// In shim mode (orchestration deps absent) the mutating nodegroup methods +// short-circuit to ServiceUnavailable, the read methods reach an empty +// per-account bucket and surface ResourceNotFoundException, and +// UpdateNodegroupVersion stays NotImplemented (v1 doesn't do AMI upgrades). +func TestEKSServiceImpl_NodegroupMethodsShimMode(t *testing.T) { svc := setupTestService(t) _, err := svc.CreateNodegroup(&eks.CreateNodegroupInput{ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + require.EqualError(t, err, awserrors.ErrorServiceUnavailable) _, err = svc.DescribeNodegroup(&eks.DescribeNodegroupInput{ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) _, err = svc.ListNodegroups(&eks.ListNodegroupsInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) _, err = svc.UpdateNodegroupConfig(&eks.UpdateNodegroupConfigInput{ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + require.EqualError(t, err, awserrors.ErrorServiceUnavailable) _, err = svc.UpdateNodegroupVersion(&eks.UpdateNodegroupVersionInput{ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1")}, testAccountID) require.EqualError(t, err, awserrors.ErrorNotImplemented) _, err = svc.DeleteNodegroup(&eks.DeleteNodegroupInput{ClusterName: aws.String("c1"), NodegroupName: aws.String("ng1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + require.EqualError(t, err, awserrors.ErrorServiceUnavailable) } -func TestEKSServiceImpl_AccessMethodsReturnNotImplemented(t *testing.T) { - svc := setupTestService(t) - - _, err := svc.CreateAccessEntry(&eks.CreateAccessEntryInput{ClusterName: aws.String("c1"), PrincipalArn: aws.String("arn:aws:iam::111122223333:user/dev")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) - - _, err = svc.DescribeAccessEntry(&eks.DescribeAccessEntryInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) - - _, err = svc.ListAccessEntries(&eks.ListAccessEntriesInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) - - _, err = svc.UpdateAccessEntry(&eks.UpdateAccessEntryInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) - - _, err = svc.DeleteAccessEntry(&eks.DeleteAccessEntryInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) - - _, err = svc.AssociateAccessPolicy(&eks.AssociateAccessPolicyInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) - - _, err = svc.DisassociateAccessPolicy(&eks.DisassociateAccessPolicyInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) - - _, err = svc.ListAssociatedAccessPolicies(&eks.ListAssociatedAccessPoliciesInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) - - _, err = svc.ListAccessPolicies(&eks.ListAccessPoliciesInput{}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) +// seedTestCluster lays down a minimal ACTIVE cluster meta in the per-account +// bucket so the AccessEntry handlers (which gate on cluster existence) can run. +func seedTestCluster(t *testing.T, svc *EKSServiceImpl, cluster string) { + t.Helper() + js, err := svc.deps.NATSConn.JetStream() + require.NoError(t, err) + kv, err := GetOrCreateAccountBucket(js, testAccountID) + require.NoError(t, err) + require.NoError(t, PutClusterMeta(kv, &ClusterMeta{Name: cluster, Status: ClusterStatusActive})) } -func TestEKSServiceImpl_AddonsMethodsReturnNotImplemented(t *testing.T) { +const testPrincipalARN = "arn:aws:iam::111122223333:role/dev" + +func TestAccessEntry_UnknownClusterIsNotFound(t *testing.T) { svc := setupTestService(t) + _, err := svc.CreateAccessEntry(&eks.CreateAccessEntryInput{ + ClusterName: aws.String("missing"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} - _, err := svc.ListAddons(&eks.ListAddonsInput{ClusterName: aws.String("c1")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) +func TestAccessEntry_CreateDescribeListDelete(t *testing.T) { + svc := setupTestService(t) + seedTestCluster(t, svc, "c1") - _, err = svc.DescribeAddonVersions(&eks.DescribeAddonVersionsInput{}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + out, err := svc.CreateAccessEntry(&eks.CreateAccessEntryInput{ + ClusterName: aws.String("c1"), + PrincipalArn: aws.String(testPrincipalARN), + KubernetesGroups: aws.StringSlice([]string{"system:masters"}), + }, testAccountID) + require.NoError(t, err) + require.NotNil(t, out.AccessEntry) + // Username defaults to the principal ARN; type defaults to STANDARD. + assert.Equal(t, testPrincipalARN, aws.StringValue(out.AccessEntry.Username)) + assert.Equal(t, AccessEntryTypeStandard, aws.StringValue(out.AccessEntry.Type)) + assert.Contains(t, aws.StringValue(out.AccessEntry.AccessEntryArn), ":access-entry/c1/") - _, err = svc.CreateAddon(&eks.CreateAddonInput{ClusterName: aws.String("c1"), AddonName: aws.String("vpc-cni")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + // Duplicate Create → ResourceInUseException. + _, err = svc.CreateAccessEntry(&eks.CreateAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceInUse) - _, err = svc.DeleteAddon(&eks.DeleteAddonInput{ClusterName: aws.String("c1"), AddonName: aws.String("vpc-cni")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + desc, err := svc.DescribeAccessEntry(&eks.DescribeAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{"system:masters"}, aws.StringValueSlice(desc.AccessEntry.KubernetesGroups)) - _, err = svc.DescribeAddon(&eks.DescribeAddonInput{ClusterName: aws.String("c1"), AddonName: aws.String("vpc-cni")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + list, err := svc.ListAccessEntries(&eks.ListAccessEntriesInput{ClusterName: aws.String("c1")}, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{testPrincipalARN}, aws.StringValueSlice(list.AccessEntries)) - _, err = svc.UpdateAddon(&eks.UpdateAddonInput{ClusterName: aws.String("c1"), AddonName: aws.String("vpc-cni")}, testAccountID) - require.EqualError(t, err, awserrors.ErrorNotImplemented) + _, err = svc.DeleteAccessEntry(&eks.DeleteAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.NoError(t, err) + + _, err = svc.DescribeAccessEntry(&eks.DescribeAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +func TestAccessEntry_RejectsNodeType(t *testing.T) { + svc := setupTestService(t) + seedTestCluster(t, svc, "c1") + _, err := svc.CreateAccessEntry(&eks.CreateAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), Type: aws.String("EC2_LINUX"), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestAccessEntry_DescribeDeleteMissingIsNotFound(t *testing.T) { + svc := setupTestService(t) + seedTestCluster(t, svc, "c1") + _, err := svc.DescribeAccessEntry(&eks.DescribeAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) + _, err = svc.DeleteAccessEntry(&eks.DeleteAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorEKSResourceNotFound) +} + +func TestAccessPolicy_AssociateListDisassociate(t *testing.T) { + svc := setupTestService(t) + seedTestCluster(t, svc, "c1") + _, err := svc.CreateAccessEntry(&eks.CreateAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.NoError(t, err) + + const viewPolicy = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" + assoc, err := svc.AssociateAccessPolicy(&eks.AssociateAccessPolicyInput{ + ClusterName: aws.String("c1"), + PrincipalArn: aws.String(testPrincipalARN), + PolicyArn: aws.String(viewPolicy), + AccessScope: &eks.AccessScope{Type: aws.String("cluster")}, + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, viewPolicy, aws.StringValue(assoc.AssociatedAccessPolicy.PolicyArn)) + + listed, err := svc.ListAssociatedAccessPolicies(&eks.ListAssociatedAccessPoliciesInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.NoError(t, err) + require.Len(t, listed.AssociatedAccessPolicies, 1) + + // Entry now discoverable by the associated-policy filter. + filtered, err := svc.ListAccessEntries(&eks.ListAccessEntriesInput{ + ClusterName: aws.String("c1"), AssociatedPolicyArn: aws.String(viewPolicy), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{testPrincipalARN}, aws.StringValueSlice(filtered.AccessEntries)) + + _, err = svc.DisassociateAccessPolicy(&eks.DisassociateAccessPolicyInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), PolicyArn: aws.String(viewPolicy), + }, testAccountID) + require.NoError(t, err) + listed, err = svc.ListAssociatedAccessPolicies(&eks.ListAssociatedAccessPoliciesInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.NoError(t, err) + assert.Empty(t, listed.AssociatedAccessPolicies) +} + +func TestAccessPolicy_AssociateRejectsUnsupportedPolicyAndScope(t *testing.T) { + svc := setupTestService(t) + seedTestCluster(t, svc, "c1") + _, err := svc.CreateAccessEntry(&eks.CreateAccessEntryInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + }, testAccountID) + require.NoError(t, err) + + // Unsupported policy ARN. + _, err = svc.AssociateAccessPolicy(&eks.AssociateAccessPolicyInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + PolicyArn: aws.String("arn:aws:eks::aws:cluster-access-policy/MadeUpPolicy"), + AccessScope: &eks.AccessScope{Type: aws.String("cluster")}, + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorInvalidParameterValue) + + // namespace scope without namespaces. + _, err = svc.AssociateAccessPolicy(&eks.AssociateAccessPolicyInput{ + ClusterName: aws.String("c1"), PrincipalArn: aws.String(testPrincipalARN), + PolicyArn: aws.String("arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy"), + AccessScope: &eks.AccessScope{Type: aws.String("namespace")}, + }, testAccountID) + require.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestListAccessPolicies_ReturnsSupportedCatalogue(t *testing.T) { + svc := setupTestService(t) + out, err := svc.ListAccessPolicies(&eks.ListAccessPoliciesInput{}, testAccountID) + require.NoError(t, err) + require.Len(t, out.AccessPolicies, len(supportedAccessPolicies)) + for _, p := range out.AccessPolicies { + _, ok := supportedAccessPolicies[aws.StringValue(p.Arn)] + assert.True(t, ok, "unexpected policy %s", aws.StringValue(p.Arn)) + assert.NotEmpty(t, aws.StringValue(p.Name)) + } } func TestEKSServiceImpl_OIDCMethodsReturnNotImplemented(t *testing.T) { diff --git a/spinifex/handlers/eks/service_nats.go b/spinifex/handlers/eks/service_nats.go index e82194731..89534411a 100644 --- a/spinifex/handlers/eks/service_nats.go +++ b/spinifex/handlers/eks/service_nats.go @@ -25,8 +25,9 @@ func NewNATSEKSService(conn *nats.Conn) EKSService { // --- Cluster --- -func (s *NATSEKSService) CreateCluster(input *eks.CreateClusterInput, accountID string) (*eks.CreateClusterOutput, error) { - return utils.NATSRequest[eks.CreateClusterOutput](s.natsConn, "eks.CreateCluster", input, defaultTimeout, accountID) +func (s *NATSEKSService) CreateCluster(input *eks.CreateClusterInput, accountID, callerPrincipalARN string) (*eks.CreateClusterOutput, error) { + return utils.NATSRequest[eks.CreateClusterOutput](s.natsConn, "eks.CreateCluster", input, defaultTimeout, accountID, + utils.NATSHeader{Key: utils.PrincipalARNHeader, Value: callerPrincipalARN}) } func (s *NATSEKSService) DescribeCluster(input *eks.DescribeClusterInput, accountID string) (*eks.DescribeClusterOutput, error) { diff --git a/spinifex/handlers/eks/spawn_registered_reconcilers_test.go b/spinifex/handlers/eks/spawn_registered_reconcilers_test.go new file mode 100644 index 000000000..63ebc71fa --- /dev/null +++ b/spinifex/handlers/eks/spawn_registered_reconcilers_test.go @@ -0,0 +1,44 @@ +package handlers_eks + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestSpawnRegisteredReconcilers_ResumesNonTerminal models a daemon restart: +// every cluster still in CREATING or ACTIVE must get a reconciler re-registered +// so lifecycle reconcile resumes without waiting for the next CreateCluster, +// while terminal (FAILED/DELETING) clusters stay untouched. +func TestSpawnRegisteredReconcilers_ResumesNonTerminal(t *testing.T) { + f := newEKSServiceFixture(t) + + active := sampleClusterMeta("alpha") + active.Status = ClusterStatusActive + require.NoError(t, PutClusterMeta(f.kv, active)) + + // CREATING resumes both the bootstrap re-subscribe and the reconciler. + creating := sampleClusterMeta("beta") + creating.Status = ClusterStatusCreating + require.NoError(t, PutClusterMeta(f.kv, creating)) + + failed := sampleClusterMeta("zeta") + failed.Status = ClusterStatusFailed + require.NoError(t, PutClusterMeta(f.kv, failed)) + + require.NoError(t, f.svc.SpawnRegisteredReconcilers()) + + assert.True(t, f.svc.registry.Has(testAccountID, "alpha"), "ACTIVE cluster must resume a reconciler") + assert.True(t, f.svc.registry.Has(testAccountID, "beta"), "CREATING cluster must resume a reconciler") + assert.False(t, f.svc.registry.Has(testAccountID, "zeta"), "FAILED cluster must not be resumed") +} + +// TestSpawnRegisteredReconcilers_DepsNotReadyNoops confirms the boot scan is a +// safe no-op when orchestration deps are absent (the shim construction path), +// so it never panics or registers phantom holders on a half-wired daemon. +func TestSpawnRegisteredReconcilers_DepsNotReadyNoops(t *testing.T) { + svc := setupTestService(t) + require.NoError(t, svc.SpawnRegisteredReconcilers()) + assert.False(t, svc.registry.Has(testAccountID, "alpha")) +} diff --git a/spinifex/handlers/eks/state_report.go b/spinifex/handlers/eks/state_report.go new file mode 100644 index 000000000..ccaa91c73 --- /dev/null +++ b/spinifex/handlers/eks/state_report.go @@ -0,0 +1,37 @@ +package handlers_eks + +import ( + "encoding/json" + "fmt" +) + +// StateSubject returns the NATS subject the control-plane VM publishes its +// periodic health self-report to: "eks.state.{accountID}.{clusterName}.server". +// The CP VM is on the management NATS bus, so this report reaches the host-side +// reconciler without any route into the VPC overlay — unlike an HTTP /healthz +// probe to the apiserver, which k3s binds to the unreachable VPC node-ip. +func StateSubject(accountID, clusterName string) string { + return fmt.Sprintf("eks.state.%s.%s.server", accountID, clusterName) +} + +// ServerStateReport is the JSON payload of a control-plane state report, emitted +// by scripts/images/eks-node/mulga-eks-state-report.sh. Healthz mirrors the +// apiserver's /healthz body ("ok" when serving); NodeCount is the number of +// nodes the apiserver reports (server + joined agents); TS is the publish time +// in unix seconds, used to reject stale reports from a CP that stopped emitting. +type ServerStateReport struct { + Healthz string `json:"healthz"` + NodeCount int `json:"node_count"` + TS int64 `json:"ts"` +} + +// Healthy reports whether the apiserver was serving at publish time. +func (s ServerStateReport) Healthy() bool { return s.Healthz == "ok" } + +func unmarshalServerStateReport(data []byte) (ServerStateReport, error) { + var r ServerStateReport + if err := json.Unmarshal(data, &r); err != nil { + return ServerStateReport{}, fmt.Errorf("eks: unmarshal server state report: %w", err) + } + return r, nil +} diff --git a/spinifex/handlers/eks/store.go b/spinifex/handlers/eks/store.go index b67aad680..937b259bc 100644 --- a/spinifex/handlers/eks/store.go +++ b/spinifex/handlers/eks/store.go @@ -1,6 +1,8 @@ package handlers_eks import ( + "crypto/sha256" + "encoding/hex" "errors" "fmt" "log/slog" @@ -24,6 +26,13 @@ const ( KVBucketEKSAccountPrefix = "eks-account-" KVBucketEKSAccountVersion = 1 + // KVBucketEKSAccountHistory pins the per-account bucket to one revision per + // key. This MUST stay 1: ZeroizeClusterOIDCKey relies on Purge dropping the + // key's whole history so no prior revision of the encrypted OIDC signing key + // survives a DeleteCluster. Keep this decoupled from the schema version so a + // future version bump can't silently widen history and resurrect ciphertext. + KVBucketEKSAccountHistory = 1 + KVBucketEKSLeader = "spinifex-eks-leader" KVBucketEKSLeaderVersion = 1 KVBucketEKSLeaderTTL = 60 * time.Second @@ -47,14 +56,34 @@ func ClusterMetaKey(name string) string { return fmt.Sprintf("clusters/%s/meta", name) } +// NodegroupsPrefix returns the KV key prefix under which all of a cluster's +// nodegroup records live. Used by ListNodegroups to enumerate. +func NodegroupsPrefix(cluster string) string { + return fmt.Sprintf("clusters/%s/nodegroups/", cluster) +} + // NodegroupKey returns the KV key for a nodegroup record under a cluster. func NodegroupKey(cluster, ng string) string { - return fmt.Sprintf("clusters/%s/nodegroups/%s", cluster, ng) + return NodegroupsPrefix(cluster) + ng +} + +// AccessEntriesPrefix returns the KV key prefix under which all of a cluster's +// AccessEntry records live. Used by ListAccessEntries to enumerate. +func AccessEntriesPrefix(cluster string) string { + return fmt.Sprintf("clusters/%s/access-entries/", cluster) } // AccessEntryKey returns the KV key for an AccessEntry record under a cluster. +// The principal ARN is hashed because IAM ARNs contain ':' which is not a legal +// NATS JetStream KV key character; the record itself carries the plaintext ARN. func AccessEntryKey(cluster, principalARN string) string { - return fmt.Sprintf("clusters/%s/access-entries/%s", cluster, principalARN) + return AccessEntriesPrefix(cluster) + PrincipalARNHash(principalARN) +} + +// PrincipalARNHash maps an IAM principal ARN to a KV-key-safe token. +func PrincipalARNHash(principalARN string) string { + sum := sha256.Sum256([]byte(principalARN)) + return hex.EncodeToString(sum[:]) } // OIDCProviderKey returns the KV key for a registered OIDC provider config @@ -74,6 +103,15 @@ func OIDCJWKSKey(cluster string) string { return fmt.Sprintf("clusters/%s/oidc-jwks.json", cluster) } +// OIDCJWKSVerifiedKey returns the KV key marking that the K3s-VM-published JWKS +// passed the controller cross-check (kid + kty match the controller-generated +// keypair). The reconciler gates the ACTIVE transition on this marker, NOT on +// OIDCJWKSKey — the controller pre-seeds OIDCJWKSKey at create time, so its +// presence proves nothing about the running cluster's actual signing key. +func OIDCJWKSVerifiedKey(cluster string) string { + return fmt.Sprintf("clusters/%s/oidc-jwks-verified", cluster) +} + // AdminKubeconfigKey returns the KV key for a cluster's encrypted admin // kubeconfig (used by the spinifex-side nodegroup reconciler). func AdminKubeconfigKey(cluster string) string { @@ -91,6 +129,23 @@ func EventKey(cluster, ts string) string { return fmt.Sprintf("clusters/%s/events/%s", cluster, ts) } +// AddonsPrefix returns the KV key prefix under which all of a cluster's managed +// add-on records live. Used by ListAddons to enumerate. +func AddonsPrefix(cluster string) string { + return fmt.Sprintf("clusters/%s/addons/", cluster) +} + +// AddonKey returns the KV key for a managed add-on record under a cluster. +func AddonKey(cluster, addon string) string { + return AddonsPrefix(cluster) + addon +} + +// AddonManifestKey returns the KV key under which the rendered manifest for a +// managed add-on is staged for the VM-side delivery transport to consume. +func AddonManifestKey(cluster, addon string) string { + return AddonsPrefix(cluster) + addon + "/manifest" +} + // Store is the per-daemon EKS KV handle. Per-account and leader buckets are // accessed via the package-level factories below. type Store struct { @@ -118,7 +173,7 @@ func AccountBucketName(accountID string) string { // accountID return the existing handle. func GetOrCreateAccountBucket(js nats.JetStreamContext, accountID string) (nats.KeyValue, error) { bucket := AccountBucketName(accountID) - kv, err := utils.GetOrCreateKVBucket(js, bucket, KVBucketEKSAccountVersion) + kv, err := utils.GetOrCreateKVBucket(js, bucket, KVBucketEKSAccountHistory) if err != nil { return nil, fmt.Errorf("failed to create EKS per-account KV bucket %s: %w", bucket, err) } diff --git a/spinifex/handlers/eks/store_test.go b/spinifex/handlers/eks/store_test.go index 64879eb2e..8432a6a88 100644 --- a/spinifex/handlers/eks/store_test.go +++ b/spinifex/handlers/eks/store_test.go @@ -47,7 +47,7 @@ func TestKeyPaths_MatchQ2Spec(t *testing.T) { }{ {"ClusterMetaKey", ClusterMetaKey("alpha"), "clusters/alpha/meta"}, {"NodegroupKey", NodegroupKey("alpha", "ng-1"), "clusters/alpha/nodegroups/ng-1"}, - {"AccessEntryKey", AccessEntryKey("alpha", "arn:aws:iam::111122223333:user/dev"), "clusters/alpha/access-entries/arn:aws:iam::111122223333:user/dev"}, + {"AccessEntryKey", AccessEntryKey("alpha", "arn:aws:iam::111122223333:user/dev"), "clusters/alpha/access-entries/" + PrincipalARNHash("arn:aws:iam::111122223333:user/dev")}, {"OIDCProviderKey", OIDCProviderKey("alpha", "abc123"), "clusters/alpha/oidc-providers/abc123"}, {"OIDCSigningKeyKey", OIDCSigningKeyKey("alpha"), "clusters/alpha/oidc-signing-key.pem.enc"}, {"OIDCJWKSKey", OIDCJWKSKey("alpha"), "clusters/alpha/oidc-jwks.json"}, diff --git a/spinifex/handlers/eks/token_review.go b/spinifex/handlers/eks/token_review.go new file mode 100644 index 000000000..748b76a3b --- /dev/null +++ b/spinifex/handlers/eks/token_review.go @@ -0,0 +1,92 @@ +package handlers_eks + +import ( + "fmt" + "log/slog" + "time" + + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" +) + +// WebhookTokenReviewResult is the gateway's reply to the eks-token-webhook's +// TokenReview relay: the resolved K8s identity for an `aws eks get-token` +// bearer token, or Authenticated=false. The webhook maps it into the +// authentication.k8s.io/v1 TokenReview the apiserver expects. The locationName +// tags let the gateway's restjson marshaler (jsonutil) emit the same keys the +// json tags name, so the webhook decodes the body with encoding/json. +type WebhookTokenReviewResult struct { + Authenticated bool `json:"authenticated" locationName:"authenticated"` + Username string `json:"username,omitempty" locationName:"username"` + UID string `json:"uid,omitempty" locationName:"uid"` + Groups []string `json:"groups,omitempty" locationName:"groups"` +} + +// Authenticate is the pure TokenReview decision core: decode token → STS verify +// → AccessEntry lookup → identity. Any failure yields Authenticated=false (never +// an error) so a forged/unknown token is a clean K8s 401, not a webhook outage. +// verify and lookup are injected so the logic is unit-testable without NATS. +func Authenticate( + token string, + verify func(presignedURL string) (*TokenVerifyResponse, error), + lookup func(principalARN string) (*AccessEntryRecord, error), +) WebhookTokenReviewResult { + presignedURL, err := DecodeGetToken(token) + if err != nil { + return WebhookTokenReviewResult{Authenticated: false} + } + ident, err := verify(presignedURL) + if err != nil { + slog.Debug("token verify rejected", "err", err) + return WebhookTokenReviewResult{Authenticated: false} + } + rec, err := lookup(ident.ARN) + if err != nil { + // No AccessEntry for an otherwise-valid IAM principal: authenticated to + // AWS, but not granted any K8s identity on this cluster. + slog.Debug("no access entry for principal", "arn", ident.ARN, "err", err) + return WebhookTokenReviewResult{Authenticated: false} + } + uid := ident.UserID + if uid == "" { + uid = ident.ARN + } + return WebhookTokenReviewResult{ + Authenticated: true, + Username: rec.KubernetesUsername, + UID: uid, + Groups: rec.KubernetesGroups, + } +} + +// ResolveTokenReview runs the TokenReview decision host-side, wiring the real +// STS verify (NATS request to the awsgw-hosted verify subject) and AccessEntry +// KV read into Authenticate. The eks-token-webhook reaches this through the +// gateway broker (POST /clusters/{name}/token-review) so the control-plane VM +// never speaks NATS. Returns an error only for a genuine infrastructure fault +// (no JetStream / missing account bucket); a forged token resolves to an +// Authenticated=false result, not an error. +func ResolveTokenReview(nc *nats.Conn, accountID, clusterName, token string, verifyTimeout time.Duration) (WebhookTokenReviewResult, error) { + if nc == nil { + return WebhookTokenReviewResult{}, fmt.Errorf("eks: ResolveTokenReview nil nats conn") + } + js, err := nc.JetStream() + if err != nil { + return WebhookTokenReviewResult{}, fmt.Errorf("eks: jetstream: %w", err) + } + kv, err := js.KeyValue(AccountBucketName(accountID)) + if err != nil { + return WebhookTokenReviewResult{}, fmt.Errorf("eks: open account KV %s: %w", accountID, err) + } + + verify := func(presignedURL string) (*TokenVerifyResponse, error) { + return utils.NATSRequest[TokenVerifyResponse]( + nc, TokenVerifySubject, + TokenVerifyRequest{PresignedURL: presignedURL, ClusterName: clusterName}, + verifyTimeout, "") + } + lookup := func(principalARN string) (*AccessEntryRecord, error) { + return GetAccessEntryRecord(kv, clusterName, principalARN) + } + return Authenticate(token, verify, lookup), nil +} diff --git a/spinifex/handlers/eks/token_review_test.go b/spinifex/handlers/eks/token_review_test.go new file mode 100644 index 000000000..4d29d0e02 --- /dev/null +++ b/spinifex/handlers/eks/token_review_test.go @@ -0,0 +1,156 @@ +package handlers_eks + +import ( + "encoding/base64" + "encoding/json" + "errors" + "testing" + "time" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func validToken(url string) string { + return "k8s-aws-v1." + base64.RawURLEncoding.EncodeToString([]byte(url)) +} + +const testARN = "arn:aws:iam::111122223333:role/admin" + +func okVerify(_ string) (*TokenVerifyResponse, error) { + return &TokenVerifyResponse{ + AccountID: "111122223333", + ARN: testARN, + UserID: "AROAEXAMPLE:session", + PrincipalType: "AssumedRole", + }, nil +} + +func TestAuthenticate_GrantsWhenEntryExists(t *testing.T) { + lookup := func(arn string) (*AccessEntryRecord, error) { + assert.Equal(t, testARN, arn) + return &AccessEntryRecord{ + KubernetesUsername: testARN, + KubernetesGroups: []string{"system:masters"}, + }, nil + } + + res := Authenticate(validToken("https://sts.amazonaws.com/?Action=GetCallerIdentity"), okVerify, lookup) + + require.True(t, res.Authenticated) + assert.Equal(t, testARN, res.Username) + assert.Equal(t, "AROAEXAMPLE:session", res.UID) + assert.Equal(t, []string{"system:masters"}, res.Groups) +} + +func TestAuthenticate_DeniesMalformedToken(t *testing.T) { + called := false + verify := func(string) (*TokenVerifyResponse, error) { called = true; return nil, nil } + lookup := func(string) (*AccessEntryRecord, error) { return nil, nil } + + res := Authenticate("not-a-k8s-aws-token", verify, lookup) + + assert.False(t, res.Authenticated) + assert.False(t, called, "must not call STS for a token that fails to decode") +} + +func TestAuthenticate_DeniesWhenVerifyFails(t *testing.T) { + verify := func(string) (*TokenVerifyResponse, error) { + return nil, errors.New("signature mismatch") + } + lookup := func(string) (*AccessEntryRecord, error) { + t.Fatal("lookup must not run when verify fails") + return nil, nil + } + + res := Authenticate(validToken("https://sts/?x=1"), verify, lookup) + assert.False(t, res.Authenticated) +} + +func TestAuthenticate_DeniesWhenNoAccessEntry(t *testing.T) { + lookup := func(string) (*AccessEntryRecord, error) { + return nil, ErrAccessEntryNotFound + } + + res := Authenticate(validToken("https://sts/?x=1"), okVerify, lookup) + assert.False(t, res.Authenticated) + assert.Empty(t, res.Username) +} + +func TestAuthenticate_FallsBackUIDToARN(t *testing.T) { + verify := func(string) (*TokenVerifyResponse, error) { + return &TokenVerifyResponse{ARN: testARN}, nil // no UserID + } + lookup := func(string) (*AccessEntryRecord, error) { + return &AccessEntryRecord{KubernetesUsername: testARN, KubernetesGroups: []string{"system:masters"}}, nil + } + + res := Authenticate(validToken("https://sts/?x=1"), verify, lookup) + require.True(t, res.Authenticated) + assert.Equal(t, testARN, res.UID) +} + +func TestResolveTokenReview_NilConn(t *testing.T) { + _, err := ResolveTokenReview(nil, "111122223333", "alpha", "tok", time.Second) + require.Error(t, err) +} + +// A genuine infra fault (account bucket never created) is an error, not a +// silent Authenticated=false — the webhook turns it into a retryable 5xx. +func TestResolveTokenReview_MissingBucketErrors(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + _, err := ResolveTokenReview(nc, "111122223333", "alpha", validToken("https://sts/?x=1"), time.Second) + require.Error(t, err) +} + +func TestResolveTokenReview_AuthenticatesViaVerifyAndKV(t *testing.T) { + _, nc, js := testutil.StartTestJetStream(t) + kv := testutil.SeedKV(t, js, AccountBucketName("111122223333"), nil) + require.NoError(t, PutAccessEntryRecord(kv, &AccessEntryRecord{ + ClusterName: "alpha", + PrincipalARN: testARN, + KubernetesUsername: testARN, + KubernetesGroups: []string{"system:masters"}, + Type: AccessEntryTypeStandard, + })) + + // Stand in for the awsgw-hosted STS verify responder. + sub, err := nc.Subscribe(TokenVerifySubject, func(m *nats.Msg) { + resp, _ := json.Marshal(TokenVerifyResponse{ + AccountID: "111122223333", + ARN: testARN, + UserID: "AROAEXAMPLE:session", + PrincipalType: "AssumedRole", + }) + _ = m.Respond(resp) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + res, err := ResolveTokenReview(nc, "111122223333", "alpha", validToken("https://sts/?Action=GetCallerIdentity"), 2*time.Second) + require.NoError(t, err) + require.True(t, res.Authenticated) + assert.Equal(t, testARN, res.Username) + assert.Equal(t, "AROAEXAMPLE:session", res.UID) + assert.Equal(t, []string{"system:masters"}, res.Groups) +} + +// A valid IAM principal with no AccessEntry resolves to Authenticated=false +// (a clean 401), not an error. +func TestResolveTokenReview_NoAccessEntryDenies(t *testing.T) { + _, nc, js := testutil.StartTestJetStream(t) + testutil.SeedKV(t, js, AccountBucketName("111122223333"), nil) + + sub, err := nc.Subscribe(TokenVerifySubject, func(m *nats.Msg) { + resp, _ := json.Marshal(TokenVerifyResponse{AccountID: "111122223333", ARN: testARN}) + _ = m.Respond(resp) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + + res, err := ResolveTokenReview(nc, "111122223333", "alpha", validToken("https://sts/?x=1"), 2*time.Second) + require.NoError(t, err) + assert.False(t, res.Authenticated) +} diff --git a/spinifex/handlers/eks/token_webhook.go b/spinifex/handlers/eks/token_webhook.go new file mode 100644 index 000000000..8f4e483e0 --- /dev/null +++ b/spinifex/handlers/eks/token_webhook.go @@ -0,0 +1,58 @@ +package handlers_eks + +import ( + "encoding/base64" + "errors" + "strings" +) + +// TokenVerifySubject is the NATS request subject the EKS token webhook calls to +// resolve an `aws eks get-token` presigned URL into a caller principal. It is +// served by the awsgw service (which hosts the in-process STS service) because +// STS is gateway-local and not otherwise exposed on NATS. +const TokenVerifySubject = "eks.VerifyToken" //nolint:gosec // NATS subject name, not a credential + +// getTokenV1Prefix is the scheme prefix `aws eks get-token` prepends to the +// base64url-encoded presigned STS GetCallerIdentity URL. +const getTokenV1Prefix = "k8s-aws-v1." //nolint:gosec // token scheme prefix, not a credential + +// ErrMalformedToken is returned by DecodeGetToken when the bearer token is not +// a well-formed `k8s-aws-v1.` value. +var ErrMalformedToken = errors.New("eks: malformed k8s-aws-v1 token") + +// TokenVerifyRequest is the NATS request payload sent to TokenVerifySubject. +// ClusterName is the anti-replay binding (Q10): the webhook passes its own +// cluster name and STS rejects a URL signed for any other x-k8s-aws-id. +type TokenVerifyRequest struct { + PresignedURL string `json:"presignedURL"` + ClusterName string `json:"clusterName"` +} + +// TokenVerifyResponse is the NATS reply: the resolved caller principal. It +// mirrors the fields of handlers_sts.PresignedCallerIdentity the webhook needs +// to key its AccessEntry lookup. +type TokenVerifyResponse struct { + AccountID string `json:"accountID"` + ARN string `json:"arn"` + UserID string `json:"userID"` + PrincipalType string `json:"principalType"` +} + +// DecodeGetToken extracts the presigned STS GetCallerIdentity URL from a +// `k8s-aws-v1.` bearer token. The encoding is base64url without +// padding, matching the aws-iam-authenticator / aws-cli get-token output. +func DecodeGetToken(token string) (string, error) { + token = strings.TrimSpace(token) + if !strings.HasPrefix(token, getTokenV1Prefix) { + return "", ErrMalformedToken + } + encoded := strings.TrimPrefix(token, getTokenV1Prefix) + raw, err := base64.RawURLEncoding.DecodeString(encoded) + if err != nil { + return "", ErrMalformedToken + } + if len(raw) == 0 { + return "", ErrMalformedToken + } + return string(raw), nil +} diff --git a/spinifex/handlers/eks/token_webhook_test.go b/spinifex/handlers/eks/token_webhook_test.go new file mode 100644 index 000000000..69f89c1a8 --- /dev/null +++ b/spinifex/handlers/eks/token_webhook_test.go @@ -0,0 +1,44 @@ +package handlers_eks + +import ( + "encoding/base64" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDecodeGetToken_RoundTrips(t *testing.T) { + url := "https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256" + token := getTokenV1Prefix + base64.RawURLEncoding.EncodeToString([]byte(url)) + + got, err := DecodeGetToken(token) + require.NoError(t, err) + assert.Equal(t, url, got) +} + +func TestDecodeGetToken_TrimsWhitespace(t *testing.T) { + url := "https://sts.amazonaws.com/?Action=GetCallerIdentity" + token := " " + getTokenV1Prefix + base64.RawURLEncoding.EncodeToString([]byte(url)) + "\n" + + got, err := DecodeGetToken(token) + require.NoError(t, err) + assert.Equal(t, url, got) +} + +func TestDecodeGetToken_RejectsMalformed(t *testing.T) { + cases := map[string]string{ + "missing prefix": base64.RawURLEncoding.EncodeToString([]byte("https://sts")), + "empty": "", + "prefix only": getTokenV1Prefix, + "bad base64": getTokenV1Prefix + "!!!not-base64!!!", + "padded base64": getTokenV1Prefix + base64.URLEncoding.EncodeToString([]byte("https://sts.amazonaws.com/?x=12")), + "wrong scheme": "k8s-aws-v2." + base64.RawURLEncoding.EncodeToString([]byte("https://sts")), + } + for name, tok := range cases { + t.Run(name, func(t *testing.T) { + _, err := DecodeGetToken(tok) + assert.ErrorIs(t, err, ErrMalformedToken) + }) + } +} diff --git a/spinifex/handlers/eks/types.go b/spinifex/handlers/eks/types.go index c42bf0997..cf17dd393 100644 --- a/spinifex/handlers/eks/types.go +++ b/spinifex/handlers/eks/types.go @@ -27,25 +27,61 @@ type ClusterRecord struct { } // NodegroupRecord is the persisted-state envelope for an EKS managed -// nodegroup. +// nodegroup. It is the source of truth for Describe/ListNodegroups and tracks +// the worker EC2 instance IDs the nodegroup owns so Update/DeleteNodegroup can +// scale and tear them down. type NodegroupRecord struct { - ClusterName string `json:"clusterName"` - Name string `json:"name"` - Status string `json:"status"` - DesiredSize int64 `json:"desiredSize"` - InstanceTypes []string `json:"instanceTypes,omitempty"` - CreatedAt time.Time `json:"createdAt"` + ClusterName string `json:"clusterName"` + Name string `json:"name"` + Arn string `json:"arn"` + Status string `json:"status"` + StatusReason string `json:"statusReason,omitempty"` + Subnets []string `json:"subnets,omitempty"` + InstanceTypes []string `json:"instanceTypes,omitempty"` + AMIType string `json:"amiType,omitempty"` + DiskSize int64 `json:"diskSize,omitempty"` + ScalingMin int64 `json:"scalingMin"` + ScalingMax int64 `json:"scalingMax"` + ScalingDesired int64 `json:"scalingDesired"` + Version string `json:"version,omitempty"` + NodeRole string `json:"nodeRole,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + InstanceIDs []string `json:"instanceIds,omitempty"` + Health string `json:"health,omitempty"` + CreatedAt time.Time `json:"createdAt"` + ModifiedAt time.Time `json:"modifiedAt"` } // AccessEntryRecord is the persisted-state envelope for an EKS API-mode // AccessEntry (Q9). type AccessEntryRecord struct { - ClusterName string `json:"clusterName"` - PrincipalARN string `json:"principalARN"` - KubernetesUsername string `json:"kubernetesUsername"` - KubernetesGroups []string `json:"kubernetesGroups,omitempty"` - Type string `json:"type"` - CreatedAt time.Time `json:"createdAt"` + ARN string `json:"arn"` + ClusterName string `json:"clusterName"` + PrincipalARN string `json:"principalARN"` + KubernetesUsername string `json:"kubernetesUsername"` + KubernetesGroups []string `json:"kubernetesGroups,omitempty"` + Type string `json:"type"` + Tags map[string]string `json:"tags,omitempty"` + AssociatedPolicies []AssociatedAccessPolicy `json:"associatedPolicies,omitempty"` + CreatedAt time.Time `json:"createdAt"` + ModifiedAt time.Time `json:"modifiedAt"` +} + +// AssociatedAccessPolicy is a managed access policy bound to an AccessEntry +// with a cluster- or namespace-scoped grant (Q9). +type AssociatedAccessPolicy struct { + PolicyARN string `json:"policyARN"` + AccessScope AccessScope `json:"accessScope"` + AssociatedAt time.Time `json:"associatedAt"` + ModifiedAt time.Time `json:"modifiedAt"` +} + +// AccessScope restricts an associated access policy to the whole cluster or a +// set of namespaces. Type is "cluster" or "namespace"; Namespaces is required +// (and only meaningful) when Type is "namespace". +type AccessScope struct { + Type string `json:"type"` + Namespaces []string `json:"namespaces,omitempty"` } // OIDCProviderConfigRecord captures the minimum needed to register an OIDC diff --git a/spinifex/handlers/elbv2/agent_types.go b/spinifex/handlers/elbv2/agent_types.go index 16a39c39f..bc4fd6fbb 100644 --- a/spinifex/handlers/elbv2/agent_types.go +++ b/spinifex/handlers/elbv2/agent_types.go @@ -32,10 +32,38 @@ type GetLBConfigInput struct { LBID *string `locationName:"LBID" type:"string"` } -// GetLBConfigOutput returns the pre-computed HAProxy config and its hash. +// GetLBConfigOutput returns the pre-computed data-plane config and its hash, +// plus any TLS certificate files the config references. Engine selects the +// data plane the agent runs: "haproxy" (ALB) or "nginx" (NLB). HealthTargets +// is populated for NLBs only: nginx `stream` has no active upstream probing, +// so the agent probes these and reports the results via heartbeat. type GetLBConfigOutput struct { - ConfigText *string `type:"string"` - ConfigHash *string `type:"string"` + ConfigText *string `type:"string"` + ConfigHash *string `type:"string"` + CertFiles []*CertFile `locationName:"CertFiles" type:"list"` + Engine *string `type:"string"` + HealthTargets []*HealthTarget `locationName:"HealthTargets" type:"list"` +} + +// HealthTarget is one backend the nginx agent must actively health-check. +// ServerName is daemon-computed (sanitizeName("srv", target.Id)) so it matches +// the name the health checker keys on — the agent echoes it back verbatim and +// needs no ELBv2 naming logic. Address is the ip:port to probe; Protocol is +// the target group's health-check protocol (TCP/HTTP/HTTPS); Path is the +// HTTP(S) health-check path. +type HealthTarget struct { + ServerName *string `locationName:"ServerName" type:"string"` + Address *string `locationName:"Address" type:"string"` + Protocol *string `locationName:"Protocol" type:"string"` + Path *string `locationName:"Path" type:"string"` +} + +// CertFile is one TLS certificate PEM delivered to the LB agent. Path is the +// absolute destination (under lbagent.CertDir) the agent writes 0600 before +// reload; PEM is the combined cert+chain+key material. +type CertFile struct { + Path *string `locationName:"Path" type:"string"` + PEM *string `locationName:"PEM" type:"string"` } // toHealthReport converts the heartbeat input's server list to the lbagent diff --git a/spinifex/handlers/elbv2/haproxy.go b/spinifex/handlers/elbv2/haproxy.go index 92c487c82..8e24fd5c8 100644 --- a/spinifex/handlers/elbv2/haproxy.go +++ b/spinifex/handlers/elbv2/haproxy.go @@ -6,11 +6,14 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strconv" "strings" "sync" "syscall" "text/template" + + "github.com/mulgadc/spinifex/spinifex/lbagent" ) // Every interpolated string field in the templates below MUST be regex-validated @@ -33,7 +36,7 @@ defaults timeout server 60s {{range .Frontends}} frontend {{.Name}} - bind *:{{.Port}} + bind *:{{.Port}}{{if .CertPath}} ssl crt {{.CertPath}}{{end}} {{- range .Rules}} {{- range .ACLs}} acl {{.Name}} {{.Expr}} @@ -44,58 +47,32 @@ frontend {{.Name}} {{end}} {{range .Backends}} backend {{.Name}} +{{- if .Redirect}} + http-request redirect {{if .Redirect.Scheme}}scheme {{.Redirect.Scheme}}{{else}}location {{.Redirect.Location}}{{end}} code {{.Redirect.Code}} +{{- else if .FixedResponse}} + http-request return status {{.FixedResponse.StatusCode}}{{if .FixedResponse.ContentType}} content-type "{{.FixedResponse.ContentType}}"{{end}}{{if .FixedResponse.Body}} string "{{.FixedResponse.Body}}"{{end}} +{{- else}} balance roundrobin{{if .HealthCheckPath}} option httpchk GET {{.HealthCheckPath}} http-check expect status {{.HealthCheckMatcher}}{{end}} {{- range .Servers}} server {{.Name}} {{.Addr}}:{{.Port}} check inter {{.CheckInterval}}s fall {{.Fall}} rise {{.Rise}} {{- end}} -{{end}} -` - -const haproxyTCPConfigTemplate = `# Auto-generated by spinifex ELBv2 (NLB) — do not edit -global - log stdout format short daemon - stats socket {{.SocketPath}} mode 660 level admin - -defaults - mode tcp - log global - option tcplog - timeout connect 5s - timeout client 60s - timeout server 60s -{{range .Frontends}} -frontend {{.Name}} - bind *:{{.Port}} - default_backend {{.DefaultBackend}} -{{end}} -{{range .Backends}} -backend {{.Name}} - balance roundrobin -{{- if .TCPCheck}} - option tcp-check -{{- end}} -{{- if .HTTPCheck}} - option httpchk GET {{.HealthCheckPath}} - http-check expect status {{.HealthCheckMatcher}} -{{- end}} -{{- range .Servers}} - server {{.Name}} {{.Addr}}:{{.Port}} check inter {{.CheckInterval}}s fall {{.Fall}} rise {{.Rise}} {{- end}} {{end}} ` -var ( - haproxyHTTPTmpl = template.Must(template.New("haproxy").Parse(haproxyConfigTemplate)) - haproxyTCPTmpl = template.Must(template.New("haproxy-tcp").Parse(haproxyTCPConfigTemplate)) -) +var haproxyHTTPTmpl = template.Must(template.New("haproxy").Parse(haproxyConfigTemplate)) // HAProxyConfig holds the full configuration for rendering the HAProxy template. type HAProxyConfig struct { SocketPath string Frontends []HAProxyFrontend Backends []HAProxyBackend + // CertFiles maps each absolute `ssl crt` path referenced by a secure + // frontend to its combined PEM (cert+chain+key). Not rendered into the + // template — carried out to the caller for delivery to the LB agent. + CertFiles map[string]string } // HAProxyFrontend represents a listener frontend. @@ -103,6 +80,8 @@ type HAProxyFrontend struct { Name string BindAddr string Port int64 + Protocol string // listener protocol (HTTP/HTTPS/TCP/TLS); drives TLS termination + CertPath string // combined-PEM path for `ssl crt`; empty for non-secure frontends DefaultBackend string Rules []HAProxyRule } @@ -123,14 +102,33 @@ type HAProxyACL struct { Expr string } -// HAProxyBackend represents a target group backend. +// HAProxyBackend represents a target group backend, or a synthetic backend that +// emits a fixed response or a redirect (when FixedResponse / Redirect is set, +// the server fields are unused). type HAProxyBackend struct { Name string HealthCheckPath string HealthCheckMatcher string - TCPCheck bool - HTTPCheck bool Servers []HAProxyServer + FixedResponse *HAProxyFixedResponse + Redirect *HAProxyRedirect +} + +// HAProxyFixedResponse renders an `http-request return` directive for a +// fixed-response listener default action. All fields are validated before use. +type HAProxyFixedResponse struct { + StatusCode string + ContentType string + Body string +} + +// HAProxyRedirect renders an `http-request redirect` directive. Exactly one of +// Scheme (the scheme-only fast path, e.g. HTTP→HTTPS) or Location (a full +// rebuilt URL with HAProxy log-format fetches) is set. +type HAProxyRedirect struct { + Scheme string + Location string + Code string } // HAProxyServer represents a single target server. @@ -145,48 +143,47 @@ type HAProxyServer struct { // GenerateHAProxyConfig builds an HAProxy config string from the LB, its // listeners, target groups, and per-listener rules. For NLBs it generates a -// TCP-mode config and ignores rules (NLB has no L7 routing). +// TCP-mode config and ignores rules (NLB has no L7 routing). TLS termination +// is off (no certificates resolved) — use GenerateHAProxyConfigWithCerts for +// HTTPS listeners. func GenerateHAProxyConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgByArn map[string]*TargetGroupRecord, rulesByListener map[string][]*RuleRecord, bindAddr string) (string, error) { + config, _, err := GenerateHAProxyConfigWithCerts(lb, listeners, tgByArn, rulesByListener, bindAddr, nil) + return config, err +} + +// GenerateHAProxyConfigWithCerts is GenerateHAProxyConfig plus TLS termination. +// certPEMByArn maps each listener certificate ARN to its combined PEM; secure +// frontends render `ssl crt ` and the returned certFiles map carries each +// path → PEM for delivery to the LB agent. A secure listener whose default cert +// ARN is absent from certPEMByArn renders without `ssl crt` (HAProxy would fail +// to bind, surfacing the missing material). +func GenerateHAProxyConfigWithCerts(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgByArn map[string]*TargetGroupRecord, rulesByListener map[string][]*RuleRecord, bindAddr string, certPEMByArn map[string]string) (string, map[string]string, error) { + // NLBs (L4) render an nginx `stream` config — HAProxy load-balances no UDP. if lb.Type == LoadBalancerTypeNetwork { - return generateNLBConfig(lb, listeners, tgByArn, bindAddr) + return generateNLBStreamConfig(lb, listeners, tgByArn, bindAddr, certPEMByArn) } - return generateALBConfig(lb, listeners, tgByArn, rulesByListener, bindAddr) + return generateALBConfig(lb, listeners, tgByArn, rulesByListener, bindAddr, certPEMByArn) } // generateALBConfig builds an HTTP-mode HAProxy config for ALBs. -func generateALBConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgByArn map[string]*TargetGroupRecord, rulesByListener map[string][]*RuleRecord, bindAddr string) (string, error) { - cfg, err := buildHAProxyConfig(lb, listeners, tgByArn, rulesByListener, bindAddr, false) +func generateALBConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgByArn map[string]*TargetGroupRecord, rulesByListener map[string][]*RuleRecord, bindAddr string, certPEMByArn map[string]string) (string, map[string]string, error) { + cfg, err := buildHAProxyConfig(lb, listeners, tgByArn, rulesByListener, bindAddr, certPEMByArn) if err != nil { - return "", err + return "", nil, err } var buf strings.Builder if err := haproxyHTTPTmpl.Execute(&buf, cfg); err != nil { - return "", fmt.Errorf("execute haproxy template: %w", err) + return "", nil, fmt.Errorf("execute haproxy template: %w", err) } - return buf.String(), nil + return buf.String(), cfg.CertFiles, nil } -// generateNLBConfig builds a TCP-mode HAProxy config for NLBs. -func generateNLBConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgByArn map[string]*TargetGroupRecord, bindAddr string) (string, error) { - cfg, err := buildHAProxyConfig(lb, listeners, tgByArn, nil, bindAddr, true) - if err != nil { - return "", err - } - - var buf strings.Builder - if err := haproxyTCPTmpl.Execute(&buf, cfg); err != nil { - return "", fmt.Errorf("execute haproxy tcp template: %w", err) - } - - return buf.String(), nil -} - -// buildHAProxyConfig constructs the HAProxyConfig struct shared by ALB and NLB -// generation. For ALBs, rulesByListener provides per-listener rules sorted by -// priority; nil/empty for NLBs. -func buildHAProxyConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgByArn map[string]*TargetGroupRecord, rulesByListener map[string][]*RuleRecord, bindAddr string, isTCP bool) (HAProxyConfig, error) { +// buildHAProxyConfig constructs the HAProxyConfig struct for an ALB (L7). +// rulesByListener provides per-listener rules sorted by priority. NLBs use the +// nginx data plane (generateNLBStreamConfig), not this builder. +func buildHAProxyConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgByArn map[string]*TargetGroupRecord, rulesByListener map[string][]*RuleRecord, bindAddr string, certPEMByArn map[string]string) (HAProxyConfig, error) { socketDir := filepath.Join(os.TempDir(), "spinifex-haproxy") // Use "lb" prefix for both ALB and NLB — must match the agent's socket path. socketPath := filepath.Join(socketDir, fmt.Sprintf("lb-%s.sock", lb.LoadBalancerID)) @@ -206,19 +203,10 @@ func buildHAProxyConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgB return } - backend := HAProxyBackend{Name: backendName} - if isTCP { - switch tg.HealthCheck.Protocol { - case ProtocolHTTP, ProtocolHTTPS: - backend.HTTPCheck = true - backend.HealthCheckPath = tg.HealthCheck.Path - backend.HealthCheckMatcher = tg.HealthCheck.Matcher - default: - backend.TCPCheck = true - } - } else { - backend.HealthCheckPath = tg.HealthCheck.Path - backend.HealthCheckMatcher = tg.HealthCheck.Matcher + backend := HAProxyBackend{ + Name: backendName, + HealthCheckPath: tg.HealthCheck.Path, + HealthCheckMatcher: tg.HealthCheck.Matcher, } for _, target := range tg.Targets { @@ -238,53 +226,176 @@ func buildHAProxyConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgB backendAdded[backendName] = struct{}{} } + addFixedResponseBackend := func(name string, fr *FixedResponseAction) { + if _, ok := backendAdded[name]; ok { + return + } + hf := &HAProxyFixedResponse{StatusCode: "503"} + if fr != nil { + if validHTTPStatusCode(fr.StatusCode) { + hf.StatusCode = fr.StatusCode + } + if validContentType(fr.ContentType) { + hf.ContentType = fr.ContentType + } + if validFixedResponseBody(fr.MessageBody) { + hf.Body = fr.MessageBody + } + } + cfg.Backends = append(cfg.Backends, HAProxyBackend{Name: name, FixedResponse: hf}) + backendAdded[name] = struct{}{} + } + + addRedirectBackend := func(name string, rd *HAProxyRedirect) { + if _, ok := backendAdded[name]; ok { + return + } + cfg.Backends = append(cfg.Backends, HAProxyBackend{Name: name, Redirect: rd}) + backendAdded[name] = struct{}{} + } + for _, l := range listeners { if len(l.DefaultActions) == 0 { continue } - tgArn := l.DefaultActions[0].TargetGroupArn - defaultBackendName := sanitizeName("bk", tgArn) + da := l.DefaultActions[0] + isForwardDefault := da.Type == ActionTypeForward && da.TargetGroupArn != "" + + var defaultBackendName string + if isForwardDefault { + defaultBackendName = sanitizeName("bk", da.TargetGroupArn) + } else { + // redirect / fixed-response (or any non-forward default, e.g. the + // shared-ingress 404): synthesize a backend so HAProxy has a valid + // default_backend instead of a dangling reference. + defaultBackendName = sanitizeName("bkdefault", l.ListenerArn) + } frontend := HAProxyFrontend{ Name: sanitizeName("ft", l.ListenerArn), BindAddr: bindAddr, Port: l.Port, + Protocol: l.Protocol, DefaultBackend: defaultBackendName, } - if !isTCP { - rules := rulesByListener[l.ListenerArn] - for _, r := range rules { - rule, err := buildHAProxyRule(r) - if err != nil { - return HAProxyConfig{}, err - } - frontend.Rules = append(frontend.Rules, rule) - for _, a := range r.Actions { - if a.Type == ActionTypeForward && a.TargetGroupArn != "" { - addBackend(a.TargetGroupArn) - } + // Secure listeners terminate TLS: resolve the default cert to a stable + // per-listener PEM path and stage it for delivery. Without resolved + // material the frontend renders plain (HAProxy then fails to bind, + // surfacing the gap rather than silently serving cleartext). + if protocolRequiresCert(l.Protocol) { + if pem, path := resolveFrontendCert(lb, l, certPEMByArn); pem != "" { + frontend.CertPath = path + if cfg.CertFiles == nil { + cfg.CertFiles = make(map[string]string) } + cfg.CertFiles[path] = pem } } + for _, r := range rulesByListener[l.ListenerArn] { + backendName, err := registerRuleBackend(r, l, addBackend, addFixedResponseBackend, addRedirectBackend) + if err != nil { + return HAProxyConfig{}, err + } + rule, err := buildHAProxyRule(r, backendName) + if err != nil { + return HAProxyConfig{}, err + } + frontend.Rules = append(frontend.Rules, rule) + } + cfg.Frontends = append(cfg.Frontends, frontend) - addBackend(tgArn) + switch { + case isForwardDefault: + addBackend(da.TargetGroupArn) + case da.Type == ActionTypeRedirect: + rd, err := buildRedirect(da.Redirect, l) + if err != nil { + return HAProxyConfig{}, err + } + addRedirectBackend(defaultBackendName, rd) + default: + addFixedResponseBackend(defaultBackendName, da.FixedResponse) + } } return cfg, nil } -// buildHAProxyRule converts a RuleRecord into the rendered ACL+use_backend -// shape. Returns an error if any condition value fails the defence-in-depth -// re-validation (HAProxy meta-character rejection). -func buildHAProxyRule(r *RuleRecord) (HAProxyRule, error) { - if len(r.Actions) == 0 || r.Actions[0].Type != ActionTypeForward { - return HAProxyRule{}, fmt.Errorf("rule %s has no forward action", r.RuleID) - } +// frontendCertPath returns the stable absolute path of a listener's combined +// PEM file under the agent's cert dir. Daemon-rendered and agent-written paths +// must match exactly. +func frontendCertPath(lb *LoadBalancerRecord, l *ListenerRecord) string { + return filepath.Join(lbagent.CertDir, fmt.Sprintf("lb-%s-%s.pem", lb.LoadBalancerID, l.ListenerID)) +} - rule := HAProxyRule{Backend: sanitizeName("bk", r.Actions[0].TargetGroupArn)} +// resolveFrontendCert picks a listener's default certificate, resolves its PEM +// from certPEMByArn, and returns (pem, path). Returns ("", "") when the listener +// has no certificate or its default ARN is unresolved. +func resolveFrontendCert(lb *LoadBalancerRecord, l *ListenerRecord, certPEMByArn map[string]string) (pem, path string) { + if len(l.Certificates) == 0 || certPEMByArn == nil { + return "", "" + } + arn := l.Certificates[0].CertificateArn + for _, c := range l.Certificates { + if c.IsDefault { + arn = c.CertificateArn + break + } + } + pem, ok := certPEMByArn[arn] + if !ok || pem == "" { + return "", "" + } + return pem, frontendCertPath(lb, l) +} + +// registerRuleBackend resolves the HAProxy backend a rule's single action +// routes to, registering a synthetic backend for redirect / fixed-response +// rule actions, and returns its name. +func registerRuleBackend( + r *RuleRecord, + l *ListenerRecord, + addBackend func(string), + addFixedResponseBackend func(string, *FixedResponseAction), + addRedirectBackend func(string, *HAProxyRedirect), +) (string, error) { + if len(r.Actions) == 0 { + return "", fmt.Errorf("rule %s has no action", r.RuleID) + } + a := r.Actions[0] + switch a.Type { + case ActionTypeForward: + if a.TargetGroupArn == "" { + return "", fmt.Errorf("rule %s forward action has no target group", r.RuleID) + } + addBackend(a.TargetGroupArn) + return sanitizeName("bk", a.TargetGroupArn), nil + case ActionTypeFixedResponse: + name := sanitizeName("bkrule", r.RuleID) + addFixedResponseBackend(name, a.FixedResponse) + return name, nil + case ActionTypeRedirect: + rd, err := buildRedirect(a.Redirect, l) + if err != nil { + return "", err + } + name := sanitizeName("bkrule", r.RuleID) + addRedirectBackend(name, rd) + return name, nil + default: + return "", fmt.Errorf("rule %s has unsupported action %q", r.RuleID, a.Type) + } +} + +// buildHAProxyRule converts a RuleRecord's conditions into the rendered +// ACL+use_backend shape, routing to backendName (already resolved/registered +// by registerRuleBackend). Returns an error if any condition value fails the +// defence-in-depth re-validation (HAProxy meta-character rejection). +func buildHAProxyRule(r *RuleRecord, backendName string) (HAProxyRule, error) { + rule := HAProxyRule{Backend: backendName} for ci, c := range r.Conditions { aclName := fmt.Sprintf("r%s_c%d", sanitizeACLToken(r.RuleID), ci) @@ -395,6 +506,155 @@ func wildcardMatch(v string) (flag, literal string) { } } +// contentTypeRegex matches a conservative MIME type (type/subtype) for the +// fixed-response content-type header rendered into the HAProxy config. +var contentTypeRegex = regexp.MustCompile(`^[a-zA-Z0-9!#$&^_.+-]+/[a-zA-Z0-9!#$&^_.+-]+$`) + +// validHTTPStatusCode accepts a 3-digit 2xx–5xx HTTP status. +func validHTTPStatusCode(s string) bool { + if len(s) != 3 || s[0] < '2' || s[0] > '5' { + return false + } + for _, r := range s { + if r < '0' || r > '9' { + return false + } + } + return true +} + +// validContentType accepts a conservative type/subtype MIME string. +func validContentType(s string) bool { + return contentTypeRegex.MatchString(s) +} + +// validFixedResponseBody rejects bytes that would terminate the quoted HAProxy +// string or inject a directive. Spaces are allowed (the body renders inside +// double quotes). +func validFixedResponseBody(s string) bool { + if s == "" { + return false + } + for _, r := range s { + if r == '\n' || r == '\r' || r == '"' || r == '\\' || r == '#' { + return false + } + } + return true +} + +// buildRedirect translates a stored RedirectAction into an HAProxy redirect +// directive in the context of listener l (which resolves the `#{protocol}` and +// `#{port}` placeholders). The common scheme-only redirect (e.g. HTTP→HTTPS +// with every other field left at its AWS default) renders as `redirect scheme` +// so HAProxy preserves host/path/query; anything else rebuilds a `location`. +func buildRedirect(rd *RedirectAction, l *ListenerRecord) (*HAProxyRedirect, error) { + if rd == nil { + return nil, fmt.Errorf("redirect action missing config") + } + + var code string + switch rd.StatusCode { + case "HTTP_301": + code = "301" + case "HTTP_302": + code = "302" + default: + return nil, fmt.Errorf("redirect has invalid status code %q", rd.StatusCode) + } + + for _, f := range []string{rd.Protocol, rd.Host, rd.Port, rd.Path, rd.Query} { + if !validRedirectField(f) { + return nil, fmt.Errorf("redirect field contains forbidden characters") + } + } + + defaultHost := rd.Host == "" || rd.Host == "#{host}" + defaultPort := rd.Port == "" || rd.Port == "#{port}" + defaultPath := rd.Path == "" || rd.Path == "#{path}" || rd.Path == "/#{path}" + defaultQuery := rd.Query == "" || rd.Query == "#{query}" + explicitScheme := rd.Protocol == ProtocolHTTP || rd.Protocol == ProtocolHTTPS + + if explicitScheme && defaultHost && defaultPort && defaultPath && defaultQuery { + return &HAProxyRedirect{Scheme: strings.ToLower(rd.Protocol), Code: code}, nil + } + + scheme := redirectScheme(rd.Protocol, l) + host := redirectPlaceholders(orDefault(rd.Host, "#{host}"), l) + + port := "" + if rd.Port != "" && rd.Port != "#{port}" { + port = ":" + redirectPlaceholders(rd.Port, l) + } + + path := "%[path]" + if !defaultPath { + path = redirectPlaceholders(rd.Path, l) + } + + query := "" + if !defaultQuery { + query = "?" + redirectPlaceholders(rd.Query, l) + } else if rd.Query == "#{query}" { + query = "?%[query]" + } + + return &HAProxyRedirect{Location: scheme + "://" + host + port + path + query, Code: code}, nil +} + +func orDefault(s, def string) string { + if s == "" { + return def + } + return s +} + +// redirectScheme resolves the redirect scheme literal, mapping the `#{protocol}` +// placeholder (or an empty protocol) to the listener's own protocol. +func redirectScheme(proto string, l *ListenerRecord) string { + switch proto { + case ProtocolHTTP, ProtocolHTTPS: + return strings.ToLower(proto) + default: + return strings.ToLower(l.Protocol) + } +} + +// redirectPlaceholders substitutes the AWS redirect placeholders with HAProxy +// log-format fetches. `#{protocol}` and `#{port}` resolve from the listener. +func redirectPlaceholders(s string, l *ListenerRecord) string { + r := strings.NewReplacer( + "#{protocol}", strings.ToLower(l.Protocol), + "#{host}", "%[req.hdr(host)]", + "#{port}", strconv.FormatInt(l.Port, 10), + "#{path}", "%[path]", + "#{query}", "%[query]", + ) + return r.Replace(s) +} + +// validRedirectField rejects bytes that would break the HAProxy redirect +// directive. The known `#{...}` placeholders are stripped first, so any +// remaining `#`, `%`, bracket, quote, backslash, whitespace, or control byte +// (which would indicate an unknown placeholder or an injection attempt) fails. +func validRedirectField(s string) bool { + known := []string{"#{protocol}", "#{host}", "#{port}", "#{path}", "#{query}"} + tmp := s + for _, k := range known { + tmp = strings.ReplaceAll(tmp, k, "") + } + for _, r := range tmp { + if r < 0x20 || r == 0x7f { + return false + } + switch r { + case ' ', '"', '#', '%', '[', ']', '\\': + return false + } + } + return true +} + // validateRenderSafe rejects bytes that could break ACL parsing or inject new // directives. Mirrors the write-time validators in service_impl_rules.go; // duplicated here so a future migration that bypasses the API cannot leak a diff --git a/spinifex/handlers/elbv2/haproxy_certs_test.go b/spinifex/handlers/elbv2/haproxy_certs_test.go new file mode 100644 index 000000000..f6575c600 --- /dev/null +++ b/spinifex/handlers/elbv2/haproxy_certs_test.go @@ -0,0 +1,221 @@ +package handlers_elbv2 + +import ( + "encoding/xml" + "path/filepath" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" + "github.com/mulgadc/spinifex/spinifex/lbagent" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const certTestArn = "arn:aws:acm:ap-southeast-2:123456789012:certificate/cccc-3333" + +func httpsListener() *ListenerRecord { + return &ListenerRecord{ + ListenerArn: "arn:aws:elasticloadbalancing:us-east-1:123:listener/app/my-alb/lb-abc123/lst-https", + ListenerID: "lst-https", + Protocol: ProtocolHTTPS, + Port: 443, + Certificates: []ListenerCertificate{{CertificateArn: certTestArn, IsDefault: true}}, + SslPolicy: DefaultSslPolicy, + DefaultActions: []ListenerAction{{Type: ActionTypeFixedResponse, FixedResponse: &FixedResponseAction{StatusCode: "200"}}}, + LoadBalancerArn: "arn:lb", + } +} + +func TestGenerateHAProxyConfigWithCerts_HTTPSRendersSslCrt(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-abc123", Name: "my-alb"} + listeners := []*ListenerRecord{httpsListener()} + certPEM := "CERTPEM\nKEYPEM\n" + + config, certFiles, err := GenerateHAProxyConfigWithCerts( + lb, listeners, nil, nil, "0.0.0.0", + map[string]string{certTestArn: certPEM}, + ) + require.NoError(t, err) + + wantPath := filepath.Join(lbagent.CertDir, "lb-lb-abc123-lst-https.pem") + assert.Contains(t, config, "bind *:443 ssl crt "+wantPath) + require.Len(t, certFiles, 1) + assert.Equal(t, certPEM, certFiles[wantPath]) +} + +func TestGenerateHAProxyConfigWithCerts_HTTPHasNoSslCrt(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-abc123", Name: "my-alb"} + listeners := []*ListenerRecord{{ + ListenerArn: "arn:aws:elasticloadbalancing:us-east-1:123:listener/app/my-alb/lb-abc123/lst-http", + ListenerID: "lst-http", + Protocol: ProtocolHTTP, + Port: 80, + DefaultActions: []ListenerAction{{Type: ActionTypeFixedResponse, FixedResponse: &FixedResponseAction{StatusCode: "200"}}}, + }} + + config, certFiles, err := GenerateHAProxyConfigWithCerts(lb, listeners, nil, nil, "0.0.0.0", nil) + require.NoError(t, err) + assert.Contains(t, config, "bind *:80") + assert.NotContains(t, config, "ssl crt") + assert.Empty(t, certFiles) +} + +// A secure listener whose cert ARN is unresolved renders without ssl crt — the +// gap surfaces at HAProxy bind time rather than silently serving cleartext. +func TestGenerateHAProxyConfigWithCerts_MissingCertRendersPlain(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-abc123", Name: "my-alb"} + config, certFiles, err := GenerateHAProxyConfigWithCerts( + lb, []*ListenerRecord{httpsListener()}, nil, nil, "0.0.0.0", nil, + ) + require.NoError(t, err) + assert.NotContains(t, config, "ssl crt") + assert.Empty(t, certFiles) +} + +func TestGenerateHAProxyConfig_BackwardCompatNoTLS(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-abc123", Name: "my-alb"} + config, err := GenerateHAProxyConfig(lb, []*ListenerRecord{httpsListener()}, nil, nil, "0.0.0.0") + require.NoError(t, err) + assert.NotContains(t, config, "ssl crt", "legacy entrypoint resolves no certs") +} + +func TestConfigCertHash_ChangesOnRotation(t *testing.T) { + const cfg = "frontend ft\n bind *:443 ssl crt /etc/haproxy/certs/x.pem\n" + path := "/etc/haproxy/certs/x.pem" + + base := configCertHash(cfg, map[string]string{path: "PEM-A"}) + rotated := configCertHash(cfg, map[string]string{path: "PEM-B"}) + same := configCertHash(cfg, map[string]string{path: "PEM-A"}) + + assert.NotEqual(t, base, rotated, "rotating cert content must change the hash") + assert.Equal(t, base, same, "identical inputs hash identically") + + // Config-only change also moves the hash. + assert.NotEqual(t, base, configCertHash(cfg+"\n", map[string]string{path: "PEM-A"})) +} + +func putTestCert(t *testing.T, svc *ELBv2ServiceImpl, arn, account, leaf, chain, key string) { + t.Helper() + require.NoError(t, svc.acmStore.PutCert(&handlers_acm.CertRecord{ + CertificateArn: arn, + AccountID: account, + Certificate: leaf, + CertificateChain: chain, + PrivateKey: key, + DomainName: "example.com", + })) +} + +func TestResolveCertPEM_ConcatOrderAndOwnership(t *testing.T) { + svc := setupTestService(t) + require.NotNil(t, svc.acmStore) + putTestCert(t, svc, certTestArn, testAccountID, "LEAF", "CHAIN", "KEY") + + pem, err := svc.resolveCertPEM(certTestArn, testAccountID) + require.NoError(t, err) + assert.Equal(t, "LEAF\nCHAIN\nKEY\n", pem) + + // Wrong account → treated as not found. + _, err = svc.resolveCertPEM(certTestArn, "999999999999") + require.Error(t, err) + + // Unknown ARN → not found. + _, err = svc.resolveCertPEM("arn:aws:acm:ap-southeast-2:123456789012:certificate/missing", testAccountID) + require.Error(t, err) +} + +func TestValidateListenerCerts_RejectsUnknownAndWrongAccount(t *testing.T) { + svc := setupTestService(t) + putTestCert(t, svc, certTestArn, testAccountID, "LEAF", "", "KEY") + + // Known, owned cert → accepted. + require.NoError(t, svc.validateListenerCerts( + []ListenerCertificate{{CertificateArn: certTestArn}}, testAccountID)) + + // Unknown ARN → CertificateNotFound, rejected at the API boundary. + err := svc.validateListenerCerts( + []ListenerCertificate{{CertificateArn: "arn:aws:acm:ap-southeast-2:123456789012:certificate/missing"}}, testAccountID) + require.EqualError(t, err, awserrors.ErrorELBv2CertificateNotFound) + + // Cert owned by another account → not found. + err = svc.validateListenerCerts( + []ListenerCertificate{{CertificateArn: certTestArn}}, "999999999999") + require.EqualError(t, err, awserrors.ErrorELBv2CertificateNotFound) + + // No ACM store (degraded) → validation skipped, never blocks. + svc.acmStore = nil + require.NoError(t, svc.validateListenerCerts( + []ListenerCertificate{{CertificateArn: certTestArn}}, testAccountID)) +} + +func TestResolveCertPEM_NoChain(t *testing.T) { + svc := setupTestService(t) + putTestCert(t, svc, certTestArn, testAccountID, "LEAF", "", "KEY") + + pem, err := svc.resolveCertPEM(certTestArn, testAccountID) + require.NoError(t, err) + assert.Equal(t, "LEAF\nKEY\n", pem) +} + +func TestResolveListenerCerts_DedupAndResolve(t *testing.T) { + svc := setupTestService(t) + putTestCert(t, svc, certTestArn, testAccountID, "LEAF", "", "KEY") + + // Two listeners share the same cert ARN — resolved once. + got, err := svc.resolveListenerCerts([]*ListenerRecord{httpsListener(), httpsListener()}, testAccountID) + require.NoError(t, err) + require.Len(t, got, 1) + assert.Equal(t, "LEAF\nKEY\n", got[certTestArn]) + + // HTTP-only listeners → nil map, no error. + got, err = svc.resolveListenerCerts([]*ListenerRecord{{Protocol: ProtocolHTTP}}, testAccountID) + require.NoError(t, err) + assert.Nil(t, got) +} + +func TestGetLBConfig_DeliversCertFiles(t *testing.T) { + svc := setupTestService(t) + rec := &LoadBalancerRecord{ + LoadBalancerID: "lb-deliver1", + AccountID: testAccountID, + ConfigText: "frontend ft\n bind *:443 ssl crt /etc/haproxy/certs/lb-deliver1-lst.pem\n", + ConfigHash: "hash123", + CertFiles: map[string]string{"/etc/haproxy/certs/lb-deliver1-lst.pem": "LEAF\nKEY\n"}, + } + require.NoError(t, svc.store.PutLoadBalancer(rec)) + + out, err := svc.GetLBConfig(&GetLBConfigInput{LBID: aws.String("lb-deliver1")}, testAccountID) + require.NoError(t, err) + require.Len(t, out.CertFiles, 1) + assert.Equal(t, "/etc/haproxy/certs/lb-deliver1-lst.pem", *out.CertFiles[0].Path) + assert.Equal(t, "LEAF\nKEY\n", *out.CertFiles[0].PEM) + + // The gateway marshals this output to XML; confirm the member shape the + // lb-agent parses (GetLBConfigResult>CertFiles>member>{Path,PEM}). + payload := utils.GenerateIAMXMLPayload("GetLBConfig", *out) + xmlBytes, err := utils.MarshalToXML(payload) + require.NoError(t, err) + var parsed struct { + Members []struct { + Path string `xml:"Path"` + PEM string `xml:"PEM"` + } `xml:"GetLBConfigResult>CertFiles>member"` + } + require.NoError(t, xml.Unmarshal(xmlBytes, &parsed)) + require.Len(t, parsed.Members, 1) + assert.Equal(t, "/etc/haproxy/certs/lb-deliver1-lst.pem", parsed.Members[0].Path) + assert.Equal(t, "LEAF\nKEY\n", parsed.Members[0].PEM) +} + +func TestCertFilesToSDK_SortedAndNilSafe(t *testing.T) { + assert.Nil(t, certFilesToSDK(nil)) + + out := certFilesToSDK(map[string]string{"/b.pem": "B", "/a.pem": "A"}) + require.Len(t, out, 2) + assert.Equal(t, "/a.pem", *out[0].Path) + assert.Equal(t, "A", *out[0].PEM) + assert.Equal(t, "/b.pem", *out[1].Path) +} diff --git a/spinifex/handlers/elbv2/haproxy_test.go b/spinifex/handlers/elbv2/haproxy_test.go index 4adaed7b4..f52e68303 100644 --- a/spinifex/handlers/elbv2/haproxy_test.go +++ b/spinifex/handlers/elbv2/haproxy_test.go @@ -163,6 +163,244 @@ func TestGenerateHAProxyConfig_SharedTargetGroup(t *testing.T) { assert.Equal(t, 1, strings.Count(config, "balance roundrobin")) } +// TestGenerateHAProxyConfig_FixedResponseDefault mirrors the shared-ingress +// shape: a listener whose default action is fixed-response (no target group), +// with host-header rules forwarding to per-app target groups. The generator +// must synthesize a default backend instead of emitting a dangling +// `default_backend bk_`, which makes HAProxy fail to start. +func TestGenerateHAProxyConfig_FixedResponseDefault(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-ingress"} + listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/wd-ingress/lb-ingress/lst-616760baf6a3031c3" + appTG := "arn:aws:elasticloadbalancing:ap-southeast-2:1:targetgroup/wd-identity/tg-app1" + + listeners := []*ListenerRecord{ + { + ListenerArn: listenerArn, + Port: 80, + Protocol: ProtocolHTTP, + DefaultActions: []ListenerAction{ + {Type: ActionTypeFixedResponse, FixedResponse: &FixedResponseAction{ + StatusCode: "404", + ContentType: "text/plain", + MessageBody: "no route", + }}, + }, + }, + } + + rulesByListener := map[string][]*RuleRecord{ + listenerArn: { + { + RuleID: "rule1", + ListenerArn: listenerArn, + Priority: 1, + Conditions: []RuleCondition{{Field: RuleFieldHostHeader, Values: []string{"identity.toc.spinifex.local"}}}, + Actions: []ListenerAction{{Type: ActionTypeForward, TargetGroupArn: appTG}}, + }, + }, + } + + tgByArn := map[string]*TargetGroupRecord{ + appTG: { + TargetGroupArn: appTG, + HealthCheck: DefaultHealthCheck(), + Targets: []Target{{Id: "i-app1", Port: 8080, PrivateIP: "10.42.0.9", HealthState: TargetHealthHealthy}}, + }, + } + + config, err := GenerateHAProxyConfig(lb, listeners, tgByArn, rulesByListener, "10.42.0.13") + require.NoError(t, err) + + // No dangling default backend. + assert.NotContains(t, config, "default_backend bk_\n") + // Synthetic default backend named off the listener ARN, returning the fixed response. + assert.Contains(t, config, "default_backend bkdefault_lst-616760baf6a3031c3") + assert.Contains(t, config, "backend bkdefault_lst-616760baf6a3031c3") + assert.Contains(t, config, `http-request return status 404 content-type "text/plain" string "no route"`) + // Host-header rule still routes to the app backend. + assert.Contains(t, config, "use_backend bk_tg-app1 if") + assert.Contains(t, config, "10.42.0.9:8080") +} + +// TestGenerateHAProxyConfig_FixedResponseRejectsUnsafeBody falls back to a bare +// status when the body or content-type contains injection bytes. +func TestGenerateHAProxyConfig_FixedResponseRejectsUnsafeBody(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-evil"} + listeners := []*ListenerRecord{ + { + ListenerArn: "arn:lst-evil", + Port: 80, + DefaultActions: []ListenerAction{ + {Type: ActionTypeFixedResponse, FixedResponse: &FixedResponseAction{ + StatusCode: "200", + ContentType: "text/plain", + MessageBody: "evil\"\n acl x always_true", + }}, + }, + }, + } + + config, err := GenerateHAProxyConfig(lb, listeners, nil, nil, "0.0.0.0") + require.NoError(t, err) + + assert.Contains(t, config, "http-request return status 200") + assert.NotContains(t, config, "always_true") + assert.NotContains(t, config, `string "evil`) +} + +// TestGenerateHAProxyConfig_RedirectSchemeDefault renders the canonical +// HTTP→HTTPS redirect (scheme-only change, all other fields default) as a +// `redirect scheme https` directive so HAProxy preserves host/path/query. +func TestGenerateHAProxyConfig_RedirectSchemeDefault(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-redir"} + listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/r/lb-redir/lst-aa" + listeners := []*ListenerRecord{ + { + ListenerArn: listenerArn, + Port: 80, + Protocol: ProtocolHTTP, + DefaultActions: []ListenerAction{ + {Type: ActionTypeRedirect, Redirect: &RedirectAction{ + Protocol: "HTTPS", + Host: "#{host}", + Path: "/#{path}", + Query: "#{query}", + StatusCode: "HTTP_301", + }}, + }, + }, + } + + config, err := GenerateHAProxyConfig(lb, listeners, nil, nil, "0.0.0.0") + require.NoError(t, err) + + assert.Contains(t, config, "default_backend bkdefault_lst-aa") + assert.Contains(t, config, "http-request redirect scheme https code 301") + assert.NotContains(t, config, "location") +} + +// TestGenerateHAProxyConfig_RedirectLocation renders a redirect that changes +// the host as a rebuilt `location` with HAProxy placeholders preserved. +func TestGenerateHAProxyConfig_RedirectLocation(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-redir2"} + listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/r/lb-redir2/lst-bb" + listeners := []*ListenerRecord{ + { + ListenerArn: listenerArn, + Port: 80, + Protocol: ProtocolHTTP, + DefaultActions: []ListenerAction{ + {Type: ActionTypeRedirect, Redirect: &RedirectAction{ + Protocol: "HTTPS", + Host: "www.example.com", + Path: "/#{path}", + Query: "#{query}", + StatusCode: "HTTP_302", + }}, + }, + }, + } + + config, err := GenerateHAProxyConfig(lb, listeners, nil, nil, "0.0.0.0") + require.NoError(t, err) + + assert.Contains(t, config, "http-request redirect location https://www.example.com%[path]?%[query] code 302") +} + +// TestGenerateHAProxyConfig_RedirectCustomAll exercises the location form with +// a placeholder protocol (resolved from the listener), an explicit port, and a +// custom literal path + query. +func TestGenerateHAProxyConfig_RedirectCustomAll(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-redir3"} + listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/r/lb-redir3/lst-dd" + listeners := []*ListenerRecord{ + { + ListenerArn: listenerArn, + Port: 80, + Protocol: ProtocolHTTPS, + DefaultActions: []ListenerAction{ + {Type: ActionTypeRedirect, Redirect: &RedirectAction{ + Protocol: "#{protocol}", + Host: "alt.example.com", + Port: "8443", + Path: "/moved", + Query: "ref=1", + StatusCode: "HTTP_302", + }}, + }, + }, + } + + config, err := GenerateHAProxyConfig(lb, listeners, nil, nil, "0.0.0.0") + require.NoError(t, err) + + assert.Contains(t, config, "http-request redirect location https://alt.example.com:8443/moved?ref=1 code 302") +} + +// TestGenerateHAProxyConfig_RedirectRule renders a per-rule redirect action via +// a synthetic backend reached by use_backend. +func TestGenerateHAProxyConfig_RedirectRule(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-rr"} + listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/r/lb-rr/lst-cc" + appTG := "arn:aws:elasticloadbalancing:ap-southeast-2:1:targetgroup/app/tg-app" + listeners := []*ListenerRecord{ + { + ListenerArn: listenerArn, + Port: 80, + Protocol: ProtocolHTTP, + DefaultActions: []ListenerAction{ + {Type: ActionTypeForward, TargetGroupArn: appTG}, + }, + }, + } + rulesByListener := map[string][]*RuleRecord{ + listenerArn: { + { + RuleID: "ruleR", + ListenerArn: listenerArn, + Priority: 1, + Conditions: []RuleCondition{{Field: RuleFieldPathPattern, Values: []string{"/old/*"}}}, + Actions: []ListenerAction{{Type: ActionTypeRedirect, Redirect: &RedirectAction{ + Protocol: "HTTPS", + StatusCode: "HTTP_301", + }}}, + }, + }, + } + tgByArn := map[string]*TargetGroupRecord{ + appTG: {TargetGroupArn: appTG, HealthCheck: DefaultHealthCheck()}, + } + + config, err := GenerateHAProxyConfig(lb, listeners, tgByArn, rulesByListener, "0.0.0.0") + require.NoError(t, err) + + assert.Contains(t, config, "use_backend bkrule_ruleR if") + assert.Contains(t, config, "backend bkrule_ruleR") + assert.Contains(t, config, "http-request redirect scheme https code 301") +} + +// TestGenerateHAProxyConfig_RedirectRejectsInjection fails the render when a +// redirect field carries HAProxy meta-characters that survived to the store. +func TestGenerateHAProxyConfig_RedirectRejectsInjection(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-evil2"} + listeners := []*ListenerRecord{ + { + ListenerArn: "arn:lst-evil2", + Port: 80, + Protocol: ProtocolHTTP, + DefaultActions: []ListenerAction{ + {Type: ActionTypeRedirect, Redirect: &RedirectAction{ + Host: "evil\"\n acl x always_true", + StatusCode: "HTTP_301", + }}, + }, + }, + } + + _, err := GenerateHAProxyConfig(lb, listeners, nil, nil, "0.0.0.0") + require.Error(t, err) +} + func TestGenerateHAProxyConfig_NoListeners(t *testing.T) { lb := &LoadBalancerRecord{LoadBalancerID: "lb-empty"} config, err := GenerateHAProxyConfig(lb, nil, nil, nil, "0.0.0.0") @@ -347,250 +585,6 @@ func TestHAProxyManager_RemoveConfig_Idempotent(t *testing.T) { require.NoError(t, err) } -// --- NLB TCP Config Generation Tests --- - -func TestGenerateNLBConfig_TCPHealthCheck(t *testing.T) { - lb := &LoadBalancerRecord{ - LoadBalancerID: "lb-nlb001", - Name: "my-nlb", - Type: LoadBalancerTypeNetwork, - } - - tgArn := "arn:aws:elasticloadbalancing:us-east-1:123:targetgroup/tcp-tg/tg-tcp001" - listeners := []*ListenerRecord{ - { - ListenerArn: "arn:aws:elasticloadbalancing:us-east-1:123:listener/net/my-nlb/lb-nlb001/lst-tcp001", - Protocol: ProtocolTCP, - Port: 5432, - DefaultActions: []ListenerAction{ - {Type: ActionTypeForward, TargetGroupArn: tgArn}, - }, - }, - } - - tgByArn := map[string]*TargetGroupRecord{ - tgArn: { - TargetGroupArn: tgArn, - Name: "tcp-tg", - Protocol: ProtocolTCP, - HealthCheck: HealthCheckConfig{ - Protocol: ProtocolTCP, - Port: "traffic-port", - IntervalSeconds: 30, - TimeoutSeconds: 10, - HealthyThreshold: 3, - UnhealthyThreshold: 3, - }, - Targets: []Target{ - {Id: "i-tcp001", Port: 5432, PrivateIP: "10.0.1.10", HealthState: TargetHealthHealthy}, - {Id: "i-tcp002", Port: 5432, PrivateIP: "10.0.1.11", HealthState: TargetHealthHealthy}, - }, - }, - } - - config, err := GenerateHAProxyConfig(lb, listeners, tgByArn, nil, "10.0.1.5") - require.NoError(t, err) - - // Verify TCP mode - assert.Contains(t, config, "mode tcp") - assert.Contains(t, config, "option tcplog") - assert.NotContains(t, config, "mode http") - assert.NotContains(t, config, "option httplog") - - // Verify socket name uses generic lb- prefix (matches agent expectation) - assert.Contains(t, config, "lb-lb-nlb001.sock") - - // Verify TCP health check - assert.Contains(t, config, "option tcp-check") - assert.NotContains(t, config, "option httpchk") - - // Verify frontend/backend - assert.Contains(t, config, "bind *:5432") - assert.Contains(t, config, "10.0.1.10:5432") - assert.Contains(t, config, "10.0.1.11:5432") - assert.Contains(t, config, "check inter 30s fall 3 rise 3") -} - -func TestGenerateNLBConfig_HTTPHealthCheckOnTCPTargetGroup(t *testing.T) { - lb := &LoadBalancerRecord{ - LoadBalancerID: "lb-nlb002", - Name: "nlb-http-hc", - Type: LoadBalancerTypeNetwork, - } - - tgArn := "arn:aws:elasticloadbalancing:us-east-1:123:targetgroup/tcp-http-hc/tg-tcp002" - listeners := []*ListenerRecord{ - { - ListenerArn: "arn:lst-nlb-http", - Protocol: ProtocolTCP, - Port: 8080, - DefaultActions: []ListenerAction{ - {Type: ActionTypeForward, TargetGroupArn: tgArn}, - }, - }, - } - - tgByArn := map[string]*TargetGroupRecord{ - tgArn: { - TargetGroupArn: tgArn, - Name: "tcp-http-hc", - Protocol: ProtocolTCP, - HealthCheck: HealthCheckConfig{ - Protocol: ProtocolHTTP, - Port: "traffic-port", - Path: "/healthz", - Matcher: "200-299", - IntervalSeconds: 10, - TimeoutSeconds: 5, - HealthyThreshold: 2, - UnhealthyThreshold: 3, - }, - Targets: []Target{ - {Id: "i-http-hc1", Port: 8080, PrivateIP: "10.0.2.10", HealthState: TargetHealthHealthy}, - }, - }, - } - - config, err := GenerateHAProxyConfig(lb, listeners, tgByArn, nil, "10.0.2.1") - require.NoError(t, err) - - // TCP mode but HTTP health check - assert.Contains(t, config, "mode tcp") - assert.Contains(t, config, "option httpchk GET /healthz") - assert.Contains(t, config, "http-check expect status 200-299") - assert.NotContains(t, config, "option tcp-check") - - assert.Contains(t, config, "10.0.2.10:8080") - assert.Contains(t, config, "check inter 10s fall 3 rise 2") -} - -func TestGenerateNLBConfig_MixedBackends(t *testing.T) { - lb := &LoadBalancerRecord{ - LoadBalancerID: "lb-nlb003", - Name: "nlb-mixed", - Type: LoadBalancerTypeNetwork, - } - - tgArnTCP := "arn:tg-tcp-mixed" - tgArnHTTP := "arn:tg-http-mixed" - - listeners := []*ListenerRecord{ - { - ListenerArn: "arn:lst-tcp-mixed", - Protocol: ProtocolTCP, - Port: 5432, - DefaultActions: []ListenerAction{ - {Type: ActionTypeForward, TargetGroupArn: tgArnTCP}, - }, - }, - { - ListenerArn: "arn:lst-http-mixed", - Protocol: ProtocolTCP, - Port: 8080, - DefaultActions: []ListenerAction{ - {Type: ActionTypeForward, TargetGroupArn: tgArnHTTP}, - }, - }, - } - - tgByArn := map[string]*TargetGroupRecord{ - tgArnTCP: { - Protocol: ProtocolTCP, - HealthCheck: HealthCheckConfig{ - Protocol: ProtocolTCP, - IntervalSeconds: 30, - HealthyThreshold: 3, - UnhealthyThreshold: 3, - }, - Targets: []Target{ - {Id: "i-db1", Port: 5432, PrivateIP: "10.0.3.10", HealthState: TargetHealthHealthy}, - }, - }, - tgArnHTTP: { - Protocol: ProtocolTCP, - HealthCheck: HealthCheckConfig{ - Protocol: ProtocolHTTP, - Path: "/ready", - Matcher: "200", - IntervalSeconds: 15, - HealthyThreshold: 2, - UnhealthyThreshold: 2, - }, - Targets: []Target{ - {Id: "i-app1", Port: 8080, PrivateIP: "10.0.3.20", HealthState: TargetHealthHealthy}, - }, - }, - } - - config, err := GenerateHAProxyConfig(lb, listeners, tgByArn, nil, "10.0.3.1") - require.NoError(t, err) - - // Overall TCP mode - assert.Contains(t, config, "mode tcp") - - // First backend: TCP health check - assert.Contains(t, config, "option tcp-check") - - // Second backend: HTTP health check - assert.Contains(t, config, "option httpchk GET /ready") - assert.Contains(t, config, "http-check expect status 200") - - // Both servers present - assert.Contains(t, config, "10.0.3.10:5432") - assert.Contains(t, config, "10.0.3.20:8080") -} - -func TestGenerateNLBConfig_NoListeners(t *testing.T) { - lb := &LoadBalancerRecord{ - LoadBalancerID: "lb-nlb-empty", - Type: LoadBalancerTypeNetwork, - } - - config, err := GenerateHAProxyConfig(lb, nil, nil, nil, "0.0.0.0") - require.NoError(t, err) - - assert.Contains(t, config, "mode tcp") - assert.Contains(t, config, "option tcplog") - assert.NotContains(t, config, "frontend") - assert.NotContains(t, config, "backend") -} - -func TestGenerateNLBConfig_SkipsDrainingTargets(t *testing.T) { - lb := &LoadBalancerRecord{ - LoadBalancerID: "lb-nlb-drain", - Type: LoadBalancerTypeNetwork, - } - - tgArn := "arn:tg-nlb-drain" - listeners := []*ListenerRecord{ - { - ListenerArn: "arn:lst-nlb-drain", - Protocol: ProtocolTCP, - Port: 3306, - DefaultActions: []ListenerAction{{Type: ActionTypeForward, TargetGroupArn: tgArn}}, - }, - } - - tgByArn := map[string]*TargetGroupRecord{ - tgArn: { - Protocol: ProtocolTCP, - HealthCheck: DefaultNLBHealthCheck(), - Targets: []Target{ - {Id: "i-active", Port: 3306, PrivateIP: "10.0.0.1", HealthState: TargetHealthHealthy}, - {Id: "i-draining", Port: 3306, PrivateIP: "10.0.0.2", HealthState: TargetHealthDraining}, - {Id: "i-no-ip", Port: 3306, PrivateIP: "", HealthState: TargetHealthInitial}, - }, - }, - } - - config, err := GenerateHAProxyConfig(lb, listeners, tgByArn, nil, "0.0.0.0") - require.NoError(t, err) - - assert.Contains(t, config, "10.0.0.1:3306") - assert.NotContains(t, config, "10.0.0.2") // draining - assert.NotContains(t, config, "i-no-ip") // no IP -} - func TestGenerateALBConfig_StillWorks(t *testing.T) { // Regression: ALB (no Type set) should still generate HTTP-mode config lb := &LoadBalancerRecord{ diff --git a/spinifex/handlers/elbv2/launcher.go b/spinifex/handlers/elbv2/launcher.go index 3ca9b6e8b..26d65c184 100644 --- a/spinifex/handlers/elbv2/launcher.go +++ b/spinifex/handlers/elbv2/launcher.go @@ -1,112 +1,17 @@ package handlers_elbv2 -// SystemInstanceLauncher is the subset of daemon functionality needed by -// ELBv2 to launch and terminate system-managed VMs (e.g. ALB VMs). -// Defined here to avoid a circular import between handlers/elbv2 and daemon. -// -// System instances are owned by the system account and hidden from -// customer DescribeInstances calls. -type SystemInstanceLauncher interface { - // LaunchSystemInstance creates and starts a system-managed VM. - // The VM is assigned to the system account and not visible to customers. - // The caller provides the instance type, AMI, subnet, user data, and - // optionally a pre-created ENI to attach. - // - // Returns the instance ID and private IP once the VM is running. - LaunchSystemInstance(input *SystemInstanceInput) (*SystemInstanceOutput, error) +import "github.com/mulgadc/spinifex/spinifex/handlers/sysinstance" - // TerminateSystemInstance stops and cleans up a system-managed VM. - TerminateSystemInstance(instanceID string) error -} - -// SystemInstanceInput describes the direct-boot microVM to launch for an -// ELBv2 load balancer. There is no AMI or cloud-init path — kernel and -// initrd come from the bundled spinifex package and per-VM config is -// delivered via QEMU fw_cfg blobs. -// -// JSON tags are explicit because this struct crosses the -// system.LaunchInstance.* NATS subject root when ELBv2 hands a launch off -// to the cluster — see daemon/daemon_system_dispatch.go. -type SystemInstanceInput struct { - InstanceType string `json:"instance_type"` // e.g. "sys.micro" - SubnetID string `json:"subnet_id"` // VPC subnet for networking - - // ENI fields — the VM always uses a pre-created ENI (the ALB's primary ENI). - ENIID string `json:"eni_id"` - ENIMac string `json:"eni_mac"` - ENIIP string `json:"eni_ip"` - - // ExtraENIs lists additional pre-created ENIs that should be attached to - // the VM alongside the primary ENI. Used for multi-subnet ALBs so the VM - // has a NIC (and tap on br-int) in every subnet the LB spans. - ExtraENIs []ExtraENIInput `json:"extra_enis,omitempty"` - - // Scheme is the ALB scheme ("internet-facing" or "internal"). - // Internet-facing ALBs get a public IP and NAT rules; internal ALBs do not. - Scheme string `json:"scheme"` - - // AccountID is the owner account of the ALB's ENI. Required so the daemon - // can look up and update the ENI record (which is keyed by account). - AccountID string `json:"account_id"` - - // HostfwdPorts specifies additional guest ports to forward from the host - // via the QEMU user-mode dev NIC (dev_networking mode only). - // Each entry is a guest port (e.g. 80, 443). The host port is auto-assigned. - HostfwdPorts []int `json:"hostfwd_ports,omitempty"` - - // NICs defines the network interfaces for the microVM. - // Index 0 is the primary VPC ENI, index 1 is the management NIC, index 2+ are extra ENIs. - NICs []NICConfig `json:"nics,omitempty"` - - // LBAgentEnv is a KEY=value blob written to /etc/conf.d/lb-agent inside - // the guest via fw_cfg. - LBAgentEnv string `json:"lb_agent_env"` - - // CACert holds PEM-encoded CA certificate bytes delivered to the guest via fw_cfg. - CACert string `json:"ca_cert"` -} - -// ExtraENIInput describes an additional pre-created ENI to attach to a -// system instance. The primary ENI is still passed via ENIID/ENIMac/ENIIP. -type ExtraENIInput struct { - ENIID string `json:"eni_id"` - ENIMac string `json:"eni_mac"` - ENIIP string `json:"eni_ip"` - SubnetID string `json:"subnet_id"` -} - -// NICConfig describes a single network interface for a microVM direct-boot launch. -// Index 0 is the primary VPC ENI, index 1 is the management NIC, index 2+ are extra ENIs. -type NICConfig struct { - MAC string `json:"mac"` // e.g. "02:0a:01:23:45:67" - CIDR string `json:"cidr"` // e.g. "10.0.1.5/24" - Gateway string `json:"gateway,omitempty"` // e.g. "10.0.1.1"; empty for mgmt NIC - IsDefault bool `json:"is_default"` // true for exactly one NIC (primary VPC ENI) - RouteDst string `json:"route_dst,omitempty"` // specific host route dst, e.g. "10.20.0.5/32" - RouteVia string `json:"route_via,omitempty"` // next-hop for RouteDst -} - -// RecoveryContext carries the per-VM fields the ELBv2 service needs to -// reconstruct a SystemInstanceInput during host-reboot recovery, without -// importing the vm package (which would cycle back through daemon). -// MgmtMAC / MgmtIP are the values the daemon already allocated for this -// instance on a prior launch; the service injects them back into NIC[1] -// rather than re-allocating from the mgmt IPAM. -type RecoveryContext struct { - InstanceID string - InstanceType string - ENIMac string - MgmtMAC string - MgmtIP string -} - -// SystemInstanceOutput contains the result of a successful launch. -type SystemInstanceOutput struct { - InstanceID string `json:"instance_id"` // e.g. "i-xxxxx" - PrivateIP string `json:"private_ip"` // VPC private IP - PublicIP string `json:"public_ip,omitempty"` // Public IP (only for internet-facing scheme) - - // HostfwdMap maps guest port → host port for any forwarded ports. - // Only populated when dev_networking is enabled and HostfwdPorts were requested. - HostfwdMap map[int]int `json:"hostfwd_map,omitempty"` -} +// The system-instance launch contract now lives in the boot-agnostic +// handlers/sysinstance package so both ELBv2 and EKS can launch system VMs +// without an eks→elbv2 dependency. These aliases keep existing ELBv2 source +// (which builds direct-boot microVM inputs) compiling unchanged. ELBv2 always +// launches with BootMode=BootDirect (the zero value). +type ( + SystemInstanceLauncher = sysinstance.SystemInstanceLauncher + SystemInstanceInput = sysinstance.SystemInstanceInput + ExtraENIInput = sysinstance.ExtraENIInput + NICConfig = sysinstance.NICConfig + RecoveryContext = sysinstance.RecoveryContext + SystemInstanceOutput = sysinstance.SystemInstanceOutput +) diff --git a/spinifex/handlers/elbv2/lifecycle_invariants_test.go b/spinifex/handlers/elbv2/lifecycle_invariants_test.go new file mode 100644 index 000000000..b2ac8b6e6 --- /dev/null +++ b/spinifex/handlers/elbv2/lifecycle_invariants_test.go @@ -0,0 +1,187 @@ +package handlers_elbv2 + +import ( + "os" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestRLC1_ELBv2DeleteIdempotentOnAbsent enforces Common Resource Lifecycle +// Contract rule #1 (idempotent delete) across the ELBv2 surface: deleting an +// absent resource is success, not a NotFound error. This mirrors AWS ELBv2 +// delete semantics and is required for tofu destroy to converge on retry. +// +// Every ELBv2 delete endpoint MUST have a case here. When a new delete +// endpoint lands, add it; a missing case is an idempotency gap. +func TestRLC1_ELBv2DeleteIdempotentOnAbsent(t *testing.T) { + const arnBase = "arn:aws:elasticloadbalancing:us-east-1:123456789012:" + + cases := []struct { + name string + call func(svc *ELBv2ServiceImpl) (any, error) + }{ + {"DeleteLoadBalancer", func(svc *ELBv2ServiceImpl) (any, error) { + return svc.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{ + LoadBalancerArn: aws.String(arnBase + "loadbalancer/app/absent/0000000000000000"), + }, testAccountID) + }}, + {"DeleteTargetGroup", func(svc *ELBv2ServiceImpl) (any, error) { + return svc.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ + TargetGroupArn: aws.String(arnBase + "targetgroup/absent/0000000000000000"), + }, testAccountID) + }}, + {"DeleteListener", func(svc *ELBv2ServiceImpl) (any, error) { + return svc.DeleteListener(&elbv2.DeleteListenerInput{ + ListenerArn: aws.String(arnBase + "listener/app/absent/0000000000000000/1111111111111111"), + }, testAccountID) + }}, + {"DeleteRule", func(svc *ELBv2ServiceImpl) (any, error) { + return svc.DeleteRule(&elbv2.DeleteRuleInput{ + RuleArn: aws.String(arnBase + "listener-rule/app/absent/0000000000000000/1111111111111111/2222222222222222"), + }, testAccountID) + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + svc := setupTestService(t) + out, err := tc.call(svc) + require.NoErrorf(t, err, "%s on an absent resource must return success (RLC rule #1)", tc.name) + assert.NotNilf(t, out, "%s must return a non-nil output on absent", tc.name) + }) + } +} + +// TestRLC2_ELBv2NoOrphanAfterDeleteLB enforces ADR-0002 §5 no-orphan +// completeness: after DeleteLoadBalancer, no listener or rule owned by that LB +// may survive in the store. Uses two listeners (each with a rule) so a +// single-listener cascade can't pass by accident. +func TestRLC2_ELBv2NoOrphanAfterDeleteLB(t *testing.T) { + svc := setupTestService(t) + + lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("rlc2-lb")}, testAccountID) + require.NoError(t, err) + lbArn := lbOut.LoadBalancers[0].LoadBalancerArn + + tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("rlc2-tg")}, testAccountID) + require.NoError(t, err) + tgArn := tgOut.TargetGroups[0].TargetGroupArn + + for i, port := range []int64{80, 8080} { + lst, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(port), + DefaultActions: []*elbv2.Action{{Type: aws.String("forward"), TargetGroupArn: tgArn}}, + }, testAccountID) + require.NoError(t, err) + + _, err = svc.CreateRule(&elbv2.CreateRuleInput{ + ListenerArn: lst.Listeners[0].ListenerArn, + Priority: aws.Int64(int64(10 + i)), + Conditions: []*elbv2.RuleCondition{{Field: aws.String("path-pattern"), Values: aws.StringSlice([]string{"/api/*"})}}, + Actions: []*elbv2.Action{{Type: aws.String("forward"), TargetGroupArn: tgArn}}, + }, testAccountID) + require.NoError(t, err) + } + + _, err = svc.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{LoadBalancerArn: lbArn}, testAccountID) + require.NoError(t, err) + + listeners, err := svc.store.ListListenersByLB(*lbArn) + require.NoError(t, err) + require.Emptyf(t, listeners, "ADR-0002 §5 no-orphan completeness: no listener owned by a deleted LB may remain") + + rules, err := svc.store.ListRules() + require.NoError(t, err) + require.Emptyf(t, rules, "ADR-0002 §5 no-orphan completeness: no rule owned by a deleted LB may remain") +} + +// TestRLC3_ELBv2DeleteLBDoesNotBypassListenerCascade enforces ADR-0002 §5 no +// store-bypass cascade: DeleteLoadBalancer must tear listeners (and their +// rules) down through the shared deleteListenerCascade helper, never by +// calling store.DeleteListener directly — a direct store delete orphans the +// listener's rules. The store field is a concrete type with no injection seam, +// so the contract is enforced structurally against the source rather than with +// a runtime spy. +func TestRLC3_ELBv2DeleteLBDoesNotBypassListenerCascade(t *testing.T) { + src, err := os.ReadFile("service_impl.go") + require.NoError(t, err) + + body := stripComments(deleteLBFuncBody(t, string(src))) + assert.Containsf(t, body, "deleteListenerCascade", + "ADR-0002 §5 no store-bypass cascade: DeleteLoadBalancer must route listener teardown through deleteListenerCascade") + assert.NotContainsf(t, body, "s.store.DeleteListener(", + "ADR-0002 §5 no store-bypass cascade: DeleteLoadBalancer must not call store.DeleteListener directly") +} + +// deleteLBFuncBody returns the source text of the DeleteLoadBalancer method, +// from its signature up to the start of the next top-level func. +func deleteLBFuncBody(t *testing.T, src string) string { + t.Helper() + const sig = "func (s *ELBv2ServiceImpl) DeleteLoadBalancer(" + start := strings.Index(src, sig) + require.GreaterOrEqual(t, start, 0, "DeleteLoadBalancer not found in service_impl.go") + rest := src[start+len(sig):] + if before, _, found := strings.Cut(rest, "\nfunc "); found { + return before + } + return rest +} + +// stripComments removes // line comments so source-guard assertions match real +// code, not the prose that documents it. +func stripComments(src string) string { + var b strings.Builder + for line := range strings.SplitSeq(src, "\n") { + if i := strings.Index(line, "//"); i >= 0 { + line = line[:i] + } + b.WriteString(line) + b.WriteByte('\n') + } + return b.String() +} + +// TestRLC4_ELBv2TGDeletableAfterLBTeardown enforces ADR-0002 §5 TG deletability +// after LB teardown: create LB+listener+rule→TG, delete the LB, then the target +// group must delete successfully rather than staying pinned as ResourceInUse by +// an orphaned rule or stale listener default action. +func TestRLC4_ELBv2TGDeletableAfterLBTeardown(t *testing.T) { + svc := setupTestService(t) + + lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("rlc4-lb")}, testAccountID) + require.NoError(t, err) + lbArn := lbOut.LoadBalancers[0].LoadBalancerArn + + tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("rlc4-tg")}, testAccountID) + require.NoError(t, err) + tgArn := tgOut.TargetGroups[0].TargetGroupArn + + lst, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(80), + DefaultActions: []*elbv2.Action{{Type: aws.String("forward"), TargetGroupArn: tgArn}}, + }, testAccountID) + require.NoError(t, err) + + _, err = svc.CreateRule(&elbv2.CreateRuleInput{ + ListenerArn: lst.Listeners[0].ListenerArn, + Priority: aws.Int64(10), + Conditions: []*elbv2.RuleCondition{{Field: aws.String("path-pattern"), Values: aws.StringSlice([]string{"/api/*"})}}, + Actions: []*elbv2.Action{{Type: aws.String("forward"), TargetGroupArn: tgArn}}, + }, testAccountID) + require.NoError(t, err) + + _, err = svc.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{LoadBalancerArn: lbArn}, testAccountID) + require.NoError(t, err) + + _, err = svc.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{TargetGroupArn: tgArn}, testAccountID) + require.NoErrorf(t, err, "ADR-0002 §5 TG deletability after LB teardown: target group must not stay pinned as ResourceInUse") +} diff --git a/spinifex/handlers/elbv2/nginx.go b/spinifex/handlers/elbv2/nginx.go new file mode 100644 index 000000000..754772e28 --- /dev/null +++ b/spinifex/handlers/elbv2/nginx.go @@ -0,0 +1,237 @@ +package handlers_elbv2 + +import ( + "fmt" + "path/filepath" + "sort" + "strconv" + "strings" + "text/template" + + "github.com/mulgadc/spinifex/spinifex/lbagent" +) + +// NLB (L4) data plane. HAProxy load-balances no UDP in any release, so NLB +// listeners (TCP/UDP/TLS/TCP_UDP) render an nginx `stream` config instead; +// ALBs stay on HAProxy. See docs/development/feature/nlb-nginx-l4-dataplane.md. +// +// Health checks are passive only (max_fails / fail_timeout) — OSS nginx stream +// has no active upstream probing. + +const nginxStreamConfigTemplate = `# Auto-generated by spinifex ELBv2 (NLB) — do not edit +load_module /usr/lib/nginx/modules/ngx_stream_module.so; +user root; +worker_processes auto; +error_log stderr; +pid {{.PIDPath}}; + +events { + worker_connections 1024; +} + +stream { +{{- range .Upstreams}} + upstream {{.Name}} { +{{- range .Servers}} + server {{.Addr}}:{{.Port}} max_fails={{.MaxFails}} fail_timeout={{.FailTimeout}}s; +{{- else}} + server 127.0.0.1:1 down; +{{- end}} + } +{{end}} +{{- range .Servers}} + server { + listen {{.Port}}{{if .UDP}} udp{{end}}{{if .SSL}} ssl{{end}}; +{{- if .SSL}} + ssl_certificate {{.CertPath}}; + ssl_certificate_key {{.CertPath}}; +{{- end}} +{{- if .UDP}} + proxy_responses 1; + proxy_timeout 10s; +{{- end}} + proxy_pass {{.Upstream}}; + } +{{end}}} +` + +var nginxStreamTmpl = template.Must( + template.New("nginx-stream").Parse(nginxStreamConfigTemplate), +) + +// nginxConfig is the render model for the NLB nginx `stream` config. +type nginxConfig struct { + PIDPath string + Upstreams []nginxUpstream + Servers []nginxServer +} + +// nginxUpstream is one target group's backend pool. An empty pool renders a +// single permanently-down placeholder so nginx (which requires >=1 server) +// still validates. +type nginxUpstream struct { + Name string + Servers []nginxUpstreamServer +} + +type nginxUpstreamServer struct { + Addr string + Port int64 + MaxFails int64 + FailTimeout int64 +} + +// nginxServer is one listener's `server` block. TCP_UDP listeners expand to two +// blocks on the same port (one TCP, one UDP). +type nginxServer struct { + Port int64 + UDP bool + SSL bool + CertPath string + Upstream string +} + +// engineForType reports the data-plane engine for a load balancer type: nginx +// for NLBs (L4), HAProxy for everything else (ALB / L7). +func engineForType(lbType string) string { + if lbType == LoadBalancerTypeNetwork { + return EngineNginx + } + return EngineHAProxy +} + +// generateNLBStreamConfig builds an nginx `stream` config for an NLB. Secure +// (TLS) listeners terminate TLS at nginx and forward plaintext to the target; +// the returned certFiles map carries each cert path → combined PEM for delivery +// to the LB agent. bindAddr is unused (nginx listens on all addresses, matching +// the HAProxy `bind *` behaviour). +func generateNLBStreamConfig(lb *LoadBalancerRecord, listeners []*ListenerRecord, tgByArn map[string]*TargetGroupRecord, _ string, certPEMByArn map[string]string) (string, map[string]string, error) { + cfg := nginxConfig{PIDPath: lbagent.NginxPIDPath} + var certFiles map[string]string + upstreamAdded := make(map[string]struct{}) + + addUpstream := func(tgArn string) string { + name := sanitizeName("us", tgArn) + if _, ok := upstreamAdded[name]; ok { + return name + } + us := nginxUpstream{Name: name} + if tg, ok := tgByArn[tgArn]; ok { + for _, t := range tg.Targets { + if t.PrivateIP == "" || t.HealthState == TargetHealthDraining { + continue + } + us.Servers = append(us.Servers, nginxUpstreamServer{ + Addr: t.PrivateIP, + Port: t.Port, + MaxFails: tg.HealthCheck.UnhealthyThreshold, + FailTimeout: tg.HealthCheck.IntervalSeconds, + }) + } + } + cfg.Upstreams = append(cfg.Upstreams, us) + upstreamAdded[name] = struct{}{} + return name + } + + for _, l := range listeners { + if len(l.DefaultActions) == 0 { + continue + } + da := l.DefaultActions[0] + // NLB default actions are forward-only (no redirect / fixed-response). + if da.Type != ActionTypeForward || da.TargetGroupArn == "" { + continue + } + upstream := addUpstream(da.TargetGroupArn) + + var certPath string + if protocolRequiresCert(l.Protocol) { + pem, _ := resolveFrontendCert(lb, l, certPEMByArn) + if pem != "" { + certPath = nginxCertPath(lb, l) + if certFiles == nil { + certFiles = make(map[string]string) + } + certFiles[certPath] = pem + } + } + + switch l.Protocol { + case ProtocolUDP: + cfg.Servers = append(cfg.Servers, nginxServer{Port: l.Port, UDP: true, Upstream: upstream}) + case ProtocolTCPUDP: + cfg.Servers = append(cfg.Servers, + nginxServer{Port: l.Port, Upstream: upstream}, + nginxServer{Port: l.Port, UDP: true, Upstream: upstream}) + default: // TCP, TLS + cfg.Servers = append(cfg.Servers, nginxServer{ + Port: l.Port, + SSL: certPath != "", + CertPath: certPath, + Upstream: upstream, + }) + } + } + + var buf strings.Builder + if err := nginxStreamTmpl.Execute(&buf, cfg); err != nil { + return "", nil, fmt.Errorf("execute nginx stream template: %w", err) + } + return buf.String(), certFiles, nil +} + +// nginxCertPath is the stable absolute PEM path for a TLS listener under the +// agent's nginx cert dir. Daemon-rendered and agent-written paths must match. +func nginxCertPath(lb *LoadBalancerRecord, l *ListenerRecord) string { + return filepath.Join(lbagent.NginxCertDir, fmt.Sprintf("lb-%s-%s.pem", lb.LoadBalancerID, l.ListenerID)) +} + +// buildNLBHealthTargets computes the active health-check targets for an NLB's +// backends. nginx `stream` has no active upstream probing, so the agent probes +// these and reports the results via heartbeat. ServerName matches the health +// checker's key (sanitizeName("srv", target.Id)); the probe port resolves +// "traffic-port" to the target's port. Sorted by server name for deterministic +// delivery. The tgByArn map is the same set used to render the upstreams. +func buildNLBHealthTargets(tgByArn map[string]*TargetGroupRecord) []HealthTargetSpec { + var out []HealthTargetSpec + for _, tg := range tgByArn { + for _, t := range tg.Targets { + if t.PrivateIP == "" || t.HealthState == TargetHealthDraining { + continue + } + out = append(out, HealthTargetSpec{ + ServerName: sanitizeName("srv", t.Id), + Address: fmt.Sprintf("%s:%d", t.PrivateIP, nlbHealthCheckPort(tg, t)), + Protocol: nlbHealthCheckProtocol(tg), + Path: tg.HealthCheck.Path, + }) + } + } + sort.Slice(out, func(i, j int) bool { return out[i].ServerName < out[j].ServerName }) + return out +} + +// nlbHealthCheckProtocol returns the target group's health-check protocol, +// defaulting to the NLB default (TCP) when unset. +func nlbHealthCheckProtocol(tg *TargetGroupRecord) string { + if tg.HealthCheck.Protocol != "" { + return tg.HealthCheck.Protocol + } + return DefaultNLBHealthCheckProtocol +} + +// nlbHealthCheckPort resolves the probe port: an explicit numeric health-check +// port wins, otherwise "traffic-port" maps to the target's port (or the target +// group's default port when the target has none). +func nlbHealthCheckPort(tg *TargetGroupRecord, t Target) int64 { + if p := tg.HealthCheck.Port; p != "" && p != DefaultHealthCheckPort { + if n, err := strconv.ParseInt(p, 10, 64); err == nil { + return n + } + } + if t.Port != 0 { + return t.Port + } + return tg.Port +} diff --git a/spinifex/handlers/elbv2/nginx_test.go b/spinifex/handlers/elbv2/nginx_test.go new file mode 100644 index 000000000..ee4ec13bb --- /dev/null +++ b/spinifex/handlers/elbv2/nginx_test.go @@ -0,0 +1,188 @@ +package handlers_elbv2 + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func nlbHealthCheck() HealthCheckConfig { + return HealthCheckConfig{ + Protocol: ProtocolTCP, + Port: "traffic-port", + IntervalSeconds: 30, + TimeoutSeconds: 10, + HealthyThreshold: 3, + UnhealthyThreshold: 3, + } +} + +func tcpListener(arn, listenerID, protocol string, port int64, tgArn string) *ListenerRecord { + return &ListenerRecord{ + ListenerArn: arn, + ListenerID: listenerID, + Protocol: protocol, + Port: port, + DefaultActions: []ListenerAction{ + {Type: ActionTypeForward, TargetGroupArn: tgArn}, + }, + } +} + +func nlbTG(arn string, targets ...Target) *TargetGroupRecord { + return &TargetGroupRecord{ + TargetGroupArn: arn, + Protocol: ProtocolTCP, + HealthCheck: nlbHealthCheck(), + Targets: targets, + } +} + +func TestGenerateNLBStream_TCP(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-tcp", Type: LoadBalancerTypeNetwork} + tgArn := "arn:aws:elasticloadbalancing:us-east-1:1:targetgroup/db/tg-db" + listeners := []*ListenerRecord{tcpListener("arn:lst-tcp", "lst-tcp", ProtocolTCP, 5432, tgArn)} + tgByArn := map[string]*TargetGroupRecord{ + tgArn: nlbTG(tgArn, + Target{Id: "i-1", Port: 5432, PrivateIP: "10.0.1.10", HealthState: TargetHealthHealthy}, + Target{Id: "i-2", Port: 5432, PrivateIP: "10.0.1.11", HealthState: TargetHealthHealthy}, + ), + } + + config, certs, err := GenerateHAProxyConfigWithCerts(lb, listeners, tgByArn, nil, "10.0.1.5", nil) + require.NoError(t, err) + assert.Empty(t, certs) + + assert.Contains(t, config, "load_module /usr/lib/nginx/modules/ngx_stream_module.so;") // stream is a separate Alpine module + assert.Contains(t, config, "stream {") + assert.NotContains(t, config, "mode tcp") // not HAProxy + assert.Contains(t, config, "listen 5432;") + assert.NotContains(t, config, "listen 5432 udp") + assert.Contains(t, config, "server 10.0.1.10:5432 max_fails=3 fail_timeout=30s;") + assert.Contains(t, config, "server 10.0.1.11:5432 max_fails=3 fail_timeout=30s;") + assert.Contains(t, config, "proxy_pass us_tg-db;") +} + +func TestGenerateNLBStream_UDP(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-udp", Type: LoadBalancerTypeNetwork} + tgArn := "arn:tg-dns" + listeners := []*ListenerRecord{tcpListener("arn:lst-udp", "lst-udp", ProtocolUDP, 53, tgArn)} + tgByArn := map[string]*TargetGroupRecord{ + tgArn: nlbTG(tgArn, Target{Id: "i-dns", Port: 53, PrivateIP: "10.0.2.10", HealthState: TargetHealthHealthy}), + } + + config, _, err := GenerateHAProxyConfigWithCerts(lb, listeners, tgByArn, nil, "10.0.2.1", nil) + require.NoError(t, err) + + assert.Contains(t, config, "listen 53 udp;") + assert.Contains(t, config, "proxy_responses 1;") + assert.Contains(t, config, "10.0.2.10:53") +} + +func TestGenerateNLBStream_TLS(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-tls", Type: LoadBalancerTypeNetwork} + tgArn := "arn:tg-tls" + lst := tcpListener("arn:lst-tls", "lst-tls", ProtocolTLS, 443, tgArn) + lst.Certificates = []ListenerCertificate{{CertificateArn: "arn:cert-1", IsDefault: true}} + tgByArn := map[string]*TargetGroupRecord{ + tgArn: nlbTG(tgArn, Target{Id: "i-tls", Port: 8443, PrivateIP: "10.0.3.10", HealthState: TargetHealthHealthy}), + } + certPEM := map[string]string{"arn:cert-1": "LEAF\nCHAIN\nKEY\n"} + + config, certs, err := GenerateHAProxyConfigWithCerts(lb, []*ListenerRecord{lst}, tgByArn, nil, "10.0.3.1", certPEM) + require.NoError(t, err) + + wantPath := "/etc/nginx/certs/lb-lb-tls-lst-tls.pem" + assert.Contains(t, config, "listen 443 ssl;") + assert.Contains(t, config, "ssl_certificate "+wantPath+";") + assert.Contains(t, config, "ssl_certificate_key "+wantPath+";") + require.Contains(t, certs, wantPath) + assert.Equal(t, "LEAF\nCHAIN\nKEY\n", certs[wantPath]) +} + +func TestGenerateNLBStream_TCPUDP(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-both", Type: LoadBalancerTypeNetwork} + tgArn := "arn:tg-both" + listeners := []*ListenerRecord{tcpListener("arn:lst-both", "lst-both", ProtocolTCPUDP, 5000, tgArn)} + tgByArn := map[string]*TargetGroupRecord{ + tgArn: nlbTG(tgArn, Target{Id: "i-both", Port: 5000, PrivateIP: "10.0.4.10", HealthState: TargetHealthHealthy}), + } + + config, _, err := GenerateHAProxyConfigWithCerts(lb, listeners, tgByArn, nil, "10.0.4.1", nil) + require.NoError(t, err) + + // Two server blocks on the same port: one TCP, one UDP. + assert.Contains(t, config, "listen 5000;") + assert.Contains(t, config, "listen 5000 udp;") + assert.Equal(t, 2, strings.Count(config, "listen 5000")) +} + +func TestGenerateNLBStream_SkipsDrainingAndNoIP(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-drain", Type: LoadBalancerTypeNetwork} + tgArn := "arn:tg-drain" + listeners := []*ListenerRecord{tcpListener("arn:lst-drain", "lst-drain", ProtocolTCP, 3306, tgArn)} + tgByArn := map[string]*TargetGroupRecord{ + tgArn: nlbTG(tgArn, + Target{Id: "i-active", Port: 3306, PrivateIP: "10.0.0.1", HealthState: TargetHealthHealthy}, + Target{Id: "i-drain", Port: 3306, PrivateIP: "10.0.0.2", HealthState: TargetHealthDraining}, + Target{Id: "i-no-ip", Port: 3306, PrivateIP: "", HealthState: TargetHealthInitial}, + ), + } + + config, _, err := GenerateHAProxyConfigWithCerts(lb, listeners, tgByArn, nil, "0.0.0.0", nil) + require.NoError(t, err) + + assert.Contains(t, config, "10.0.0.1:3306") + assert.NotContains(t, config, "10.0.0.2") +} + +func TestGenerateNLBStream_EmptyUpstreamPlaceholder(t *testing.T) { + lb := &LoadBalancerRecord{LoadBalancerID: "lb-empty", Type: LoadBalancerTypeNetwork} + tgArn := "arn:tg-empty" + listeners := []*ListenerRecord{tcpListener("arn:lst-empty", "lst-empty", ProtocolTCP, 80, tgArn)} + tgByArn := map[string]*TargetGroupRecord{tgArn: nlbTG(tgArn)} + + config, _, err := GenerateHAProxyConfigWithCerts(lb, listeners, tgByArn, nil, "0.0.0.0", nil) + require.NoError(t, err) + + // nginx requires >=1 server per upstream; an empty TG renders a down placeholder. + assert.Contains(t, config, "server 127.0.0.1:1 down;") +} + +func TestBuildNLBHealthTargets_Resolution(t *testing.T) { + tgArn := "arn:aws:elasticloadbalancing:us-east-1:1:targetgroup/db/tg-db" + tgByArn := map[string]*TargetGroupRecord{ + tgArn: nlbTG(tgArn, + Target{Id: "i-2", Port: 5432, PrivateIP: "10.0.1.11", HealthState: TargetHealthHealthy}, + Target{Id: "i-1", Port: 5432, PrivateIP: "10.0.1.10", HealthState: TargetHealthHealthy}, + Target{Id: "i-drain", Port: 5432, PrivateIP: "10.0.1.12", HealthState: TargetHealthDraining}, + Target{Id: "i-no-ip", Port: 5432, PrivateIP: "", HealthState: TargetHealthInitial}, + ), + } + + got := buildNLBHealthTargets(tgByArn) + require.Len(t, got, 2) // draining + no-IP excluded + + // Sorted by server name for deterministic delivery. + assert.Equal(t, sanitizeName("srv", "i-1"), got[0].ServerName) + assert.Equal(t, sanitizeName("srv", "i-2"), got[1].ServerName) + assert.Equal(t, "10.0.1.10:5432", got[0].Address) // traffic-port -> target port + assert.Equal(t, ProtocolTCP, got[0].Protocol) +} + +func TestBuildNLBHealthTargets_ExplicitHCPortAndHTTP(t *testing.T) { + tgArn := "arn:tg-http" + tg := nlbTG(tgArn, Target{Id: "i-1", Port: 8080, PrivateIP: "10.0.0.9", HealthState: TargetHealthHealthy}) + tg.HealthCheck.Protocol = ProtocolHTTP + tg.HealthCheck.Port = "9000" + tg.HealthCheck.Path = "/healthz" + tgByArn := map[string]*TargetGroupRecord{tgArn: tg} + + got := buildNLBHealthTargets(tgByArn) + require.Len(t, got, 1) + assert.Equal(t, "10.0.0.9:9000", got[0].Address) // explicit numeric HC port wins + assert.Equal(t, ProtocolHTTP, got[0].Protocol) + assert.Equal(t, "/healthz", got[0].Path) +} diff --git a/spinifex/handlers/elbv2/service.go b/spinifex/handlers/elbv2/service.go index 4a6276c46..9492e1eab 100644 --- a/spinifex/handlers/elbv2/service.go +++ b/spinifex/handlers/elbv2/service.go @@ -9,6 +9,9 @@ type ELBv2Service interface { DescribeLoadBalancers(input *elbv2.DescribeLoadBalancersInput, accountID string) (*elbv2.DescribeLoadBalancersOutput, error) ModifyLoadBalancerAttributes(input *elbv2.ModifyLoadBalancerAttributesInput, accountID string) (*elbv2.ModifyLoadBalancerAttributesOutput, error) DescribeLoadBalancerAttributes(input *elbv2.DescribeLoadBalancerAttributesInput, accountID string) (*elbv2.DescribeLoadBalancerAttributesOutput, error) + SetSecurityGroups(input *elbv2.SetSecurityGroupsInput, accountID string) (*elbv2.SetSecurityGroupsOutput, error) + SetIpAddressType(input *elbv2.SetIpAddressTypeInput, accountID string) (*elbv2.SetIpAddressTypeOutput, error) + SetSubnets(input *elbv2.SetSubnetsInput, accountID string) (*elbv2.SetSubnetsOutput, error) CreateTargetGroup(input *elbv2.CreateTargetGroupInput, accountID string) (*elbv2.CreateTargetGroupOutput, error) ModifyTargetGroup(input *elbv2.ModifyTargetGroupInput, accountID string) (*elbv2.ModifyTargetGroupOutput, error) @@ -26,6 +29,11 @@ type ELBv2Service interface { ModifyListener(input *elbv2.ModifyListenerInput, accountID string) (*elbv2.ModifyListenerOutput, error) DescribeListeners(input *elbv2.DescribeListenersInput, accountID string) (*elbv2.DescribeListenersOutput, error) + AddListenerCertificates(input *elbv2.AddListenerCertificatesInput, accountID string) (*elbv2.AddListenerCertificatesOutput, error) + RemoveListenerCertificates(input *elbv2.RemoveListenerCertificatesInput, accountID string) (*elbv2.RemoveListenerCertificatesOutput, error) + DescribeListenerCertificates(input *elbv2.DescribeListenerCertificatesInput, accountID string) (*elbv2.DescribeListenerCertificatesOutput, error) + DescribeSSLPolicies(input *elbv2.DescribeSSLPoliciesInput, accountID string) (*elbv2.DescribeSSLPoliciesOutput, error) + CreateRule(input *elbv2.CreateRuleInput, accountID string) (*elbv2.CreateRuleOutput, error) ModifyRule(input *elbv2.ModifyRuleInput, accountID string) (*elbv2.ModifyRuleOutput, error) DeleteRule(input *elbv2.DeleteRuleInput, accountID string) (*elbv2.DeleteRuleOutput, error) @@ -33,6 +41,8 @@ type ELBv2Service interface { SetRulePriorities(input *elbv2.SetRulePrioritiesInput, accountID string) (*elbv2.SetRulePrioritiesOutput, error) DescribeTags(input *elbv2.DescribeTagsInput, accountID string) (*elbv2.DescribeTagsOutput, error) + AddTags(input *elbv2.AddTagsInput, accountID string) (*elbv2.AddTagsOutput, error) + RemoveTags(input *elbv2.RemoveTagsInput, accountID string) (*elbv2.RemoveTagsOutput, error) LBAgentHeartbeat(input *LBAgentHeartbeatInput, accountID string) (*LBAgentHeartbeatOutput, error) GetLBConfig(input *GetLBConfigInput, accountID string) (*GetLBConfigOutput, error) diff --git a/spinifex/handlers/elbv2/service_impl.go b/spinifex/handlers/elbv2/service_impl.go index 8491c62a4..ed4bb1e5a 100644 --- a/spinifex/handlers/elbv2/service_impl.go +++ b/spinifex/handlers/elbv2/service_impl.go @@ -3,13 +3,14 @@ package handlers_elbv2 import ( "context" "crypto/sha256" + "encoding/hex" "errors" "fmt" "log/slog" - "maps" "net" "regexp" "slices" + "sort" "strings" "sync" "time" @@ -19,6 +20,7 @@ import ( "github.com/aws/aws-sdk-go/service/elbv2" "github.com/mulgadc/spinifex/spinifex/awserrors" "github.com/mulgadc/spinifex/spinifex/config" + handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" "github.com/mulgadc/spinifex/spinifex/tags" "github.com/mulgadc/spinifex/spinifex/utils" @@ -87,6 +89,13 @@ func (s *ELBv2ServiceImpl) resolveMgmtRoute(scheme string) (gateway, target stri if scheme == SchemeInternal && s.MgmtBridgeIP != "" && s.AdvertiseIP != "" { return s.MgmtBridgeIP, s.AdvertiseIP } + if scheme == SchemeInternal { + slog.Warn("resolveMgmtRoute: internal LB has no mgmt return route; lb-agent cannot reach AWSGW and the LB will stay provisioning", + "mgmtRouteGateway", s.MgmtRouteGateway, + "mgmtRouteTarget", s.MgmtRouteTarget, + "mgmtBridgeIP", s.MgmtBridgeIP, + "advertiseIP", s.AdvertiseIP) + } return "", "" } @@ -97,6 +106,7 @@ var _ ELBv2Service = (*ELBv2ServiceImpl)(nil) type ELBv2ServiceImpl struct { config *config.Config store *Store + acmStore *handlers_acm.Store // resolves listener cert ARNs → PEM; nil-safe (HTTPS unavailable when nil) nc *nats.Conn // NATS connection for JetStream KV store VPCService *handlers_ec2_vpc.VPCServiceImpl // nil-safe: ENI ops skipped when nil (e.g. in tests) InstanceLauncher SystemInstanceLauncher // nil-safe: system VM ops skipped when nil @@ -129,6 +139,16 @@ func NewELBv2ServiceImplWithNATS(cfg *config.Config, nc *nats.Conn) (*ELBv2Servi return nil, fmt.Errorf("failed to create ELBv2 store: %w", err) } + // ACM store shares the same JetStream KV — read cert PEM directly, no + // cross-service NATS hop. Non-fatal: a failure here only disables HTTPS + // termination (resolveCertPEM returns an error), it must not block the + // daemon from serving ELBv2 control-plane requests. + acmStore, acmErr := handlers_acm.NewStore(nc) + if acmErr != nil { + slog.Warn("ELBv2: ACM store unavailable, HTTPS listeners cannot resolve certs", "err", acmErr) + acmStore = nil + } + region := "us-east-1" nodeID := "" if cfg != nil { @@ -142,14 +162,15 @@ func NewELBv2ServiceImplWithNATS(cfg *config.Config, nc *nats.Conn) (*ELBv2Servi hc := newHealthChecker(store) return &ELBv2ServiceImpl{ - config: cfg, - store: store, - nc: nc, - nodeID: nodeID, - region: region, - ctx: ctx, - cancel: cancel, - hc: hc, + config: cfg, + store: store, + acmStore: acmStore, + nc: nc, + nodeID: nodeID, + region: region, + ctx: ctx, + cancel: cancel, + hc: hc, }, nil } @@ -183,8 +204,6 @@ func (s *ELBv2ServiceImpl) Close() { // heartbeat re-converges. Acceptable trade-off — heartbeats arrive within // one health-check interval (default 5-30s) and round-robin transiently // excluding a backend is safer than serving traffic to a dead one. -// -// Refs mulga-siv-119. func (s *ELBv2ServiceImpl) ResetTargetHealthOnStartup(ctx context.Context) error { if s == nil || s.store == nil { return nil @@ -417,6 +436,81 @@ func buildExtraENIInputs(eniIDs []string, eniDetails map[string]*ec2.NetworkInte return extras } +// lbVMLaunch is the outcome of booting the system VM that backs a load +// balancer. failed is true when the VM could not be launched (missing system +// credentials or a launcher error) so the caller can mark the LB failed. +type lbVMLaunch struct { + instanceID string + vpcIP string + publicIP string + hostPorts map[int]int + failed bool +} + +// launchLBVM boots the system VM backing a load balancer with the given ENI +// set. The first ENI is the primary NIC; any additional ENIs are passed as +// ExtraENIs so the daemon sets up a tap + QEMU NIC per subnet, giving the LB VM +// data-plane presence in every subnet it spans. When no launcher is configured +// the call is a no-op (zero value, failed=false) so launcher-less deployments +// keep the LB Active. Shared by CreateLoadBalancer and the SetSubnets relaunch. +func (s *ELBv2ServiceImpl) launchLBVM(lbID, scheme string, eniIDs, subnets []string, accountID string) lbVMLaunch { + var res lbVMLaunch + if s.InstanceLauncher == nil || len(eniIDs) == 0 || len(subnets) == 0 { + return res + } + + eniDetails := s.describeENIs(eniIDs, accountID) + primary := eniDetails[eniIDs[0]] + primaryIP := "" + primaryMAC := "" + if primary != nil { + primaryIP = aws.StringValue(primary.PrivateIpAddress) + primaryMAC = aws.StringValue(primary.MacAddress) + } + + extraENIInputs := buildExtraENIInputs(eniIDs, eniDetails) + + if s.GatewayURL == "" || s.SystemAccessKey == "" || s.SystemSecretKey == "" { + slog.Error("launchLBVM: system credentials not configured — cannot launch LB VM", "lbId", lbID) + res.failed = true + return res + } + + nics := s.buildMicrovmNICs(primaryIP, primaryMAC, subnets[0], eniIDs[0], scheme, extraENIInputs, accountID) + launchInput := &SystemInstanceInput{ + InstanceType: s.getSystemInstanceType(), + SubnetID: subnets[0], + ENIID: eniIDs[0], + ENIMac: primaryMAC, + ENIIP: primaryIP, + ExtraENIs: extraENIInputs, + Scheme: scheme, + AccountID: accountID, + NICs: nics, + LBAgentEnv: s.buildLBAgentEnv(lbID), + CACert: s.CACert, + } + // Dev-mode only: forward HTTP/HTTPS ports from host for local testing. + // In production (VPC networking), traffic reaches the LB VM's VPC IP directly. + if s.config != nil && s.config.Daemon.DevNetworking { + launchInput.HostfwdPorts = []int{80, 443} + } + + out, launchErr := s.InstanceLauncher.LaunchSystemInstance(launchInput) + if launchErr != nil { + slog.Error("launchLBVM: failed to launch LB VM", "lbId", lbID, "err", launchErr) + res.failed = true + return res + } + + res.instanceID = out.InstanceID + res.vpcIP = out.PrivateIP + res.publicIP = out.PublicIP + res.hostPorts = out.HostfwdMap + slog.Info("launchLBVM: LB VM launched", "lbId", lbID, "instanceId", out.InstanceID, "ip", out.PrivateIP, "publicIp", out.PublicIP, "hostfwd", out.HostfwdMap) + return res +} + // loadRecoveryLBIndex returns the instanceID->LB map, populated lazily on // first successful call. Errors are not cached: a transient JetStream // failure at startup must not condemn every subsequent recovery candidate @@ -580,15 +674,29 @@ func (s *ELBv2ServiceImpl) updateStoredConfig(lb *LoadBalancerRecord) error { } } - configContent, err := GenerateHAProxyConfig(lb, listeners, tgByArn, rulesByListener, bindAddr) + certPEMByArn, err := s.resolveListenerCerts(listeners, lb.AccountID) + if err != nil { + slog.Error("updateStoredConfig: failed to resolve certs", "lbId", lb.LoadBalancerID, "err", err) + return fmt.Errorf("resolve certs: %w", err) + } + + configContent, certFiles, err := GenerateHAProxyConfigWithCerts(lb, listeners, tgByArn, rulesByListener, bindAddr, certPEMByArn) if err != nil { slog.Error("updateStoredConfig: failed to generate config", "lbId", lb.LoadBalancerID, "err", err) return fmt.Errorf("generate config: %w", err) } - hash := fmt.Sprintf("%x", sha256.Sum256([]byte(configContent))) + hash := configCertHash(configContent, certFiles) lb.ConfigText = configContent lb.ConfigHash = hash + lb.CertFiles = certFiles + // NLBs run nginx, which has no active upstream probing — the agent + // probes these targets and reports health. ALBs report via HAProxy stats. + if lb.Type == LoadBalancerTypeNetwork { + lb.HealthTargets = buildNLBHealthTargets(tgByArn) + } else { + lb.HealthTargets = nil + } if err := s.store.PutLoadBalancer(lb); err != nil { slog.Error("updateStoredConfig: failed to persist LB", "lbId", lb.LoadBalancerID, "err", err) @@ -596,10 +704,100 @@ func (s *ELBv2ServiceImpl) updateStoredConfig(lb *LoadBalancerRecord) error { } slog.Info("updateStoredConfig: config stored", - "lbId", lb.LoadBalancerID, "hash", hash[:12], "size", len(configContent)) + "lbId", lb.LoadBalancerID, "hash", hash[:12], "size", len(configContent), "certs", len(certFiles)) return nil } +// resolveListenerCerts resolves every distinct certificate ARN referenced by +// the listeners to its combined PEM via the ACM store. Returns nil when no +// listener carries a certificate (the common HTTP-only case). +func (s *ELBv2ServiceImpl) resolveListenerCerts(listeners []*ListenerRecord, accountID string) (map[string]string, error) { + var out map[string]string + for _, l := range listeners { + for _, c := range l.Certificates { + if _, ok := out[c.CertificateArn]; ok { + continue + } + pem, err := s.resolveCertPEM(c.CertificateArn, accountID) + if err != nil { + return nil, fmt.Errorf("cert %s: %w", c.CertificateArn, err) + } + if out == nil { + out = make(map[string]string) + } + out[c.CertificateArn] = pem + } + } + return out, nil +} + +// resolveCertPEM loads a certificate by ARN from the ACM store and returns its +// combined PEM (leaf + chain + key) in the order HAProxy `ssl crt` expects. +// Ownership is enforced: a cert belonging to another account is treated as +// absent. +func (s *ELBv2ServiceImpl) resolveCertPEM(arn, accountID string) (string, error) { + if s.acmStore == nil { + return "", errors.New(awserrors.ErrorELBv2CertificateNotFound) + } + rec, err := s.acmStore.GetCert(arn) + if err != nil { + return "", fmt.Errorf("get cert: %w", err) + } + if rec == nil || rec.AccountID != accountID { + return "", errors.New(awserrors.ErrorELBv2CertificateNotFound) + } + + var b strings.Builder + b.WriteString(strings.TrimRight(rec.Certificate, "\n")) + b.WriteByte('\n') + if rec.CertificateChain != "" { + b.WriteString(strings.TrimRight(rec.CertificateChain, "\n")) + b.WriteByte('\n') + } + b.WriteString(strings.TrimRight(rec.PrivateKey, "\n")) + b.WriteByte('\n') + return b.String(), nil +} + +// validateListenerCerts confirms every certificate ARN resolves in the ACM +// store and is owned by the account, rejecting an unknown ARN at the API +// boundary (CreateListener/ModifyListener) with ErrorELBv2CertificateNotFound. +// Without this, an unresolvable ARN is accepted into the listener and only +// fails later at config-render time, which aborts updateStoredConfig wholesale +// and silently freezes the LB's data-plane convergence. Skipped when the ACM +// store is unavailable (cannot validate; matches the nil-safe resolve path). +func (s *ELBv2ServiceImpl) validateListenerCerts(certs []ListenerCertificate, accountID string) error { + if s.acmStore == nil { + return nil + } + for _, c := range certs { + if _, err := s.resolveCertPEM(c.CertificateArn, accountID); err != nil { + return errors.New(awserrors.ErrorELBv2CertificateNotFound) + } + } + return nil +} + +// configCertHash hashes the rendered config plus every delivered cert file +// (path + content, in sorted path order) so a cert rotation changes the hash +// and triggers an agent reload even when the config text is unchanged. +func configCertHash(configContent string, certFiles map[string]string) string { + h := sha256.New() + h.Write([]byte(configContent)) + paths := make([]string, 0, len(certFiles)) + for p := range certFiles { + paths = append(paths, p) + } + sort.Strings(paths) + for _, p := range paths { + h.Write([]byte(p)) + h.Write([]byte{0}) + h.Write([]byte(certFiles[p])) + h.Write([]byte{0}) + } + return hex.EncodeToString(h.Sum(nil)) +} + // updateStoredConfigForTargetGroup finds all LBs that reference the given target // group (via listeners) and updates their stored config. func (s *ELBv2ServiceImpl) updateStoredConfigForTargetGroup(tgArn string) error { @@ -661,12 +859,19 @@ func (s *ELBv2ServiceImpl) LBAgentHeartbeat(input *LBAgentHeartbeatInput, accoun } lbID := *input.LBID + slog.Debug("LBAgentHeartbeat received", "lbId", lbID, "accountId", accountID) lb, err := s.store.GetLoadBalancer(lbID) if err != nil { slog.Error("LBAgentHeartbeat: failed to get LB", "lbId", lbID, "err", err) return nil, errors.New(awserrors.ErrorServerInternal) } if lb == nil || (lb.AccountID != accountID && accountID != utils.GlobalAccountID) { + // The heartbeat reached AWSGW (so the mgmt return path works) but + // cannot transition any LB. Without this the reject is silent and a + // stuck-in-provisioning LB looks identical to one whose heartbeat + // never arrived. + slog.Warn("LBAgentHeartbeat: LB not found or account mismatch", + "lbId", lbID, "accountId", accountID, "found", lb != nil) return nil, errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) } @@ -719,11 +924,50 @@ func (s *ELBv2ServiceImpl) GetLBConfig(input *GetLBConfigInput, accountID string } return &GetLBConfigOutput{ - ConfigText: aws.String(lb.ConfigText), - ConfigHash: aws.String(lb.ConfigHash), + ConfigText: aws.String(lb.ConfigText), + ConfigHash: aws.String(lb.ConfigHash), + CertFiles: certFilesToSDK(lb.CertFiles), + Engine: aws.String(engineForType(lb.Type)), + HealthTargets: healthTargetsToSDK(lb.HealthTargets), }, nil } +// healthTargetsToSDK converts the stored health-target specs into the SDK +// HealthTarget slice delivered to the nginx agent. +func healthTargetsToSDK(specs []HealthTargetSpec) []*HealthTarget { + if len(specs) == 0 { + return nil + } + out := make([]*HealthTarget, 0, len(specs)) + for i := range specs { + out = append(out, &HealthTarget{ + ServerName: aws.String(specs[i].ServerName), + Address: aws.String(specs[i].Address), + Protocol: aws.String(specs[i].Protocol), + Path: aws.String(specs[i].Path), + }) + } + return out +} + +// certFilesToSDK converts the stored path→PEM map into the sorted CertFile +// slice delivered to the agent. Sorted for deterministic output. +func certFilesToSDK(certFiles map[string]string) []*CertFile { + if len(certFiles) == 0 { + return nil + } + paths := make([]string, 0, len(certFiles)) + for p := range certFiles { + paths = append(paths, p) + } + sort.Strings(paths) + out := make([]*CertFile, 0, len(paths)) + for _, p := range paths { + out = append(out, &CertFile{Path: aws.String(p), PEM: aws.String(certFiles[p])}) + } + return out +} + // lbArnPathSegment returns the ARN path segment for the given LB type: // "app" for application LBs, "net" for network LBs. func lbArnPathSegment(lbType string) string { @@ -762,6 +1006,28 @@ func (s *ELBv2ServiceImpl) resolveTargetIP(instanceID, accountID string) string return "" } +// resolveRegisteredTargetIP returns the private IP to route to for a target +// being registered, based on the target group's target type. For ip targets the +// supplied ID is the IP itself; for instance targets it is resolved via the +// instance's primary ENI. Returns an error when the ID's shape does not match +// the target type. +func (s *ELBv2ServiceImpl) resolveRegisteredTargetIP(targetType, id, accountID string) (string, error) { + switch targetType { + case TargetTypeIP: + if net.ParseIP(id) == nil { + slog.Warn("RegisterTargets: ip target id is not a valid IP", "id", id) + return "", errors.New(awserrors.ErrorInvalidParameterValue) + } + return id, nil + default: // instance + if net.ParseIP(id) != nil { + slog.Warn("RegisterTargets: instance target group given an IP address", "id", id) + return "", errors.New(awserrors.ErrorInvalidParameterValue) + } + return s.resolveTargetIP(id, accountID), nil + } +} + // buildTGArn constructs a target group ARN from components. func buildTGArn(region, accountID, name, tgID string) string { return fmt.Sprintf("arn:aws:elasticloadbalancing:%s:%s:targetgroup/%s/%s", region, accountID, name, tgID) @@ -777,13 +1043,15 @@ const ( elbv2ResourceLoadBalancer = "loadbalancer" elbv2ResourceTargetGroup = "targetgroup" elbv2ResourceListener = "listener" + elbv2ResourceListenerRule = "listener-rule" ) // elbv2ResourceTypeFromArn extracts the resource type from an ELBv2 ARN. // ELBv2 ARNs have the form // "arn:aws:elasticloadbalancing:{region}:{account}:{type}/...". Returns one -// of "loadbalancer", "targetgroup", "listener" — anything else yields -// ErrorInvalidParameterValue so callers can surface a proper API error. +// of "loadbalancer", "targetgroup", "listener", "listener-rule" — anything +// else yields ErrorInvalidParameterValue so callers can surface a proper API +// error. func elbv2ResourceTypeFromArn(arn string) (string, error) { parts := strings.SplitN(arn, ":", 6) if len(parts) < 6 || parts[0] != "arn" || parts[2] != "elasticloadbalancing" { @@ -796,7 +1064,7 @@ func elbv2ResourceTypeFromArn(arn string) (string, error) { } resourceType := resourceSegment[:slash] switch resourceType { - case elbv2ResourceLoadBalancer, elbv2ResourceTargetGroup, elbv2ResourceListener: + case elbv2ResourceLoadBalancer, elbv2ResourceTargetGroup, elbv2ResourceListener, elbv2ResourceListenerRule: return resourceType, nil default: return "", errors.New(awserrors.ErrorInvalidParameterValue) @@ -887,18 +1155,28 @@ func (s *ELBv2ServiceImpl) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInp } } - tags := make(map[string]string) - for _, tag := range input.Tags { - if tag.Key != nil && tag.Value != nil { - tags[*tag.Key] = *tag.Value - } - } + tags := tagsFromSDK(input.Tags) // Create ENIs in each subnet (when VPC service is available) var eniIDs []string var availZones []AvailZoneInfo vpcID := "" + var nlbManagedSGID string if s.VPCService != nil && len(subnets) > 0 { + // NLBs reject customer SGs (rejected above), so the ENIs would fall back + // to the VPC default SG whose intra-SG-only ingress drops inbound + // listener traffic. Mint a dedicated managed SG up front and attach it to + // every LB ENI; CreateListener opens the listener ports on it. + eniGroups := securityGroups + if lbType == LoadBalancerTypeNetwork { + sgID, sgErr := s.createNLBManagedSG(lbID, lbArn, subnets[0], accountID) + if sgErr != nil { + slog.Error("CreateLoadBalancer: failed to create managed NLB SG", "lbId", lbID, "err", sgErr) + return nil, errors.New(awserrors.ErrorServerInternal) + } + nlbManagedSGID = sgID + eniGroups = []string{sgID} + } for _, subnetID := range subnets { eniIn := &ec2.CreateNetworkInterfaceInput{ SubnetId: aws.String(subnetID), @@ -914,11 +1192,12 @@ func (s *ELBv2ServiceImpl) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInp }, } // SG enforcement gates inbound traffic by the ENI's port-group - // membership, which is set at ENI creation. Empty Groups - // intentionally falls back to the VPC default SG inside + // membership, which is set at ENI creation. For NLBs this is the + // managed SG minted above; for ALBs it is the customer SGs. Empty + // Groups intentionally falls back to the VPC default SG inside // eni.CreateNetworkInterface. - if len(securityGroups) > 0 { - eniIn.Groups = aws.StringSlice(securityGroups) + if len(eniGroups) > 0 { + eniIn.Groups = aws.StringSlice(eniGroups) } eniOut, eniErr := s.VPCService.CreateNetworkInterface(eniIn, accountID) if eniErr != nil { @@ -930,6 +1209,7 @@ func (s *ELBv2ServiceImpl) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInp slog.Error("CreateLoadBalancer: rollback failed to delete ENI", "eni", rollbackENI, "err", delErr) } } + s.deleteNLBManagedSG(nlbManagedSGID, accountID) slog.Error("CreateLoadBalancer: failed to create ENI", "subnet", subnetID, "err", eniErr) return nil, errors.New(awserrors.ErrorELBv2SubnetNotFound) } @@ -955,64 +1235,14 @@ func (s *ELBv2ServiceImpl) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInp // The first ENI is the primary NIC; any additional ENIs are passed as // ExtraENIs so the daemon sets up a tap + QEMU NIC for each, giving the // ALB VM data-plane presence in every subnet the LB spans. - var albInstanceID string - var albVPCIP string - var hostPorts map[int]int - var launchFailed bool - if s.InstanceLauncher != nil && len(eniIDs) > 0 && len(subnets) > 0 { - eniDetails := s.describeENIs(eniIDs, accountID) - - primary := eniDetails[eniIDs[0]] - primaryIP := "" - primaryMAC := "" - if primary != nil { - primaryIP = aws.StringValue(primary.PrivateIpAddress) - primaryMAC = aws.StringValue(primary.MacAddress) - } - - extraENIInputs := buildExtraENIInputs(eniIDs, eniDetails) - - var launchInput *SystemInstanceInput - if s.GatewayURL == "" || s.SystemAccessKey == "" || s.SystemSecretKey == "" { - slog.Error("CreateLoadBalancer: system credentials not configured — cannot launch ALB VM", "lbId", lbID) - launchFailed = true - } else { - nics := s.buildMicrovmNICs(primaryIP, primaryMAC, subnets[0], eniIDs[0], scheme, extraENIInputs, accountID) - launchInput = &SystemInstanceInput{ - InstanceType: s.getSystemInstanceType(), - SubnetID: subnets[0], - ENIID: eniIDs[0], - ENIMac: primaryMAC, - ENIIP: primaryIP, - ExtraENIs: extraENIInputs, - Scheme: scheme, - AccountID: accountID, - NICs: nics, - LBAgentEnv: s.buildLBAgentEnv(lbID), - CACert: s.CACert, - } - } - if !launchFailed && launchInput != nil { - // Dev-mode only: forward HTTP/HTTPS ports from host for local testing. - // In production (VPC networking), traffic reaches the ALB VM's VPC IP directly. - if s.config != nil && s.config.Daemon.DevNetworking { - launchInput.HostfwdPorts = []int{80, 443} - } - out, launchErr := s.InstanceLauncher.LaunchSystemInstance(launchInput) - if launchErr != nil { - slog.Error("CreateLoadBalancer: failed to launch ALB VM", "lbId", lbID, "err", launchErr) - launchFailed = true - } else { - albInstanceID = out.InstanceID - albVPCIP = out.PrivateIP - hostPorts = out.HostfwdMap - // Record public IP on the first AZ entry for internet-facing ALBs. - if out.PublicIP != "" && len(availZones) > 0 { - availZones[0].PublicIP = out.PublicIP - } - slog.Info("CreateLoadBalancer: ALB VM launched", "lbId", lbID, "instanceId", albInstanceID, "ip", out.PrivateIP, "publicIp", out.PublicIP, "hostfwd", hostPorts) - } - } + launch := s.launchLBVM(lbID, scheme, eniIDs, subnets, accountID) + albInstanceID := launch.instanceID + albVPCIP := launch.vpcIP + hostPorts := launch.hostPorts + launchFailed := launch.failed + // Record public IP on the first AZ entry for internet-facing LBs. + if launch.publicIP != "" && len(availZones) > 0 { + availZones[0].PublicIP = launch.publicIP } // ALB starts in provisioning until the agent inside the VM connects and @@ -1021,6 +1251,19 @@ func (s *ELBv2ServiceImpl) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInp state := StateActive if albInstanceID != "" { state = StateProvisioning + // Internal LBs only leave provisioning once the lb-agent heartbeats, + // which needs the mgmt return route. If it can't be resolved the LB + // hangs in provisioning forever — fail loud instead so the broken + // return path is visible immediately rather than as a generic timeout. + if scheme == SchemeInternal { + if gw, tgt := s.resolveMgmtRoute(scheme); gw == "" || tgt == "" { + slog.Error("CreateLoadBalancer: internal LB has no mgmt return route; marking failed (lb-agent cannot heartbeat AWSGW)", + "lbId", lbID, + "mgmtBridgeIP", s.MgmtBridgeIP, + "advertiseIP", s.AdvertiseIP) + state = StateFailed + } + } } else if launchFailed { state = StateFailed } @@ -1038,6 +1281,7 @@ func (s *ELBv2ServiceImpl) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInp State: state, VpcId: vpcID, SecurityGroups: securityGroups, + NLBManagedSGID: nlbManagedSGID, Subnets: subnets, AvailZones: availZones, ENIs: eniIDs, @@ -1076,7 +1320,27 @@ func (s *ELBv2ServiceImpl) DeleteLoadBalancer(input *elbv2.DeleteLoadBalancerInp return nil, errors.New(awserrors.ErrorServerInternal) } if lb == nil || lb.AccountID != accountID { - return nil, errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) + // Idempotent: AWS ELBv2 delete returns success on an absent (or + // not-owned) load balancer, so tofu destroy retries converge. + return &elbv2.DeleteLoadBalancerOutput{}, nil + } + + // Delete all listeners (and their rules) for this LB before tearing down + // backing artifacts. Cascade through the shared helper, not + // store.DeleteListener directly, so rules don't survive and pin their + // target groups as ResourceInUse after the LB is gone. A list or cascade + // failure aborts the delete with the record intact, so tofu retries + // converge instead of leaving orphaned rules behind. + listeners, err := s.store.ListListenersByLB(lb.LoadBalancerArn) + if err != nil { + slog.Error("DeleteLoadBalancer: failed to list listeners for cascade", "lbArn", lb.LoadBalancerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + for _, l := range listeners { + if err := s.deleteListenerCascade(l); err != nil { + slog.Error("DeleteLoadBalancer: failed to cascade listener delete", "listenerID", l.ListenerID, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } } // Terminate ALB VM in background (VM termination also cleans up tap device). @@ -1091,17 +1355,6 @@ func (s *ELBv2ServiceImpl) DeleteLoadBalancer(input *elbv2.DeleteLoadBalancerInp }() } - // Delete all listeners for this LB - listeners, err := s.store.ListListenersByLB(lb.LoadBalancerArn) - if err != nil { - slog.Warn("Failed to list listeners for cleanup", "lbArn", lb.LoadBalancerArn, "err", err) - } - for _, l := range listeners { - if err := s.store.DeleteListener(l.ListenerID); err != nil { - slog.Warn("Failed to delete listener during LB cleanup", "listenerID", l.ListenerID, "err", err) - } - } - // Delete system-managed ENIs. Detach first to clear in-use status. if s.VPCService != nil { for _, eniID := range lb.ENIs { @@ -1114,6 +1367,8 @@ func (s *ELBv2ServiceImpl) DeleteLoadBalancer(input *elbv2.DeleteLoadBalancerInp slog.Warn("Failed to delete ALB ENI during cleanup", "eniId", eniID, "err", eniErr) } } + // The managed NLB SG can only be removed once its ENIs are gone. + s.deleteNLBManagedSG(lb.NLBManagedSGID, accountID) } if err := s.store.DeleteLoadBalancer(lb.LoadBalancerID); err != nil { @@ -1208,10 +1463,14 @@ func (s *ELBv2ServiceImpl) CreateTargetGroup(input *elbv2.CreateTargetGroupInput return nil, errors.New(awserrors.ErrorELBv2DuplicateTargetGroup) } - targetType := "instance" + targetType := TargetTypeInstance if input.TargetType != nil && *input.TargetType != "" { targetType = *input.TargetType } + if targetType != TargetTypeInstance && targetType != TargetTypeIP { + slog.Warn("CreateTargetGroup: unsupported target type", "targetType", targetType) + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } // Use NLB health check defaults for NLB protocols. var hc HealthCheckConfig @@ -1258,12 +1517,7 @@ func (s *ELBv2ServiceImpl) CreateTargetGroup(input *elbv2.CreateTargetGroupInput tgID := utils.GenerateResourceID("tg") tgArn := buildTGArn(s.region, accountID, name, tgID) - tags := make(map[string]string) - for _, tag := range input.Tags { - if tag.Key != nil && tag.Value != nil { - tags[*tag.Key] = *tag.Value - } - } + tags := tagsFromSDK(input.Tags) record := &TargetGroupRecord{ TargetGroupArn: tgArn, @@ -1364,16 +1618,37 @@ func (s *ELBv2ServiceImpl) DeleteTargetGroup(input *elbv2.DeleteTargetGroupInput return nil, errors.New(awserrors.ErrorServerInternal) } if tg == nil || tg.AccountID != accountID { - return nil, errors.New(awserrors.ErrorELBv2TargetGroupNotFound) + // Idempotent: AWS ELBv2 delete returns success on an absent (or + // not-owned) target group, so tofu destroy retries converge. + return &elbv2.DeleteTargetGroupOutput{}, nil } - // Check if any listener references this target group + // Only a *live* listener or rule pins the target group. A listener (or + // rule) whose owning load balancer no longer exists is treated as already + // torn down, not a live reference, so an orphan left by a partial teardown + // can never pin this target group as ResourceInUse permanently. + lbs, err := s.store.ListLoadBalancers() + if err != nil { + slog.Error("DeleteTargetGroup: failed to list load balancers for in-use check", "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + liveLB := make(map[string]bool, len(lbs)) + for _, lb := range lbs { + liveLB[lb.LoadBalancerArn] = true + } + + // Check if any live listener references this target group. listeners, err := s.store.ListListeners() if err != nil { slog.Error("DeleteTargetGroup: failed to list listeners for in-use check", "err", err) return nil, errors.New(awserrors.ErrorServerInternal) } + liveListener := make(map[string]bool, len(listeners)) for _, l := range listeners { + if !liveLB[l.LoadBalancerArn] { + continue + } + liveListener[l.ListenerArn] = true for _, action := range l.DefaultActions { if action.TargetGroupArn == tg.TargetGroupArn { return nil, errors.New(awserrors.ErrorELBv2TargetGroupInUse) @@ -1381,13 +1656,16 @@ func (s *ELBv2ServiceImpl) DeleteTargetGroup(input *elbv2.DeleteTargetGroupInput } } - // Block deletion when a rule still forwards to the target group. + // Block deletion when a live rule still forwards to the target group. allRules, err := s.store.ListRules() if err != nil { slog.Error("DeleteTargetGroup: failed to list rules for in-use check", "err", err) return nil, errors.New(awserrors.ErrorServerInternal) } for _, r := range allRules { + if !liveListener[r.ListenerArn] { + continue + } for _, action := range r.Actions { if action.TargetGroupArn == tg.TargetGroupArn { return nil, errors.New(awserrors.ErrorELBv2TargetGroupInUse) @@ -1496,8 +1774,11 @@ func (s *ELBv2ServiceImpl) RegisterTargets(input *elbv2.RegisterTargetsInput, ac continue // Already registered } - // Resolve instance ID → private IP via ENI lookup - privateIP := s.resolveTargetIP(*td.Id, accountID) + // Resolve target ID → private IP (instance ENI lookup, or raw IP for ip-type TGs) + privateIP, err := s.resolveRegisteredTargetIP(tg.TargetType, *td.Id, accountID) + if err != nil { + return nil, err + } tg.Targets = append(tg.Targets, Target{ Id: *td.Id, @@ -1626,6 +1907,145 @@ func (s *ELBv2ServiceImpl) DescribeTargetHealth(input *elbv2.DescribeTargetHealt // --- Listener operations --- +// listenerActionFromSDK converts an SDK listener action into the stored form, +// preserving fixed-response config so HAProxy generation and tofu read-back see +// the full action (a forward-only conversion drops the fixed-response default +// and produces a dangling backend + perpetual plan diff). +func listenerActionFromSDK(a *elbv2.Action) ListenerAction { + action := ListenerAction{} + if a.Type != nil { + action.Type = *a.Type + } + if a.TargetGroupArn != nil { + action.TargetGroupArn = *a.TargetGroupArn + } + if a.FixedResponseConfig != nil { + fr := &FixedResponseAction{} + if a.FixedResponseConfig.StatusCode != nil { + fr.StatusCode = *a.FixedResponseConfig.StatusCode + } + if a.FixedResponseConfig.ContentType != nil { + fr.ContentType = *a.FixedResponseConfig.ContentType + } + if a.FixedResponseConfig.MessageBody != nil { + fr.MessageBody = *a.FixedResponseConfig.MessageBody + } + action.FixedResponse = fr + } + if a.RedirectConfig != nil { + rd := &RedirectAction{} + if a.RedirectConfig.Protocol != nil { + rd.Protocol = *a.RedirectConfig.Protocol + } + if a.RedirectConfig.Host != nil { + rd.Host = *a.RedirectConfig.Host + } + if a.RedirectConfig.Port != nil { + rd.Port = *a.RedirectConfig.Port + } + if a.RedirectConfig.Path != nil { + rd.Path = *a.RedirectConfig.Path + } + if a.RedirectConfig.Query != nil { + rd.Query = *a.RedirectConfig.Query + } + if a.RedirectConfig.StatusCode != nil { + rd.StatusCode = *a.RedirectConfig.StatusCode + } + action.Redirect = rd + } + return action +} + +// validateListenerAction enforces the per-type action contract for listener +// default actions and rule actions. Forward actions are validated against the +// target group elsewhere; this covers redirect (status code + render-safe +// fields) so invalid input fails at the API instead of being silently +// defaulted by the renderer. +func validateListenerAction(a ListenerAction) error { + if a.Type == ActionTypeRedirect { + if a.Redirect == nil { + return errors.New(awserrors.ErrorMissingParameter) + } + return validateRedirectAction(a.Redirect) + } + return nil +} + +// validateRedirectAction rejects an unsupported status code or any field that +// would break the HAProxy redirect directive once the known AWS placeholders +// are stripped. +func validateRedirectAction(rd *RedirectAction) error { + if rd == nil { + return errors.New(awserrors.ErrorMissingParameter) + } + switch rd.StatusCode { + case "HTTP_301", "HTTP_302": + default: + return errors.New(awserrors.ErrorInvalidParameterValue) + } + for _, f := range []string{rd.Protocol, rd.Host, rd.Port, rd.Path, rd.Query} { + if !validRedirectField(f) { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + } + return nil +} + +// buildListenerCertificates validates and normalises the certificate set and +// SSL policy for a listener of the given protocol. Secure protocols (HTTPS, +// TLS) require at least one certificate and receive a default SslPolicy when +// unset; non-secure protocols must carry neither certificates nor an SslPolicy. +// Exactly one certificate is marked default. +func buildListenerCertificates(protocol string, in []*elbv2.Certificate, sslPolicy *string) ([]ListenerCertificate, string, error) { + policy := "" + if sslPolicy != nil { + policy = *sslPolicy + } + + if !protocolRequiresCert(protocol) { + if len(in) > 0 || policy != "" { + return nil, "", errors.New(awserrors.ErrorInvalidParameterValue) + } + return nil, "", nil + } + + if len(in) == 0 { + return nil, "", errors.New(awserrors.ErrorELBv2CertificateNotFound) + } + + certs := make([]ListenerCertificate, 0, len(in)) + defaults := 0 + for _, c := range in { + if c == nil || c.CertificateArn == nil || *c.CertificateArn == "" { + return nil, "", errors.New(awserrors.ErrorInvalidParameterValue) + } + isDefault := c.IsDefault != nil && *c.IsDefault + if isDefault { + defaults++ + } + certs = append(certs, ListenerCertificate{ + CertificateArn: *c.CertificateArn, + IsDefault: isDefault, + }) + } + switch defaults { + case 0: + certs[0].IsDefault = true + case 1: + default: + return nil, "", errors.New(awserrors.ErrorInvalidParameterValue) + } + + if policy == "" { + policy = DefaultSslPolicy + } else if !isKnownSslPolicy(policy) { + return nil, "", errors.New(awserrors.ErrorELBv2SSLPolicyNotFound) + } + + return certs, policy, nil +} + func (s *ELBv2ServiceImpl) CreateListener(input *elbv2.CreateListenerInput, accountID string) (*elbv2.CreateListenerOutput, error) { if input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { return nil, errors.New(awserrors.ErrorMissingParameter) @@ -1700,21 +2120,28 @@ func (s *ELBv2ServiceImpl) CreateListener(input *elbv2.CreateListenerInput, acco } } + certs, sslPolicy, err := buildListenerCertificates(protocol, input.Certificates, input.SslPolicy) + if err != nil { + return nil, err + } + if err := s.validateListenerCerts(certs, accountID); err != nil { + return nil, err + } + listenerID := utils.GenerateResourceID("lst") listenerArn := buildListenerArn(s.region, accountID, lb.Name, lb.LoadBalancerID, listenerID, lb.Type) var actions []ListenerAction for _, a := range input.DefaultActions { - action := ListenerAction{} - if a.Type != nil { - action.Type = *a.Type - } - if a.TargetGroupArn != nil { - action.TargetGroupArn = *a.TargetGroupArn + action := listenerActionFromSDK(a) + if err := validateListenerAction(action); err != nil { + return nil, err } actions = append(actions, action) } + tags := tagsFromSDK(input.Tags) + record := &ListenerRecord{ ListenerArn: listenerArn, ListenerID: listenerID, @@ -1724,6 +2151,9 @@ func (s *ELBv2ServiceImpl) CreateListener(input *elbv2.CreateListenerInput, acco DefaultActions: actions, AccountID: accountID, CreatedAt: time.Now().UTC(), + Certificates: certs, + SslPolicy: sslPolicy, + Tags: tags, } if err := s.store.PutListener(record); err != nil { @@ -1731,6 +2161,21 @@ func (s *ELBv2ServiceImpl) CreateListener(input *elbv2.CreateListenerInput, acco return nil, errors.New(awserrors.ErrorServerInternal) } + // Open the listener port on the NLB's managed front-end SG so inbound + // traffic from the configured client CIDRs is admitted by the OVN ACL + // (NLB ENIs would otherwise sit in the intra-SG-only default SG). + if lb.Type == LoadBalancerTypeNetwork && lb.NLBManagedSGID != "" && s.VPCService != nil { + cidrs, cidrErr := s.resolveNLBIngressCIDRs(lb) + if cidrErr != nil { + slog.Error("CreateListener: resolve ingress CIDRs failed", "lbArn", lb.LoadBalancerArn, "err", cidrErr) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if authErr := s.authorizeNLBListenerPort(lb, protocol, port, cidrs, accountID); authErr != nil { + slog.Error("CreateListener: authorize listener port failed", "lbArn", lb.LoadBalancerArn, "port", port, "err", authErr) + return nil, errors.New(awserrors.ErrorServerInternal) + } + } + // Start or reload HAProxy now that a listener exists if err := s.updateStoredConfig(lb); err != nil { slog.Error("CreateListener: failed to update config", "listenerId", listenerID, "err", err) @@ -1744,6 +2189,25 @@ func (s *ELBv2ServiceImpl) CreateListener(input *elbv2.CreateListenerInput, acco }, nil } +// deleteListenerCascade removes a listener and all of its rules. Shared by +// DeleteListener and DeleteLoadBalancer so LB teardown never bypasses the rule +// cascade and leaves orphan rules that pin a target group as ResourceInUse. +func (s *ELBv2ServiceImpl) deleteListenerCascade(listener *ListenerRecord) error { + rules, err := s.store.ListRulesByListener(listener.ListenerArn) + if err != nil { + return fmt.Errorf("list rules for listener %s: %w", listener.ListenerArn, err) + } + for _, r := range rules { + if err := s.store.DeleteRule(r.RuleID); err != nil { + return fmt.Errorf("delete rule %s: %w", r.RuleID, err) + } + } + if err := s.store.DeleteListener(listener.ListenerID); err != nil { + return fmt.Errorf("delete listener %s: %w", listener.ListenerID, err) + } + return nil +} + func (s *ELBv2ServiceImpl) DeleteListener(input *elbv2.DeleteListenerInput, accountID string) (*elbv2.DeleteListenerOutput, error) { if input.ListenerArn == nil || *input.ListenerArn == "" { return nil, errors.New(awserrors.ErrorMissingParameter) @@ -1755,30 +2219,30 @@ func (s *ELBv2ServiceImpl) DeleteListener(input *elbv2.DeleteListenerInput, acco return nil, errors.New(awserrors.ErrorServerInternal) } if listener == nil || listener.AccountID != accountID { - return nil, errors.New(awserrors.ErrorELBv2ListenerNotFound) + // Idempotent: AWS ELBv2 delete returns success on an absent (or + // not-owned) listener, so tofu destroy retries converge. + return &elbv2.DeleteListenerOutput{}, nil } // Cascade-delete rules so a recreated listener doesn't inherit orphans. - rules, ruleErr := s.store.ListRulesByListener(listener.ListenerArn) - if ruleErr != nil { - slog.Error("DeleteListener: failed to list rules", "listenerArn", listener.ListenerArn, "err", ruleErr) - return nil, errors.New(awserrors.ErrorServerInternal) - } - for _, r := range rules { - if err := s.store.DeleteRule(r.RuleID); err != nil { - slog.Error("DeleteListener: failed to delete rule", "ruleId", r.RuleID, "err", err) - return nil, errors.New(awserrors.ErrorServerInternal) - } - } - - if err := s.store.DeleteListener(listener.ListenerID); err != nil { - slog.Error("DeleteListener: failed to delete record", "listenerId", listener.ListenerID, "err", err) + if err := s.deleteListenerCascade(listener); err != nil { + slog.Error("DeleteListener: failed to cascade-delete listener", "listenerArn", listener.ListenerArn, "err", err) return nil, errors.New(awserrors.ErrorServerInternal) } // Reload or stop HAProxy after listener removal lb, lbErr := s.store.GetLoadBalancerByArn(listener.LoadBalancerArn) if lbErr == nil && lb != nil { + // Close the listener port on the NLB's managed front-end SG. + if lb.Type == LoadBalancerTypeNetwork && lb.NLBManagedSGID != "" && s.VPCService != nil { + if cidrs, cidrErr := s.resolveNLBIngressCIDRs(lb); cidrErr == nil { + if revokeErr := s.revokeNLBListenerPort(lb, listener.Protocol, listener.Port, cidrs, accountID); revokeErr != nil { + slog.Warn("DeleteListener: revoke listener port failed", "lbArn", lb.LoadBalancerArn, "port", listener.Port, "err", revokeErr) + } + } else { + slog.Warn("DeleteListener: resolve ingress CIDRs failed", "lbArn", lb.LoadBalancerArn, "err", cidrErr) + } + } if err := s.updateStoredConfig(lb); err != nil { slog.Error("DeleteListener: failed to update config", "listenerArn", *input.ListenerArn, "err", err) return nil, errors.New(awserrors.ErrorServerInternal) @@ -1857,12 +2321,9 @@ func (s *ELBv2ServiceImpl) ModifyListener(input *elbv2.ModifyListenerInput, acco if len(input.DefaultActions) > 0 { var actions []ListenerAction for _, a := range input.DefaultActions { - action := ListenerAction{} - if a.Type != nil { - action.Type = *a.Type - } - if a.TargetGroupArn != nil { - action.TargetGroupArn = *a.TargetGroupArn + action := listenerActionFromSDK(a) + if err := validateListenerAction(action); err != nil { + return nil, err } if action.Type == ActionTypeForward && action.TargetGroupArn != "" { tg, tgErr := s.store.GetTargetGroupByArn(action.TargetGroupArn) @@ -1899,6 +2360,40 @@ func (s *ELBv2ServiceImpl) ModifyListener(input *elbv2.ModifyListenerInput, acco } } + // Certificates / SSL policy. The effective protocol after any change drives + // the requirement: switching to a non-secure protocol clears cert material; + // staying on or switching to a secure protocol validates and defaults it. + if !protocolRequiresCert(updated.Protocol) { + if len(input.Certificates) > 0 || (input.SslPolicy != nil && *input.SslPolicy != "") { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + updated.Certificates = nil + updated.SslPolicy = "" + } else { + inCerts := input.Certificates + if inCerts == nil { + for _, c := range updated.Certificates { + inCerts = append(inCerts, &elbv2.Certificate{ + CertificateArn: aws.String(c.CertificateArn), + IsDefault: aws.Bool(c.IsDefault), + }) + } + } + sslPolicy := input.SslPolicy + if sslPolicy == nil && updated.SslPolicy != "" { + sslPolicy = aws.String(updated.SslPolicy) + } + certs, policy, certErr := buildListenerCertificates(updated.Protocol, inCerts, sslPolicy) + if certErr != nil { + return nil, certErr + } + if certErr := s.validateListenerCerts(certs, accountID); certErr != nil { + return nil, certErr + } + updated.Certificates = certs + updated.SslPolicy = policy + } + if err := s.store.PutListener(&updated); err != nil { slog.Error("ModifyListener: failed to persist record", "listenerId", updated.ListenerID, "err", err) return nil, errors.New(awserrors.ErrorServerInternal) @@ -1957,11 +2452,11 @@ func (s *ELBv2ServiceImpl) DescribeListeners(input *elbv2.DescribeListenersInput // --- Tag operations --- // DescribeTags returns tags for one or more ELBv2 resources (load balancers, -// target groups, listeners). Tag data is read from the existing record stores -// — Spinifex doesn't have a separate tag KV. Listeners currently never store -// tags, so they always return an empty Tags slice (matches AWS behaviour for -// untagged resources). Cross-account or unknown ARNs return the per-resource -// not-found error so existence isn't leaked across accounts. +// target groups, listeners, listener rules). Tag data is read from the existing +// record stores — Spinifex doesn't have a separate tag KV. Untagged resources +// return an empty Tags slice (matches AWS behaviour). Cross-account or unknown +// ARNs return the per-resource not-found error so existence isn't leaked across +// accounts. func (s *ELBv2ServiceImpl) DescribeTags(input *elbv2.DescribeTagsInput, accountID string) (*elbv2.DescribeTagsOutput, error) { if input == nil || len(input.ResourceArns) == 0 { return nil, errors.New(awserrors.ErrorMissingParameter) @@ -2019,8 +2514,20 @@ func (s *ELBv2ServiceImpl) DescribeTags(input *elbv2.DescribeTagsInput, accountI } if l != nil { found = true + tags = l.Tags ownerAccount = l.AccountID - // Listeners don't store tags yet — leave map nil. + } + case elbv2ResourceListenerRule: + notFoundError = awserrors.ErrorELBv2RuleNotFound + r, rErr := s.store.GetRuleByArn(arn) + if rErr != nil { + slog.Error("DescribeTags: failed to get rule", "arn", arn, "err", rErr) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if r != nil { + found = true + tags = r.Tags + ownerAccount = r.AccountID } } @@ -2039,6 +2546,18 @@ func (s *ELBv2ServiceImpl) DescribeTags(input *elbv2.DescribeTagsInput, accountI }, nil } +// tagsFromSDK converts an SDK Tag slice into a tag map, skipping entries with a +// nil key or value. Returns a non-nil (possibly empty) map for storage. +func tagsFromSDK(tags []*elbv2.Tag) map[string]string { + m := make(map[string]string, len(tags)) + for _, tag := range tags { + if tag.Key != nil && tag.Value != nil { + m[*tag.Key] = *tag.Value + } + } + return m +} + // tagsMapToSDK converts a tag map into the SDK Tag slice with deterministic // (sorted-by-key) ordering. Returns nil for empty/nil input so the response // matches AWS for untagged resources. @@ -2058,210 +2577,6 @@ func tagsMapToSDK(tags map[string]string) []*elbv2.Tag { return out } -// --- SDK type conversion helpers --- - -func (s *ELBv2ServiceImpl) ModifyTargetGroupAttributes(input *elbv2.ModifyTargetGroupAttributesInput, accountID string) (*elbv2.ModifyTargetGroupAttributesOutput, error) { - if input.TargetGroupArn == nil || *input.TargetGroupArn == "" { - return nil, errors.New(awserrors.ErrorMissingParameter) - } - - tg, err := s.store.GetTargetGroupByArn(*input.TargetGroupArn) - if err != nil { - slog.Error("ModifyTargetGroupAttributes: failed to get TG", "arn", *input.TargetGroupArn, "err", err) - return nil, errors.New(awserrors.ErrorServerInternal) - } - if tg == nil || tg.AccountID != accountID { - return nil, errors.New(awserrors.ErrorELBv2TargetGroupNotFound) - } - - knownTGAttrs := DefaultTargetGroupAttributes() - var submitted []*elbv2.TargetGroupAttribute - dirty := false - for _, attr := range input.Attributes { - if attr == nil { - slog.Warn("ModifyTargetGroupAttributes: skipping nil attribute element", "arn", *input.TargetGroupArn) - continue - } - if attr.Key == nil || attr.Value == nil { - slog.Warn("ModifyTargetGroupAttributes: skipping attribute with nil Key or Value", "arn", *input.TargetGroupArn) - continue - } - if _, ok := knownTGAttrs[*attr.Key]; !ok { - slog.Warn("ModifyTargetGroupAttributes: rejecting unknown attribute key", "arn", *input.TargetGroupArn, "key", *attr.Key) - return nil, errors.New(awserrors.ErrorValidationError) - } - submitted = append(submitted, &elbv2.TargetGroupAttribute{ - Key: attr.Key, - Value: attr.Value, - }) - existing, exists := tg.Attributes[*attr.Key] - if exists && existing == *attr.Value { - continue // value already matches stored — no mutation needed - } - if tg.Attributes == nil { - tg.Attributes = make(map[string]string) - } - tg.Attributes[*attr.Key] = *attr.Value - dirty = true - } - - // If the caller sent attributes but every single one was rejected by the - // nil guard above, surface that as an error instead of silently returning - // a successful empty response — otherwise the caller thinks the write - // landed when nothing was actually applied. - if len(input.Attributes) > 0 && len(submitted) == 0 { - slog.Warn("ModifyTargetGroupAttributes: all submitted attributes were invalid", "arn", *input.TargetGroupArn, "submitted_count", len(input.Attributes)) - return nil, errors.New(awserrors.ErrorInvalidParameterValue) - } - - // Skip the NATS/KV write when nothing changed. Terraform re-applies the same - // attribute set on every drift check, so this kills ~all Modify traffic - // during steady state and narrows the read-modify-write race window. - if dirty { - if err := s.store.PutTargetGroup(tg); err != nil { - slog.Error("ModifyTargetGroupAttributes: failed to persist TG", "arn", *input.TargetGroupArn, "err", err) - return nil, errors.New(awserrors.ErrorServerInternal) - } - } - - return &elbv2.ModifyTargetGroupAttributesOutput{ - Attributes: submitted, - }, nil -} - -func (s *ELBv2ServiceImpl) DescribeTargetGroupAttributes(input *elbv2.DescribeTargetGroupAttributesInput, accountID string) (*elbv2.DescribeTargetGroupAttributesOutput, error) { - if input.TargetGroupArn == nil || *input.TargetGroupArn == "" { - return nil, errors.New(awserrors.ErrorMissingParameter) - } - - tg, err := s.store.GetTargetGroupByArn(*input.TargetGroupArn) - if err != nil { - slog.Error("DescribeTargetGroupAttributes: failed to get TG", "arn", *input.TargetGroupArn, "err", err) - return nil, errors.New(awserrors.ErrorServerInternal) - } - if tg == nil || tg.AccountID != accountID { - return nil, errors.New(awserrors.ErrorELBv2TargetGroupNotFound) - } - - merged := DefaultTargetGroupAttributes() - maps.Copy(merged, tg.Attributes) - - // Sort keys for deterministic output — Terraform diffs and snapshot tests - // depend on stable attribute ordering. - keys := slices.Sorted(maps.Keys(merged)) - attrs := make([]*elbv2.TargetGroupAttribute, 0, len(keys)) - for _, k := range keys { - attrs = append(attrs, &elbv2.TargetGroupAttribute{ - Key: aws.String(k), - Value: aws.String(merged[k]), - }) - } - - return &elbv2.DescribeTargetGroupAttributesOutput{ - Attributes: attrs, - }, nil -} - -func (s *ELBv2ServiceImpl) ModifyLoadBalancerAttributes(input *elbv2.ModifyLoadBalancerAttributesInput, accountID string) (*elbv2.ModifyLoadBalancerAttributesOutput, error) { - if input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { - return nil, errors.New(awserrors.ErrorMissingParameter) - } - - lb, err := s.store.GetLoadBalancerByArn(*input.LoadBalancerArn) - if err != nil { - slog.Error("ModifyLoadBalancerAttributes: failed to get LB", "arn", *input.LoadBalancerArn, "err", err) - return nil, errors.New(awserrors.ErrorServerInternal) - } - if lb == nil || lb.AccountID != accountID { - return nil, errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) - } - - knownLBAttrs := DefaultLoadBalancerAttributes(lb.Type) - var submitted []*elbv2.LoadBalancerAttribute - dirty := false - for _, attr := range input.Attributes { - if attr == nil { - slog.Warn("ModifyLoadBalancerAttributes: skipping nil attribute element", "arn", *input.LoadBalancerArn) - continue - } - if attr.Key == nil || attr.Value == nil { - slog.Warn("ModifyLoadBalancerAttributes: skipping attribute with nil Key or Value", "arn", *input.LoadBalancerArn) - continue - } - if _, ok := knownLBAttrs[*attr.Key]; !ok { - slog.Warn("ModifyLoadBalancerAttributes: rejecting unknown attribute key", "arn", *input.LoadBalancerArn, "key", *attr.Key, "lbType", lb.Type) - return nil, errors.New(awserrors.ErrorValidationError) - } - submitted = append(submitted, &elbv2.LoadBalancerAttribute{ - Key: attr.Key, - Value: attr.Value, - }) - existing, exists := lb.Attributes[*attr.Key] - if exists && existing == *attr.Value { - continue - } - if lb.Attributes == nil { - lb.Attributes = make(map[string]string) - } - lb.Attributes[*attr.Key] = *attr.Value - dirty = true - } - - // If the caller sent attributes but every single one was rejected by the - // nil guard above, surface that as an error instead of silently returning - // a successful empty response. - if len(input.Attributes) > 0 && len(submitted) == 0 { - slog.Warn("ModifyLoadBalancerAttributes: all submitted attributes were invalid", "arn", *input.LoadBalancerArn, "submitted_count", len(input.Attributes)) - return nil, errors.New(awserrors.ErrorInvalidParameterValue) - } - - // Skip the NATS/KV write when nothing changed. See - // ModifyTargetGroupAttributes for the Terraform-drift-check motivation. - if dirty { - if err := s.store.PutLoadBalancer(lb); err != nil { - slog.Error("ModifyLoadBalancerAttributes: failed to persist LB", "arn", *input.LoadBalancerArn, "err", err) - return nil, errors.New(awserrors.ErrorServerInternal) - } - } - - return &elbv2.ModifyLoadBalancerAttributesOutput{ - Attributes: submitted, - }, nil -} - -func (s *ELBv2ServiceImpl) DescribeLoadBalancerAttributes(input *elbv2.DescribeLoadBalancerAttributesInput, accountID string) (*elbv2.DescribeLoadBalancerAttributesOutput, error) { - if input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { - return nil, errors.New(awserrors.ErrorMissingParameter) - } - - lb, err := s.store.GetLoadBalancerByArn(*input.LoadBalancerArn) - if err != nil { - slog.Error("DescribeLoadBalancerAttributes: failed to get LB", "arn", *input.LoadBalancerArn, "err", err) - return nil, errors.New(awserrors.ErrorServerInternal) - } - if lb == nil || lb.AccountID != accountID { - return nil, errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) - } - - merged := DefaultLoadBalancerAttributes(lb.Type) - maps.Copy(merged, lb.Attributes) - - // Sort keys for deterministic output — Terraform diffs and snapshot tests - // depend on stable attribute ordering. - keys := slices.Sorted(maps.Keys(merged)) - attrs := make([]*elbv2.LoadBalancerAttribute, 0, len(keys)) - for _, k := range keys { - attrs = append(attrs, &elbv2.LoadBalancerAttribute{ - Key: aws.String(k), - Value: aws.String(merged[k]), - }) - } - - return &elbv2.DescribeLoadBalancerAttributesOutput{ - Attributes: attrs, - }, nil -} - func (s *ELBv2ServiceImpl) lbRecordToSDK(r *LoadBalancerRecord) *elbv2.LoadBalancer { lb := &elbv2.LoadBalancer{ LoadBalancerArn: aws.String(r.LoadBalancerArn), @@ -2357,12 +2672,59 @@ func (s *ELBv2ServiceImpl) listenerRecordToSDK(r *ListenerRecord) *elbv2.Listene } for _, a := range r.DefaultActions { - action := &elbv2.Action{ - Type: aws.String(a.Type), - TargetGroupArn: aws.String(a.TargetGroupArn), - } - listener.DefaultActions = append(listener.DefaultActions, action) + listener.DefaultActions = append(listener.DefaultActions, listenerActionToSDK(a)) + } + + if len(r.Certificates) > 0 { + listener.Certificates = listenerCertsToSDK(r.Certificates) + } + if r.SslPolicy != "" { + listener.SslPolicy = aws.String(r.SslPolicy) } return listener } + +// listenerActionToSDK converts a stored action into the AWS SDK shape, +// preserving forward / fixed-response / redirect detail so DescribeListeners +// and DescribeRules round-trip cleanly. Shared by listeners and rules. +func listenerActionToSDK(a ListenerAction) *elbv2.Action { + action := &elbv2.Action{Type: aws.String(a.Type)} + if a.TargetGroupArn != "" { + action.TargetGroupArn = aws.String(a.TargetGroupArn) + } + if a.FixedResponse != nil { + fr := &elbv2.FixedResponseActionConfig{ + StatusCode: aws.String(a.FixedResponse.StatusCode), + } + if a.FixedResponse.ContentType != "" { + fr.ContentType = aws.String(a.FixedResponse.ContentType) + } + if a.FixedResponse.MessageBody != "" { + fr.MessageBody = aws.String(a.FixedResponse.MessageBody) + } + action.FixedResponseConfig = fr + } + if a.Redirect != nil { + rd := &elbv2.RedirectActionConfig{ + StatusCode: aws.String(a.Redirect.StatusCode), + } + if a.Redirect.Protocol != "" { + rd.Protocol = aws.String(a.Redirect.Protocol) + } + if a.Redirect.Host != "" { + rd.Host = aws.String(a.Redirect.Host) + } + if a.Redirect.Port != "" { + rd.Port = aws.String(a.Redirect.Port) + } + if a.Redirect.Path != "" { + rd.Path = aws.String(a.Redirect.Path) + } + if a.Redirect.Query != "" { + rd.Query = aws.String(a.Redirect.Query) + } + action.RedirectConfig = rd + } + return action +} diff --git a/spinifex/handlers/elbv2/service_impl_attributes.go b/spinifex/handlers/elbv2/service_impl_attributes.go new file mode 100644 index 000000000..362c469e5 --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_attributes.go @@ -0,0 +1,265 @@ +package handlers_elbv2 + +import ( + "errors" + "log/slog" + "maps" + "slices" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +// attrPair is the internal key/value shape the generic attribute handlers work +// in, decoupled from the two near-identical SDK attribute types +// (*elbv2.TargetGroupAttribute and *elbv2.LoadBalancerAttribute). +type attrPair struct { + Key string + Value string +} + +// attrResource bundles the per-resource-type plumbing the generic Modify/ +// Describe attribute handlers need, so the target-group and load-balancer +// paths share one implementation. +type attrResource[R any] struct { + arn string + accountID string + opName string // slog prefix / error context + notFound string // awserrors code returned for missing/cross-account + fetch func(string) (*R, error) + save func(*R) error + accountIDOf func(*R) string + defaults func(*R) map[string]string + attrsOf func(*R) map[string]string + setAttrs func(*R, map[string]string) +} + +// load fetches and authorises the record. An empty accountID is rejected +// explicitly so a direct NATS/internal caller cannot match a record whose +// AccountID is also empty. +func (r attrResource[R]) load() (*R, error) { + rec, err := r.fetch(r.arn) + if err != nil { + slog.Error(r.opName+": failed to load record", "arn", r.arn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if rec == nil || r.accountID == "" || r.accountIDOf(rec) != r.accountID { + return nil, errors.New(r.notFound) + } + return rec, nil +} + +// modifyResourceAttributes validates and merges submitted attributes into the +// record, persisting only when something actually changed. rawCount is the +// number of attributes the caller submitted before nil filtering, used to +// distinguish "nothing sent" from "everything sent was invalid". +func modifyResourceAttributes[R any](res attrResource[R], pairs []attrPair, rawCount int) ([]attrPair, error) { + if res.arn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + rec, err := res.load() + if err != nil { + return nil, err + } + + known := res.defaults(rec) + attrs := res.attrsOf(rec) + submitted := make([]attrPair, 0, len(pairs)) + dirty := false + for _, p := range pairs { + if _, ok := known[p.Key]; !ok { + slog.Warn(res.opName+": rejecting unknown attribute key", "arn", res.arn, "key", p.Key) + return nil, errors.New(awserrors.ErrorValidationError) + } + submitted = append(submitted, p) + if existing, ok := attrs[p.Key]; ok && existing == p.Value { + continue // value already matches stored — no mutation needed + } + if attrs == nil { + attrs = make(map[string]string) + } + attrs[p.Key] = p.Value + dirty = true + } + + // Caller sent attributes but every one was dropped by nil filtering: surface + // an error rather than a successful empty response the caller misreads as a + // landed write. + if rawCount > 0 && len(submitted) == 0 { + slog.Warn(res.opName+": all submitted attributes were invalid", "arn", res.arn, "submitted_count", rawCount) + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + // Skip the NATS/KV write when nothing changed. Terraform re-applies the same + // attribute set on every drift check, so this kills ~all Modify traffic + // during steady state and narrows the read-modify-write race window. + if dirty { + res.setAttrs(rec, attrs) + if err := res.save(rec); err != nil { + slog.Error(res.opName+": failed to persist", "arn", res.arn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + } + + return submitted, nil +} + +// describeResourceAttributes returns the record's attributes merged over the +// per-type defaults, sorted by key for deterministic output (Terraform diffs +// and snapshot tests depend on stable ordering). +func describeResourceAttributes[R any](res attrResource[R]) ([]attrPair, error) { + if res.arn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + rec, err := res.load() + if err != nil { + return nil, err + } + + merged := res.defaults(rec) + maps.Copy(merged, res.attrsOf(rec)) + + keys := slices.Sorted(maps.Keys(merged)) + out := make([]attrPair, 0, len(keys)) + for _, k := range keys { + out = append(out, attrPair{Key: k, Value: merged[k]}) + } + return out, nil +} + +// sdkAttrsIn converts an SDK attribute slice into internal pairs, dropping nil +// elements and elements with a nil Key/Value. Returns the cleaned pairs and the +// raw submitted count. +func sdkAttrsIn[A any](in []A, keyVal func(A) (*string, *string), arn, opName string) ([]attrPair, int) { + out := make([]attrPair, 0, len(in)) + for _, a := range in { + k, v := keyVal(a) + if k == nil || v == nil { + slog.Warn(opName+": skipping invalid attribute element", "arn", arn) + continue + } + out = append(out, attrPair{Key: *k, Value: *v}) + } + return out, len(in) +} + +// sdkAttrsOut converts internal pairs back into a fresh SDK attribute slice via +// mk. Fresh strings are allocated (never aliasing caller input). Returns nil for +// an empty set to match AWS for resources with no submitted attributes. +func sdkAttrsOut[A any](pairs []attrPair, mk func(key, value string) A) []A { + if len(pairs) == 0 { + return nil + } + out := make([]A, 0, len(pairs)) + for _, p := range pairs { + out = append(out, mk(p.Key, p.Value)) + } + return out +} + +func (s *ELBv2ServiceImpl) tgAttrResource(arn, accountID, opName string) attrResource[TargetGroupRecord] { + return attrResource[TargetGroupRecord]{ + arn: arn, + accountID: accountID, + opName: opName, + notFound: awserrors.ErrorELBv2TargetGroupNotFound, + fetch: s.store.GetTargetGroupByArn, + save: s.store.PutTargetGroup, + accountIDOf: func(r *TargetGroupRecord) string { return r.AccountID }, + defaults: func(*TargetGroupRecord) map[string]string { return DefaultTargetGroupAttributes() }, + attrsOf: func(r *TargetGroupRecord) map[string]string { return r.Attributes }, + setAttrs: func(r *TargetGroupRecord, m map[string]string) { r.Attributes = m }, + } +} + +func (s *ELBv2ServiceImpl) lbAttrResource(arn, accountID, opName string) attrResource[LoadBalancerRecord] { + return attrResource[LoadBalancerRecord]{ + arn: arn, + accountID: accountID, + opName: opName, + notFound: awserrors.ErrorELBv2LoadBalancerNotFound, + fetch: s.store.GetLoadBalancerByArn, + save: s.store.PutLoadBalancer, + accountIDOf: func(r *LoadBalancerRecord) string { return r.AccountID }, + defaults: func(r *LoadBalancerRecord) map[string]string { return DefaultLoadBalancerAttributes(r.Type) }, + attrsOf: func(r *LoadBalancerRecord) map[string]string { return r.Attributes }, + setAttrs: func(r *LoadBalancerRecord, m map[string]string) { r.Attributes = m }, + } +} + +func tgAttrKeyVal(a *elbv2.TargetGroupAttribute) (*string, *string) { + if a == nil { + return nil, nil + } + return a.Key, a.Value +} + +func lbAttrKeyVal(a *elbv2.LoadBalancerAttribute) (*string, *string) { + if a == nil { + return nil, nil + } + return a.Key, a.Value +} + +func mkTGAttr(key, value string) *elbv2.TargetGroupAttribute { + return &elbv2.TargetGroupAttribute{Key: aws.String(key), Value: aws.String(value)} +} + +func mkLBAttr(key, value string) *elbv2.LoadBalancerAttribute { + return &elbv2.LoadBalancerAttribute{Key: aws.String(key), Value: aws.String(value)} +} + +func (s *ELBv2ServiceImpl) ModifyTargetGroupAttributes(input *elbv2.ModifyTargetGroupAttributesInput, accountID string) (*elbv2.ModifyTargetGroupAttributesOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + const op = "ModifyTargetGroupAttributes" + arn := aws.StringValue(input.TargetGroupArn) + pairs, raw := sdkAttrsIn(input.Attributes, tgAttrKeyVal, arn, op) + submitted, err := modifyResourceAttributes(s.tgAttrResource(arn, accountID, op), pairs, raw) + if err != nil { + return nil, err + } + return &elbv2.ModifyTargetGroupAttributesOutput{Attributes: sdkAttrsOut(submitted, mkTGAttr)}, nil +} + +func (s *ELBv2ServiceImpl) DescribeTargetGroupAttributes(input *elbv2.DescribeTargetGroupAttributesInput, accountID string) (*elbv2.DescribeTargetGroupAttributesOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + const op = "DescribeTargetGroupAttributes" + arn := aws.StringValue(input.TargetGroupArn) + pairs, err := describeResourceAttributes(s.tgAttrResource(arn, accountID, op)) + if err != nil { + return nil, err + } + return &elbv2.DescribeTargetGroupAttributesOutput{Attributes: sdkAttrsOut(pairs, mkTGAttr)}, nil +} + +func (s *ELBv2ServiceImpl) ModifyLoadBalancerAttributes(input *elbv2.ModifyLoadBalancerAttributesInput, accountID string) (*elbv2.ModifyLoadBalancerAttributesOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + const op = "ModifyLoadBalancerAttributes" + arn := aws.StringValue(input.LoadBalancerArn) + pairs, raw := sdkAttrsIn(input.Attributes, lbAttrKeyVal, arn, op) + submitted, err := modifyResourceAttributes(s.lbAttrResource(arn, accountID, op), pairs, raw) + if err != nil { + return nil, err + } + return &elbv2.ModifyLoadBalancerAttributesOutput{Attributes: sdkAttrsOut(submitted, mkLBAttr)}, nil +} + +func (s *ELBv2ServiceImpl) DescribeLoadBalancerAttributes(input *elbv2.DescribeLoadBalancerAttributesInput, accountID string) (*elbv2.DescribeLoadBalancerAttributesOutput, error) { + if input == nil { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + const op = "DescribeLoadBalancerAttributes" + arn := aws.StringValue(input.LoadBalancerArn) + pairs, err := describeResourceAttributes(s.lbAttrResource(arn, accountID, op)) + if err != nil { + return nil, err + } + return &elbv2.DescribeLoadBalancerAttributesOutput{Attributes: sdkAttrsOut(pairs, mkLBAttr)}, nil +} diff --git a/spinifex/handlers/elbv2/service_impl_health_test.go b/spinifex/handlers/elbv2/service_impl_health_test.go index 1ea659b14..19553fe75 100644 --- a/spinifex/handlers/elbv2/service_impl_health_test.go +++ b/spinifex/handlers/elbv2/service_impl_health_test.go @@ -164,7 +164,7 @@ func TestRegisterTargets_MultipleInstances(t *testing.T) { // TestResetTargetHealthOnStartup_TransitionsAllNonDrainingToInitial verifies // that the startup reset rewrites every non-draining target's HealthState // back to "initial" so a daemon restart can't surface stale "healthy" claims -// before the lb-agent has posted a fresh report. Refs mulga-siv-119. +// before the lb-agent has posted a fresh report. func TestResetTargetHealthOnStartup_TransitionsAllNonDrainingToInitial(t *testing.T) { store := setupTestNATS(t) svc := &ELBv2ServiceImpl{store: store} diff --git a/spinifex/handlers/elbv2/service_impl_lb_network.go b/spinifex/handlers/elbv2/service_impl_lb_network.go new file mode 100644 index 000000000..215d8e40d --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_lb_network.go @@ -0,0 +1,373 @@ +package handlers_elbv2 + +import ( + "errors" + "fmt" + "log/slog" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +// SetIpAddressType sets the load balancer's IP address type. Spinifex ALBs are +// IPv4-only, so the only accepted value is "ipv4"; dualstack variants are +// rejected with InvalidConfigurationRequest. The call is idempotent. +func (s *ELBv2ServiceImpl) SetIpAddressType(input *elbv2.SetIpAddressTypeInput, accountID string) (*elbv2.SetIpAddressTypeOutput, error) { + if input == nil || input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + if input.IpAddressType == nil || *input.IpAddressType == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + if *input.IpAddressType != IPAddressTypeIPv4 { + return nil, errors.New(awserrors.ErrorELBv2InvalidConfigurationRequest) + } + + lb, err := s.store.GetLoadBalancerByArn(*input.LoadBalancerArn) + if err != nil { + slog.Error("SetIpAddressType: failed to get LB", "arn", *input.LoadBalancerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if lb == nil || lb.AccountID != accountID { + return nil, errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) + } + + if lb.IPAddressType != IPAddressTypeIPv4 { + lb.IPAddressType = IPAddressTypeIPv4 + if err := s.store.PutLoadBalancer(lb); err != nil { + slog.Error("SetIpAddressType: failed to persist LB", "arn", *input.LoadBalancerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + } + + return &elbv2.SetIpAddressTypeOutput{ + IpAddressType: aws.String(lb.IPAddressType), + }, nil +} + +// SetSecurityGroups replaces the security groups associated with an +// (application) load balancer. The new groups are re-attached to every ENI the +// ALB spans via ModifyNetworkInterfaceAttribute, which validates them against +// the ENI's VPC and pushes the change to the live data-plane port before the +// record is persisted. +func (s *ELBv2ServiceImpl) SetSecurityGroups(input *elbv2.SetSecurityGroupsInput, accountID string) (*elbv2.SetSecurityGroupsOutput, error) { + if input == nil || input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + if len(input.SecurityGroups) == 0 { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + + lb, err := s.store.GetLoadBalancerByArn(*input.LoadBalancerArn) + if err != nil { + slog.Error("SetSecurityGroups: failed to get LB", "arn", *input.LoadBalancerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if lb == nil || lb.AccountID != accountID { + return nil, errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) + } + + // NLBs do not support security groups (mirrors CreateLoadBalancer). + if lb.Type == LoadBalancerTypeNetwork { + return nil, errors.New(awserrors.ErrorELBv2InvalidConfigurationRequest) + } + + sgs := make([]string, 0, len(input.SecurityGroups)) + for _, sg := range input.SecurityGroups { + if sg == nil || *sg == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + sgs = append(sgs, *sg) + } + + // Re-attach the new groups to each ALB ENI. This validates the groups + // against the ENI's VPC and fires the live port-SG update; a failure here + // (e.g. unknown SG) aborts before the record is persisted. All ENIs share + // the LB's VPC and groups, so a successful first apply implies the rest. + if s.VPCService != nil { + for _, eniID := range lb.ENIs { + if _, err := s.VPCService.ModifyNetworkInterfaceAttribute(&ec2.ModifyNetworkInterfaceAttributeInput{ + NetworkInterfaceId: aws.String(eniID), + Groups: aws.StringSlice(sgs), + }, accountID); err != nil { + slog.Error("SetSecurityGroups: failed to update ENI groups", "arn", *input.LoadBalancerArn, "eni", eniID, "err", err) + return nil, err + } + } + } + + lb.SecurityGroups = sgs + if err := s.store.PutLoadBalancer(lb); err != nil { + slog.Error("SetSecurityGroups: failed to persist LB", "arn", *input.LoadBalancerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + + return &elbv2.SetSecurityGroupsOutput{ + SecurityGroupIds: aws.StringSlice(sgs), + }, nil +} + +// SetSubnets enables the subnets (and their backing ENIs) for a load balancer. +// It is a full add+remove: a subnet in the request but not on the LB gets a new +// managed ENI; a subnet on the LB but absent from the request has its ENI +// removed. The system VM binds its data-plane taps at boot and the launcher +// exposes no live ENI hotplug, so the new ENI set is applied by relaunching the +// LB VM — a brief data-plane interruption. The record is mutated only after the +// new ENIs exist and the VM has been relaunched. +func (s *ELBv2ServiceImpl) SetSubnets(input *elbv2.SetSubnetsInput, accountID string) (*elbv2.SetSubnetsOutput, error) { + if input == nil || input.LoadBalancerArn == nil || *input.LoadBalancerArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + + desired := desiredSubnetSet(input) + if len(desired) == 0 { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + + lb, err := s.store.GetLoadBalancerByArn(*input.LoadBalancerArn) + if err != nil { + slog.Error("SetSubnets: failed to get LB", "arn", *input.LoadBalancerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if lb == nil || lb.AccountID != accountID { + return nil, errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) + } + + current := subnetENIMap(lb) + desiredSet := make(map[string]bool, len(desired)) + for _, sn := range desired { + desiredSet[sn] = true + } + + var toAdd, toRemove []string + for _, sn := range desired { + if _, ok := current[sn]; !ok { + toAdd = append(toAdd, sn) + } + } + for sn := range current { + if !desiredSet[sn] { + toRemove = append(toRemove, sn) + } + } + + if len(toAdd) == 0 && len(toRemove) == 0 { + return s.setSubnetsOutput(lb), nil // idempotent — no change + } + + // Without a VPC service we cannot manage ENIs; record the requested subnet + // set so the API stays consistent for launcher-less / test deployments. + if s.VPCService == nil { + lb.Subnets = desired + lb.AvailZones = rebuildAvailZones(desired, lb.AvailZones, nil) + if err := s.store.PutLoadBalancer(lb); err != nil { + slog.Error("SetSubnets: failed to persist LB", "arn", *input.LoadBalancerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + return s.setSubnetsOutput(lb), nil + } + + // Create ENIs for the added subnets first; roll them back on any failure so + // a partial apply never leaks ENIs or mutates the live LB. + newENIBySubnet := make(map[string]string, len(toAdd)) + newAZBySubnet := make(map[string]string, len(toAdd)) + rollbackNewENIs := func() { + for _, created := range newENIBySubnet { + if _, delErr := s.VPCService.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ + NetworkInterfaceId: aws.String(created), + }, accountID); delErr != nil { + slog.Error("SetSubnets: rollback failed to delete ENI", "eni", created, "err", delErr) + } + } + } + for _, subnetID := range toAdd { + eniID, az, eniErr := s.createLBENI(subnetID, lb, accountID) + if eniErr != nil { + rollbackNewENIs() + slog.Error("SetSubnets: failed to create ENI", "subnet", subnetID, "err", eniErr) + return nil, errors.New(awserrors.ErrorELBv2SubnetNotFound) + } + newENIBySubnet[subnetID] = eniID + newAZBySubnet[subnetID] = az + } + + // Assemble the new ENI set in desired-subnet order (primary = first subnet). + newENIs := make([]string, 0, len(desired)) + for _, sn := range desired { + if eniID, ok := current[sn]; ok { + newENIs = append(newENIs, eniID) + } else { + newENIs = append(newENIs, newENIBySubnet[sn]) + } + } + + // Terminate the LB VM before reshaping its ENI set, then relaunch it on the + // new set. + if lb.InstanceID != "" && s.InstanceLauncher != nil { + if err := s.InstanceLauncher.TerminateSystemInstance(lb.InstanceID); err != nil { + rollbackNewENIs() + slog.Error("SetSubnets: failed to terminate LB VM for relaunch", "arn", *input.LoadBalancerArn, "instanceId", lb.InstanceID, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + } + + // TerminateSystemInstance tears down the VM but leaves its ENIs marked + // in-use, so detach the existing ENIs explicitly: retained ones must be free + // to re-attach to the relaunched VM, and removed ones must be detached before + // they can be deleted. + for _, eniID := range current { + if detachErr := s.VPCService.DetachENI(accountID, eniID); detachErr != nil { + slog.Warn("SetSubnets: failed to detach ENI before relaunch", "eni", eniID, "err", detachErr) + } + } + + // Delete ENIs for removed subnets now that they are detached. + for _, sn := range toRemove { + eniID := current[sn] + if _, delErr := s.VPCService.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ + NetworkInterfaceId: aws.String(eniID), + }, accountID); delErr != nil { + slog.Error("SetSubnets: failed to delete removed ENI", "subnet", sn, "eni", eniID, "err", delErr) + } + } + + launch := s.launchLBVM(lb.LoadBalancerID, lb.Scheme, newENIs, desired, accountID) + availZones := rebuildAvailZones(desired, lb.AvailZones, newAZBySubnet) + if launch.publicIP != "" && len(availZones) > 0 { + availZones[0].PublicIP = launch.publicIP + } + + lb.Subnets = desired + lb.ENIs = newENIs + lb.AvailZones = availZones + lb.InstanceID = launch.instanceID + lb.VPCIP = launch.vpcIP + lb.HostPorts = launch.hostPorts + lb.State = s.lbStateAfterLaunch(launch, lb.Scheme) + + if err := s.store.PutLoadBalancer(lb); err != nil { + slog.Error("SetSubnets: failed to persist LB", "arn", *input.LoadBalancerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + + slog.Info("SetSubnets completed", "arn", *input.LoadBalancerArn, "subnets", len(desired), "added", len(toAdd), "removed", len(toRemove), "state", lb.State) + return s.setSubnetsOutput(lb), nil +} + +// desiredSubnetSet flattens the request's Subnets and SubnetMappings into a +// de-duplicated, order-preserving subnet-ID list. +func desiredSubnetSet(input *elbv2.SetSubnetsInput) []string { + seen := make(map[string]bool) + var out []string + add := func(sn string) { + if sn != "" && !seen[sn] { + seen[sn] = true + out = append(out, sn) + } + } + for _, sn := range input.Subnets { + if sn != nil { + add(*sn) + } + } + for _, m := range input.SubnetMappings { + if m != nil && m.SubnetId != nil { + add(*m.SubnetId) + } + } + return out +} + +// subnetENIMap pairs each of the LB's current subnets with its backing ENI. +// CreateLoadBalancer and SetSubnets write Subnets and ENIs in lockstep, so the +// parallel arrays are authoritative for which ENI lives in which subnet. +func subnetENIMap(lb *LoadBalancerRecord) map[string]string { + m := make(map[string]string, len(lb.Subnets)) + for i, sn := range lb.Subnets { + if i < len(lb.ENIs) { + m[sn] = lb.ENIs[i] + } + } + return m +} + +// createLBENI creates a single managed ENI for the LB in the given subnet, +// tagged and security-grouped like the create-time ENIs. Returns the ENI ID and +// its availability zone. +func (s *ELBv2ServiceImpl) createLBENI(subnetID string, lb *LoadBalancerRecord, accountID string) (eniID, az string, err error) { + eniIn := &ec2.CreateNetworkInterfaceInput{ + SubnetId: aws.String(subnetID), + Description: aws.String(fmt.Sprintf("ELB %s/%s", lb.Name, lb.LoadBalancerID)), + TagSpecifications: []*ec2.TagSpecification{ + { + ResourceType: aws.String("network-interface"), + Tags: []*ec2.Tag{ + {Key: aws.String(elbv2ManagedByTag), Value: aws.String(elbv2ManagedByValue)}, + {Key: aws.String(elbv2LBTag), Value: aws.String(lb.LoadBalancerArn)}, + }, + }, + }, + } + if groups := lbENIGroups(lb); len(groups) > 0 { + eniIn.Groups = aws.StringSlice(groups) + } + out, err := s.VPCService.CreateNetworkInterface(eniIn, accountID) + if err != nil { + return "", "", err + } + eni := out.NetworkInterface + return aws.StringValue(eni.NetworkInterfaceId), aws.StringValue(eni.AvailabilityZone), nil +} + +// rebuildAvailZones produces the AvailZoneInfo list for the new subnet set in +// order, preserving the existing zone name for retained subnets and using +// newAZBySubnet for added ones. PublicIP is cleared — it is re-derived from the +// relaunched VM by the caller. +func rebuildAvailZones(subnets []string, existing []AvailZoneInfo, newAZBySubnet map[string]string) []AvailZoneInfo { + bySubnet := make(map[string]AvailZoneInfo, len(existing)) + for _, az := range existing { + bySubnet[az.SubnetId] = az + } + out := make([]AvailZoneInfo, 0, len(subnets)) + for _, sn := range subnets { + if az, ok := bySubnet[sn]; ok { + out = append(out, AvailZoneInfo{ZoneName: az.ZoneName, SubnetId: sn}) + continue + } + out = append(out, AvailZoneInfo{ZoneName: newAZBySubnet[sn], SubnetId: sn}) + } + return out +} + +// lbStateAfterLaunch mirrors CreateLoadBalancer's post-launch state gate: a VM +// that came up enters provisioning until the lb-agent heartbeats; an internal +// LB with no mgmt return route is failed loud; a failed launch is failed. +func (s *ELBv2ServiceImpl) lbStateAfterLaunch(launch lbVMLaunch, scheme string) string { + if launch.instanceID == "" { + if launch.failed { + return StateFailed + } + return StateActive + } + if scheme == SchemeInternal { + if gw, tgt := s.resolveMgmtRoute(scheme); gw == "" || tgt == "" { + slog.Error("SetSubnets: internal LB has no mgmt return route; marking failed (lb-agent cannot heartbeat AWSGW)", + "mgmtBridgeIP", s.MgmtBridgeIP, "advertiseIP", s.AdvertiseIP) + return StateFailed + } + } + return StateProvisioning +} + +// setSubnetsOutput builds the SetSubnets response from the persisted record, +// reusing the SDK availability-zone projection (which surfaces per-AZ private +// IPs) so the response matches DescribeLoadBalancers. +func (s *ELBv2ServiceImpl) setSubnetsOutput(lb *LoadBalancerRecord) *elbv2.SetSubnetsOutput { + sdk := s.lbRecordToSDK(lb) + return &elbv2.SetSubnetsOutput{ + AvailabilityZones: sdk.AvailabilityZones, + IpAddressType: aws.String(lb.IPAddressType), + } +} diff --git a/spinifex/handlers/elbv2/service_impl_lb_network_test.go b/spinifex/handlers/elbv2/service_impl_lb_network_test.go new file mode 100644 index 000000000..578374b5f --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_lb_network_test.go @@ -0,0 +1,399 @@ +package handlers_elbv2 + +import ( + "errors" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func createLBArn(t *testing.T, svc *ELBv2ServiceImpl, name string) string { + t.Helper() + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String(name), + }, testAccountID) + require.NoError(t, err) + require.Len(t, out.LoadBalancers, 1) + return *out.LoadBalancers[0].LoadBalancerArn +} + +func describeLB(t *testing.T, svc *ELBv2ServiceImpl, arn string) *elbv2.LoadBalancer { + t.Helper() + out, err := svc.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{ + LoadBalancerArns: []*string{aws.String(arn)}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, out.LoadBalancers, 1) + return out.LoadBalancers[0] +} + +func TestSetIpAddressType_IPv4Idempotent(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "ipt-lb") + + out, err := svc.SetIpAddressType(&elbv2.SetIpAddressTypeInput{ + LoadBalancerArn: aws.String(arn), + IpAddressType: aws.String("ipv4"), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, "ipv4", *out.IpAddressType) + assert.Equal(t, "ipv4", *describeLB(t, svc, arn).IpAddressType) +} + +func TestSetIpAddressType_DualstackRejected(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "ipt-ds") + + _, err := svc.SetIpAddressType(&elbv2.SetIpAddressTypeInput{ + LoadBalancerArn: aws.String(arn), + IpAddressType: aws.String("dualstack"), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2InvalidConfigurationRequest) +} + +func TestSetIpAddressType_MissingParams(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "ipt-mp") + + _, err := svc.SetIpAddressType(&elbv2.SetIpAddressTypeInput{ + LoadBalancerArn: aws.String(arn), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorMissingParameter) + + _, err = svc.SetIpAddressType(&elbv2.SetIpAddressTypeInput{ + IpAddressType: aws.String("ipv4"), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorMissingParameter) +} + +func TestSetIpAddressType_NotFound(t *testing.T) { + svc := setupTestService(t) + _, err := svc.SetIpAddressType(&elbv2.SetIpAddressTypeInput{ + LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-deadbeef"), + IpAddressType: aws.String("ipv4"), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2LoadBalancerNotFound) +} + +func TestSetSecurityGroups_UpdatesRecord(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "sg-lb") + + out, err := svc.SetSecurityGroups(&elbv2.SetSecurityGroupsInput{ + LoadBalancerArn: aws.String(arn), + SecurityGroups: aws.StringSlice([]string{"sg-aaa", "sg-bbb"}), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, []string{"sg-aaa", "sg-bbb"}, aws.StringValueSlice(out.SecurityGroupIds)) + assert.Equal(t, []string{"sg-aaa", "sg-bbb"}, aws.StringValueSlice(describeLB(t, svc, arn).SecurityGroups)) +} + +func TestSetSecurityGroups_EmptyRejected(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "sg-empty") + + _, err := svc.SetSecurityGroups(&elbv2.SetSecurityGroupsInput{ + LoadBalancerArn: aws.String(arn), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorMissingParameter) +} + +func TestSetSecurityGroups_NLBRejected(t *testing.T) { + svc := setupTestService(t) + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("sg-nlb"), + Type: aws.String("network"), + }, testAccountID) + require.NoError(t, err) + arn := *out.LoadBalancers[0].LoadBalancerArn + + _, err = svc.SetSecurityGroups(&elbv2.SetSecurityGroupsInput{ + LoadBalancerArn: aws.String(arn), + SecurityGroups: aws.StringSlice([]string{"sg-aaa"}), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2InvalidConfigurationRequest) +} + +func TestSetSecurityGroups_CrossAccount(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "sg-xacct") + + _, err := svc.SetSecurityGroups(&elbv2.SetSecurityGroupsInput{ + LoadBalancerArn: aws.String(arn), + SecurityGroups: aws.StringSlice([]string{"sg-aaa"}), + }, "999999999999") + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2LoadBalancerNotFound) +} + +// setupSubnetTestService wires a VPC-backed ELBv2 service with a launcher mock +// so SetSubnets can exercise ENI create/delete plus the LB-VM relaunch. +func setupSubnetTestService(t *testing.T) (*ELBv2ServiceImpl, *handlers_ec2_vpc.VPCServiceImpl, *mockSystemInstanceLauncher) { + t.Helper() + svc, vpcSvc := setupTestServiceWithVPC(t) + mock := &mockSystemInstanceLauncher{ + launchResult: &SystemInstanceOutput{InstanceID: "i-lb", PrivateIP: "10.0.1.5"}, + } + svc.InstanceLauncher = mock + svc.GatewayURL = "https://10.0.0.1:9999" + svc.SystemAccessKey = "AKID" + svc.SystemSecretKey = "SECRET" + return svc, vpcSvc, mock +} + +func countManagedENIs(t *testing.T, vpcSvc *handlers_ec2_vpc.VPCServiceImpl) int { + t.Helper() + out, err := vpcSvc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{}, testAccountID) + require.NoError(t, err) + n := 0 + for _, eni := range out.NetworkInterfaces { + if eni.RequesterManaged != nil && *eni.RequesterManaged { + n++ + } + } + return n +} + +func vpcID(t *testing.T, vpcSvc *handlers_ec2_vpc.VPCServiceImpl) string { + t.Helper() + vpcs, err := vpcSvc.DescribeVpcs(&ec2.DescribeVpcsInput{}, testAccountID) + require.NoError(t, err) + require.NotEmpty(t, vpcs.Vpcs) + return *vpcs.Vpcs[0].VpcId +} + +func TestSetSubnets_AddSubnet(t *testing.T) { + svc, vpcSvc, mock := setupSubnetTestService(t) + vid := vpcID(t, vpcSvc) + sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.20.0/24", "us-east-1a") + sub2 := getTestSubnetID(t, vpcSvc, vid, "10.0.21.0/24", "us-east-1b") + + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("add-lb"), + Subnets: []*string{aws.String(sub1)}, + }, testAccountID) + require.NoError(t, err) + arn := *out.LoadBalancers[0].LoadBalancerArn + require.Equal(t, 1, countManagedENIs(t, vpcSvc)) + require.Len(t, mock.launchCalls, 1) + + res, err := svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + Subnets: []*string{aws.String(sub1), aws.String(sub2)}, + }, testAccountID) + require.NoError(t, err) + assert.Len(t, res.AvailabilityZones, 2) + + // New ENI created in sub2; LB VM relaunched on the new set. + assert.Equal(t, 2, countManagedENIs(t, vpcSvc)) + assert.Len(t, mock.terminateCalls, 1) + require.Len(t, mock.launchCalls, 2) + assert.Len(t, mock.launchCalls[1].ExtraENIs, 1, "relaunch must carry the added subnet as an ExtraENI") + + lb := describeLB(t, svc, arn) + assert.ElementsMatch(t, []string{sub1, sub2}, subnetIDsOfLB(lb)) +} + +func TestSetSubnets_RemoveSubnet(t *testing.T) { + svc, vpcSvc, mock := setupSubnetTestService(t) + vid := vpcID(t, vpcSvc) + sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.22.0/24", "us-east-1a") + sub2 := getTestSubnetID(t, vpcSvc, vid, "10.0.23.0/24", "us-east-1b") + + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("rm-lb"), + Subnets: []*string{aws.String(sub1), aws.String(sub2)}, + }, testAccountID) + require.NoError(t, err) + arn := *out.LoadBalancers[0].LoadBalancerArn + require.Equal(t, 2, countManagedENIs(t, vpcSvc)) + + res, err := svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + Subnets: []*string{aws.String(sub1)}, + }, testAccountID) + require.NoError(t, err) + assert.Len(t, res.AvailabilityZones, 1) + + // sub2's ENI is deleted; only sub1's remains. + assert.Equal(t, 1, countManagedENIs(t, vpcSvc)) + assert.Len(t, mock.terminateCalls, 1) + lb := describeLB(t, svc, arn) + assert.Equal(t, []string{sub1}, subnetIDsOfLB(lb)) +} + +func TestSetSubnets_Replace(t *testing.T) { + svc, vpcSvc, _ := setupSubnetTestService(t) + vid := vpcID(t, vpcSvc) + sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.24.0/24", "us-east-1a") + sub2 := getTestSubnetID(t, vpcSvc, vid, "10.0.25.0/24", "us-east-1b") + + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("swap-lb"), + Subnets: []*string{aws.String(sub1)}, + }, testAccountID) + require.NoError(t, err) + arn := *out.LoadBalancers[0].LoadBalancerArn + + _, err = svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + Subnets: []*string{aws.String(sub2)}, + }, testAccountID) + require.NoError(t, err) + + // One ENI swapped for another — still a single managed ENI, now in sub2. + assert.Equal(t, 1, countManagedENIs(t, vpcSvc)) + lb := describeLB(t, svc, arn) + assert.Equal(t, []string{sub2}, subnetIDsOfLB(lb)) +} + +func TestSetSubnets_TerminateFailureRollsBackNewENIs(t *testing.T) { + svc, vpcSvc, mock := setupSubnetTestService(t) + vid := vpcID(t, vpcSvc) + sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.26.0/24", "us-east-1a") + sub2 := getTestSubnetID(t, vpcSvc, vid, "10.0.27.0/24", "us-east-1b") + + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("rollback-lb"), + Subnets: []*string{aws.String(sub1)}, + }, testAccountID) + require.NoError(t, err) + arn := *out.LoadBalancers[0].LoadBalancerArn + require.Equal(t, 1, countManagedENIs(t, vpcSvc)) + + // Terminate fails mid-relaunch (e.g. owning daemon unreachable). The ENI + // created for the added subnet must be rolled back, not leaked. + mock.terminateErr = errors.New("no responders available") + + _, err = svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + Subnets: []*string{aws.String(sub1), aws.String(sub2)}, + }, testAccountID) + require.Error(t, err) + + // No leak: still the single original ENI; LB record unchanged; no relaunch. + assert.Equal(t, 1, countManagedENIs(t, vpcSvc)) + assert.Len(t, mock.launchCalls, 1) + lb := describeLB(t, svc, arn) + assert.Equal(t, []string{sub1}, subnetIDsOfLB(lb)) +} + +func TestSetSubnets_Idempotent(t *testing.T) { + svc, vpcSvc, mock := setupSubnetTestService(t) + vid := vpcID(t, vpcSvc) + sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.26.0/24", "us-east-1a") + + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("idem-lb"), + Subnets: []*string{aws.String(sub1)}, + }, testAccountID) + require.NoError(t, err) + arn := *out.LoadBalancers[0].LoadBalancerArn + + _, err = svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + Subnets: []*string{aws.String(sub1)}, + }, testAccountID) + require.NoError(t, err) + + // No change: no relaunch, no terminate, ENI count unchanged. + assert.Equal(t, 1, countManagedENIs(t, vpcSvc)) + assert.Empty(t, mock.terminateCalls) + assert.Len(t, mock.launchCalls, 1) +} + +func TestSetSubnets_SubnetMappings(t *testing.T) { + svc, vpcSvc, _ := setupSubnetTestService(t) + vid := vpcID(t, vpcSvc) + sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.27.0/24", "us-east-1a") + sub2 := getTestSubnetID(t, vpcSvc, vid, "10.0.28.0/24", "us-east-1b") + + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("mapping-lb"), + Subnets: []*string{aws.String(sub1)}, + }, testAccountID) + require.NoError(t, err) + arn := *out.LoadBalancers[0].LoadBalancerArn + + _, err = svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + SubnetMappings: []*elbv2.SubnetMapping{ + {SubnetId: aws.String(sub1)}, + {SubnetId: aws.String(sub2)}, + }, + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, 2, countManagedENIs(t, vpcSvc)) +} + +func TestSetSubnets_WithoutVPCService(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "novpc-subnets") + + res, err := svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + Subnets: []*string{aws.String("subnet-aaa"), aws.String("subnet-bbb")}, + }, testAccountID) + require.NoError(t, err) + assert.Len(t, res.AvailabilityZones, 2) + assert.ElementsMatch(t, []string{"subnet-aaa", "subnet-bbb"}, subnetIDsOfLB(describeLB(t, svc, arn))) +} + +func TestSetSubnets_MissingParams(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "subnets-mp") + + _, err := svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorMissingParameter) + + _, err = svc.SetSubnets(&elbv2.SetSubnetsInput{ + Subnets: []*string{aws.String("subnet-aaa")}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorMissingParameter) +} + +func TestSetSubnets_NotFound(t *testing.T) { + svc := setupTestService(t) + _, err := svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-deadbeef"), + Subnets: []*string{aws.String("subnet-aaa")}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2LoadBalancerNotFound) +} + +func TestSetSubnets_CrossAccount(t *testing.T) { + svc := setupTestService(t) + arn := createLBArn(t, svc, "subnets-xacct") + + _, err := svc.SetSubnets(&elbv2.SetSubnetsInput{ + LoadBalancerArn: aws.String(arn), + Subnets: []*string{aws.String("subnet-aaa")}, + }, "999999999999") + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2LoadBalancerNotFound) +} + +func subnetIDsOfLB(lb *elbv2.LoadBalancer) []string { + out := make([]string, 0, len(lb.AvailabilityZones)) + for _, az := range lb.AvailabilityZones { + out = append(out, aws.StringValue(az.SubnetId)) + } + return out +} diff --git a/spinifex/handlers/elbv2/service_impl_listener_certs.go b/spinifex/handlers/elbv2/service_impl_listener_certs.go new file mode 100644 index 000000000..2ba76fe6b --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_listener_certs.go @@ -0,0 +1,174 @@ +package handlers_elbv2 + +import ( + "errors" + "log/slog" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +// listenerCertsToSDK renders stored listener certificates as SDK certificates. +func listenerCertsToSDK(certs []ListenerCertificate) []*elbv2.Certificate { + out := make([]*elbv2.Certificate, 0, len(certs)) + for _, c := range certs { + out = append(out, &elbv2.Certificate{ + CertificateArn: aws.String(c.CertificateArn), + IsDefault: aws.Bool(c.IsDefault), + }) + } + return out +} + +// AddListenerCertificates attaches additional (SNI) certificates to a secure +// listener. The default certificate is set at create time and is unaffected; +// added certificates are non-default. Re-adding an existing certificate is a +// no-op. +func (s *ELBv2ServiceImpl) AddListenerCertificates(input *elbv2.AddListenerCertificatesInput, accountID string) (*elbv2.AddListenerCertificatesOutput, error) { + if input == nil || input.ListenerArn == nil || *input.ListenerArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + if len(input.Certificates) == 0 { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + + listener, err := s.store.GetListenerByArn(*input.ListenerArn) + if err != nil { + slog.Error("AddListenerCertificates: failed to get listener", "arn", *input.ListenerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if listener == nil || listener.AccountID != accountID { + return nil, errors.New(awserrors.ErrorELBv2ListenerNotFound) + } + if !protocolRequiresCert(listener.Protocol) { + return nil, errors.New(awserrors.ErrorELBv2InvalidConfigurationRequest) + } + + updated := *listener + certs := append([]ListenerCertificate(nil), updated.Certificates...) + seen := make(map[string]bool, len(certs)) + for _, c := range certs { + seen[c.CertificateArn] = true + } + for _, c := range input.Certificates { + if c == nil || c.CertificateArn == nil || *c.CertificateArn == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + if seen[*c.CertificateArn] { + continue + } + certs = append(certs, ListenerCertificate{CertificateArn: *c.CertificateArn, IsDefault: false}) + seen[*c.CertificateArn] = true + } + updated.Certificates = certs + + if err := s.validateListenerCerts(certs, accountID); err != nil { + return nil, err + } + + if err := s.store.PutListener(&updated); err != nil { + slog.Error("AddListenerCertificates: failed to persist record", "listenerId", updated.ListenerID, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + + return &elbv2.AddListenerCertificatesOutput{ + Certificates: listenerCertsToSDK(certs), + }, nil +} + +// RemoveListenerCertificates detaches certificates from a listener by ARN. The +// default certificate cannot be removed. Removing an absent certificate is a +// no-op (idempotent). +func (s *ELBv2ServiceImpl) RemoveListenerCertificates(input *elbv2.RemoveListenerCertificatesInput, accountID string) (*elbv2.RemoveListenerCertificatesOutput, error) { + if input == nil || input.ListenerArn == nil || *input.ListenerArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + if len(input.Certificates) == 0 { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + + listener, err := s.store.GetListenerByArn(*input.ListenerArn) + if err != nil { + slog.Error("RemoveListenerCertificates: failed to get listener", "arn", *input.ListenerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if listener == nil || listener.AccountID != accountID { + return nil, errors.New(awserrors.ErrorELBv2ListenerNotFound) + } + + remove := make(map[string]bool, len(input.Certificates)) + for _, c := range input.Certificates { + if c == nil || c.CertificateArn == nil || *c.CertificateArn == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + remove[*c.CertificateArn] = true + } + + updated := *listener + kept := make([]ListenerCertificate, 0, len(updated.Certificates)) + for _, c := range updated.Certificates { + if remove[c.CertificateArn] { + if c.IsDefault { + // The default certificate is managed via the listener itself. + return nil, errors.New(awserrors.ErrorELBv2InvalidConfigurationRequest) + } + continue + } + kept = append(kept, c) + } + updated.Certificates = kept + + if err := s.store.PutListener(&updated); err != nil { + slog.Error("RemoveListenerCertificates: failed to persist record", "listenerId", updated.ListenerID, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + + return &elbv2.RemoveListenerCertificatesOutput{}, nil +} + +// DescribeListenerCertificates returns the certificates attached to a listener. +func (s *ELBv2ServiceImpl) DescribeListenerCertificates(input *elbv2.DescribeListenerCertificatesInput, accountID string) (*elbv2.DescribeListenerCertificatesOutput, error) { + if input == nil || input.ListenerArn == nil || *input.ListenerArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + + listener, err := s.store.GetListenerByArn(*input.ListenerArn) + if err != nil { + slog.Error("DescribeListenerCertificates: failed to get listener", "arn", *input.ListenerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if listener == nil || listener.AccountID != accountID { + return nil, errors.New(awserrors.ErrorELBv2ListenerNotFound) + } + + return &elbv2.DescribeListenerCertificatesOutput{ + Certificates: listenerCertsToSDK(listener.Certificates), + }, nil +} + +// DescribeSSLPolicies returns the fixed catalog of supported security policies. +// An explicit Names filter selects a subset; an unknown name is rejected. +func (s *ELBv2ServiceImpl) DescribeSSLPolicies(input *elbv2.DescribeSSLPoliciesInput, _ string) (*elbv2.DescribeSSLPoliciesOutput, error) { + var names []string + if input != nil && len(input.Names) > 0 { + for _, n := range input.Names { + if n == nil || *n == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + if !isKnownSslPolicy(*n) { + return nil, errors.New(awserrors.ErrorELBv2SSLPolicyNotFound) + } + names = append(names, *n) + } + } else { + names = sslPolicyOrder + } + + policies := make([]*elbv2.SslPolicy, 0, len(names)) + for _, n := range names { + policies = append(policies, sslPolicyToSDK(sslPolicyCatalog[n])) + } + + return &elbv2.DescribeSSLPoliciesOutput{SslPolicies: policies}, nil +} diff --git a/spinifex/handlers/elbv2/service_impl_listener_certs_test.go b/spinifex/handlers/elbv2/service_impl_listener_certs_test.go new file mode 100644 index 000000000..4528af027 --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_listener_certs_test.go @@ -0,0 +1,317 @@ +package handlers_elbv2 + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const testCertArn = "arn:aws:acm:ap-southeast-2:000000000001:certificate/aaaaaaaa-1111" +const testCertArn2 = "arn:aws:acm:ap-southeast-2:000000000001:certificate/bbbbbbbb-2222" + +// fixedResponseAction is a valid default action that needs no target group. +func fixedResponseAction() []*elbv2.Action { + return []*elbv2.Action{{ + Type: aws.String(ActionTypeFixedResponse), + FixedResponseConfig: &elbv2.FixedResponseActionConfig{ + StatusCode: aws.String("200"), + }, + }} +} + +func createHTTPSListener(t *testing.T, svc *ELBv2ServiceImpl) string { + t.Helper() + lbArn := createLBArn(t, svc, "https-lb") + out, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: aws.String(lbArn), + Protocol: aws.String(ProtocolHTTPS), + Port: aws.Int64(443), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + DefaultActions: fixedResponseAction(), + }, testAccountID) + require.NoError(t, err) + return *out.Listeners[0].ListenerArn +} + +func TestCreateListener_HTTPSRequiresCert(t *testing.T) { + svc := setupTestService(t) + lbArn := createLBArn(t, svc, "https-nocert") + + _, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: aws.String(lbArn), + Protocol: aws.String(ProtocolHTTPS), + Port: aws.Int64(443), + DefaultActions: fixedResponseAction(), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2CertificateNotFound) +} + +func TestCreateListener_HTTPSDefaultsSslPolicy(t *testing.T) { + svc := setupTestService(t) + lbArn := createLBArn(t, svc, "https-default-policy") + + out, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: aws.String(lbArn), + Protocol: aws.String(ProtocolHTTPS), + Port: aws.Int64(443), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + DefaultActions: fixedResponseAction(), + }, testAccountID) + require.NoError(t, err) + + l := out.Listeners[0] + assert.Equal(t, DefaultSslPolicy, aws.StringValue(l.SslPolicy)) + require.Len(t, l.Certificates, 1) + assert.Equal(t, testCertArn, aws.StringValue(l.Certificates[0].CertificateArn)) + assert.True(t, aws.BoolValue(l.Certificates[0].IsDefault), "the sole cert must be marked default") + + // DescribeListeners round-trips certificates and SslPolicy. + desc, err := svc.DescribeListeners(&elbv2.DescribeListenersInput{ + ListenerArns: []*string{l.ListenerArn}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, desc.Listeners, 1) + assert.Equal(t, DefaultSslPolicy, aws.StringValue(desc.Listeners[0].SslPolicy)) + require.Len(t, desc.Listeners[0].Certificates, 1) +} + +func TestCreateListener_HTTPRejectsCert(t *testing.T) { + svc := setupTestService(t) + lbArn := createLBArn(t, svc, "http-withcert") + + _, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: aws.String(lbArn), + Protocol: aws.String(ProtocolHTTP), + Port: aws.Int64(80), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + DefaultActions: fixedResponseAction(), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorInvalidParameterValue) +} + +func TestCreateListener_UnknownSslPolicy(t *testing.T) { + svc := setupTestService(t) + lbArn := createLBArn(t, svc, "https-badpolicy") + + _, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: aws.String(lbArn), + Protocol: aws.String(ProtocolHTTPS), + Port: aws.Int64(443), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + SslPolicy: aws.String("ELBSecurityPolicy-Does-Not-Exist"), + DefaultActions: fixedResponseAction(), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2SSLPolicyNotFound) +} + +func TestModifyListener_SwitchToHTTPSRequiresCert(t *testing.T) { + svc := setupTestService(t) + lbArn := createLBArn(t, svc, "switch-lb") + lst, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: aws.String(lbArn), + Protocol: aws.String(ProtocolHTTP), + Port: aws.Int64(80), + DefaultActions: fixedResponseAction(), + }, testAccountID) + require.NoError(t, err) + arn := lst.Listeners[0].ListenerArn + + // HTTP -> HTTPS without a cert is rejected. + _, err = svc.ModifyListener(&elbv2.ModifyListenerInput{ + ListenerArn: arn, + Protocol: aws.String(ProtocolHTTPS), + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2CertificateNotFound) + + // HTTP -> HTTPS with a cert succeeds and defaults the policy. + out, err := svc.ModifyListener(&elbv2.ModifyListenerInput{ + ListenerArn: arn, + Protocol: aws.String(ProtocolHTTPS), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, DefaultSslPolicy, aws.StringValue(out.Listeners[0].SslPolicy)) + require.Len(t, out.Listeners[0].Certificates, 1) +} + +func TestModifyListener_SwitchAwayClearsCerts(t *testing.T) { + svc := setupTestService(t) + arn := createHTTPSListener(t, svc) + + out, err := svc.ModifyListener(&elbv2.ModifyListenerInput{ + ListenerArn: aws.String(arn), + Protocol: aws.String(ProtocolHTTP), + }, testAccountID) + require.NoError(t, err) + assert.Empty(t, out.Listeners[0].Certificates, "certs cleared when leaving a secure protocol") + assert.Nil(t, out.Listeners[0].SslPolicy) +} + +func TestListenerCertificates_AddRemoveDescribe(t *testing.T) { + svc := setupTestService(t) + arn := createHTTPSListener(t, svc) + + // Add an SNI cert. + addOut, err := svc.AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + ListenerArn: aws.String(arn), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn2)}}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, addOut.Certificates, 2) + + // Re-adding is idempotent. + addOut, err = svc.AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + ListenerArn: aws.String(arn), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn2)}}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, addOut.Certificates, 2) + + // Describe shows both. + desc, err := svc.DescribeListenerCertificates(&elbv2.DescribeListenerCertificatesInput{ + ListenerArn: aws.String(arn), + }, testAccountID) + require.NoError(t, err) + require.Len(t, desc.Certificates, 2) + + // Removing the default cert is rejected. + _, err = svc.RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{ + ListenerArn: aws.String(arn), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2InvalidConfigurationRequest) + + // Removing the SNI cert succeeds. + _, err = svc.RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{ + ListenerArn: aws.String(arn), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn2)}}, + }, testAccountID) + require.NoError(t, err) + + desc, err = svc.DescribeListenerCertificates(&elbv2.DescribeListenerCertificatesInput{ + ListenerArn: aws.String(arn), + }, testAccountID) + require.NoError(t, err) + require.Len(t, desc.Certificates, 1) + assert.Equal(t, testCertArn, aws.StringValue(desc.Certificates[0].CertificateArn)) +} + +func TestAddListenerCertificates_HTTPRejected(t *testing.T) { + svc := setupTestService(t) + lbArn := createLBArn(t, svc, "http-addcert") + lst, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: aws.String(lbArn), + Protocol: aws.String(ProtocolHTTP), + Port: aws.Int64(80), + DefaultActions: fixedResponseAction(), + }, testAccountID) + require.NoError(t, err) + + _, err = svc.AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + ListenerArn: lst.Listeners[0].ListenerArn, + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2InvalidConfigurationRequest) +} + +func TestListenerCertificates_ValidationAndNotFound(t *testing.T) { + svc := setupTestService(t) + arn := createHTTPSListener(t, svc) + const otherAccount = "999999999999" + badArn := "arn:aws:elasticloadbalancing:us-east-1:000000000001:listener/app/x/y/z" + + // Add: missing arn, empty certs, nil cert entry, cross-account not-found. + _, err := svc.AddListenerCertificates(&elbv2.AddListenerCertificatesInput{}, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) + + _, err = svc.AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + ListenerArn: aws.String(arn), + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) + + _, err = svc.AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + ListenerArn: aws.String(arn), + Certificates: []*elbv2.Certificate{{}}, + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) + + _, err = svc.AddListenerCertificates(&elbv2.AddListenerCertificatesInput{ + ListenerArn: aws.String(arn), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn2)}}, + }, otherAccount) + assert.EqualError(t, err, awserrors.ErrorELBv2ListenerNotFound) + + // Remove: missing arn, empty certs, nil cert entry, cross-account not-found. + _, err = svc.RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{}, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) + + _, err = svc.RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{ + ListenerArn: aws.String(arn), + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) + + _, err = svc.RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{ + ListenerArn: aws.String(arn), + Certificates: []*elbv2.Certificate{{}}, + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) + + _, err = svc.RemoveListenerCertificates(&elbv2.RemoveListenerCertificatesInput{ + ListenerArn: aws.String(badArn), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorELBv2ListenerNotFound) + + // Describe: missing arn, cross-account not-found. + _, err = svc.DescribeListenerCertificates(&elbv2.DescribeListenerCertificatesInput{}, testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) + + _, err = svc.DescribeListenerCertificates(&elbv2.DescribeListenerCertificatesInput{ + ListenerArn: aws.String(arn), + }, otherAccount) + assert.EqualError(t, err, awserrors.ErrorELBv2ListenerNotFound) +} + +func TestDescribeSSLPolicies_EmptyName(t *testing.T) { + svc := setupTestService(t) + _, err := svc.DescribeSSLPolicies(&elbv2.DescribeSSLPoliciesInput{ + Names: []*string{aws.String("")}, + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestDescribeSSLPolicies(t *testing.T) { + svc := setupTestService(t) + + // No filter → full catalog. + all, err := svc.DescribeSSLPolicies(&elbv2.DescribeSSLPoliciesInput{}, testAccountID) + require.NoError(t, err) + require.Len(t, all.SslPolicies, len(sslPolicyOrder)) + assert.Equal(t, DefaultSslPolicy, aws.StringValue(all.SslPolicies[0].Name)) + require.NotEmpty(t, all.SslPolicies[0].Ciphers) + require.NotEmpty(t, all.SslPolicies[0].SslProtocols) + + // Name filter → subset. + one, err := svc.DescribeSSLPolicies(&elbv2.DescribeSSLPoliciesInput{ + Names: []*string{aws.String(DefaultSslPolicy)}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, one.SslPolicies, 1) + + // Unknown name → error. + _, err = svc.DescribeSSLPolicies(&elbv2.DescribeSSLPoliciesInput{ + Names: []*string{aws.String("nope")}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2SSLPolicyNotFound) +} diff --git a/spinifex/handlers/elbv2/service_impl_nlb_sg.go b/spinifex/handlers/elbv2/service_impl_nlb_sg.go new file mode 100644 index 000000000..a1a2f92b5 --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_nlb_sg.go @@ -0,0 +1,252 @@ +package handlers_elbv2 + +import ( + "errors" + "fmt" + "log/slog" + "net" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +// nlbManagedSGName returns the deterministic name of the internal security +// group minted for an NLB. Stable so recovery paths can re-resolve it. +func nlbManagedSGName(lbID string) string { + return "spinifex-nlb-" + lbID +} + +// lbENIGroups returns the security groups to attach to an LB's data-plane ENIs. +// NLBs use the internally-minted managed SG (customer SGs are rejected at +// create time); ALBs use the customer-supplied SGs. Shared by the create-time +// ENI loop and the SetSubnets relaunch path so both attach the same SG. +func lbENIGroups(lb *LoadBalancerRecord) []string { + if lb.Type == LoadBalancerTypeNetwork { + if lb.NLBManagedSGID != "" { + return []string{lb.NLBManagedSGID} + } + return nil + } + return lb.SecurityGroups +} + +// createNLBManagedSG mints the internal front-end SG for an NLB in the LB's VPC +// and returns its ID. The VPC is resolved from the LB's first subnet. The SG is +// tagged like the LB's other managed resources so the ownership sweep can find +// it. +func (s *ELBv2ServiceImpl) createNLBManagedSG(lbID, lbArn, subnetID, accountID string) (string, error) { + subnet, err := s.VPCService.GetSubnet(accountID, subnetID) + if err != nil { + return "", fmt.Errorf("resolve subnet %s vpc: %w", subnetID, err) + } + if subnet == nil || subnet.VpcId == "" { + return "", fmt.Errorf("subnet %s has no vpc", subnetID) + } + + out, err := s.VPCService.CreateSecurityGroup(&ec2.CreateSecurityGroupInput{ + GroupName: aws.String(nlbManagedSGName(lbID)), + Description: aws.String(fmt.Sprintf("Managed front-end SG for NLB %s", lbID)), + VpcId: aws.String(subnet.VpcId), + TagSpecifications: []*ec2.TagSpecification{{ + ResourceType: aws.String("security-group"), + Tags: []*ec2.Tag{ + {Key: aws.String(elbv2ManagedByTag), Value: aws.String(elbv2ManagedByValue)}, + {Key: aws.String(elbv2LBTag), Value: aws.String(lbArn)}, + }, + }}, + }, accountID) + if err != nil { + return "", fmt.Errorf("create NLB SG: %w", err) + } + if out == nil || out.GroupId == nil || *out.GroupId == "" { + return "", errors.New("CreateSecurityGroup returned empty GroupId") + } + return *out.GroupId, nil +} + +// deleteNLBManagedSG best-effort deletes an NLB's managed SG. Callable on the +// create-rollback and delete paths; a nil VPC service or empty ID is a no-op, +// and a delete failure is logged but not surfaced (the SG can only be deleted +// once its ENIs are gone, which the delete path guarantees). +func (s *ELBv2ServiceImpl) deleteNLBManagedSG(sgID, accountID string) { + if s.VPCService == nil || sgID == "" { + return + } + if _, err := s.VPCService.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{ + GroupId: aws.String(sgID), + }, accountID); err != nil { + slog.Warn("deleteNLBManagedSG: delete failed", "sg", sgID, "err", err) + } +} + +// resolveNLBIngressCIDRs returns the client CIDRs a listener port is opened to. +// An explicit override wins; otherwise the default is scheme-based: +// internet-facing → 0.0.0.0/0, internal → the LB's VPC CIDR. +func (s *ELBv2ServiceImpl) resolveNLBIngressCIDRs(lb *LoadBalancerRecord) ([]string, error) { + if len(lb.NLBIngressCIDRs) > 0 { + return lb.NLBIngressCIDRs, nil + } + if lb.Scheme == SchemeInternal { + cidr, err := s.vpcCIDR(lb.VpcId, lb.AccountID) + if err != nil { + return nil, err + } + return []string{cidr}, nil + } + return []string{"0.0.0.0/0"}, nil +} + +// vpcCIDR returns the primary CIDR block of a VPC. +func (s *ELBv2ServiceImpl) vpcCIDR(vpcID, accountID string) (string, error) { + if s.VPCService == nil { + return "", errors.New("vpc service unavailable") + } + if vpcID == "" { + return "", errors.New("empty vpc id") + } + out, err := s.VPCService.DescribeVpcs(&ec2.DescribeVpcsInput{ + VpcIds: aws.StringSlice([]string{vpcID}), + }, accountID) + if err != nil { + return "", fmt.Errorf("describe vpc %s: %w", vpcID, err) + } + for _, v := range out.Vpcs { + if v != nil && aws.StringValue(v.CidrBlock) != "" { + return *v.CidrBlock, nil + } + } + return "", fmt.Errorf("vpc %s has no cidr", vpcID) +} + +// listenerIPProtocols maps an ELBv2 listener protocol to the IP protocols its +// SG ingress rule(s) cover. TCP/TLS ride tcp; UDP rides udp; TCP_UDP opens both. +func listenerIPProtocols(protocol string) []string { + switch protocol { + case ProtocolUDP: + return []string{"udp"} + case ProtocolTCPUDP: + return []string{"tcp", "udp"} + default: // TCP, TLS + return []string{"tcp"} + } +} + +// authorizeNLBListenerPort opens a listener port on the NLB's managed SG for +// each (protocol, CIDR) pair. Each rule is authorized in its own call so an +// already-present rule (Duplicate, swallowed) never blocks a sibling rule — +// keeping re-create / reconcile idempotent. No-op when there is no managed SG. +func (s *ELBv2ServiceImpl) authorizeNLBListenerPort(lb *LoadBalancerRecord, protocol string, port int64, cidrs []string, accountID string) error { + if s.VPCService == nil || lb.NLBManagedSGID == "" { + return nil + } + for _, proto := range listenerIPProtocols(protocol) { + for _, cidr := range cidrs { + _, err := s.VPCService.AuthorizeSecurityGroupIngress(&ec2.AuthorizeSecurityGroupIngressInput{ + GroupId: aws.String(lb.NLBManagedSGID), + IpPermissions: []*ec2.IpPermission{{ + IpProtocol: aws.String(proto), + FromPort: aws.Int64(port), + ToPort: aws.Int64(port), + IpRanges: []*ec2.IpRange{{ + CidrIp: aws.String(cidr), + Description: aws.String(fmt.Sprintf("NLB %s listener :%d", lb.LoadBalancerID, port)), + }}, + }}, + }, accountID) + if err != nil && !awserrors.IsErrorCode(err, awserrors.ErrorInvalidPermissionDuplicate) { + return fmt.Errorf("authorize :%d/%s from %s: %w", port, proto, cidr, err) + } + } + } + return nil +} + +// revokeNLBListenerPort removes the listener-port rules authorizeNLBListenerPort +// added. An absent rule (NotFound, swallowed) is fine so delete / re-CIDR paths +// stay idempotent. No-op when there is no managed SG. +func (s *ELBv2ServiceImpl) revokeNLBListenerPort(lb *LoadBalancerRecord, protocol string, port int64, cidrs []string, accountID string) error { + if s.VPCService == nil || lb.NLBManagedSGID == "" { + return nil + } + for _, proto := range listenerIPProtocols(protocol) { + for _, cidr := range cidrs { + _, err := s.VPCService.RevokeSecurityGroupIngress(&ec2.RevokeSecurityGroupIngressInput{ + GroupId: aws.String(lb.NLBManagedSGID), + IpPermissions: []*ec2.IpPermission{{ + IpProtocol: aws.String(proto), + FromPort: aws.Int64(port), + ToPort: aws.Int64(port), + IpRanges: []*ec2.IpRange{{CidrIp: aws.String(cidr)}}, + }}, + }, accountID) + if err != nil && !awserrors.IsErrorCode(err, awserrors.ErrorInvalidPermissionNotFound) { + return fmt.Errorf("revoke :%d/%s from %s: %w", port, proto, cidr, err) + } + } + } + return nil +} + +// SetLoadBalancerIngressCIDRs overrides the client CIDRs the NLB's listener +// ports are opened to (e.g. an EKS public endpoint's publicAccessCidrs) and +// re-authorizes every existing listener accordingly. Passing nil/empty reverts +// to the scheme-based default. Idempotent: the old rules are revoked and the +// new ones authorized, both swallowing the benign duplicate / not-found errors. +func (s *ELBv2ServiceImpl) SetLoadBalancerIngressCIDRs(lbArn string, cidrs []string, accountID string) error { + if lbArn == "" { + return errors.New(awserrors.ErrorMissingParameter) + } + lb, err := s.store.GetLoadBalancerByArn(lbArn) + if err != nil { + slog.Error("SetLoadBalancerIngressCIDRs: failed to get LB", "arn", lbArn, "err", err) + return errors.New(awserrors.ErrorServerInternal) + } + if lb == nil { + return errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) + } + if lb.Type != LoadBalancerTypeNetwork { + return errors.New(awserrors.ErrorELBv2InvalidConfigurationRequest) + } + for _, c := range cidrs { + if _, _, perr := net.ParseCIDR(c); perr != nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + } + + oldCIDRs, err := s.resolveNLBIngressCIDRs(lb) + if err != nil { + slog.Error("SetLoadBalancerIngressCIDRs: resolve old CIDRs failed", "arn", lbArn, "err", err) + return errors.New(awserrors.ErrorServerInternal) + } + listeners, err := s.store.ListListenersByLB(lb.LoadBalancerArn) + if err != nil { + slog.Error("SetLoadBalancerIngressCIDRs: list listeners failed", "arn", lbArn, "err", err) + return errors.New(awserrors.ErrorServerInternal) + } + + lb.NLBIngressCIDRs = cidrs + newCIDRs, err := s.resolveNLBIngressCIDRs(lb) + if err != nil { + slog.Error("SetLoadBalancerIngressCIDRs: resolve new CIDRs failed", "arn", lbArn, "err", err) + return errors.New(awserrors.ErrorServerInternal) + } + + for _, l := range listeners { + if err := s.revokeNLBListenerPort(lb, l.Protocol, l.Port, oldCIDRs, accountID); err != nil { + slog.Error("SetLoadBalancerIngressCIDRs: revoke failed", "arn", lbArn, "port", l.Port, "err", err) + return errors.New(awserrors.ErrorServerInternal) + } + if err := s.authorizeNLBListenerPort(lb, l.Protocol, l.Port, newCIDRs, accountID); err != nil { + slog.Error("SetLoadBalancerIngressCIDRs: authorize failed", "arn", lbArn, "port", l.Port, "err", err) + return errors.New(awserrors.ErrorServerInternal) + } + } + + if err := s.store.PutLoadBalancer(lb); err != nil { + slog.Error("SetLoadBalancerIngressCIDRs: persist failed", "arn", lbArn, "err", err) + return errors.New(awserrors.ErrorServerInternal) + } + slog.Info("SetLoadBalancerIngressCIDRs completed", "arn", lbArn, "cidrs", cidrs, "listeners", len(listeners), "accountID", accountID) + return nil +} diff --git a/spinifex/handlers/elbv2/service_impl_nlb_sg_test.go b/spinifex/handlers/elbv2/service_impl_nlb_sg_test.go new file mode 100644 index 000000000..b63e6618d --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_nlb_sg_test.go @@ -0,0 +1,298 @@ +package handlers_elbv2 + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/elbv2" + handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// firstSubnet returns the ID + VPC ID of the subnet setupTestServiceWithVPC +// pre-creates. +func firstSubnet(t *testing.T, vpcSvc *handlers_ec2_vpc.VPCServiceImpl) (subnetID, vpcID string) { + t.Helper() + subnets, err := vpcSvc.DescribeSubnets(&ec2.DescribeSubnetsInput{}, testAccountID) + require.NoError(t, err) + require.NotEmpty(t, subnets.Subnets) + return *subnets.Subnets[0].SubnetId, *subnets.Subnets[0].VpcId +} + +// describeSG returns the security group record by ID. +func describeSG(t *testing.T, vpcSvc *handlers_ec2_vpc.VPCServiceImpl, sgID string) *ec2.SecurityGroup { + t.Helper() + out, err := vpcSvc.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ + GroupIds: aws.StringSlice([]string{sgID}), + }, testAccountID) + require.NoError(t, err) + require.Len(t, out.SecurityGroups, 1) + return out.SecurityGroups[0] +} + +// sgHasRule reports whether the SG has an ingress rule for the proto/port/cidr. +func sgHasRule(sg *ec2.SecurityGroup, proto string, port int64, cidr string) bool { + for _, p := range sg.IpPermissions { + if aws.StringValue(p.IpProtocol) != proto { + continue + } + if aws.Int64Value(p.FromPort) != port || aws.Int64Value(p.ToPort) != port { + continue + } + for _, r := range p.IpRanges { + if aws.StringValue(r.CidrIp) == cidr { + return true + } + } + } + return false +} + +func managedENI(t *testing.T, vpcSvc *handlers_ec2_vpc.VPCServiceImpl) *ec2.NetworkInterface { + t.Helper() + out, err := vpcSvc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{}, testAccountID) + require.NoError(t, err) + for _, eni := range out.NetworkInterfaces { + if eni.RequesterManaged != nil && *eni.RequesterManaged { + return eni + } + } + return nil +} + +func createNLB(t *testing.T, svc *ELBv2ServiceImpl, name, scheme, subnetID string) *elbv2.LoadBalancer { + t.Helper() + in := &elbv2.CreateLoadBalancerInput{ + Name: aws.String(name), + Type: aws.String("network"), + Subnets: []*string{aws.String(subnetID)}, + } + if scheme != "" { + in.Scheme = aws.String(scheme) + } + out, err := svc.CreateLoadBalancer(in, testAccountID) + require.NoError(t, err) + require.Len(t, out.LoadBalancers, 1) + return out.LoadBalancers[0] +} + +func createTCPListener(t *testing.T, svc *ELBv2ServiceImpl, lbArn *string, port int64) { + t.Helper() + tg, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("tg-nlb-sg"), + Protocol: aws.String(elbv2.ProtocolEnumTcp), + Port: aws.Int64(port), + }, testAccountID) + require.NoError(t, err) + _, err = svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbArn, + Protocol: aws.String(elbv2.ProtocolEnumTcp), + Port: aws.Int64(port), + DefaultActions: []*elbv2.Action{{Type: aws.String("forward"), TargetGroupArn: tg.TargetGroups[0].TargetGroupArn}}, + }, testAccountID) + require.NoError(t, err) +} + +// TestCreateNLB_MintsManagedSGAttachedToENI verifies a network LB gets a +// dedicated internal SG (not the VPC default) attached to its ENI, recorded in +// NLBManagedSGID, while DescribeLoadBalancers still reports no customer SGs. +func TestCreateNLB_MintsManagedSGAttachedToENI(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, vpcID := firstSubnet(t, vpcSvc) + + defaultSGID, err := vpcSvc.FindDefaultSGForVPC(testAccountID, vpcID) + require.NoError(t, err) + + lb := createNLB(t, svc, "nlb-sg", "internal", subnetID) + assert.Empty(t, lb.SecurityGroups, "NLB must not surface the managed SG as a customer SG") + + rec, err := svc.store.GetLoadBalancerByArn(*lb.LoadBalancerArn) + require.NoError(t, err) + require.NotEmpty(t, rec.NLBManagedSGID, "NLB record must carry the managed SG id") + assert.NotEqual(t, defaultSGID, rec.NLBManagedSGID, "managed SG must be distinct from the VPC default SG") + + eni := managedENI(t, vpcSvc) + require.NotNil(t, eni) + require.Len(t, eni.Groups, 1, "NLB ENI must join exactly the managed SG, not the default SG") + assert.Equal(t, rec.NLBManagedSGID, *eni.Groups[0].GroupId) +} + +// TestCreateALB_NoManagedSG verifies the managed-SG behavior is NLB-only: an ALB +// keeps the existing customer-SG / default-SG semantics. +func TestCreateALB_NoManagedSG(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, _ := firstSubnet(t, vpcSvc) + + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("alb-no-sg"), + Subnets: []*string{aws.String(subnetID)}, + }, testAccountID) + require.NoError(t, err) + + rec, err := svc.store.GetLoadBalancerByArn(*out.LoadBalancers[0].LoadBalancerArn) + require.NoError(t, err) + assert.Empty(t, rec.NLBManagedSGID, "ALB must not mint a managed NLB SG") +} + +// TestCreateListener_Internal_OpensPortFromVPCCIDR verifies an internal NLB's +// listener port is authorized on the managed SG from the VPC CIDR. +func TestCreateListener_Internal_OpensPortFromVPCCIDR(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, _ := firstSubnet(t, vpcSvc) + + lb := createNLB(t, svc, "nlb-int", "internal", subnetID) + createTCPListener(t, svc, lb.LoadBalancerArn, 443) + + rec, err := svc.store.GetLoadBalancerByArn(*lb.LoadBalancerArn) + require.NoError(t, err) + sg := describeSG(t, vpcSvc, rec.NLBManagedSGID) + assert.True(t, sgHasRule(sg, "tcp", 443, "10.0.0.0/16"), + "internal NLB listener :443 must be open from the VPC CIDR") + assert.False(t, sgHasRule(sg, "tcp", 443, "0.0.0.0/0")) +} + +// TestCreateListener_InternetFacing_OpensPortFromAnywhere verifies an +// internet-facing NLB's listener port is authorized from 0.0.0.0/0. +func TestCreateListener_InternetFacing_OpensPortFromAnywhere(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, _ := firstSubnet(t, vpcSvc) + + lb := createNLB(t, svc, "nlb-pub", "internet-facing", subnetID) + createTCPListener(t, svc, lb.LoadBalancerArn, 443) + + rec, err := svc.store.GetLoadBalancerByArn(*lb.LoadBalancerArn) + require.NoError(t, err) + sg := describeSG(t, vpcSvc, rec.NLBManagedSGID) + assert.True(t, sgHasRule(sg, "tcp", 443, "0.0.0.0/0"), + "internet-facing NLB listener :443 must be open from 0.0.0.0/0") +} + +// TestCreateListener_TCPUDP_OpensBothProtocols verifies a TCP_UDP listener opens +// both tcp and udp on the listener port. +func TestCreateListener_TCPUDP_OpensBothProtocols(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, _ := firstSubnet(t, vpcSvc) + + lb := createNLB(t, svc, "nlb-tcpudp", "internet-facing", subnetID) + tg, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("tg-tcpudp"), + Protocol: aws.String(elbv2.ProtocolEnumTcpUdp), + Port: aws.Int64(53), + }, testAccountID) + require.NoError(t, err) + _, err = svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lb.LoadBalancerArn, + Protocol: aws.String(elbv2.ProtocolEnumTcpUdp), + Port: aws.Int64(53), + DefaultActions: []*elbv2.Action{{Type: aws.String("forward"), TargetGroupArn: tg.TargetGroups[0].TargetGroupArn}}, + }, testAccountID) + require.NoError(t, err) + + rec, err := svc.store.GetLoadBalancerByArn(*lb.LoadBalancerArn) + require.NoError(t, err) + sg := describeSG(t, vpcSvc, rec.NLBManagedSGID) + assert.True(t, sgHasRule(sg, "tcp", 53, "0.0.0.0/0")) + assert.True(t, sgHasRule(sg, "udp", 53, "0.0.0.0/0")) +} + +// TestSetLoadBalancerIngressCIDRs_RewritesListenerRules verifies the override API +// swaps the scheme-default rule for the supplied CIDRs across existing listeners +// and is idempotent on re-invocation. +func TestSetLoadBalancerIngressCIDRs_RewritesListenerRules(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, _ := firstSubnet(t, vpcSvc) + + lb := createNLB(t, svc, "nlb-cidr", "internet-facing", subnetID) + createTCPListener(t, svc, lb.LoadBalancerArn, 443) + + rec, err := svc.store.GetLoadBalancerByArn(*lb.LoadBalancerArn) + require.NoError(t, err) + sgID := rec.NLBManagedSGID + + // Default opened 0.0.0.0/0. + assert.True(t, sgHasRule(describeSG(t, vpcSvc, sgID), "tcp", 443, "0.0.0.0/0")) + + require.NoError(t, svc.SetLoadBalancerIngressCIDRs(*lb.LoadBalancerArn, []string{"203.0.113.0/24"}, testAccountID)) + + sg := describeSG(t, vpcSvc, sgID) + assert.True(t, sgHasRule(sg, "tcp", 443, "203.0.113.0/24"), "new CIDR must be authorized") + assert.False(t, sgHasRule(sg, "tcp", 443, "0.0.0.0/0"), "old default rule must be revoked") + + // Idempotent re-apply. + require.NoError(t, svc.SetLoadBalancerIngressCIDRs(*lb.LoadBalancerArn, []string{"203.0.113.0/24"}, testAccountID)) + sg = describeSG(t, vpcSvc, sgID) + assert.True(t, sgHasRule(sg, "tcp", 443, "203.0.113.0/24")) + + // Persisted override drives the resolved CIDRs. + rec, err = svc.store.GetLoadBalancerByArn(*lb.LoadBalancerArn) + require.NoError(t, err) + assert.Equal(t, []string{"203.0.113.0/24"}, rec.NLBIngressCIDRs) +} + +func TestSetLoadBalancerIngressCIDRs_RejectsBadInput(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, _ := firstSubnet(t, vpcSvc) + + lb := createNLB(t, svc, "nlb-badcidr", "internet-facing", subnetID) + err := svc.SetLoadBalancerIngressCIDRs(*lb.LoadBalancerArn, []string{"not-a-cidr"}, testAccountID) + require.Error(t, err) + + // ALB is rejected — managed SG is NLB-only. + albOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("alb-reject"), + Subnets: []*string{aws.String(subnetID)}, + }, testAccountID) + require.NoError(t, err) + err = svc.SetLoadBalancerIngressCIDRs(*albOut.LoadBalancers[0].LoadBalancerArn, []string{"10.0.0.0/8"}, testAccountID) + require.Error(t, err) +} + +// TestDeleteListener_RevokesPort verifies removing a listener closes its port on +// the managed SG. +func TestDeleteListener_RevokesPort(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, _ := firstSubnet(t, vpcSvc) + + lb := createNLB(t, svc, "nlb-del-lst", "internet-facing", subnetID) + createTCPListener(t, svc, lb.LoadBalancerArn, 443) + + lst, err := svc.DescribeListeners(&elbv2.DescribeListenersInput{LoadBalancerArn: lb.LoadBalancerArn}, testAccountID) + require.NoError(t, err) + require.Len(t, lst.Listeners, 1) + + rec, err := svc.store.GetLoadBalancerByArn(*lb.LoadBalancerArn) + require.NoError(t, err) + require.True(t, sgHasRule(describeSG(t, vpcSvc, rec.NLBManagedSGID), "tcp", 443, "0.0.0.0/0")) + + _, err = svc.DeleteListener(&elbv2.DeleteListenerInput{ListenerArn: lst.Listeners[0].ListenerArn}, testAccountID) + require.NoError(t, err) + + assert.False(t, sgHasRule(describeSG(t, vpcSvc, rec.NLBManagedSGID), "tcp", 443, "0.0.0.0/0"), + "deleting the listener must close its port") +} + +// TestDeleteNLB_DeletesManagedSG verifies the managed SG is removed on LB +// teardown. +func TestDeleteNLB_DeletesManagedSG(t *testing.T) { + svc, vpcSvc := setupTestServiceWithVPC(t) + subnetID, _ := firstSubnet(t, vpcSvc) + + lb := createNLB(t, svc, "nlb-del", "internal", subnetID) + rec, err := svc.store.GetLoadBalancerByArn(*lb.LoadBalancerArn) + require.NoError(t, err) + sgID := rec.NLBManagedSGID + require.NotEmpty(t, sgID) + + _, err = svc.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{LoadBalancerArn: lb.LoadBalancerArn}, testAccountID) + require.NoError(t, err) + + out, err := vpcSvc.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ + GroupIds: aws.StringSlice([]string{sgID}), + }, testAccountID) + // Deleted SG: either an empty result or a not-found error is acceptable. + if err == nil { + assert.Empty(t, out.SecurityGroups, "managed SG must be deleted with the NLB") + } +} diff --git a/spinifex/handlers/elbv2/service_impl_rules.go b/spinifex/handlers/elbv2/service_impl_rules.go index 476dc8d51..c1818a868 100644 --- a/spinifex/handlers/elbv2/service_impl_rules.go +++ b/spinifex/handlers/elbv2/service_impl_rules.go @@ -86,6 +86,8 @@ func (s *ELBv2ServiceImpl) CreateRule(input *elbv2.CreateRuleInput, accountID st return nil, errors.New(awserrors.ErrorServerInternal) } + tags := tagsFromSDK(input.Tags) + record := &RuleRecord{ RuleArn: ruleArn, RuleID: ruleID, @@ -95,6 +97,7 @@ func (s *ELBv2ServiceImpl) CreateRule(input *elbv2.CreateRuleInput, accountID st Actions: actions, AccountID: accountID, CreatedAt: time.Now().UTC(), + Tags: tags, } if err := s.store.PutRule(record); err != nil { @@ -176,10 +179,16 @@ func (s *ELBv2ServiceImpl) DeleteRule(input *elbv2.DeleteRuleInput, accountID st return nil, errors.New(awserrors.ErrorServerInternal) } if rule == nil || rule.AccountID != accountID { - return nil, errors.New(awserrors.ErrorELBv2RuleNotFound) + // Idempotent: AWS ELBv2 delete returns success on an absent (or + // not-owned) rule, so tofu destroy retries converge. + return &elbv2.DeleteRuleOutput{}, nil } - listener, _ := s.store.GetListenerByArn(rule.ListenerArn) + listener, err := s.store.GetListenerByArn(rule.ListenerArn) + if err != nil { + slog.Error("DeleteRule: failed to get listener for reload", "listenerArn", rule.ListenerArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } if err := s.store.DeleteRule(rule.RuleID); err != nil { slog.Error("DeleteRule: failed to delete", "ruleId", rule.RuleID, "err", err) @@ -549,25 +558,35 @@ func (s *ELBv2ServiceImpl) validateAndConvertRuleActions(in []*elbv2.Action, lis if a == nil || a.Type == nil { return nil, errors.New(awserrors.ErrorMissingParameter) } - if *a.Type != ActionTypeForward { - // redirect / fixed-response / authenticate-* deferred to mulga-951. + action := listenerActionFromSDK(a) + switch action.Type { + case ActionTypeForward: + if action.TargetGroupArn == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + tg, err := s.store.GetTargetGroupByArn(action.TargetGroupArn) + if err != nil { + slog.Error("rule action: failed to get target group", "arn", action.TargetGroupArn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + if tg == nil { + return nil, errors.New(awserrors.ErrorELBv2TargetGroupNotFound) + } + if !isCompatibleProtocol(listenerProto, tg.Protocol) { + return nil, errors.New(awserrors.ErrorELBv2IncompatibleProtocols) + } + case ActionTypeRedirect: + if err := validateRedirectAction(action.Redirect); err != nil { + return nil, err + } + case ActionTypeFixedResponse: + if action.FixedResponse == nil { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + default: return nil, errors.New(awserrors.ErrorELBv2InvalidConfigurationRequest) } - if a.TargetGroupArn == nil || *a.TargetGroupArn == "" { - return nil, errors.New(awserrors.ErrorMissingParameter) - } - tg, err := s.store.GetTargetGroupByArn(*a.TargetGroupArn) - if err != nil { - slog.Error("rule action: failed to get target group", "arn", *a.TargetGroupArn, "err", err) - return nil, errors.New(awserrors.ErrorServerInternal) - } - if tg == nil { - return nil, errors.New(awserrors.ErrorELBv2TargetGroupNotFound) - } - if !isCompatibleProtocol(listenerProto, tg.Protocol) { - return nil, errors.New(awserrors.ErrorELBv2IncompatibleProtocols) - } - out = append(out, ListenerAction{Type: *a.Type, TargetGroupArn: *a.TargetGroupArn}) + out = append(out, action) } return out, nil } @@ -596,10 +615,7 @@ func ruleRecordToSDK(r *RuleRecord) *elbv2.Rule { rule.Conditions = append(rule.Conditions, ruleConditionToSDK(c)) } for _, a := range r.Actions { - rule.Actions = append(rule.Actions, &elbv2.Action{ - Type: aws.String(a.Type), - TargetGroupArn: aws.String(a.TargetGroupArn), - }) + rule.Actions = append(rule.Actions, listenerActionToSDK(a)) } return rule } @@ -647,10 +663,7 @@ func defaultRuleFromListener(l *ListenerRecord) *elbv2.Rule { IsDefault: aws.Bool(true), } for _, a := range l.DefaultActions { - rule.Actions = append(rule.Actions, &elbv2.Action{ - Type: aws.String(a.Type), - TargetGroupArn: aws.String(a.TargetGroupArn), - }) + rule.Actions = append(rule.Actions, listenerActionToSDK(a)) } return rule } diff --git a/spinifex/handlers/elbv2/service_impl_rules_extra_test.go b/spinifex/handlers/elbv2/service_impl_rules_extra_test.go index 3a1ba79ae..1f8c03f21 100644 --- a/spinifex/handlers/elbv2/service_impl_rules_extra_test.go +++ b/spinifex/handlers/elbv2/service_impl_rules_extra_test.go @@ -87,9 +87,10 @@ func TestDeleteRule_InputValidation(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "MissingParameter") - _, err = env.svc.DeleteRule(&elbv2.DeleteRuleInput{RuleArn: aws.String("arn:none")}, testAccountID) - require.Error(t, err) - assert.Contains(t, err.Error(), "RuleNotFound") + // AWS ELBv2 delete is idempotent: absent rule -> success, not NotFound. + out, err := env.svc.DeleteRule(&elbv2.DeleteRuleInput{RuleArn: aws.String("arn:none")}, testAccountID) + require.NoError(t, err) + assert.NotNil(t, out) } func TestDescribeRules_InputValidation(t *testing.T) { @@ -377,11 +378,23 @@ func TestHAProxyRender_UnsupportedField(t *testing.T) { require.Error(t, err) } -func TestBuildHAProxyRule_RejectsNonForward(t *testing.T) { - _, err := buildHAProxyRule(&RuleRecord{ +func TestBuildHAProxyRule_RoutesToBackend(t *testing.T) { + rule, err := buildHAProxyRule(&RuleRecord{ + RuleID: "r1", + Conditions: []RuleCondition{{Field: RuleFieldPathPattern, Values: []string{"/a"}}}, + }, "bk_target") + require.NoError(t, err) + assert.Equal(t, "bk_target", rule.Backend) +} + +func TestRegisterRuleBackend_RejectsUnknownAction(t *testing.T) { + noopBackend := func(string) {} + noopFixed := func(string, *FixedResponseAction) {} + noopRedirect := func(string, *HAProxyRedirect) {} + _, err := registerRuleBackend(&RuleRecord{ RuleID: "r1", - Actions: []ListenerAction{{Type: "redirect"}}, - }) + Actions: []ListenerAction{{Type: "authenticate-cognito"}}, + }, &ListenerRecord{Protocol: ProtocolHTTP}, noopBackend, noopFixed, noopRedirect) require.Error(t, err) } diff --git a/spinifex/handlers/elbv2/service_impl_rules_test.go b/spinifex/handlers/elbv2/service_impl_rules_test.go index 384d5951f..4bc334d7a 100644 --- a/spinifex/handlers/elbv2/service_impl_rules_test.go +++ b/spinifex/handlers/elbv2/service_impl_rules_test.go @@ -70,6 +70,46 @@ func TestCreateRule_PathPattern(t *testing.T) { assert.Contains(t, *out.Rules[0].RuleArn, ":listener-rule/") } +// TestDeleteLoadBalancer_CascadesRuleDeletion is a regression guard: +// DeleteLoadBalancer must cascade through listener rule deletion. Previously it +// called store.DeleteListener directly, bypassing the rule cascade, so a rule's +// target group stayed pinned as ResourceInUse and was permanently undeletable. +func TestDeleteLoadBalancer_CascadesRuleDeletion(t *testing.T) { + env := newRuleTestEnv(t, "del-cascade") + + // Rule on the listener forwards to the alt TG. + _, err := env.svc.CreateRule(&elbv2.CreateRuleInput{ + ListenerArn: aws.String(env.listenerArn), + Priority: aws.Int64(10), + Conditions: []*elbv2.RuleCondition{{Field: aws.String("path-pattern"), Values: aws.StringSlice([]string{"/api/*"})}}, + Actions: []*elbv2.Action{{Type: aws.String("forward"), TargetGroupArn: aws.String(env.tgAltArn)}}, + }, testAccountID) + require.NoError(t, err) + + // Tear down the LB. + _, err = env.svc.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{ + LoadBalancerArn: aws.String(env.lbArn), + }, testAccountID) + require.NoError(t, err) + + // LB deletion must cascade rule deletion — no rule may survive. + rules, err := env.svc.store.ListRules() + require.NoError(t, err) + assert.Empty(t, rules, "LB deletion must cascade rule deletion") + + // Both target groups must now be deletable: neither an orphan rule nor a + // stale listener default action may pin them as ResourceInUse. + _, err = env.svc.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ + TargetGroupArn: aws.String(env.tgAltArn), + }, testAccountID) + require.NoError(t, err, "rule target group must not be pinned after LB deletion") + + _, err = env.svc.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ + TargetGroupArn: aws.String(env.tgArn), + }, testAccountID) + require.NoError(t, err, "listener default-action target group must not be pinned after LB deletion") +} + func TestCreateRule_PriorityInUse(t *testing.T) { env := newRuleTestEnv(t, "cr-pri") @@ -107,14 +147,75 @@ func TestCreateRule_InvalidPriority(t *testing.T) { } } -func TestCreateRule_RejectsRedirect(t *testing.T) { +func TestCreateRule_RedirectAction(t *testing.T) { + env := newRuleTestEnv(t, "cr-redir") + + out, err := env.svc.CreateRule(&elbv2.CreateRuleInput{ + ListenerArn: aws.String(env.listenerArn), + Priority: aws.Int64(1), + Conditions: []*elbv2.RuleCondition{{Field: aws.String("path-pattern"), Values: aws.StringSlice([]string{"/old/*"})}}, + Actions: []*elbv2.Action{{ + Type: aws.String("redirect"), + RedirectConfig: &elbv2.RedirectActionConfig{ + Protocol: aws.String("HTTPS"), + StatusCode: aws.String("HTTP_301"), + }, + }}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, out.Rules, 1) + require.Len(t, out.Rules[0].Actions, 1) + assert.Equal(t, "redirect", *out.Rules[0].Actions[0].Type) + require.NotNil(t, out.Rules[0].Actions[0].RedirectConfig) + assert.Equal(t, "HTTP_301", *out.Rules[0].Actions[0].RedirectConfig.StatusCode) +} + +func TestCreateRule_FixedResponseAction(t *testing.T) { + env := newRuleTestEnv(t, "cr-fixed") + + out, err := env.svc.CreateRule(&elbv2.CreateRuleInput{ + ListenerArn: aws.String(env.listenerArn), + Priority: aws.Int64(1), + Conditions: []*elbv2.RuleCondition{{Field: aws.String("path-pattern"), Values: aws.StringSlice([]string{"/down"})}}, + Actions: []*elbv2.Action{{ + Type: aws.String("fixed-response"), + FixedResponseConfig: &elbv2.FixedResponseActionConfig{ + StatusCode: aws.String("503"), + ContentType: aws.String("text/plain"), + MessageBody: aws.String("maintenance"), + }, + }}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, out.Rules, 1) + assert.Equal(t, "fixed-response", *out.Rules[0].Actions[0].Type) + require.NotNil(t, out.Rules[0].Actions[0].FixedResponseConfig) +} + +func TestCreateRule_RejectsBadRedirectStatus(t *testing.T) { + env := newRuleTestEnv(t, "cr-badredir") + + _, err := env.svc.CreateRule(&elbv2.CreateRuleInput{ + ListenerArn: aws.String(env.listenerArn), + Priority: aws.Int64(1), + Conditions: []*elbv2.RuleCondition{{Field: aws.String("path-pattern"), Values: aws.StringSlice([]string{"/a"})}}, + Actions: []*elbv2.Action{{ + Type: aws.String("redirect"), + RedirectConfig: &elbv2.RedirectActionConfig{StatusCode: aws.String("HTTP_418")}, + }}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), "InvalidParameterValue") +} + +func TestCreateRule_RejectsUnknownAction(t *testing.T) { env := newRuleTestEnv(t, "cr-rej") _, err := env.svc.CreateRule(&elbv2.CreateRuleInput{ ListenerArn: aws.String(env.listenerArn), Priority: aws.Int64(1), Conditions: []*elbv2.RuleCondition{{Field: aws.String("path-pattern"), Values: aws.StringSlice([]string{"/a"})}}, - Actions: []*elbv2.Action{{Type: aws.String("redirect")}}, + Actions: []*elbv2.Action{{Type: aws.String("authenticate-cognito")}}, }, testAccountID) require.Error(t, err) assert.Contains(t, err.Error(), "InvalidConfigurationRequest") diff --git a/spinifex/handlers/elbv2/service_impl_tags.go b/spinifex/handlers/elbv2/service_impl_tags.go new file mode 100644 index 000000000..5238397d2 --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_tags.go @@ -0,0 +1,185 @@ +package handlers_elbv2 + +import ( + "errors" + "log/slog" + "maps" + + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +// taggable bundles a resource's current tags with a persist closure so the +// AddTags/RemoveTags mutation logic is identical across resource types. +type taggable struct { + tags map[string]string + owner string + save func(map[string]string) error +} + +// resolveTaggable loads the ELBv2 resource named by arn and returns a handle for +// reading and persisting its tags. found is false when the resource doesn't +// exist; notFoundError carries the per-type API error for that case. Store +// failures are logged and returned as ErrorServerInternal. +func (s *ELBv2ServiceImpl) resolveTaggable(arn string) (h taggable, notFoundError string, found bool, err error) { + resourceType, terr := elbv2ResourceTypeFromArn(arn) + if terr != nil { + return taggable{}, "", false, terr + } + + switch resourceType { + case elbv2ResourceLoadBalancer: + notFoundError = awserrors.ErrorELBv2LoadBalancerNotFound + lb, e := s.store.GetLoadBalancerByArn(arn) + if e != nil { + slog.Error("resolveTaggable: failed to get LB", "arn", arn, "err", e) + return taggable{}, notFoundError, false, errors.New(awserrors.ErrorServerInternal) + } + if lb != nil { + found = true + h = taggable{tags: lb.Tags, owner: lb.AccountID, save: func(t map[string]string) error { + lb.Tags = t + return s.store.PutLoadBalancer(lb) + }} + } + case elbv2ResourceTargetGroup: + notFoundError = awserrors.ErrorELBv2TargetGroupNotFound + tg, e := s.store.GetTargetGroupByArn(arn) + if e != nil { + slog.Error("resolveTaggable: failed to get target group", "arn", arn, "err", e) + return taggable{}, notFoundError, false, errors.New(awserrors.ErrorServerInternal) + } + if tg != nil { + found = true + h = taggable{tags: tg.Tags, owner: tg.AccountID, save: func(t map[string]string) error { + tg.Tags = t + return s.store.PutTargetGroup(tg) + }} + } + case elbv2ResourceListener: + notFoundError = awserrors.ErrorELBv2ListenerNotFound + l, e := s.store.GetListenerByArn(arn) + if e != nil { + slog.Error("resolveTaggable: failed to get listener", "arn", arn, "err", e) + return taggable{}, notFoundError, false, errors.New(awserrors.ErrorServerInternal) + } + if l != nil { + found = true + h = taggable{tags: l.Tags, owner: l.AccountID, save: func(t map[string]string) error { + l.Tags = t + return s.store.PutListener(l) + }} + } + case elbv2ResourceListenerRule: + notFoundError = awserrors.ErrorELBv2RuleNotFound + r, e := s.store.GetRuleByArn(arn) + if e != nil { + slog.Error("resolveTaggable: failed to get rule", "arn", arn, "err", e) + return taggable{}, notFoundError, false, errors.New(awserrors.ErrorServerInternal) + } + if r != nil { + found = true + h = taggable{tags: r.Tags, owner: r.AccountID, save: func(t map[string]string) error { + r.Tags = t + return s.store.PutRule(r) + }} + } + } + + return h, notFoundError, found, nil +} + +// AddTags adds or overwrites tags on one or more ELBv2 resources. Tags are +// validated up-front so a malformed key can't leave a partial apply spanning +// the ResourceArns. Cross-account or unknown ARNs yield the per-resource +// not-found error. +func (s *ELBv2ServiceImpl) AddTags(input *elbv2.AddTagsInput, accountID string) (*elbv2.AddTagsOutput, error) { + if input == nil || len(input.ResourceArns) == 0 || len(input.Tags) == 0 { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + + newTags := make(map[string]string, len(input.Tags)) + for _, t := range input.Tags { + if t == nil || t.Key == nil || *t.Key == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + v := "" + if t.Value != nil { + v = *t.Value + } + newTags[*t.Key] = v + } + + for _, arnPtr := range input.ResourceArns { + if arnPtr == nil || *arnPtr == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + arn := *arnPtr + + h, notFoundError, found, err := s.resolveTaggable(arn) + if err != nil { + return nil, err + } + if !found || h.owner != accountID { + return nil, errors.New(notFoundError) + } + + merged := h.tags + if merged == nil { + merged = make(map[string]string, len(newTags)) + } + maps.Copy(merged, newTags) + if err := h.save(merged); err != nil { + slog.Error("AddTags: failed to persist", "arn", arn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + } + + return &elbv2.AddTagsOutput{}, nil +} + +// RemoveTags removes the named tag keys from one or more ELBv2 resources. +// Removal is idempotent — keys absent from a resource are silently ignored, +// matching AWS. Cross-account or unknown ARNs yield the per-resource not-found +// error. +func (s *ELBv2ServiceImpl) RemoveTags(input *elbv2.RemoveTagsInput, accountID string) (*elbv2.RemoveTagsOutput, error) { + if input == nil || len(input.ResourceArns) == 0 || len(input.TagKeys) == 0 { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + + keys := make([]string, 0, len(input.TagKeys)) + for _, k := range input.TagKeys { + if k == nil || *k == "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + keys = append(keys, *k) + } + + for _, arnPtr := range input.ResourceArns { + if arnPtr == nil || *arnPtr == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + arn := *arnPtr + + h, notFoundError, found, err := s.resolveTaggable(arn) + if err != nil { + return nil, err + } + if !found || h.owner != accountID { + return nil, errors.New(notFoundError) + } + + if len(h.tags) == 0 { + continue + } + for _, k := range keys { + delete(h.tags, k) + } + if err := h.save(h.tags); err != nil { + slog.Error("RemoveTags: failed to persist", "arn", arn, "err", err) + return nil, errors.New(awserrors.ErrorServerInternal) + } + } + + return &elbv2.RemoveTagsOutput{}, nil +} diff --git a/spinifex/handlers/elbv2/service_impl_tags_test.go b/spinifex/handlers/elbv2/service_impl_tags_test.go new file mode 100644 index 000000000..bd54509e6 --- /dev/null +++ b/spinifex/handlers/elbv2/service_impl_tags_test.go @@ -0,0 +1,200 @@ +package handlers_elbv2 + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// tagsFor is a small helper that reads back a resource's tags via DescribeTags +// as a plain map for assertions. +func tagsFor(t *testing.T, svc *ELBv2ServiceImpl, arn string) map[string]string { + t.Helper() + out, err := svc.DescribeTags(&elbv2.DescribeTagsInput{ResourceArns: []*string{aws.String(arn)}}, testAccountID) + require.NoError(t, err) + require.Len(t, out.TagDescriptions, 1) + m := make(map[string]string) + for _, tag := range out.TagDescriptions[0].Tags { + m[*tag.Key] = *tag.Value + } + return m +} + +func TestAddTags_LoadBalancer(t *testing.T) { + svc := setupTestService(t) + lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("addtags-lb"), + Tags: []*elbv2.Tag{{Key: aws.String("Env"), Value: aws.String("dev")}}, + }, testAccountID) + require.NoError(t, err) + arn := *lbOut.LoadBalancers[0].LoadBalancerArn + + _, err = svc.AddTags(&elbv2.AddTagsInput{ + ResourceArns: []*string{aws.String(arn)}, + Tags: []*elbv2.Tag{ + {Key: aws.String("App"), Value: aws.String("nginx")}, + {Key: aws.String("Env"), Value: aws.String("prod")}, // overwrite + }, + }, testAccountID) + require.NoError(t, err) + + assert.Equal(t, map[string]string{"App": "nginx", "Env": "prod"}, tagsFor(t, svc, arn)) +} + +func TestAddTags_Listener(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-lst-lb")}, testAccountID) + tgOut, _ := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("addtags-lst-tg")}, testAccountID) + lstOut, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(80), + DefaultActions: []*elbv2.Action{ + {Type: aws.String("forward"), TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn}, + }, + }, testAccountID) + require.NoError(t, err) + arn := *lstOut.Listeners[0].ListenerArn + + _, err = svc.AddTags(&elbv2.AddTagsInput{ + ResourceArns: []*string{aws.String(arn)}, + Tags: []*elbv2.Tag{{Key: aws.String("team"), Value: aws.String("platform")}}, + }, testAccountID) + require.NoError(t, err) + + assert.Equal(t, map[string]string{"team": "platform"}, tagsFor(t, svc, arn)) +} + +func TestAddTags_CreateTimeTagsPreserved(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-tg-lb")}, testAccountID) + tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("addtags-tg"), + Tags: []*elbv2.Tag{{Key: aws.String("owner"), Value: aws.String("alice")}}, + }, testAccountID) + require.NoError(t, err) + _ = lbOut + arn := *tgOut.TargetGroups[0].TargetGroupArn + + _, err = svc.AddTags(&elbv2.AddTagsInput{ + ResourceArns: []*string{aws.String(arn)}, + Tags: []*elbv2.Tag{{Key: aws.String("cost-center"), Value: aws.String("42")}}, + }, testAccountID) + require.NoError(t, err) + + assert.Equal(t, map[string]string{"owner": "alice", "cost-center": "42"}, tagsFor(t, svc, arn)) +} + +func TestAddTags_NotFound(t *testing.T) { + svc := setupTestService(t) + _, err := svc.AddTags(&elbv2.AddTagsInput{ + ResourceArns: []*string{aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-deadbeef")}, + Tags: []*elbv2.Tag{{Key: aws.String("k"), Value: aws.String("v")}}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2LoadBalancerNotFound) +} + +func TestAddTags_CrossAccount(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-xacct")}, testAccountID) + arn := *lbOut.LoadBalancers[0].LoadBalancerArn + + _, err := svc.AddTags(&elbv2.AddTagsInput{ + ResourceArns: []*string{aws.String(arn)}, + Tags: []*elbv2.Tag{{Key: aws.String("k"), Value: aws.String("v")}}, + }, "999999999999") + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2LoadBalancerNotFound) + + // Owner's tags untouched. + assert.Empty(t, tagsFor(t, svc, arn)) +} + +func TestAddTags_MissingParams(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-mp")}, testAccountID) + arn := lbOut.LoadBalancers[0].LoadBalancerArn + + _, err := svc.AddTags(&elbv2.AddTagsInput{ResourceArns: []*string{arn}}, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorMissingParameter) + + _, err = svc.AddTags(&elbv2.AddTagsInput{Tags: []*elbv2.Tag{{Key: aws.String("k"), Value: aws.String("v")}}}, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorMissingParameter) +} + +func TestAddTags_EmptyKeyRejected(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-ek")}, testAccountID) + arn := lbOut.LoadBalancers[0].LoadBalancerArn + + _, err := svc.AddTags(&elbv2.AddTagsInput{ + ResourceArns: []*string{arn}, + Tags: []*elbv2.Tag{{Key: aws.String(""), Value: aws.String("v")}}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorInvalidParameterValue) +} + +func TestRemoveTags_LoadBalancer(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("rmtags-lb"), + Tags: []*elbv2.Tag{ + {Key: aws.String("App"), Value: aws.String("nginx")}, + {Key: aws.String("Env"), Value: aws.String("prod")}, + }, + }, testAccountID) + arn := *lbOut.LoadBalancers[0].LoadBalancerArn + + _, err := svc.RemoveTags(&elbv2.RemoveTagsInput{ + ResourceArns: []*string{aws.String(arn)}, + TagKeys: []*string{aws.String("Env")}, + }, testAccountID) + require.NoError(t, err) + + assert.Equal(t, map[string]string{"App": "nginx"}, tagsFor(t, svc, arn)) +} + +func TestRemoveTags_Idempotent(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("rmtags-idem"), + Tags: []*elbv2.Tag{{Key: aws.String("App"), Value: aws.String("nginx")}}, + }, testAccountID) + arn := *lbOut.LoadBalancers[0].LoadBalancerArn + + // Removing an absent key is a no-op, not an error. + _, err := svc.RemoveTags(&elbv2.RemoveTagsInput{ + ResourceArns: []*string{aws.String(arn)}, + TagKeys: []*string{aws.String("DoesNotExist")}, + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, map[string]string{"App": "nginx"}, tagsFor(t, svc, arn)) +} + +func TestRemoveTags_NotFound(t *testing.T) { + svc := setupTestService(t) + _, err := svc.RemoveTags(&elbv2.RemoveTagsInput{ + ResourceArns: []*string{aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/missing/tg-deadbeef")}, + TagKeys: []*string{aws.String("k")}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2TargetGroupNotFound) +} + +func TestRemoveTags_MissingParams(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("rmtags-mp")}, testAccountID) + arn := lbOut.LoadBalancers[0].LoadBalancerArn + + _, err := svc.RemoveTags(&elbv2.RemoveTagsInput{ResourceArns: []*string{arn}}, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorMissingParameter) +} diff --git a/spinifex/handlers/elbv2/service_impl_test.go b/spinifex/handlers/elbv2/service_impl_test.go index 3a9f08118..0696c6f48 100644 --- a/spinifex/handlers/elbv2/service_impl_test.go +++ b/spinifex/handlers/elbv2/service_impl_test.go @@ -1,6 +1,7 @@ package handlers_elbv2 import ( + "encoding/xml" "sort" "strings" "sync" @@ -22,6 +23,10 @@ func setupTestService(t *testing.T) *ELBv2ServiceImpl { svc, err := NewELBv2ServiceImplWithNATS(nil, nc) require.NoError(t, err) + // Seed the certificate ARNs the listener tests attach so the fail-closed + // cert validation in Create/Modify/AddListenerCertificates resolves them. + putTestCert(t, svc, testCertArn, testAccountID, "LEAF", "", "KEY") + putTestCert(t, svc, testCertArn2, testAccountID, "LEAF", "", "KEY") return svc } @@ -142,7 +147,7 @@ func TestCreateLoadBalancer_NetworkType_RejectsSecurityGroups(t *testing.T) { assert.Contains(t, err.Error(), "InvalidConfigurationRequest") } -func TestCreateLoadBalancer_CrossZoneAttributes(t *testing.T) { +func TestCreateLoadBalancer_NLBCrossZoneAttribute(t *testing.T) { svc := setupTestService(t) // NLB: no stored attributes, Describe should return default cross-zone=false. @@ -164,6 +169,10 @@ func TestCreateLoadBalancer_CrossZoneAttributes(t *testing.T) { nlbAttrs[*a.Key] = *a.Value } assert.Equal(t, "false", nlbAttrs["load_balancing.cross_zone.enabled"]) +} + +func TestCreateLoadBalancer_ALBCrossZoneAttribute(t *testing.T) { + svc := setupTestService(t) // ALB: no stored attributes either, Describe should return default cross-zone=true. albOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ @@ -229,15 +238,16 @@ func TestDeleteLoadBalancer(t *testing.T) { assert.Empty(t, desc.LoadBalancers) } -func TestDeleteLoadBalancer_NotFound(t *testing.T) { +func TestDeleteLoadBalancer_IdempotentOnAbsent(t *testing.T) { svc := setupTestService(t) - _, err := svc.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{ + out, err := svc.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{ LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/nope/xyz"), }, testAccountID) - assert.Error(t, err) - assert.Contains(t, err.Error(), "LoadBalancerNotFound") + // AWS ELBv2 delete is idempotent: absent LB -> success, not NotFound. + require.NoError(t, err) + assert.NotNil(t, out) } func TestDeleteLoadBalancer_CleansUpListeners(t *testing.T) { @@ -696,14 +706,15 @@ func TestDeleteTargetGroup_InUse(t *testing.T) { assert.Contains(t, err.Error(), "ResourceInUse") } -func TestDeleteTargetGroup_NotFound(t *testing.T) { +func TestDeleteTargetGroup_IdempotentOnAbsent(t *testing.T) { svc := setupTestService(t) - _, err := svc.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ + out, err := svc.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ TargetGroupArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/nope/xyz"), }, testAccountID) - assert.Error(t, err) - assert.Contains(t, err.Error(), "TargetGroupNotFound") + // AWS ELBv2 delete is idempotent: absent TG -> success, not NotFound. + require.NoError(t, err) + assert.NotNil(t, out) } func TestDescribeTargetGroups_FilterByLBArn(t *testing.T) { @@ -763,6 +774,93 @@ func TestRegisterTargets(t *testing.T) { assert.Equal(t, int64(8080), *health.TargetHealthDescriptions[1].Target.Port) } +func TestRegisterTargets_IPType(t *testing.T) { + svc := setupTestService(t) + + tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("ip-tg"), + Port: aws.Int64(80), + TargetType: aws.String("ip"), + }, testAccountID) + require.NoError(t, err) + tgArn := tgOut.TargetGroups[0].TargetGroupArn + assert.Equal(t, "ip", *tgOut.TargetGroups[0].TargetType) + + _, err = svc.RegisterTargets(&elbv2.RegisterTargetsInput{ + TargetGroupArn: tgArn, + Targets: []*elbv2.TargetDescription{ + {Id: aws.String("10.0.1.20")}, + {Id: aws.String("10.0.1.21"), Port: aws.Int64(8080)}, + }, + }, testAccountID) + require.NoError(t, err) + + // ip targets must carry the supplied IP as PrivateIP, not an empty + // ENI-resolution result — otherwise HAProxy/health-check silently drop them. + tg, err := svc.store.GetTargetGroupByArn(*tgArn) + require.NoError(t, err) + require.Len(t, tg.Targets, 2) + ipByID := make(map[string]string) + for _, target := range tg.Targets { + ipByID[target.Id] = target.PrivateIP + } + assert.Equal(t, "10.0.1.20", ipByID["10.0.1.20"]) + assert.Equal(t, "10.0.1.21", ipByID["10.0.1.21"]) + + health, err := svc.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{ + TargetGroupArn: tgArn, + }, testAccountID) + require.NoError(t, err) + require.Len(t, health.TargetHealthDescriptions, 2) +} + +func TestRegisterTargets_IPType_RejectsNonIP(t *testing.T) { + svc := setupTestService(t) + tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("ip-tg-bad"), + TargetType: aws.String("ip"), + }, testAccountID) + require.NoError(t, err) + + _, err = svc.RegisterTargets(&elbv2.RegisterTargetsInput{ + TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn, + Targets: []*elbv2.TargetDescription{{Id: aws.String("i-notanip")}}, + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestRegisterTargets_InstanceType_RejectsIP(t *testing.T) { + svc := setupTestService(t) + tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("inst-tg-badid"), + }, testAccountID) + require.NoError(t, err) + + _, err = svc.RegisterTargets(&elbv2.RegisterTargetsInput{ + TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn, + Targets: []*elbv2.TargetDescription{{Id: aws.String("10.0.0.5")}}, + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestCreateTargetGroup_RejectsUnsupportedTargetType(t *testing.T) { + svc := setupTestService(t) + _, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("lambda-tg"), + TargetType: aws.String("lambda"), + }, testAccountID) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) +} + +func TestCreateTargetGroup_DefaultsToInstanceType(t *testing.T) { + svc := setupTestService(t) + tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("default-tt"), + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, "instance", *tgOut.TargetGroups[0].TargetType) +} + func TestRegisterTargets_Idempotent(t *testing.T) { svc := setupTestService(t) @@ -865,6 +963,127 @@ func TestCreateListener(t *testing.T) { assert.Equal(t, "forward", *l.DefaultActions[0].Type) } +func TestCreateListener_RedirectDefault(t *testing.T) { + svc := setupTestService(t) + + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("redir-lb")}, testAccountID) + + out, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(80), + DefaultActions: []*elbv2.Action{{ + Type: aws.String("redirect"), + RedirectConfig: &elbv2.RedirectActionConfig{ + Protocol: aws.String("HTTPS"), + Port: aws.String("443"), + StatusCode: aws.String("HTTP_301"), + }, + }}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, out.Listeners, 1) + require.Len(t, out.Listeners[0].DefaultActions, 1) + a := out.Listeners[0].DefaultActions[0] + assert.Equal(t, "redirect", *a.Type) + require.NotNil(t, a.RedirectConfig) + assert.Equal(t, "HTTPS", *a.RedirectConfig.Protocol) + assert.Equal(t, "HTTP_301", *a.RedirectConfig.StatusCode) +} + +func TestCreateListener_RedirectFullFields(t *testing.T) { + svc := setupTestService(t) + + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("redirfull-lb")}, testAccountID) + + out, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(80), + DefaultActions: []*elbv2.Action{{ + Type: aws.String("redirect"), + RedirectConfig: &elbv2.RedirectActionConfig{ + Protocol: aws.String("HTTPS"), + Host: aws.String("new.example.com"), + Port: aws.String("8443"), + Path: aws.String("/moved"), + Query: aws.String("ref=1"), + StatusCode: aws.String("HTTP_302"), + }, + }}, + }, testAccountID) + require.NoError(t, err) + + rc := out.Listeners[0].DefaultActions[0].RedirectConfig + require.NotNil(t, rc) + assert.Equal(t, "new.example.com", *rc.Host) + assert.Equal(t, "8443", *rc.Port) + assert.Equal(t, "/moved", *rc.Path) + assert.Equal(t, "ref=1", *rc.Query) + assert.Equal(t, "HTTP_302", *rc.StatusCode) + + // Read back through Describe to exercise the stored→SDK path. + desc, err := svc.DescribeListeners(&elbv2.DescribeListenersInput{ + LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, + }, testAccountID) + require.NoError(t, err) + require.Len(t, desc.Listeners, 1) + assert.Equal(t, "new.example.com", *desc.Listeners[0].DefaultActions[0].RedirectConfig.Host) +} + +func TestModifyListener_ToRedirect(t *testing.T) { + svc := setupTestService(t) + + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("mod-redir-lb")}, testAccountID) + tgOut, _ := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("mod-redir-tg")}, testAccountID) + + lstOut, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(80), + DefaultActions: []*elbv2.Action{{Type: aws.String("forward"), TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn}}, + }, testAccountID) + require.NoError(t, err) + + out, err := svc.ModifyListener(&elbv2.ModifyListenerInput{ + ListenerArn: lstOut.Listeners[0].ListenerArn, + DefaultActions: []*elbv2.Action{{ + Type: aws.String("redirect"), + RedirectConfig: &elbv2.RedirectActionConfig{Protocol: aws.String("HTTPS"), StatusCode: aws.String("HTTP_301")}, + }}, + }, testAccountID) + require.NoError(t, err) + assert.Equal(t, "redirect", *out.Listeners[0].DefaultActions[0].Type) + + // A bad redirect on modify is rejected. + _, err = svc.ModifyListener(&elbv2.ModifyListenerInput{ + ListenerArn: lstOut.Listeners[0].ListenerArn, + DefaultActions: []*elbv2.Action{{ + Type: aws.String("redirect"), + RedirectConfig: &elbv2.RedirectActionConfig{StatusCode: aws.String("HTTP_999")}, + }}, + }, testAccountID) + require.Error(t, err) +} + +func TestCreateListener_RejectsBadRedirect(t *testing.T) { + svc := setupTestService(t) + + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("badredir-lb")}, testAccountID) + + _, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(80), + DefaultActions: []*elbv2.Action{{ + Type: aws.String("redirect"), + RedirectConfig: &elbv2.RedirectActionConfig{StatusCode: aws.String("HTTP_500")}, + }}, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), "InvalidParameterValue") +} + func TestCreateListener_DuplicatePort(t *testing.T) { svc := setupTestService(t) @@ -924,14 +1143,15 @@ func TestDeleteListener(t *testing.T) { assert.Empty(t, desc.Listeners) } -func TestDeleteListener_NotFound(t *testing.T) { +func TestDeleteListener_IdempotentOnAbsent(t *testing.T) { svc := setupTestService(t) - _, err := svc.DeleteListener(&elbv2.DeleteListenerInput{ + out, err := svc.DeleteListener(&elbv2.DeleteListenerInput{ ListenerArn: aws.String("arn:nonexistent"), }, testAccountID) - assert.Error(t, err) - assert.Contains(t, err.Error(), "ListenerNotFound") + // AWS ELBv2 delete is idempotent: absent listener -> success, not NotFound. + require.NoError(t, err) + assert.NotNil(t, out) } func TestModifyListener_Port(t *testing.T) { @@ -980,8 +1200,9 @@ func TestModifyListener_Protocol(t *testing.T) { require.NoError(t, err) out, err := svc.ModifyListener(&elbv2.ModifyListenerInput{ - ListenerArn: lstOut.Listeners[0].ListenerArn, - Protocol: aws.String("HTTPS"), + ListenerArn: lstOut.Listeners[0].ListenerArn, + Protocol: aws.String("HTTPS"), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, }, testAccountID) require.NoError(t, err) assert.Equal(t, "HTTPS", *out.Listeners[0].Protocol) @@ -1204,10 +1425,14 @@ func TestModifyListener_NLB_AcceptsAllProtocols(t *testing.T) { }, testAccountID) require.NoError(t, err) - out, err := svc.ModifyListener(&elbv2.ModifyListenerInput{ + modIn := &elbv2.ModifyListenerInput{ ListenerArn: lstOut.Listeners[0].ListenerArn, Protocol: aws.String(tc.listenerProto), - }, testAccountID) + } + if protocolRequiresCert(tc.listenerProto) { + modIn.Certificates = []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}} + } + out, err := svc.ModifyListener(modIn, testAccountID) require.NoError(t, err) assert.Equal(t, tc.listenerProto, *out.Listeners[0].Protocol) }) @@ -1389,14 +1614,18 @@ func TestCreateListener_NLB_AllProtocols(t *testing.T) { Port: aws.Int64(8080), }, testAccountID) - out, err := svc.CreateListener(&elbv2.CreateListenerInput{ + createIn := &elbv2.CreateListenerInput{ LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, Protocol: aws.String(proto), Port: aws.Int64(8080), DefaultActions: []*elbv2.Action{ {Type: aws.String("forward"), TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn}, }, - }, testAccountID) + } + if protocolRequiresCert(proto) { + createIn.Certificates = []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}} + } + out, err := svc.CreateListener(createIn, testAccountID) require.NoError(t, err) assert.Equal(t, proto, *out.Listeners[0].Protocol) @@ -1492,6 +1721,7 @@ func TestCreateListener_NLB_ProtocolCompatibility_TLSToTCP(t *testing.T) { LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, Protocol: aws.String("TLS"), Port: aws.Int64(443), + Certificates: []*elbv2.Certificate{{CertificateArn: aws.String(testCertArn)}}, DefaultActions: []*elbv2.Action{ {Type: aws.String("forward"), TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn}, }, @@ -1819,6 +2049,91 @@ func TestGetLBConfig_ReturnsStoredConfig(t *testing.T) { assert.Equal(t, "deadbeef", *out.ConfigHash) } +func TestGetLBConfig_DeliversHealthTargetsForNLB(t *testing.T) { + svc := setupTestService(t) + + lb := &LoadBalancerRecord{ + LoadBalancerArn: "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/net/nlb-hc/lb-nlbhc", + LoadBalancerID: "lb-nlbhc", + Name: "nlb-hc", + Type: LoadBalancerTypeNetwork, + State: StateActive, + ConfigText: "stream {}\n", + ConfigHash: "hc1", + AccountID: testAccountID, + HealthTargets: []HealthTargetSpec{ + {ServerName: "srv_i-1", Address: "10.0.0.1:80", Protocol: ProtocolTCP}, + }, + } + require.NoError(t, svc.store.PutLoadBalancer(lb)) + + out, err := svc.GetLBConfig(&GetLBConfigInput{LBID: aws.String("lb-nlbhc")}, testAccountID) + require.NoError(t, err) + assert.Equal(t, EngineNginx, *out.Engine) + require.Len(t, out.HealthTargets, 1) + assert.Equal(t, "srv_i-1", *out.HealthTargets[0].ServerName) + assert.Equal(t, "10.0.0.1:80", *out.HealthTargets[0].Address) + assert.Equal(t, ProtocolTCP, *out.HealthTargets[0].Protocol) +} + +func TestGetLBConfig_HealthTargetsWireShape(t *testing.T) { + svc := setupTestService(t) + require.NoError(t, svc.store.PutLoadBalancer(&LoadBalancerRecord{ + LoadBalancerID: "lb-wire", + Type: LoadBalancerTypeNetwork, + AccountID: testAccountID, + ConfigText: "stream {}\n", + ConfigHash: "w1", + HealthTargets: []HealthTargetSpec{ + {ServerName: "srv_i-1", Address: "10.0.0.1:80", Protocol: ProtocolHTTP, Path: "/healthz"}, + }, + })) + + out, err := svc.GetLBConfig(&GetLBConfigInput{LBID: aws.String("lb-wire")}, testAccountID) + require.NoError(t, err) + + // Confirm the marshalled member shape matches what the lb-agent parses + // (GetLBConfigResult>HealthTargets>member>{ServerName,Address,Protocol,Path}). + payload := utils.GenerateIAMXMLPayload("GetLBConfig", *out) + xmlBytes, err := utils.MarshalToXML(payload) + require.NoError(t, err) + var parsed struct { + Members []struct { + ServerName string `xml:"ServerName"` + Address string `xml:"Address"` + Protocol string `xml:"Protocol"` + Path string `xml:"Path"` + } `xml:"GetLBConfigResult>HealthTargets>member"` + } + require.NoError(t, xml.Unmarshal(xmlBytes, &parsed)) + require.Len(t, parsed.Members, 1) + assert.Equal(t, "srv_i-1", parsed.Members[0].ServerName) + assert.Equal(t, "10.0.0.1:80", parsed.Members[0].Address) + assert.Equal(t, ProtocolHTTP, parsed.Members[0].Protocol) + assert.Equal(t, "/healthz", parsed.Members[0].Path) +} + +func TestGetLBConfig_NoHealthTargetsForALB(t *testing.T) { + svc := setupTestService(t) + + lb := &LoadBalancerRecord{ + LoadBalancerArn: "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/alb-hc/lb-albhc", + LoadBalancerID: "lb-albhc", + Name: "alb-hc", + Type: LoadBalancerTypeApplication, + State: StateActive, + ConfigText: "global\n", + ConfigHash: "hc2", + AccountID: testAccountID, + } + require.NoError(t, svc.store.PutLoadBalancer(lb)) + + out, err := svc.GetLBConfig(&GetLBConfigInput{LBID: aws.String("lb-albhc")}, testAccountID) + require.NoError(t, err) + assert.Equal(t, EngineHAProxy, *out.Engine) + assert.Empty(t, out.HealthTargets) // ALBs report health via HAProxy stats +} + func TestGetLBConfig_MissingLBID(t *testing.T) { svc := setupTestService(t) @@ -2181,7 +2496,11 @@ func TestDescribeLoadBalancerAttributes_ALBDefaults(t *testing.T) { for _, a := range out.Attributes { attrMap[*a.Key] = *a.Value } - // ALB default cross-zone is true — comes from the per-type default, not seeding. + // Every default key/value must round-trip through Describe, not just + // cross-zone. ALB default cross-zone is true — from the per-type default. + for k, v := range defaults { + assert.Equal(t, v, attrMap[k], "default mismatch for key %s", k) + } assert.Equal(t, "true", attrMap["load_balancing.cross_zone.enabled"]) } @@ -2273,8 +2592,8 @@ func TestDefaultLoadBalancerAttributes_NLBCoversExpectedKeys(t *testing.T) { } // TestModifyLoadBalancerAttributes_AcceptsConnectionLogsKey is a regression -// guard for mulga-931: terraform sends connection_logs.s3.enabled on every -// aws_lb apply and the handler must accept it. +// guard: terraform sends connection_logs.s3.enabled on every aws_lb apply and +// the handler must accept it. func TestModifyLoadBalancerAttributes_AcceptsConnectionLogsKey(t *testing.T) { svc := setupTestService(t) lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ @@ -2295,70 +2614,33 @@ func TestModifyLoadBalancerAttributes_AcceptsConnectionLogsKey(t *testing.T) { require.NoError(t, err, "terraform-sent attribute keys must be accepted") } -func TestModifyDescribeTargetGroupAttributes_RoundTrip(t *testing.T) { +// TestModifyLoadBalancerAttributes_EmptyStringClears verifies that a non-nil +// empty-string Value is treated as a real value, not skipped as invalid. AWS +// uses "" to clear access_logs.s3.bucket, so the handler must persist it. +func TestModifyLoadBalancerAttributes_EmptyStringClears(t *testing.T) { svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-attr-rt")}, testAccountID) + lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("alb-clear")}, testAccountID) require.NoError(t, err) - arn := tgOut.TargetGroups[0].TargetGroupArn + arn := lbOut.LoadBalancers[0].LoadBalancerArn - modOut, err := svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: arn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("deregistration_delay.timeout_seconds"), Value: aws.String("60")}, - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, + // Set a bucket, then clear it with an empty string. + _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ + LoadBalancerArn: arn, + Attributes: []*elbv2.LoadBalancerAttribute{ + {Key: aws.String("access_logs.s3.bucket"), Value: aws.String("my-logs")}, }, }, testAccountID) require.NoError(t, err) - // Assert the exact echoed key/value pairs, not just the length — a regression - // that pointed at the wrong source or dropped Value would pass a length check. - require.Len(t, modOut.Attributes, 2) - modMap := make(map[string]string, len(modOut.Attributes)) - for _, a := range modOut.Attributes { - require.NotNil(t, a.Key) - require.NotNil(t, a.Value) - modMap[*a.Key] = *a.Value - } - assert.Equal(t, "60", modMap["deregistration_delay.timeout_seconds"]) - assert.Equal(t, "true", modMap["stickiness.enabled"]) - - descOut, err := svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ - TargetGroupArn: arn, - }, testAccountID) - require.NoError(t, err) - attrMap := make(map[string]string) - for _, a := range descOut.Attributes { - attrMap[*a.Key] = *a.Value - } - assert.Equal(t, "60", attrMap["deregistration_delay.timeout_seconds"]) - assert.Equal(t, "true", attrMap["stickiness.enabled"]) - // Unmodified defaults should still be present - assert.Equal(t, "lb_cookie", attrMap["stickiness.type"]) -} - -func TestModifyDescribeLoadBalancerAttributes_RoundTrip(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-attr-rt")}, testAccountID) - require.NoError(t, err) - arn := lbOut.LoadBalancers[0].LoadBalancerArn modOut, err := svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ LoadBalancerArn: arn, Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("120")}, - {Key: aws.String("deletion_protection.enabled"), Value: aws.String("true")}, + {Key: aws.String("access_logs.s3.bucket"), Value: aws.String("")}, }, }, testAccountID) require.NoError(t, err) - // Assert the exact echoed key/value pairs, not just the length. - require.Len(t, modOut.Attributes, 2) - modMap := make(map[string]string, len(modOut.Attributes)) - for _, a := range modOut.Attributes { - require.NotNil(t, a.Key) - require.NotNil(t, a.Value) - modMap[*a.Key] = *a.Value - } - assert.Equal(t, "120", modMap["idle_timeout.timeout_seconds"]) - assert.Equal(t, "true", modMap["deletion_protection.enabled"]) + require.Len(t, modOut.Attributes, 1, "empty-string Value must be echoed, not skipped") + assert.Equal(t, "", *modOut.Attributes[0].Value) descOut, err := svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ LoadBalancerArn: arn, @@ -2368,560 +2650,455 @@ func TestModifyDescribeLoadBalancerAttributes_RoundTrip(t *testing.T) { for _, a := range descOut.Attributes { attrMap[*a.Key] = *a.Value } - assert.Equal(t, "120", attrMap["idle_timeout.timeout_seconds"]) - assert.Equal(t, "true", attrMap["deletion_protection.enabled"]) - // Unmodified defaults should still be present - assert.Equal(t, "true", attrMap["routing.http2.enabled"]) + assert.Equal(t, "", attrMap["access_logs.s3.bucket"], "empty-string clear must persist") } -func TestModifyTargetGroupAttributes_NotFound(t *testing.T) { - svc := setupTestService(t) - _, err := svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/missing/tg-missing"), - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, - }, - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorELBv2TargetGroupNotFound) +// --- Attribute mirror-pair tests (table-driven over TG/LB) --- +// +// ModifyTargetGroupAttributes/ModifyLoadBalancerAttributes (and their Describe +// counterparts) are mirror pairs differing only by record type, store methods, +// default set and not-found error. The behavioural tests below run once per +// kind via t.Run instead of being hand-copied per resource. + +// rawAttr models one submitted SDK attribute, including the invalid shapes a +// handler must skip: a nil slice element, a nil Key, or a nil Value. +type rawAttr struct { + nilElem bool + key *string + val *string } -func TestDescribeTargetGroupAttributes_NotFound(t *testing.T) { - svc := setupTestService(t) - _, err := svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ - TargetGroupArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/missing/tg-missing"), - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorELBv2TargetGroupNotFound) -} +func kvAttr(k, v string) rawAttr { return rawAttr{key: aws.String(k), val: aws.String(v)} } -func TestModifyLoadBalancerAttributes_NotFound(t *testing.T) { - svc := setupTestService(t) - _, err := svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-missing"), - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("30")}, - }, - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorELBv2LoadBalancerNotFound) -} - -func TestDescribeLoadBalancerAttributes_NotFound(t *testing.T) { - svc := setupTestService(t) - _, err := svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ - LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-missing"), - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorELBv2LoadBalancerNotFound) -} - -func TestModifyTargetGroupAttributes_MissingArn(t *testing.T) { - svc := setupTestService(t) - _, err := svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, - }, - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorMissingParameter) -} - -func TestDescribeTargetGroupAttributes_MissingArn(t *testing.T) { - svc := setupTestService(t) - _, err := svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{}, testAccountID) - assert.EqualError(t, err, awserrors.ErrorMissingParameter) -} - -func TestModifyLoadBalancerAttributes_MissingArn(t *testing.T) { - svc := setupTestService(t) - _, err := svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("30")}, - }, - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorMissingParameter) -} - -func TestDescribeLoadBalancerAttributes_MissingArn(t *testing.T) { - svc := setupTestService(t) - _, err := svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{}, testAccountID) - assert.EqualError(t, err, awserrors.ErrorMissingParameter) -} - -func TestModifyTargetGroupAttributes_WrongAccount(t *testing.T) { - svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-attr-wrong-acct")}, testAccountID) - require.NoError(t, err) - - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, - }, - }, "999999999999") - assert.EqualError(t, err, awserrors.ErrorELBv2TargetGroupNotFound) -} - -func TestDescribeTargetGroupAttributes_WrongAccount(t *testing.T) { - svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-desc-attr-wrong-acct")}, testAccountID) - require.NoError(t, err) - - _, err = svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ - TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn, - }, "999999999999") - assert.EqualError(t, err, awserrors.ErrorELBv2TargetGroupNotFound) -} - -func TestModifyLoadBalancerAttributes_WrongAccount(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-attr-wrong-acct")}, testAccountID) - require.NoError(t, err) - - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("30")}, - }, - }, "999999999999") - assert.EqualError(t, err, awserrors.ErrorELBv2LoadBalancerNotFound) -} - -func TestDescribeLoadBalancerAttributes_WrongAccount(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-desc-attr-wrong-acct")}, testAccountID) - require.NoError(t, err) - - _, err = svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ - LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, - }, "999999999999") - assert.EqualError(t, err, awserrors.ErrorELBv2LoadBalancerNotFound) -} - -// TestModifyTargetGroupAttributes_SkipsInvalidEntries verifies that nil slice -// elements, nil Keys, and nil Values are skipped (with a warning) rather than -// panicking or being silently dropped. Valid attributes in the same call must -// still be applied. -func TestModifyTargetGroupAttributes_SkipsInvalidEntries(t *testing.T) { - svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-attr-skip")}, testAccountID) - require.NoError(t, err) - arn := tgOut.TargetGroups[0].TargetGroupArn - - modOut, err := svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: arn, - Attributes: []*elbv2.TargetGroupAttribute{ - nil, // nil element must not panic - {Key: nil, Value: aws.String("v")}, - {Key: aws.String("k"), Value: nil}, - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, - }, - }, testAccountID) - require.NoError(t, err) - // Only the one valid attribute should be returned. - require.Len(t, modOut.Attributes, 1) - assert.Equal(t, "stickiness.enabled", *modOut.Attributes[0].Key) - assert.Equal(t, "true", *modOut.Attributes[0].Value) - - // The valid attribute should have been persisted. - descOut, err := svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ - TargetGroupArn: arn, - }, testAccountID) - require.NoError(t, err) - attrMap := make(map[string]string) - for _, a := range descOut.Attributes { - attrMap[*a.Key] = *a.Value +func kvAttrs(kv ...[2]string) []rawAttr { + out := make([]rawAttr, len(kv)) + for i, p := range kv { + out[i] = kvAttr(p[0], p[1]) } - assert.Equal(t, "true", attrMap["stickiness.enabled"]) + return out } -// TestModifyLoadBalancerAttributes_SkipsInvalidEntries mirrors the TG case for -// the LB handler: nil elements and nil Key/Value fields must be skipped, not -// panic or swallow valid attributes in the same request. -func TestModifyLoadBalancerAttributes_SkipsInvalidEntries(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-attr-skip")}, testAccountID) - require.NoError(t, err) - arn := lbOut.LoadBalancers[0].LoadBalancerArn +type echoPair struct{ Key, Val string } - modOut, err := svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - Attributes: []*elbv2.LoadBalancerAttribute{ - nil, // nil element must not panic - {Key: nil, Value: aws.String("v")}, - {Key: aws.String("k"), Value: nil}, - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("75")}, - }, - }, testAccountID) - require.NoError(t, err) - require.Len(t, modOut.Attributes, 1) - assert.Equal(t, "idle_timeout.timeout_seconds", *modOut.Attributes[0].Key) - assert.Equal(t, "75", *modOut.Attributes[0].Value) - - descOut, err := svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - }, testAccountID) - require.NoError(t, err) - attrMap := make(map[string]string) - for _, a := range descOut.Attributes { - attrMap[*a.Key] = *a.Value +func echoMap(pairs []echoPair) map[string]string { + m := make(map[string]string, len(pairs)) + for _, p := range pairs { + m[p.Key] = p.Val } - assert.Equal(t, "75", attrMap["idle_timeout.timeout_seconds"]) + return m } -// TestModifyTargetGroupAttributes_AllInvalidReturnsError guards against the -// silent-success case where every submitted attribute trips the nil guard and -// the handler returned 200 OK with an empty response body — the caller would -// think the write landed when nothing was actually applied. -func TestModifyTargetGroupAttributes_AllInvalidReturnsError(t *testing.T) { - svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-all-invalid")}, testAccountID) - require.NoError(t, err) - - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn, - Attributes: []*elbv2.TargetGroupAttribute{ - nil, - {Key: nil, Value: aws.String("v")}, - {Key: aws.String("k"), Value: nil}, - }, - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) -} - -// TestModifyLoadBalancerAttributes_AllInvalidReturnsError mirrors the TG case. -func TestModifyLoadBalancerAttributes_AllInvalidReturnsError(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-all-invalid")}, testAccountID) - require.NoError(t, err) - - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, - Attributes: []*elbv2.LoadBalancerAttribute{ - nil, - {Key: nil, Value: aws.String("v")}, - {Key: aws.String("k"), Value: nil}, - }, - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) -} - -// TestModifyTargetGroupAttributes_SequentialMerge verifies that successive -// Modify calls accumulate keys instead of replacing the entire attribute map. -// A future refactor that did `tg.Attributes = newMap` would pass every other -// test (single-call round-trip covers the happy path) but silently wipe -// previous attributes on every subsequent Modify — the most likely real-world -// ALB/TG bug. -func TestModifyTargetGroupAttributes_SequentialMerge(t *testing.T) { - svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-seq-merge")}, testAccountID) - require.NoError(t, err) - arn := tgOut.TargetGroups[0].TargetGroupArn - - // Call 1: set deregistration_delay.timeout_seconds. - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: arn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("deregistration_delay.timeout_seconds"), Value: aws.String("45")}, - }, - }, testAccountID) - require.NoError(t, err) - - // Call 2: set a different key; must not wipe the first one. - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: arn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, - }, - }, testAccountID) - require.NoError(t, err) - - descOut, err := svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ - TargetGroupArn: arn, - }, testAccountID) - require.NoError(t, err) - attrMap := make(map[string]string) - for _, a := range descOut.Attributes { - attrMap[*a.Key] = *a.Value +// attrArnPtr returns nil for an empty ARN so the MissingArn cases exercise the +// real "no ARN supplied" path rather than an empty-string ARN. +func attrArnPtr(arn string) *string { + if arn == "" { + return nil } - assert.Equal(t, "45", attrMap["deregistration_delay.timeout_seconds"], "first-call key must survive second Modify") - assert.Equal(t, "true", attrMap["stickiness.enabled"], "second-call key must be present") - - // Call 3: overwrite the same key with a new value. - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: arn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickiness.enabled"), Value: aws.String("false")}, - }, - }, testAccountID) - require.NoError(t, err) - - descOut, err = svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ - TargetGroupArn: arn, - }, testAccountID) - require.NoError(t, err) - attrMap = make(map[string]string) - for _, a := range descOut.Attributes { - attrMap[*a.Key] = *a.Value - } - assert.Equal(t, "false", attrMap["stickiness.enabled"], "same-key overwrite must replace the value") - assert.Equal(t, "45", attrMap["deregistration_delay.timeout_seconds"], "unrelated key must still survive") + return aws.String(arn) } -// TestModifyLoadBalancerAttributes_SequentialMerge mirrors the TG case. -func TestModifyLoadBalancerAttributes_SequentialMerge(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-seq-merge")}, testAccountID) - require.NoError(t, err) - arn := lbOut.LoadBalancers[0].LoadBalancerArn +// attrKind bundles everything the table-driven attribute tests need to drive +// one resource type (target group or load balancer). +type attrKind struct { + name string + notFound string + missingArn string - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("120")}, - }, - }, testAccountID) - require.NoError(t, err) + keyA, valA, valA2 string // primary key, value, and an overwrite value + keyB, valB string // secondary key/value + defaultKey string // a default present after create + defaultVal string + unknownKey string // a key the handler must reject - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("deletion_protection.enabled"), Value: aws.String("true")}, - }, - }, testAccountID) - require.NoError(t, err) - - descOut, err := svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - }, testAccountID) - require.NoError(t, err) - attrMap := make(map[string]string) - for _, a := range descOut.Attributes { - attrMap[*a.Key] = *a.Value - } - assert.Equal(t, "120", attrMap["idle_timeout.timeout_seconds"], "first-call key must survive second Modify") - assert.Equal(t, "true", attrMap["deletion_protection.enabled"], "second-call key must be present") - - // Same-key overwrite. - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("90")}, - }, - }, testAccountID) - require.NoError(t, err) - - descOut, err = svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - }, testAccountID) - require.NoError(t, err) - attrMap = make(map[string]string) - for _, a := range descOut.Attributes { - attrMap[*a.Key] = *a.Value - } - assert.Equal(t, "90", attrMap["idle_timeout.timeout_seconds"], "same-key overwrite must replace the value") - assert.Equal(t, "true", attrMap["deletion_protection.enabled"], "unrelated key must still survive") + create func(t *testing.T, svc *ELBv2ServiceImpl, name string) string + modify func(svc *ELBv2ServiceImpl, arn, accountID string, attrs []rawAttr) ([]echoPair, error) + describe func(svc *ELBv2ServiceImpl, arn, accountID string) ([]echoPair, error) + revision func(t *testing.T, svc *ELBv2ServiceImpl, arn string) uint64 } -// TestModifyTargetGroupAttributes_NoopSkipsPersist verifies that a -// re-submission of the same attribute values does not bump the underlying KV -// revision — Terraform's drift check hits Modify on every apply, so the -// steady-state path must be a no-op at the storage layer. -func TestModifyTargetGroupAttributes_NoopSkipsPersist(t *testing.T) { - svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-noop")}, testAccountID) - require.NoError(t, err) - tgArn := tgOut.TargetGroups[0].TargetGroupArn - - // First modify — must hit the store. - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: tgArn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, +func attrKinds() []attrKind { + return []attrKind{ + { + name: "TargetGroup", + notFound: awserrors.ErrorELBv2TargetGroupNotFound, + missingArn: "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/missing/tg-missing", + keyA: "deregistration_delay.timeout_seconds", valA: "45", valA2: "30", + keyB: "stickiness.enabled", valB: "true", + defaultKey: "stickiness.type", defaultVal: "lb_cookie", + unknownKey: "stickness.enabled", // typo of "stickiness" + create: func(t *testing.T, svc *ELBv2ServiceImpl, name string) string { + out, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String(name)}, testAccountID) + require.NoError(t, err) + return *out.TargetGroups[0].TargetGroupArn + }, + modify: func(svc *ELBv2ServiceImpl, arn, accountID string, attrs []rawAttr) ([]echoPair, error) { + sdk := make([]*elbv2.TargetGroupAttribute, len(attrs)) + for i, a := range attrs { + if a.nilElem { + continue + } + sdk[i] = &elbv2.TargetGroupAttribute{Key: a.key, Value: a.val} + } + out, err := svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ + TargetGroupArn: attrArnPtr(arn), Attributes: sdk, + }, accountID) + if err != nil { + return nil, err + } + pairs := make([]echoPair, len(out.Attributes)) + for i, a := range out.Attributes { + pairs[i] = echoPair{*a.Key, *a.Value} + } + return pairs, nil + }, + describe: func(svc *ELBv2ServiceImpl, arn, accountID string) ([]echoPair, error) { + out, err := svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ + TargetGroupArn: attrArnPtr(arn), + }, accountID) + if err != nil { + return nil, err + } + pairs := make([]echoPair, len(out.Attributes)) + for i, a := range out.Attributes { + pairs[i] = echoPair{*a.Key, *a.Value} + } + return pairs, nil + }, + revision: func(t *testing.T, svc *ELBv2ServiceImpl, arn string) uint64 { + tg, err := svc.store.GetTargetGroupByArn(arn) + require.NoError(t, err) + entry, err := svc.store.kv.Get(KeyPrefixTG + tg.TargetGroupID) + require.NoError(t, err) + return entry.Revision() + }, }, - }, testAccountID) - require.NoError(t, err) - - // Capture KV revision after the first write. - tg, err := svc.store.GetTargetGroupByArn(*tgArn) - require.NoError(t, err) - entry, err := svc.store.kv.Get(KeyPrefixTG + tg.TargetGroupID) - require.NoError(t, err) - revBefore := entry.Revision() - - // Second modify with identical values — must skip the Put. - modOut, err := svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: tgArn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, + { + name: "LoadBalancer", + notFound: awserrors.ErrorELBv2LoadBalancerNotFound, + missingArn: "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-missing", + keyA: "idle_timeout.timeout_seconds", valA: "120", valA2: "90", + keyB: "deletion_protection.enabled", valB: "true", + defaultKey: "routing.http2.enabled", defaultVal: "true", + unknownKey: "stickiness.enabled", // valid TG key — cross-product mistake + create: func(t *testing.T, svc *ELBv2ServiceImpl, name string) string { + out, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String(name)}, testAccountID) + require.NoError(t, err) + return *out.LoadBalancers[0].LoadBalancerArn + }, + modify: func(svc *ELBv2ServiceImpl, arn, accountID string, attrs []rawAttr) ([]echoPair, error) { + sdk := make([]*elbv2.LoadBalancerAttribute, len(attrs)) + for i, a := range attrs { + if a.nilElem { + continue + } + sdk[i] = &elbv2.LoadBalancerAttribute{Key: a.key, Value: a.val} + } + out, err := svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ + LoadBalancerArn: attrArnPtr(arn), Attributes: sdk, + }, accountID) + if err != nil { + return nil, err + } + pairs := make([]echoPair, len(out.Attributes)) + for i, a := range out.Attributes { + pairs[i] = echoPair{*a.Key, *a.Value} + } + return pairs, nil + }, + describe: func(svc *ELBv2ServiceImpl, arn, accountID string) ([]echoPair, error) { + out, err := svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ + LoadBalancerArn: attrArnPtr(arn), + }, accountID) + if err != nil { + return nil, err + } + pairs := make([]echoPair, len(out.Attributes)) + for i, a := range out.Attributes { + pairs[i] = echoPair{*a.Key, *a.Value} + } + return pairs, nil + }, + revision: func(t *testing.T, svc *ELBv2ServiceImpl, arn string) uint64 { + lb, err := svc.store.GetLoadBalancerByArn(arn) + require.NoError(t, err) + entry, err := svc.store.kv.Get(KeyPrefixLB + lb.LoadBalancerID) + require.NoError(t, err) + return entry.Revision() + }, }, - }, testAccountID) - require.NoError(t, err) - require.Len(t, modOut.Attributes, 1) - - entry, err = svc.store.kv.Get(KeyPrefixTG + tg.TargetGroupID) - require.NoError(t, err) - assert.Equal(t, revBefore, entry.Revision(), "identical modify must not increment KV revision") - - // Empty attribute list is also a no-op. - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: tgArn, - }, testAccountID) - require.NoError(t, err) - entry, err = svc.store.kv.Get(KeyPrefixTG + tg.TargetGroupID) - require.NoError(t, err) - assert.Equal(t, revBefore, entry.Revision(), "empty modify must not increment KV revision") -} - -// TestModifyLoadBalancerAttributes_NoopSkipsPersist mirrors the TG case. -func TestModifyLoadBalancerAttributes_NoopSkipsPersist(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-noop")}, testAccountID) - require.NoError(t, err) - lbArn := lbOut.LoadBalancers[0].LoadBalancerArn - - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: lbArn, - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("75")}, - }, - }, testAccountID) - require.NoError(t, err) - - lb, err := svc.store.GetLoadBalancerByArn(*lbArn) - require.NoError(t, err) - entry, err := svc.store.kv.Get(KeyPrefixLB + lb.LoadBalancerID) - require.NoError(t, err) - revBefore := entry.Revision() - - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: lbArn, - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("75")}, - }, - }, testAccountID) - require.NoError(t, err) - - entry, err = svc.store.kv.Get(KeyPrefixLB + lb.LoadBalancerID) - require.NoError(t, err) - assert.Equal(t, revBefore, entry.Revision(), "identical modify must not increment KV revision") -} - -// TestModifyTargetGroupAttributes_RejectsUnknownKey guards against silently -// persisting typo'd or cross-product attribute keys. AWS rejects unknown keys -// with ValidationError; we must match that so Terraform surfaces the typo at -// plan time instead of letting it drift into KV forever. -func TestModifyTargetGroupAttributes_RejectsUnknownKey(t *testing.T) { - svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-unknown-key")}, testAccountID) - require.NoError(t, err) - - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickness.enabled"), Value: aws.String("true")}, // typo of "stickiness" - }, - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorValidationError) - - // The rejected key must not have been persisted. - descOut, err := svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ - TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn, - }, testAccountID) - require.NoError(t, err) - for _, a := range descOut.Attributes { - assert.NotEqual(t, "stickness.enabled", *a.Key, "unknown key must not appear in Describe") } } -// TestModifyLoadBalancerAttributes_RejectsUnknownKey mirrors the TG case. -func TestModifyLoadBalancerAttributes_RejectsUnknownKey(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-unknown-key")}, testAccountID) - require.NoError(t, err) +func TestAttributeRoundTrip(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + arn := k.create(t, svc, "attr-rt") - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, - Attributes: []*elbv2.LoadBalancerAttribute{ - // Valid TG attribute key sent to LB handler — common cross-product mistake. - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, - }, - }, testAccountID) - assert.EqualError(t, err, awserrors.ErrorValidationError) -} + // Assert the exact echoed key/value pairs, not just the length — a + // regression that pointed at the wrong source or dropped Value would + // pass a length check. + echoed, err := k.modify(svc, arn, testAccountID, kvAttrs([2]string{k.keyA, k.valA}, [2]string{k.keyB, k.valB})) + require.NoError(t, err) + require.Len(t, echoed, 2) + em := echoMap(echoed) + assert.Equal(t, k.valA, em[k.keyA]) + assert.Equal(t, k.valB, em[k.keyB]) -// TestDescribeTargetGroupAttributes_SortedOrder verifies that attributes are -// returned in a stable, sorted-by-key order. Go map iteration is randomised, -// so without explicit sorting Terraform would see spurious plan diffs between -// back-to-back describe calls. -func TestDescribeTargetGroupAttributes_SortedOrder(t *testing.T) { - svc := setupTestService(t) - tgOut, err := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tg-attr-sorted")}, testAccountID) - require.NoError(t, err) - arn := tgOut.TargetGroups[0].TargetGroupArn - - // Modify a few attributes to ensure the merged map contains both defaults - // and overrides. - _, err = svc.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ - TargetGroupArn: arn, - Attributes: []*elbv2.TargetGroupAttribute{ - {Key: aws.String("stickiness.enabled"), Value: aws.String("true")}, - {Key: aws.String("deregistration_delay.timeout_seconds"), Value: aws.String("45")}, - }, - }, testAccountID) - require.NoError(t, err) - - // Call Describe multiple times; each result must be identical and sorted. - var firstKeys []string - for i := range 5 { - descOut, err := svc.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ - TargetGroupArn: arn, - }, testAccountID) - require.NoError(t, err) - - keys := make([]string, len(descOut.Attributes)) - for j, a := range descOut.Attributes { - keys[j] = *a.Key - } - assert.True(t, sort.StringsAreSorted(keys), "attributes must be sorted by key, got %v", keys) - if i == 0 { - firstKeys = keys - } else { - assert.Equal(t, firstKeys, keys, "attribute order must be stable across calls") - } + desc, err := k.describe(svc, arn, testAccountID) + require.NoError(t, err) + dm := echoMap(desc) + assert.Equal(t, k.valA, dm[k.keyA]) + assert.Equal(t, k.valB, dm[k.keyB]) + // Unmodified defaults should still be present. + assert.Equal(t, k.defaultVal, dm[k.defaultKey]) + }) } } -// TestDescribeLoadBalancerAttributes_SortedOrder mirrors the TG test: LB -// describe responses must be sorted and stable across repeated calls. -func TestDescribeLoadBalancerAttributes_SortedOrder(t *testing.T) { - svc := setupTestService(t) - lbOut, err := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("lb-attr-sorted")}, testAccountID) - require.NoError(t, err) - arn := lbOut.LoadBalancers[0].LoadBalancerArn +func TestAttributeNotFound(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) - _, err = svc.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - Attributes: []*elbv2.LoadBalancerAttribute{ - {Key: aws.String("idle_timeout.timeout_seconds"), Value: aws.String("90")}, - {Key: aws.String("deletion_protection.enabled"), Value: aws.String("true")}, - }, - }, testAccountID) - require.NoError(t, err) + _, err := k.modify(svc, k.missingArn, testAccountID, kvAttrs([2]string{k.keyB, k.valB})) + assert.EqualError(t, err, k.notFound) - var firstKeys []string - for i := range 5 { - descOut, err := svc.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ - LoadBalancerArn: arn, - }, testAccountID) - require.NoError(t, err) + _, err = k.describe(svc, k.missingArn, testAccountID) + assert.EqualError(t, err, k.notFound) + }) + } +} - keys := make([]string, len(descOut.Attributes)) - for j, a := range descOut.Attributes { - keys[j] = *a.Key - } - assert.True(t, sort.StringsAreSorted(keys), "attributes must be sorted by key, got %v", keys) - if i == 0 { - firstKeys = keys - } else { - assert.Equal(t, firstKeys, keys, "attribute order must be stable across calls") - } +func TestAttributeMissingArn(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + + _, err := k.modify(svc, "", testAccountID, kvAttrs([2]string{k.keyB, k.valB})) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) + + _, err = k.describe(svc, "", testAccountID) + assert.EqualError(t, err, awserrors.ErrorMissingParameter) + }) + } +} + +func TestAttributeWrongAccount(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + arn := k.create(t, svc, "attr-wrong-acct") + + _, err := k.modify(svc, arn, "999999999999", kvAttrs([2]string{k.keyB, k.valB})) + assert.EqualError(t, err, k.notFound) + + _, err = k.describe(svc, arn, "999999999999") + assert.EqualError(t, err, k.notFound) + + // The rejected modify must not have mutated the record — read back as + // the real owner and confirm keyB is still at its default, not valB. + desc, err := k.describe(svc, arn, testAccountID) + require.NoError(t, err) + assert.NotEqual(t, k.valB, echoMap(desc)[k.keyB], "wrong-account modify must not mutate the record") + }) + } +} + +// TestAttributeCrossAccountIsolation verifies that two accounts each holding a +// resource never see each other's attributes: a Modify by account A must not be +// visible to a Describe by account B, even for similarly-named resources. +func TestAttributeCrossAccountIsolation(t *testing.T) { + const accountB = "222222222222" + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svcA := setupTestService(t) + arnA := k.create(t, svcA, "attr-iso") + + // Account A sets keyB. + _, err := k.modify(svcA, arnA, testAccountID, kvAttrs([2]string{k.keyB, k.valB})) + require.NoError(t, err) + + // Account B cannot see or describe A's resource at all. + _, err = k.describe(svcA, arnA, accountB) + assert.EqualError(t, err, k.notFound) + _, err = k.modify(svcA, arnA, accountB, kvAttrs([2]string{k.keyB, "false"})) + assert.EqualError(t, err, k.notFound) + + // A's value is untouched. + desc, err := k.describe(svcA, arnA, testAccountID) + require.NoError(t, err) + assert.Equal(t, k.valB, echoMap(desc)[k.keyB]) + }) + } +} + +// TestAttributeSkipsInvalidEntries verifies that nil slice elements, nil Keys, +// and nil Values are skipped (with a warning) rather than panicking or being +// silently dropped. Valid attributes in the same call must still be applied. +func TestAttributeSkipsInvalidEntries(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + arn := k.create(t, svc, "attr-skip") + + echoed, err := k.modify(svc, arn, testAccountID, []rawAttr{ + {nilElem: true}, // nil element must not panic + {key: nil, val: aws.String("v")}, + {key: aws.String("k"), val: nil}, + kvAttr(k.keyB, k.valB), + }) + require.NoError(t, err) + // Only the one valid attribute should be returned. + require.Len(t, echoed, 1) + assert.Equal(t, k.keyB, echoed[0].Key) + assert.Equal(t, k.valB, echoed[0].Val) + + // The valid attribute should have been persisted. + desc, err := k.describe(svc, arn, testAccountID) + require.NoError(t, err) + assert.Equal(t, k.valB, echoMap(desc)[k.keyB]) + }) + } +} + +// TestAttributeAllInvalidReturnsError guards against the silent-success case +// where every submitted attribute trips the nil guard and the handler returns +// 200 OK with an empty body — the caller would think the write landed when +// nothing was applied. +func TestAttributeAllInvalidReturnsError(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + arn := k.create(t, svc, "attr-all-invalid") + + _, err := k.modify(svc, arn, testAccountID, []rawAttr{ + {nilElem: true}, + {key: nil, val: aws.String("v")}, + {key: aws.String("k"), val: nil}, + }) + assert.EqualError(t, err, awserrors.ErrorInvalidParameterValue) + }) + } +} + +// TestAttributeSequentialMerge verifies that successive Modify calls accumulate +// keys instead of replacing the entire attribute map. A refactor that did +// `rec.Attributes = newMap` would pass every other test (single-call round-trip +// covers the happy path) but silently wipe previous attributes on every +// subsequent Modify — the most likely real-world bug. +func TestAttributeSequentialMerge(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + arn := k.create(t, svc, "attr-seq-merge") + + // Call 1: set keyA. + _, err := k.modify(svc, arn, testAccountID, kvAttrs([2]string{k.keyA, k.valA})) + require.NoError(t, err) + // Call 2: set a different key; must not wipe the first. + _, err = k.modify(svc, arn, testAccountID, kvAttrs([2]string{k.keyB, k.valB})) + require.NoError(t, err) + + desc, err := k.describe(svc, arn, testAccountID) + require.NoError(t, err) + dm := echoMap(desc) + assert.Equal(t, k.valA, dm[k.keyA], "first-call key must survive second Modify") + assert.Equal(t, k.valB, dm[k.keyB], "second-call key must be present") + + // Call 3: overwrite keyA with a new value. + _, err = k.modify(svc, arn, testAccountID, kvAttrs([2]string{k.keyA, k.valA2})) + require.NoError(t, err) + + desc, err = k.describe(svc, arn, testAccountID) + require.NoError(t, err) + dm = echoMap(desc) + assert.Equal(t, k.valA2, dm[k.keyA], "same-key overwrite must replace the value") + assert.Equal(t, k.valB, dm[k.keyB], "unrelated key must still survive") + }) + } +} + +// TestAttributeNoopSkipsPersist verifies that re-submitting identical values +// does not bump the KV revision — Terraform's drift check hits Modify on every +// apply, so the steady-state path must be a storage-layer no-op. +func TestAttributeNoopSkipsPersist(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + arn := k.create(t, svc, "attr-noop") + + // First modify — must hit the store. + _, err := k.modify(svc, arn, testAccountID, kvAttrs([2]string{k.keyA, k.valA})) + require.NoError(t, err) + revBefore := k.revision(t, svc, arn) + + // Second modify with identical values — must skip the Put. + echoed, err := k.modify(svc, arn, testAccountID, kvAttrs([2]string{k.keyA, k.valA})) + require.NoError(t, err) + require.Len(t, echoed, 1) + assert.Equal(t, revBefore, k.revision(t, svc, arn), "identical modify must not increment KV revision") + + // Empty attribute list is also a no-op. + _, err = k.modify(svc, arn, testAccountID, nil) + require.NoError(t, err) + assert.Equal(t, revBefore, k.revision(t, svc, arn), "empty modify must not increment KV revision") + }) + } +} + +// TestAttributeRejectsUnknownKey guards against silently persisting typo'd or +// cross-product attribute keys. AWS rejects unknown keys with ValidationError; +// we match that so Terraform surfaces the typo at plan time instead of letting +// it drift into KV forever. +func TestAttributeRejectsUnknownKey(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + arn := k.create(t, svc, "attr-unknown-key") + + _, err := k.modify(svc, arn, testAccountID, kvAttrs([2]string{k.unknownKey, "true"})) + assert.EqualError(t, err, awserrors.ErrorValidationError) + + // The rejected key must not have been persisted. + desc, err := k.describe(svc, arn, testAccountID) + require.NoError(t, err) + _, present := echoMap(desc)[k.unknownKey] + assert.False(t, present, "unknown key must not appear in Describe") + }) + } +} + +// TestAttributeSortedOrder verifies attributes are returned in a stable, +// sorted-by-key order. Go map iteration is randomised, so without explicit +// sorting Terraform would see spurious plan diffs between describe calls. +func TestAttributeSortedOrder(t *testing.T) { + for _, k := range attrKinds() { + t.Run(k.name, func(t *testing.T) { + svc := setupTestService(t) + arn := k.create(t, svc, "attr-sorted") + + // Modify a couple of attributes so the merged map mixes defaults and + // overrides. + _, err := k.modify(svc, arn, testAccountID, kvAttrs([2]string{k.keyA, k.valA}, [2]string{k.keyB, k.valB})) + require.NoError(t, err) + + var firstKeys []string + for i := range 5 { + desc, err := k.describe(svc, arn, testAccountID) + require.NoError(t, err) + keys := make([]string, len(desc)) + for j, p := range desc { + keys[j] = p.Key + } + assert.True(t, sort.StringsAreSorted(keys), "attributes must be sorted by key, got %v", keys) + if i == 0 { + firstKeys = keys + } else { + assert.Equal(t, firstKeys, keys, "attribute order must be stable across calls") + } + } + }) } } @@ -2968,9 +3145,9 @@ func TestDescribeTags_Listener_NoTags(t *testing.T) { }, testAccountID) require.NoError(t, err) - // Listeners don't store tags yet — must still return a TagDescription - // (with an empty Tags slice), not an error. This is the case the - // Terraform AWS provider hits during post-create refresh. + // Listener created without tags must still return a TagDescription (with an + // empty Tags slice), not an error. This is the case the Terraform AWS + // provider hits during post-create refresh. out, err := svc.DescribeTags(&elbv2.DescribeTagsInput{ ResourceArns: []*string{lstOut.Listeners[0].ListenerArn}, }, testAccountID) @@ -2980,6 +3157,55 @@ func TestDescribeTags_Listener_NoTags(t *testing.T) { assert.Empty(t, out.TagDescriptions[0].Tags) } +func TestDescribeTags_ListenerRule_NoTags(t *testing.T) { + svc := setupTestService(t) + lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("tags-rule-lb")}, testAccountID) + tgOut, _ := svc.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("tags-rule-tg")}, testAccountID) + lstOut, err := svc.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbOut.LoadBalancers[0].LoadBalancerArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(80), + DefaultActions: []*elbv2.Action{ + {Type: aws.String("forward"), TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn}, + }, + }, testAccountID) + require.NoError(t, err) + + ruleOut, err := svc.CreateRule(&elbv2.CreateRuleInput{ + ListenerArn: lstOut.Listeners[0].ListenerArn, + Priority: aws.Int64(10), + Conditions: []*elbv2.RuleCondition{ + {Field: aws.String("host-header"), Values: aws.StringSlice([]string{"app.example.com"})}, + }, + Actions: []*elbv2.Action{ + {Type: aws.String("forward"), TargetGroupArn: tgOut.TargetGroups[0].TargetGroupArn}, + }, + }, testAccountID) + require.NoError(t, err) + + // Rule created without tags must still return a TagDescription (with an + // empty Tags slice), not an error. This is the case the Terraform AWS + // provider hits during post-create refresh of aws_lb_listener_rule. + out, err := svc.DescribeTags(&elbv2.DescribeTagsInput{ + ResourceArns: []*string{ruleOut.Rules[0].RuleArn}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, out.TagDescriptions, 1) + assert.Equal(t, *ruleOut.Rules[0].RuleArn, *out.TagDescriptions[0].ResourceArn) + assert.Empty(t, out.TagDescriptions[0].Tags) +} + +func TestDescribeTags_RuleNotFound(t *testing.T) { + svc := setupTestService(t) + _, err := svc.DescribeTags(&elbv2.DescribeTagsInput{ + ResourceArns: []*string{ + aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:listener-rule/app/missing/lb-x/lst-y/rule-deadbeef"), + }, + }, testAccountID) + require.Error(t, err) + assert.Contains(t, err.Error(), awserrors.ErrorELBv2RuleNotFound) +} + func TestDescribeTags_MultipleArns(t *testing.T) { svc := setupTestService(t) lbOut, _ := svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ diff --git a/spinifex/handlers/elbv2/service_impl_vpc_test.go b/spinifex/handlers/elbv2/service_impl_vpc_test.go index 652f12227..2e316ed50 100644 --- a/spinifex/handlers/elbv2/service_impl_vpc_test.go +++ b/spinifex/handlers/elbv2/service_impl_vpc_test.go @@ -139,7 +139,7 @@ func TestCreateLoadBalancer_MultipleSubnets(t *testing.T) { // TestCreateLoadBalancer_MultiSubnet_AllENIsPassedToLauncher verifies that // every subnet's ENI is threaded through to SystemInstanceInput, not just -// the first one. Regression guard for mulga-929. +// the first one. func TestCreateLoadBalancer_MultiSubnet_AllENIsPassedToLauncher(t *testing.T) { svc, vpcSvc := setupTestServiceWithVPC(t) diff --git a/spinifex/handlers/elbv2/service_nats.go b/spinifex/handlers/elbv2/service_nats.go index 605ac2fbf..903cfdf30 100644 --- a/spinifex/handlers/elbv2/service_nats.go +++ b/spinifex/handlers/elbv2/service_nats.go @@ -111,6 +111,14 @@ func (s *NATSELBv2Service) DescribeTags(input *elbv2.DescribeTagsInput, accountI return utils.NATSRequest[elbv2.DescribeTagsOutput](s.natsConn, "elbv2.DescribeTags", input, defaultTimeout, accountID) } +func (s *NATSELBv2Service) AddTags(input *elbv2.AddTagsInput, accountID string) (*elbv2.AddTagsOutput, error) { + return utils.NATSRequest[elbv2.AddTagsOutput](s.natsConn, "elbv2.AddTags", input, defaultTimeout, accountID) +} + +func (s *NATSELBv2Service) RemoveTags(input *elbv2.RemoveTagsInput, accountID string) (*elbv2.RemoveTagsOutput, error) { + return utils.NATSRequest[elbv2.RemoveTagsOutput](s.natsConn, "elbv2.RemoveTags", input, defaultTimeout, accountID) +} + func (s *NATSELBv2Service) ModifyTargetGroupAttributes(input *elbv2.ModifyTargetGroupAttributesInput, accountID string) (*elbv2.ModifyTargetGroupAttributesOutput, error) { return utils.NATSRequest[elbv2.ModifyTargetGroupAttributesOutput](s.natsConn, "elbv2.ModifyTargetGroupAttributes", input, defaultTimeout, accountID) } @@ -127,6 +135,34 @@ func (s *NATSELBv2Service) DescribeLoadBalancerAttributes(input *elbv2.DescribeL return utils.NATSRequest[elbv2.DescribeLoadBalancerAttributesOutput](s.natsConn, "elbv2.DescribeLoadBalancerAttributes", input, defaultTimeout, accountID) } +func (s *NATSELBv2Service) SetSecurityGroups(input *elbv2.SetSecurityGroupsInput, accountID string) (*elbv2.SetSecurityGroupsOutput, error) { + return utils.NATSRequest[elbv2.SetSecurityGroupsOutput](s.natsConn, "elbv2.SetSecurityGroups", input, defaultTimeout, accountID) +} + +func (s *NATSELBv2Service) SetIpAddressType(input *elbv2.SetIpAddressTypeInput, accountID string) (*elbv2.SetIpAddressTypeOutput, error) { + return utils.NATSRequest[elbv2.SetIpAddressTypeOutput](s.natsConn, "elbv2.SetIpAddressType", input, defaultTimeout, accountID) +} + +func (s *NATSELBv2Service) SetSubnets(input *elbv2.SetSubnetsInput, accountID string) (*elbv2.SetSubnetsOutput, error) { + return utils.NATSRequest[elbv2.SetSubnetsOutput](s.natsConn, "elbv2.SetSubnets", input, defaultTimeout, accountID) +} + +func (s *NATSELBv2Service) AddListenerCertificates(input *elbv2.AddListenerCertificatesInput, accountID string) (*elbv2.AddListenerCertificatesOutput, error) { + return utils.NATSRequest[elbv2.AddListenerCertificatesOutput](s.natsConn, "elbv2.AddListenerCertificates", input, defaultTimeout, accountID) +} + +func (s *NATSELBv2Service) RemoveListenerCertificates(input *elbv2.RemoveListenerCertificatesInput, accountID string) (*elbv2.RemoveListenerCertificatesOutput, error) { + return utils.NATSRequest[elbv2.RemoveListenerCertificatesOutput](s.natsConn, "elbv2.RemoveListenerCertificates", input, defaultTimeout, accountID) +} + +func (s *NATSELBv2Service) DescribeListenerCertificates(input *elbv2.DescribeListenerCertificatesInput, accountID string) (*elbv2.DescribeListenerCertificatesOutput, error) { + return utils.NATSRequest[elbv2.DescribeListenerCertificatesOutput](s.natsConn, "elbv2.DescribeListenerCertificates", input, defaultTimeout, accountID) +} + +func (s *NATSELBv2Service) DescribeSSLPolicies(input *elbv2.DescribeSSLPoliciesInput, accountID string) (*elbv2.DescribeSSLPoliciesOutput, error) { + return utils.NATSRequest[elbv2.DescribeSSLPoliciesOutput](s.natsConn, "elbv2.DescribeSSLPolicies", input, defaultTimeout, accountID) +} + func (s *NATSELBv2Service) LBAgentHeartbeat(input *LBAgentHeartbeatInput, accountID string) (*LBAgentHeartbeatOutput, error) { return utils.NATSRequest[LBAgentHeartbeatOutput](s.natsConn, "elbv2.LBAgentHeartbeat", input, defaultTimeout, accountID) } diff --git a/spinifex/handlers/elbv2/service_nats_e2e_test.go b/spinifex/handlers/elbv2/service_nats_e2e_test.go index 243772e79..604f1ae3c 100644 --- a/spinifex/handlers/elbv2/service_nats_e2e_test.go +++ b/spinifex/handlers/elbv2/service_nats_e2e_test.go @@ -38,6 +38,20 @@ func setupNATSELBv2Test(t *testing.T) (ELBv2Service, *ELBv2ServiceImpl) { "elbv2.DeleteListener": func(msg *nats.Msg) { handleNATSMsg(msg, backend.DeleteListener) }, "elbv2.ModifyListener": func(msg *nats.Msg) { handleNATSMsg(msg, backend.ModifyListener) }, "elbv2.DescribeListeners": func(msg *nats.Msg) { handleNATSMsg(msg, backend.DescribeListeners) }, + "elbv2.CreateRule": func(msg *nats.Msg) { handleNATSMsg(msg, backend.CreateRule) }, + "elbv2.DescribeRules": func(msg *nats.Msg) { handleNATSMsg(msg, backend.DescribeRules) }, + "elbv2.AddTags": func(msg *nats.Msg) { handleNATSMsg(msg, backend.AddTags) }, + "elbv2.RemoveTags": func(msg *nats.Msg) { handleNATSMsg(msg, backend.RemoveTags) }, + "elbv2.DescribeTags": func(msg *nats.Msg) { handleNATSMsg(msg, backend.DescribeTags) }, + "elbv2.SetSecurityGroups": func(msg *nats.Msg) { handleNATSMsg(msg, backend.SetSecurityGroups) }, + "elbv2.SetIpAddressType": func(msg *nats.Msg) { handleNATSMsg(msg, backend.SetIpAddressType) }, + + "elbv2.ModifyTargetGroupAttributes": func(msg *nats.Msg) { handleNATSMsg(msg, backend.ModifyTargetGroupAttributes) }, + "elbv2.DescribeTargetGroupAttributes": func(msg *nats.Msg) { handleNATSMsg(msg, backend.DescribeTargetGroupAttributes) }, + "elbv2.ModifyLoadBalancerAttributes": func(msg *nats.Msg) { handleNATSMsg(msg, backend.ModifyLoadBalancerAttributes) }, + "elbv2.DescribeLoadBalancerAttributes": func(msg *nats.Msg) { + handleNATSMsg(msg, backend.DescribeLoadBalancerAttributes) + }, } for topic, handler := range topics { @@ -281,3 +295,192 @@ func TestNATSE2E_TargetGroupInUseProtection(t *testing.T) { }, testAccountID) require.Error(t, err) } + +func TestNATSE2E_TagsLifecycle(t *testing.T) { + client, _ := setupNATSELBv2Test(t) + + lbOut, err := client.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("e2e-tags-lb")}, testAccountID) + require.NoError(t, err) + lbArn := lbOut.LoadBalancers[0].LoadBalancerArn + + _, err = client.AddTags(&elbv2.AddTagsInput{ + ResourceArns: []*string{lbArn}, + Tags: []*elbv2.Tag{ + {Key: aws.String("App"), Value: aws.String("nginx")}, + {Key: aws.String("Env"), Value: aws.String("prod")}, + }, + }, testAccountID) + require.NoError(t, err) + + desc, err := client.DescribeTags(&elbv2.DescribeTagsInput{ResourceArns: []*string{lbArn}}, testAccountID) + require.NoError(t, err) + require.Len(t, desc.TagDescriptions, 1) + got := map[string]string{} + for _, tag := range desc.TagDescriptions[0].Tags { + got[*tag.Key] = *tag.Value + } + assert.Equal(t, map[string]string{"App": "nginx", "Env": "prod"}, got) + + _, err = client.RemoveTags(&elbv2.RemoveTagsInput{ + ResourceArns: []*string{lbArn}, + TagKeys: []*string{aws.String("Env")}, + }, testAccountID) + require.NoError(t, err) + + desc2, err := client.DescribeTags(&elbv2.DescribeTagsInput{ResourceArns: []*string{lbArn}}, testAccountID) + require.NoError(t, err) + require.Len(t, desc2.TagDescriptions, 1) + got2 := map[string]string{} + for _, tag := range desc2.TagDescriptions[0].Tags { + got2[*tag.Key] = *tag.Value + } + assert.Equal(t, map[string]string{"App": "nginx"}, got2) +} + +func TestNATSE2E_AttributeRoundTrip(t *testing.T) { + client, _ := setupNATSELBv2Test(t) + + tgOut, err := client.CreateTargetGroup(&elbv2.CreateTargetGroupInput{Name: aws.String("e2e-attr-tg")}, testAccountID) + require.NoError(t, err) + tgArn := tgOut.TargetGroups[0].TargetGroupArn + + _, err = client.ModifyTargetGroupAttributes(&elbv2.ModifyTargetGroupAttributesInput{ + TargetGroupArn: tgArn, + Attributes: []*elbv2.TargetGroupAttribute{ + {Key: aws.String("deregistration_delay.timeout_seconds"), Value: aws.String("120")}, + }, + }, testAccountID) + require.NoError(t, err) + + tgAttrs, err := client.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{TargetGroupArn: tgArn}, testAccountID) + require.NoError(t, err) + tgGot := map[string]string{} + for _, a := range tgAttrs.Attributes { + tgGot[*a.Key] = *a.Value + } + assert.Equal(t, "120", tgGot["deregistration_delay.timeout_seconds"]) + + lbOut, err := client.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("e2e-attr-lb")}, testAccountID) + require.NoError(t, err) + lbArn := lbOut.LoadBalancers[0].LoadBalancerArn + + _, err = client.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ + LoadBalancerArn: lbArn, + Attributes: []*elbv2.LoadBalancerAttribute{ + {Key: aws.String("deletion_protection.enabled"), Value: aws.String("true")}, + }, + }, testAccountID) + require.NoError(t, err) + + lbAttrs, err := client.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{LoadBalancerArn: lbArn}, testAccountID) + require.NoError(t, err) + lbGot := map[string]string{} + for _, a := range lbAttrs.Attributes { + lbGot[*a.Key] = *a.Value + } + assert.Equal(t, "true", lbGot["deletion_protection.enabled"]) +} + +func TestNATSE2E_RedirectListener(t *testing.T) { + client, _ := setupNATSELBv2Test(t) + + lbOut, err := client.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{Name: aws.String("e2e-redir-lb")}, testAccountID) + require.NoError(t, err) + lbArn := lbOut.LoadBalancers[0].LoadBalancerArn + + _, err = client.CreateListener(&elbv2.CreateListenerInput{ + LoadBalancerArn: lbArn, + Protocol: aws.String("HTTP"), + Port: aws.Int64(80), + DefaultActions: []*elbv2.Action{{ + Type: aws.String("redirect"), + RedirectConfig: &elbv2.RedirectActionConfig{ + Protocol: aws.String("HTTPS"), + Port: aws.String("443"), + StatusCode: aws.String("HTTP_301"), + }, + }}, + }, testAccountID) + require.NoError(t, err) + + desc, err := client.DescribeListeners(&elbv2.DescribeListenersInput{LoadBalancerArn: lbArn}, testAccountID) + require.NoError(t, err) + require.Len(t, desc.Listeners, 1) + require.Len(t, desc.Listeners[0].DefaultActions, 1) + action := desc.Listeners[0].DefaultActions[0] + assert.Equal(t, "redirect", *action.Type) + require.NotNil(t, action.RedirectConfig, "redirect action config must survive the NATS round-trip") + assert.Equal(t, "HTTP_301", *action.RedirectConfig.StatusCode) + assert.Equal(t, "HTTPS", *action.RedirectConfig.Protocol) +} + +func TestNATSE2E_IPTargetType(t *testing.T) { + client, _ := setupNATSELBv2Test(t) + + tgOut, err := client.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ + Name: aws.String("e2e-ip-tg"), + TargetType: aws.String("ip"), + }, testAccountID) + require.NoError(t, err) + require.Equal(t, "ip", *tgOut.TargetGroups[0].TargetType) + tgArn := tgOut.TargetGroups[0].TargetGroupArn + + _, err = client.RegisterTargets(&elbv2.RegisterTargetsInput{ + TargetGroupArn: tgArn, + Targets: []*elbv2.TargetDescription{ + {Id: aws.String("10.0.1.20")}, + {Id: aws.String("10.0.1.21"), Port: aws.Int64(8080)}, + }, + }, testAccountID) + require.NoError(t, err) + + health, err := client.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{TargetGroupArn: tgArn}, testAccountID) + require.NoError(t, err) + require.Len(t, health.TargetHealthDescriptions, 2) + + // A non-IP id must be rejected for an ip target group, end-to-end. + _, err = client.RegisterTargets(&elbv2.RegisterTargetsInput{ + TargetGroupArn: tgArn, + Targets: []*elbv2.TargetDescription{{Id: aws.String("i-notanip")}}, + }, testAccountID) + require.Error(t, err) +} + +func TestNATSE2E_SetSecurityGroupsAndIpAddressType(t *testing.T) { + client, _ := setupNATSELBv2Test(t) + + lbOut, err := client.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ + Name: aws.String("e2e-net-lb"), + SecurityGroups: []*string{aws.String("sg-aaa")}, + }, testAccountID) + require.NoError(t, err) + lbArn := lbOut.LoadBalancers[0].LoadBalancerArn + + _, err = client.SetSecurityGroups(&elbv2.SetSecurityGroupsInput{ + LoadBalancerArn: lbArn, + SecurityGroups: []*string{aws.String("sg-bbb"), aws.String("sg-ccc")}, + }, testAccountID) + require.NoError(t, err) + + // Spinifex ALBs are IPv4-only; "ipv4" is the only accepted value. + _, err = client.SetIpAddressType(&elbv2.SetIpAddressTypeInput{ + LoadBalancerArn: lbArn, + IpAddressType: aws.String("ipv4"), + }, testAccountID) + require.NoError(t, err) + + // A dualstack request must be rejected end-to-end. + _, err = client.SetIpAddressType(&elbv2.SetIpAddressTypeInput{ + LoadBalancerArn: lbArn, + IpAddressType: aws.String("dualstack"), + }, testAccountID) + require.Error(t, err) + + desc, err := client.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{ + LoadBalancerArns: []*string{lbArn}, + }, testAccountID) + require.NoError(t, err) + require.Len(t, desc.LoadBalancers, 1) + assert.ElementsMatch(t, []string{"sg-bbb", "sg-ccc"}, aws.StringValueSlice(desc.LoadBalancers[0].SecurityGroups)) + assert.Equal(t, "ipv4", *desc.LoadBalancers[0].IpAddressType) +} diff --git a/spinifex/handlers/elbv2/ssl_policies.go b/spinifex/handlers/elbv2/ssl_policies.go new file mode 100644 index 000000000..104cefc21 --- /dev/null +++ b/spinifex/handlers/elbv2/ssl_policies.go @@ -0,0 +1,92 @@ +package handlers_elbv2 + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" +) + +// protocolRequiresCert reports whether a listener protocol terminates TLS and +// therefore requires at least one certificate: ALB HTTPS and NLB TLS. +func protocolRequiresCert(protocol string) bool { + switch protocol { + case ProtocolHTTPS, ProtocolTLS: + return true + default: + return false + } +} + +// sslPolicyCipher is a named cipher with its negotiation priority. +type sslPolicyCipher struct { + name string + priority int64 +} + +// sslPolicyDef is a static security-policy definition served by +// DescribeSSLPolicies. Spinifex does not terminate TLS yet, so these exist to +// satisfy listener SslPolicy validation and terraform reads. +type sslPolicyDef struct { + name string + protocols []string + ciphers []sslPolicyCipher +} + +// sslPolicyCatalog is the fixed set of supported security policies, keyed by +// name. Static data — no persistence. +var sslPolicyCatalog = map[string]sslPolicyDef{ + DefaultSslPolicy: { + name: DefaultSslPolicy, + protocols: []string{"TLSv1.2"}, + ciphers: []sslPolicyCipher{ + {"ECDHE-ECDSA-AES128-GCM-SHA256", 1}, + {"ECDHE-RSA-AES128-GCM-SHA256", 2}, + {"ECDHE-ECDSA-AES256-GCM-SHA384", 3}, + {"ECDHE-RSA-AES256-GCM-SHA384", 4}, + }, + }, + "ELBSecurityPolicy-TLS13-1-2-2021-06": { + name: "ELBSecurityPolicy-TLS13-1-2-2021-06", + protocols: []string{"TLSv1.3", "TLSv1.2"}, + ciphers: []sslPolicyCipher{ + {"TLS_AES_128_GCM_SHA256", 1}, + {"TLS_AES_256_GCM_SHA384", 2}, + {"TLS_CHACHA20_POLY1305_SHA256", 3}, + {"ECDHE-ECDSA-AES128-GCM-SHA256", 4}, + {"ECDHE-RSA-AES128-GCM-SHA256", 5}, + }, + }, +} + +// sslPolicyOrder is the deterministic order DescribeSSLPolicies returns the +// catalog in. +var sslPolicyOrder = []string{ + DefaultSslPolicy, + "ELBSecurityPolicy-TLS13-1-2-2021-06", +} + +// isKnownSslPolicy reports whether name is in the catalog. +func isKnownSslPolicy(name string) bool { + _, ok := sslPolicyCatalog[name] + return ok +} + +// sslPolicyToSDK renders a catalog definition as an SDK SslPolicy. +func sslPolicyToSDK(def sslPolicyDef) *elbv2.SslPolicy { + ciphers := make([]*elbv2.Cipher, 0, len(def.ciphers)) + for _, c := range def.ciphers { + ciphers = append(ciphers, &elbv2.Cipher{ + Name: aws.String(c.name), + Priority: aws.Int64(c.priority), + }) + } + protocols := make([]*string, 0, len(def.protocols)) + for _, p := range def.protocols { + protocols = append(protocols, aws.String(p)) + } + return &elbv2.SslPolicy{ + Name: aws.String(def.name), + SslProtocols: protocols, + Ciphers: ciphers, + SupportedLoadBalancerTypes: []*string{aws.String("application")}, + } +} diff --git a/spinifex/handlers/elbv2/store.go b/spinifex/handlers/elbv2/store.go index e852dce01..e2c04c012 100644 --- a/spinifex/handlers/elbv2/store.go +++ b/spinifex/handlers/elbv2/store.go @@ -92,9 +92,9 @@ func (s *Store) ListLoadBalancers() ([]*LoadBalancerRecord, error) { } // GetLoadBalancerByArn finds a load balancer by its ARN via a direct KV lookup -// on the short ID embedded in the ARN's final path segment. Falls back to a -// linear scan only if the ARN can't be parsed. Terraform hits Describe*Attributes -// on every plan/refresh so this must be O(1), not O(n). +// on the short ID embedded in the ARN's final path segment. Returns (nil, nil) +// if the ARN can't be parsed or no record matches. Terraform hits +// Describe*Attributes on every plan/refresh so this must be O(1), not O(n). func (s *Store) GetLoadBalancerByArn(arn string) (*LoadBalancerRecord, error) { // ELBv2 LB ARN: arn:aws:elasticloadbalancing:{region}:{account}:loadbalancer/{app,net}/{name}/{lbID} idx := strings.LastIndex(arn, "/") @@ -106,11 +106,16 @@ func (s *Store) GetLoadBalancerByArn(arn string) (*LoadBalancerRecord, error) { if err != nil { return nil, err } + if lb == nil { + return nil, nil + } // Defence-in-depth: ensure the record actually belongs to this ARN. // Short IDs are random hex, so collisions are effectively impossible, but // a mismatch here would indicate KV corruption and must not be silently // served as a successful lookup. - if lb == nil || lb.LoadBalancerArn != arn { + if lb.LoadBalancerArn != arn { + slog.Error("load balancer KV record ARN mismatch", + "requested_arn", arn, "stored_arn", lb.LoadBalancerArn, "lb_id", lbID) return nil, nil } return lb, nil @@ -187,7 +192,12 @@ func (s *Store) GetTargetGroupByArn(arn string) (*TargetGroupRecord, error) { if err != nil { return nil, err } - if tg == nil || tg.TargetGroupArn != arn { + if tg == nil { + return nil, nil + } + if tg.TargetGroupArn != arn { + slog.Error("target group KV record ARN mismatch", + "requested_arn", arn, "stored_arn", tg.TargetGroupArn, "tg_id", tgID) return nil, nil } return tg, nil @@ -322,18 +332,26 @@ func (s *Store) ListListenersByLB(lbArn string) ([]*ListenerRecord, error) { return result, nil } -// GetListenerByArn finds a listener by its ARN. +// GetListenerByArn finds a listener by its ARN via a direct KV lookup on the +// short ID embedded in the ARN's final path segment. See GetLoadBalancerByArn +// for the motivation — Terraform calls DescribeTags on every aws_lb_listener +// on every plan, and DescribeTags can carry many ARNs per call, so this must +// be O(1) per ARN, not an O(n) listener-KV scan. func (s *Store) GetListenerByArn(arn string) (*ListenerRecord, error) { - listeners, err := s.ListListeners() + // ELBv2 listener ARN: arn:aws:elasticloadbalancing:{region}:{account}:listener/{app,net}/{name}/{lbID}/{listenerID} + idx := strings.LastIndex(arn, "/") + if idx < 0 || idx == len(arn)-1 { + return nil, nil + } + listenerID := arn[idx+1:] + l, err := s.GetListener(listenerID) if err != nil { return nil, err } - for _, l := range listeners { - if l.ListenerArn == arn { - return l, nil - } + if l == nil || l.ListenerArn != arn { + return nil, nil } - return nil, nil + return l, nil } // --- Rule CRUD --- diff --git a/spinifex/handlers/elbv2/store_test.go b/spinifex/handlers/elbv2/store_test.go index ab9d4bf86..6c1285c90 100644 --- a/spinifex/handlers/elbv2/store_test.go +++ b/spinifex/handlers/elbv2/store_test.go @@ -350,6 +350,16 @@ func TestGetListenerByArn(t *testing.T) { require.NoError(t, err) require.NotNil(t, got) assert.Equal(t, l.ListenerID, got.ListenerID) + + got, err = store.GetListenerByArn("arn:nonexistent") + require.NoError(t, err) + assert.Nil(t, got) + + // ARN that parses to a real listener ID but a different ARN must not match + // (defence-in-depth against the parse trick serving a wrong record). + got, err = store.GetListenerByArn("arn:aws:elasticloadbalancing:us-east-1:" + testAccountID + ":listener/app/other/lbX/" + l.ListenerID) + require.NoError(t, err) + assert.Nil(t, got) } // --- Cross-resource isolation: shared IDs across record types must not collide --- diff --git a/spinifex/handlers/elbv2/types.go b/spinifex/handlers/elbv2/types.go index f0ef8daa3..e11682691 100644 --- a/spinifex/handlers/elbv2/types.go +++ b/spinifex/handlers/elbv2/types.go @@ -1,6 +1,9 @@ package handlers_elbv2 -import "time" +import ( + "maps" + "time" +) const ( // LoadBalancer types @@ -16,6 +19,10 @@ const ( StateActive = "active" StateFailed = "failed" + // Target group target types (v1 supports instance and ip) + TargetTypeInstance = "instance" + TargetTypeIP = "ip" + // Target health states TargetHealthInitial = "initial" TargetHealthHealthy = "healthy" @@ -33,8 +40,20 @@ const ( ProtocolTLS = "TLS" ProtocolTCPUDP = "TCP_UDP" + // Data-plane engines. ALBs (L7) run HAProxy; NLBs (L4) run nginx `stream` + // because HAProxy cannot load-balance UDP. The agent learns which engine to + // run from the GetLBConfig response. + EngineHAProxy = "haproxy" + EngineNginx = "nginx" + + // DefaultSslPolicy is applied to a secure listener when the caller does + // not specify an SslPolicy. + DefaultSslPolicy = "ELBSecurityPolicy-2016-08" + // Listener action types - ActionTypeForward = "forward" + ActionTypeForward = "forward" + ActionTypeFixedResponse = "fixed-response" + ActionTypeRedirect = "redirect" // Rule condition fields RuleFieldHostHeader = "host-header" @@ -52,7 +71,7 @@ const ( MaxRulesPerListener = 100 MaxConditionsPerRule = 5 MaxValuesPerCondition = 5 - MaxActionsPerRule = 1 // forward only; mulga-951 may bump + MaxActionsPerRule = 1 // single terminal action: forward, redirect, or fixed-response MaxConditionValueLen = 128 MaxHTTPHeaderNameLen = 40 @@ -81,30 +100,52 @@ const ( // LoadBalancerRecord represents a stored load balancer (ALB or NLB). type LoadBalancerRecord struct { - LoadBalancerArn string `json:"load_balancer_arn"` - LoadBalancerID string `json:"load_balancer_id"` // Short ID (hex suffix) - DNSName string `json:"dns_name"` - Name string `json:"name"` - Scheme string `json:"scheme"` // "internet-facing" or "internal" - Type string `json:"type"` // Always "application" - State string `json:"state"` // "provisioning", "active", "failed" - VpcId string `json:"vpc_id"` - SecurityGroups []string `json:"security_groups"` - Subnets []string `json:"subnets"` - AvailZones []AvailZoneInfo `json:"availability_zones"` - ENIs []string `json:"enis,omitempty"` // ENI IDs created for this ALB (internal) - InstanceID string `json:"instance_id,omitempty"` // ALB VM instance ID (system-managed) - VPCIP string `json:"vpc_ip,omitempty"` // VPC private IP of the ALB VM - ConfigText string `json:"config_text,omitempty"` // Pre-computed HAProxy config - ConfigHash string `json:"config_hash,omitempty"` // SHA256 of ConfigText - LastHeartbeat time.Time `json:"last_heartbeat"` // Last agent heartbeat timestamp - HostPorts map[int]int `json:"host_ports,omitempty"` // Dev mode: guest port → host port forwarding - NodeID string `json:"node_id"` // Daemon node running this ALB - IPAddressType string `json:"ip_address_type"` // "ipv4" - Attributes map[string]string `json:"attributes,omitempty"` - Tags map[string]string `json:"tags,omitempty"` - AccountID string `json:"account_id"` - CreatedAt time.Time `json:"created_at"` + LoadBalancerArn string `json:"load_balancer_arn"` + LoadBalancerID string `json:"load_balancer_id"` // Short ID (hex suffix) + DNSName string `json:"dns_name"` + Name string `json:"name"` + Scheme string `json:"scheme"` // "internet-facing" or "internal" + Type string `json:"type"` // Always "application" + State string `json:"state"` // "provisioning", "active", "failed" + VpcId string `json:"vpc_id"` + SecurityGroups []string `json:"security_groups"` + // NLBManagedSGID is the internal security group the ELBv2 service mints for + // a network LB (NLBs reject customer SGs, so this is created and owned here). + // It is attached to every LB ENI and is the SG listener-port ingress is + // authorized on. Not surfaced by DescribeLoadBalancers. + NLBManagedSGID string `json:"nlb_managed_sg_id,omitempty"` + // NLBIngressCIDRs overrides the scheme-based default client CIDRs that + // listener ports are opened to on NLBManagedSGID. Empty ⇒ default + // (internet-facing: 0.0.0.0/0; internal: the VPC CIDR). + NLBIngressCIDRs []string `json:"nlb_ingress_cidrs,omitempty"` + Subnets []string `json:"subnets"` + AvailZones []AvailZoneInfo `json:"availability_zones"` + ENIs []string `json:"enis,omitempty"` // ENI IDs created for this ALB (internal) + InstanceID string `json:"instance_id,omitempty"` // ALB VM instance ID (system-managed) + VPCIP string `json:"vpc_ip,omitempty"` // VPC private IP of the ALB VM + ConfigText string `json:"config_text,omitempty"` // Pre-computed HAProxy config + ConfigHash string `json:"config_hash,omitempty"` // SHA256 of ConfigText + cert material + CertFiles map[string]string `json:"cert_files,omitempty"` // Absolute path → combined PEM (cert+chain+key), delivered with config + HealthTargets []HealthTargetSpec `json:"health_targets,omitempty"` // NLB only: backends the nginx agent actively probes (HAProxy reports its own) + LastHeartbeat time.Time `json:"last_heartbeat"` // Last agent heartbeat timestamp + HostPorts map[int]int `json:"host_ports,omitempty"` // Dev mode: guest port → host port forwarding + NodeID string `json:"node_id"` // Daemon node running this ALB + IPAddressType string `json:"ip_address_type"` // "ipv4" + Attributes map[string]string `json:"attributes,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + AccountID string `json:"account_id"` + CreatedAt time.Time `json:"created_at"` +} + +// HealthTargetSpec is a backend the nginx agent actively health-checks. It is +// computed when the NLB config is stored and delivered to the agent via +// GetLBConfig. ServerName matches the health checker's key +// (sanitizeName("srv", target.Id)). +type HealthTargetSpec struct { + ServerName string `json:"server_name"` + Address string `json:"address"` // ip:port to probe + Protocol string `json:"protocol"` // TCP | HTTP | HTTPS + Path string `json:"path,omitempty"` } // AvailZoneInfo tracks subnet-to-AZ mapping for a load balancer. @@ -122,7 +163,7 @@ type TargetGroupRecord struct { Protocol string `json:"protocol"` // "HTTP" or "HTTPS" Port int64 `json:"port"` // Default target port VpcId string `json:"vpc_id"` - TargetType string `json:"target_type"` // "instance" for v1 + TargetType string `json:"target_type"` // TargetTypeInstance or TargetTypeIP HealthCheck HealthCheckConfig `json:"health_check"` Targets []Target `json:"targets"` Attributes map[string]string `json:"attributes,omitempty"` @@ -175,11 +216,11 @@ func DefaultNLBHealthCheck() HealthCheckConfig { // Target represents a registered target in a target group. type Target struct { - Id string `json:"id"` // Instance ID (e.g. i-xxxxx) + Id string `json:"id"` // Instance ID (i-xxxxx) or IP for ip-type TGs Port int64 `json:"port"` // Override port (0 = use TG default) HealthState string `json:"health_state"` // "initial", "healthy", "unhealthy", "draining" HealthDesc string `json:"health_desc"` // Reason for current state - PrivateIP string `json:"private_ip"` // Resolved from instance ENI + PrivateIP string `json:"private_ip"` // Instance ENI IP, or the target IP for ip-type TGs } // ListenerRecord represents a stored Listener. @@ -192,12 +233,50 @@ type ListenerRecord struct { DefaultActions []ListenerAction `json:"default_actions"` AccountID string `json:"account_id"` CreatedAt time.Time `json:"created_at"` + + // Certificates holds the listener's TLS certificates for a secure + // protocol (ALB HTTPS / NLB TLS). The first IsDefault entry is the + // default cert; the rest are additional SNI certs. Empty for non-secure + // listeners. SslPolicy is the negotiated security policy name. + Certificates []ListenerCertificate `json:"certificates,omitempty"` + SslPolicy string `json:"ssl_policy,omitempty"` + + Tags map[string]string `json:"tags,omitempty"` } -// ListenerAction defines a listener's default action. +// ListenerCertificate is a TLS certificate reference attached to a listener. +type ListenerCertificate struct { + CertificateArn string `json:"certificate_arn"` + IsDefault bool `json:"is_default"` +} + +// ListenerAction defines a listener default action or a rule action. type ListenerAction struct { - Type string `json:"type"` // "forward" + Type string `json:"type"` // "forward", "redirect", or "fixed-response" TargetGroupArn string `json:"target_group_arn"` + // FixedResponse is populated when Type == "fixed-response" (no target group). + FixedResponse *FixedResponseAction `json:"fixed_response,omitempty"` + // Redirect is populated when Type == "redirect". + Redirect *RedirectAction `json:"redirect,omitempty"` +} + +// FixedResponseAction holds the canned reply for a "fixed-response" action. +type FixedResponseAction struct { + StatusCode string `json:"status_code"` + ContentType string `json:"content_type,omitempty"` + MessageBody string `json:"message_body,omitempty"` +} + +// RedirectAction holds the target for a "redirect" action. Fields mirror the +// AWS RedirectActionConfig and may contain the `#{protocol}`, `#{host}`, +// `#{port}`, `#{path}`, and `#{query}` placeholders. +type RedirectAction struct { + Protocol string `json:"protocol,omitempty"` + Host string `json:"host,omitempty"` + Port string `json:"port,omitempty"` + Path string `json:"path,omitempty"` + Query string `json:"query,omitempty"` + StatusCode string `json:"status_code"` // "HTTP_301" or "HTTP_302" } // RuleRecord represents a stored listener rule. @@ -210,6 +289,8 @@ type RuleRecord struct { Actions []ListenerAction `json:"actions"` AccountID string `json:"account_id"` CreatedAt time.Time `json:"created_at"` + + Tags map[string]string `json:"tags,omitempty"` } // RuleCondition is one routing predicate on a rule. Only the field block @@ -240,50 +321,71 @@ type RuleQueryStringKV struct { // provider sends every attribute it knows about, and any key missing here // gets rejected with ValidationError, surfacing as "UnknownError" in tofu. func DefaultLoadBalancerAttributes(lbType string) map[string]string { - // Common to all load balancer types. - attrs := map[string]string{ - "deletion_protection.enabled": "false", - "load_balancing.cross_zone.enabled": "false", - } - switch lbType { case LoadBalancerTypeApplication: - // ALB-specific attributes. Defaults match real AWS values as of the - // aws-sdk-go 1.55 elbv2 API documentation. - attrs["load_balancing.cross_zone.enabled"] = "true" - attrs["access_logs.s3.enabled"] = "false" - attrs["access_logs.s3.bucket"] = "" - attrs["access_logs.s3.prefix"] = "" - attrs["connection_logs.s3.enabled"] = "false" - attrs["connection_logs.s3.bucket"] = "" - attrs["connection_logs.s3.prefix"] = "" - attrs["idle_timeout.timeout_seconds"] = "60" - attrs["client_keep_alive.seconds"] = "3600" - attrs["routing.http.desync_mitigation_mode"] = "defensive" - attrs["routing.http.drop_invalid_header_fields.enabled"] = "false" - attrs["routing.http.preserve_host_header.enabled"] = "false" - attrs["routing.http.x_amzn_tls_version_and_cipher_suite.enabled"] = "false" - attrs["routing.http.xff_client_port.enabled"] = "false" - attrs["routing.http.xff_header_processing.mode"] = "append" - attrs["routing.http2.enabled"] = "true" - attrs["waf.fail_open.enabled"] = "false" - attrs["zonal_shift.config.enabled"] = "false" + return maps.Clone(albAttributeDefaults) case LoadBalancerTypeNetwork: - // NLB-specific attributes. - attrs["access_logs.s3.enabled"] = "false" - attrs["access_logs.s3.bucket"] = "" - attrs["access_logs.s3.prefix"] = "" - attrs["dns_record.client_routing_policy"] = "any_availability_zone" - attrs["ipv6.deny_all_igw_traffic"] = "false" - attrs["zonal_shift.config.enabled"] = "false" + return maps.Clone(nlbAttributeDefaults) + default: + return maps.Clone(lbBaseAttributeDefaults) } - - return attrs } // DefaultTargetGroupAttributes returns the default attribute set for target groups. func DefaultTargetGroupAttributes() map[string]string { - return map[string]string{ + return maps.Clone(targetGroupAttributeDefaults) +} + +// Default attribute sets are package-level so Describe calls clone a prebuilt +// map instead of rebuilding it on every request. Callers receive a clone, never +// the shared map, so mutation cannot leak back into the defaults. +// +// access_logs.s3.bucket / .prefix (and the connection_logs equivalents) default +// to empty strings: that matches real AWS, where logging is disabled by default +// and the bucket/prefix are unset until the operator opts in. Terraform sends +// these empty strings on every apply, so they must be present as known keys. +var ( + // Common to all load balancer types; also the fallback for unknown types. + lbBaseAttributeDefaults = map[string]string{ + "deletion_protection.enabled": "false", + "load_balancing.cross_zone.enabled": "false", + } + + // ALB defaults match real AWS values as of the aws-sdk-go 1.55 elbv2 API. + albAttributeDefaults = map[string]string{ + "deletion_protection.enabled": "false", + "load_balancing.cross_zone.enabled": "true", + "access_logs.s3.enabled": "false", + "access_logs.s3.bucket": "", + "access_logs.s3.prefix": "", + "connection_logs.s3.enabled": "false", + "connection_logs.s3.bucket": "", + "connection_logs.s3.prefix": "", + "idle_timeout.timeout_seconds": "60", + "client_keep_alive.seconds": "3600", + "routing.http.desync_mitigation_mode": "defensive", + "routing.http.drop_invalid_header_fields.enabled": "false", + "routing.http.preserve_host_header.enabled": "false", + "routing.http.x_amzn_tls_version_and_cipher_suite.enabled": "false", + "routing.http.xff_client_port.enabled": "false", + "routing.http.xff_header_processing.mode": "append", + "routing.http2.enabled": "true", + "waf.fail_open.enabled": "false", + "zonal_shift.config.enabled": "false", + } + + nlbAttributeDefaults = map[string]string{ + "deletion_protection.enabled": "false", + "load_balancing.cross_zone.enabled": "false", + "access_logs.s3.enabled": "false", + "access_logs.s3.bucket": "", + "access_logs.s3.prefix": "", + "dns_record.client_routing_policy": "any_availability_zone", + "ipv6.deny_all_igw_traffic": "false", + "zonal_shift.config.enabled": "false", + } + + targetGroupAttributeDefaults = map[string]string{ "deregistration_delay.timeout_seconds": "300", "stickiness.enabled": "false", "stickiness.type": "lb_cookie", @@ -292,4 +394,4 @@ func DefaultTargetGroupAttributes() map[string]string { "load_balancing.algorithm.type": "round_robin", "slow_start.duration_seconds": "0", } -} +) diff --git a/spinifex/handlers/iam/imds_rpc.go b/spinifex/handlers/iam/imds_rpc.go new file mode 100644 index 000000000..9cb1bc48e --- /dev/null +++ b/spinifex/handlers/iam/imds_rpc.go @@ -0,0 +1,57 @@ +package handlers_iam + +import ( + "github.com/aws/aws-sdk-go/service/iam" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" +) + +// imdsResponderQueue load-balances the IMDS control-plane RPCs across every +// awsgw in the cluster, matching the cluster-wide worker convention. +const imdsResponderQueue = "spinifex-workers" + +// SubjectResolveInstanceProfile and SubjectGetRole are the internal, ACL-gated +// subjects the IMDS handler (in vpcd) calls to resolve an instance profile and its +// role from awsgw's in-process IAM. Only these two read RPCs are surfaced. +const ( + SubjectResolveInstanceProfile = "imds.iam.resolve_instance_profile" + SubjectGetRole = "imds.iam.get_role" +) + +// ResolveInstanceProfileRequest is the SubjectResolveInstanceProfile payload. +type ResolveInstanceProfileRequest struct { + AccountID string `json:"account_id"` + NameOrARN string `json:"name_or_arn"` +} + +// GetRoleRequest is the SubjectGetRole payload. +type GetRoleRequest struct { + AccountID string `json:"account_id"` + Input *iam.GetRoleInput `json:"input"` +} + +// SubscribeIMDSResponders wires the profile/role responders onto nc, +// queue-grouped so any awsgw can answer. The returned subscriptions are for +// caller-side cleanup. +func (s *IAMServiceImpl) SubscribeIMDSResponders(nc *nats.Conn) ([]*nats.Subscription, error) { + profileSub, err := nc.QueueSubscribe(SubjectResolveInstanceProfile, imdsResponderQueue, func(msg *nats.Msg) { + utils.ServeNATSRequest(msg, func(req *ResolveInstanceProfileRequest) (*InstanceProfile, error) { + return s.ResolveInstanceProfile(req.AccountID, req.NameOrARN) + }) + }) + if err != nil { + return nil, err + } + + roleSub, err := nc.QueueSubscribe(SubjectGetRole, imdsResponderQueue, func(msg *nats.Msg) { + utils.ServeNATSRequest(msg, func(req *GetRoleRequest) (*iam.GetRoleOutput, error) { + return s.GetRole(req.AccountID, req.Input) + }) + }) + if err != nil { + _ = profileSub.Unsubscribe() + return nil, err + } + + return []*nats.Subscription{profileSub, roleSub}, nil +} diff --git a/spinifex/handlers/iam/instance_profiles.go b/spinifex/handlers/iam/instance_profiles.go index a4b86ead1..12a445d7f 100644 --- a/spinifex/handlers/iam/instance_profiles.go +++ b/spinifex/handlers/iam/instance_profiles.go @@ -42,13 +42,7 @@ func (s *IAMServiceImpl) CreateInstanceProfile(accountID string, input *iam.Crea ARN: fmt.Sprintf("arn:aws:iam::%s:instance-profile%s%s", accountID, path, profileName), Path: path, CreatedAt: time.Now().UTC().Format(time.RFC3339), - Tags: []Tag{}, - } - - for _, tag := range input.Tags { - if tag.Key != nil && tag.Value != nil { - profile.Tags = append(profile.Tags, Tag{Key: *tag.Key, Value: *tag.Value}) - } + Tags: copyTags(input.Tags), } data, err := json.Marshal(profile) diff --git a/spinifex/handlers/iam/oidc_providers.go b/spinifex/handlers/iam/oidc_providers.go index 8ddd65ccf..c1e8109fa 100644 --- a/spinifex/handlers/iam/oidc_providers.go +++ b/spinifex/handlers/iam/oidc_providers.go @@ -3,13 +3,25 @@ package handlers_iam import ( "crypto/sha256" "encoding/hex" + "encoding/json" + "errors" "fmt" + "log/slog" + "net/url" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" ) // Per-account IAM JetStream KV bucket holds resources that are inherently // account-scoped and not appropriate for the global IAM buckets — currently // the OIDC identity-provider registry consumed by STS AssumeRoleWithWebIdentity -// and (in a later sprint) by IAM's CreateOpenIDConnectProvider CRUD. +// and by IAM's CreateOpenIDConnectProvider CRUD. // // Lazy creation: callers that read MUST tolerate ErrBucketNotFound (no provider // has been registered for the account yet); callers that write MUST use @@ -52,3 +64,241 @@ func OIDCProviderKey(issuer string) string { func OIDCProviderARN(accountID, issuerHostPath string) string { return fmt.Sprintf("arn:aws:iam::%s:oidc-provider/%s", accountID, issuerHostPath) } + +// GetOrCreateIAMAccountBucket opens the per-account IAM bucket, creating it on +// first use. Writers (CreateOpenIDConnectProvider) MUST use this; readers that +// must tolerate a not-yet-created bucket should call js.KeyValue directly and +// treat nats.ErrBucketNotFound as "empty". +func GetOrCreateIAMAccountBucket(js nats.JetStreamContext, accountID string, replicas int) (nats.KeyValue, error) { + return getOrCreateBucket(js, IAMAccountBucketName(accountID), 1, max(replicas, 1)) +} + +// OIDCProviderRecord is the stored shape of a registered OIDC identity provider. +// Url carries the full issuer URL including scheme (the exact string the +// cluster apiserver places in the token `iss` claim); the KV key is its hash, +// and the provider ARN is derived by stripping the scheme. +type OIDCProviderRecord struct { + Url string `json:"url"` + ClientIDList []string `json:"clientIDList,omitempty"` + ThumbprintList []string `json:"thumbprintList,omitempty"` + CreatedAt string `json:"createdAt"` + Tags []Tag `json:"tags,omitempty"` +} + +// issuerFromOIDCProviderARN reverses OIDCProviderARN: it extracts the +// scheme-less host/path suffix and prepends https:// to reconstruct the issuer +// URL (and therefore its KV key). v1 issuers are always https, so this +// round-trips Create's input.Url. +func issuerFromOIDCProviderARN(arn string) (string, error) { + _, hostPath, ok := strings.Cut(arn, ":oidc-provider/") + if !ok { + return "", errors.New("not an oidc-provider ARN") + } + if hostPath == "" { + return "", errors.New("oidc-provider ARN missing host/path") + } + return "https://" + hostPath, nil +} + +// validateOIDCProviderURL enforces the v1 issuer shape: a parseable https URL +// with a host. We are the IdP, so no thumbprint/cert-chain validation applies. +func validateOIDCProviderURL(raw string) error { + if raw == "" { + return errors.New("url is required") + } + u, err := url.Parse(raw) + if err != nil { + return fmt.Errorf("parse url: %w", err) + } + if u.Scheme != "https" { + return fmt.Errorf("url scheme must be https, got %q", u.Scheme) + } + if u.Host == "" { + return errors.New("url missing host") + } + return nil +} + +func awsStrings(in []*string) []string { + out := make([]string, 0, len(in)) + for _, s := range in { + if s != nil { + out = append(out, *s) + } + } + return out +} + +func (s *IAMServiceImpl) CreateOpenIDConnectProvider(accountID string, input *iam.CreateOpenIDConnectProviderInput) (*iam.CreateOpenIDConnectProviderOutput, error) { + issuer := aws.StringValue(input.Url) + if err := validateOIDCProviderURL(issuer); err != nil { + slog.Debug("CreateOpenIDConnectProvider: invalid Url", "url", issuer, "err", err) + return nil, errors.New(awserrors.ErrorIAMInvalidInput) + } + + record := OIDCProviderRecord{ + Url: issuer, + ClientIDList: awsStrings(input.ClientIDList), + ThumbprintList: awsStrings(input.ThumbprintList), + CreatedAt: time.Now().UTC().Format(time.RFC3339), + Tags: copyTags(input.Tags), + } + data, err := json.Marshal(record) + if err != nil { + return nil, fmt.Errorf("marshal OIDC provider: %w", err) + } + + kv, err := GetOrCreateIAMAccountBucket(s.js, accountID, s.replicas) + if err != nil { + return nil, fmt.Errorf("open IAM account bucket: %w", err) + } + + if _, err := kv.Create(OIDCProviderKey(issuer), data); err != nil { + if errors.Is(err, nats.ErrKeyExists) { + return nil, errors.New(awserrors.ErrorIAMEntityAlreadyExists) + } + return nil, fmt.Errorf("store OIDC provider: %w", err) + } + + arn := OIDCProviderARN(accountID, stripIssuerScheme(issuer)) + slog.Info("IAM OIDC provider created", "accountID", accountID, "url", issuer, "arn", arn) + return &iam.CreateOpenIDConnectProviderOutput{ + OpenIDConnectProviderArn: aws.String(arn), + Tags: tagsToSDK(record.Tags), + }, nil +} + +func (s *IAMServiceImpl) GetOpenIDConnectProvider(accountID string, input *iam.GetOpenIDConnectProviderInput) (*iam.GetOpenIDConnectProviderOutput, error) { + record, err := s.getOIDCProvider(accountID, aws.StringValue(input.OpenIDConnectProviderArn)) + if err != nil { + return nil, err + } + return &iam.GetOpenIDConnectProviderOutput{ + Url: aws.String(stripIssuerScheme(record.Url)), + ClientIDList: aws.StringSlice(record.ClientIDList), + ThumbprintList: aws.StringSlice(record.ThumbprintList), + CreateDate: aws.Time(parseCreatedAt(record.CreatedAt)), + Tags: tagsToSDK(record.Tags), + }, nil +} + +func (s *IAMServiceImpl) ListOpenIDConnectProviders(accountID string, _ *iam.ListOpenIDConnectProvidersInput) (*iam.ListOpenIDConnectProvidersOutput, error) { + out := &iam.ListOpenIDConnectProvidersOutput{ + OpenIDConnectProviderList: []*iam.OpenIDConnectProviderListEntry{}, + } + + kv, err := s.js.KeyValue(IAMAccountBucketName(accountID)) + if err != nil { + if errors.Is(err, nats.ErrBucketNotFound) { + return out, nil + } + return nil, fmt.Errorf("open IAM account bucket: %w", err) + } + + keys, err := kv.Keys() + if err != nil { + if errors.Is(err, nats.ErrNoKeysFound) { + return out, nil + } + return nil, fmt.Errorf("list OIDC provider keys: %w", err) + } + + for _, key := range keys { + if key == utils.VersionKey || !strings.HasPrefix(key, oidcProvidersKeyPrefix) { + continue + } + entry, err := kv.Get(key) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + continue + } + slog.Warn("ListOpenIDConnectProviders: get failed", "key", key, "err", err) + continue + } + var record OIDCProviderRecord + if err := json.Unmarshal(entry.Value(), &record); err != nil { + slog.Warn("ListOpenIDConnectProviders: unmarshal failed", "key", key, "err", err) + continue + } + out.OpenIDConnectProviderList = append(out.OpenIDConnectProviderList, + &iam.OpenIDConnectProviderListEntry{ + Arn: aws.String(OIDCProviderARN(accountID, stripIssuerScheme(record.Url))), + }) + } + return out, nil +} + +func (s *IAMServiceImpl) DeleteOpenIDConnectProvider(accountID string, input *iam.DeleteOpenIDConnectProviderInput) (*iam.DeleteOpenIDConnectProviderOutput, error) { + arn := aws.StringValue(input.OpenIDConnectProviderArn) + issuer, err := issuerFromOIDCProviderARN(arn) + if err != nil { + return nil, errors.New(awserrors.ErrorIAMNoSuchEntity) + } + + kv, err := s.js.KeyValue(IAMAccountBucketName(accountID)) + if err != nil { + if errors.Is(err, nats.ErrBucketNotFound) { + return nil, errors.New(awserrors.ErrorIAMNoSuchEntity) + } + return nil, fmt.Errorf("open IAM account bucket: %w", err) + } + + key := OIDCProviderKey(issuer) + if _, err := kv.Get(key); err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, errors.New(awserrors.ErrorIAMNoSuchEntity) + } + return nil, fmt.Errorf("get OIDC provider: %w", err) + } + if err := kv.Delete(key); err != nil { + return nil, fmt.Errorf("delete OIDC provider: %w", err) + } + + slog.Info("IAM OIDC provider deleted", "accountID", accountID, "arn", arn) + return &iam.DeleteOpenIDConnectProviderOutput{}, nil +} + +func (s *IAMServiceImpl) getOIDCProvider(accountID, arn string) (*OIDCProviderRecord, error) { + issuer, err := issuerFromOIDCProviderARN(arn) + if err != nil { + return nil, errors.New(awserrors.ErrorIAMNoSuchEntity) + } + kv, err := s.js.KeyValue(IAMAccountBucketName(accountID)) + if err != nil { + if errors.Is(err, nats.ErrBucketNotFound) { + return nil, errors.New(awserrors.ErrorIAMNoSuchEntity) + } + return nil, fmt.Errorf("open IAM account bucket: %w", err) + } + entry, err := kv.Get(OIDCProviderKey(issuer)) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, errors.New(awserrors.ErrorIAMNoSuchEntity) + } + return nil, fmt.Errorf("get OIDC provider: %w", err) + } + var record OIDCProviderRecord + if err := json.Unmarshal(entry.Value(), &record); err != nil { + return nil, fmt.Errorf("unmarshal OIDC provider: %w", err) + } + return &record, nil +} + +// stripIssuerScheme drops the https:// prefix to produce the scheme-less +// host/path form AWS uses in the oidc-provider ARN suffix. +func stripIssuerScheme(issuer string) string { + return strings.TrimPrefix(issuer, "https://") +} + +// tagsToSDK converts stored Tags into the SDK shape used by OIDC-provider +// responses. +func tagsToSDK(tags []Tag) []*iam.Tag { + if len(tags) == 0 { + return nil + } + out := make([]*iam.Tag, 0, len(tags)) + for _, t := range tags { + out = append(out, &iam.Tag{Key: aws.String(t.Key), Value: aws.String(t.Value)}) + } + return out +} diff --git a/spinifex/handlers/iam/oidc_providers_test.go b/spinifex/handlers/iam/oidc_providers_test.go new file mode 100644 index 000000000..f1f886a6a --- /dev/null +++ b/spinifex/handlers/iam/oidc_providers_test.go @@ -0,0 +1,173 @@ +package handlers_iam + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +const testIssuer = "https://10.0.0.1:9999/oidc/eks/ap-southeast-2/000000000001/h1" + +func TestCreateOpenIDConnectProvider(t *testing.T) { + svc := setupTestIAMService(t) + acct := "000000000001" + + out, err := svc.CreateOpenIDConnectProvider(acct, &iam.CreateOpenIDConnectProviderInput{ + Url: aws.String(testIssuer), + ClientIDList: aws.StringSlice([]string{"sts.amazonaws.com"}), + }) + if err != nil { + t.Fatalf("CreateOpenIDConnectProvider: %v", err) + } + + wantARN := OIDCProviderARN(acct, stripIssuerScheme(testIssuer)) + if aws.StringValue(out.OpenIDConnectProviderArn) != wantARN { + t.Fatalf("ARN = %q, want %q", aws.StringValue(out.OpenIDConnectProviderArn), wantARN) + } + + // The registry key STS reads must now exist for this exact issuer. + kv, err := svc.js.KeyValue(IAMAccountBucketName(acct)) + if err != nil { + t.Fatalf("open account bucket: %v", err) + } + if _, err := kv.Get(OIDCProviderKey(testIssuer)); err != nil { + t.Fatalf("provider key not written for issuer: %v", err) + } +} + +func TestCreateOpenIDConnectProvider_Idempotent(t *testing.T) { + svc := setupTestIAMService(t) + acct := "000000000001" + in := &iam.CreateOpenIDConnectProviderInput{Url: aws.String(testIssuer)} + + if _, err := svc.CreateOpenIDConnectProvider(acct, in); err != nil { + t.Fatalf("first create: %v", err) + } + _, err := svc.CreateOpenIDConnectProvider(acct, in) + if err == nil || err.Error() != awserrors.ErrorIAMEntityAlreadyExists { + t.Fatalf("duplicate create err = %v, want EntityAlreadyExists", err) + } +} + +func TestCreateOpenIDConnectProvider_InvalidURL(t *testing.T) { + svc := setupTestIAMService(t) + for _, bad := range []string{"", "http://insecure.example", "://nohost", "ftp://x"} { + _, err := svc.CreateOpenIDConnectProvider("000000000001", + &iam.CreateOpenIDConnectProviderInput{Url: aws.String(bad)}) + if err == nil || err.Error() != awserrors.ErrorIAMInvalidInput { + t.Fatalf("url %q: err = %v, want InvalidInput", bad, err) + } + } +} + +func TestGetOpenIDConnectProvider(t *testing.T) { + svc := setupTestIAMService(t) + acct := "000000000001" + created, err := svc.CreateOpenIDConnectProvider(acct, &iam.CreateOpenIDConnectProviderInput{ + Url: aws.String(testIssuer), + ClientIDList: aws.StringSlice([]string{"sts.amazonaws.com"}), + }) + if err != nil { + t.Fatalf("create: %v", err) + } + + got, err := svc.GetOpenIDConnectProvider(acct, &iam.GetOpenIDConnectProviderInput{ + OpenIDConnectProviderArn: created.OpenIDConnectProviderArn, + }) + if err != nil { + t.Fatalf("get: %v", err) + } + if aws.StringValue(got.Url) != stripIssuerScheme(testIssuer) { + t.Fatalf("Url = %q, want %q", aws.StringValue(got.Url), stripIssuerScheme(testIssuer)) + } + if len(got.ClientIDList) != 1 || aws.StringValue(got.ClientIDList[0]) != "sts.amazonaws.com" { + t.Fatalf("ClientIDList = %v", aws.StringValueSlice(got.ClientIDList)) + } +} + +func TestGetOpenIDConnectProvider_NotFound(t *testing.T) { + svc := setupTestIAMService(t) + _, err := svc.GetOpenIDConnectProvider("000000000001", &iam.GetOpenIDConnectProviderInput{ + OpenIDConnectProviderArn: aws.String(OIDCProviderARN("000000000001", "10.0.0.1:9999/oidc/eks/x/y/z")), + }) + if err == nil || err.Error() != awserrors.ErrorIAMNoSuchEntity { + t.Fatalf("err = %v, want NoSuchEntity", err) + } +} + +func TestListOpenIDConnectProviders(t *testing.T) { + svc := setupTestIAMService(t) + acct := "000000000001" + + // Empty account (bucket not yet created) lists nothing, not an error. + empty, err := svc.ListOpenIDConnectProviders(acct, &iam.ListOpenIDConnectProvidersInput{}) + if err != nil { + t.Fatalf("list empty: %v", err) + } + if len(empty.OpenIDConnectProviderList) != 0 { + t.Fatalf("empty list = %d entries", len(empty.OpenIDConnectProviderList)) + } + + issuers := []string{testIssuer, "https://10.0.0.1:9999/oidc/eks/ap-southeast-2/000000000001/h2"} + for _, iss := range issuers { + if _, err := svc.CreateOpenIDConnectProvider(acct, &iam.CreateOpenIDConnectProviderInput{Url: aws.String(iss)}); err != nil { + t.Fatalf("create %s: %v", iss, err) + } + } + + out, err := svc.ListOpenIDConnectProviders(acct, &iam.ListOpenIDConnectProvidersInput{}) + if err != nil { + t.Fatalf("list: %v", err) + } + if len(out.OpenIDConnectProviderList) != 2 { + t.Fatalf("list = %d entries, want 2", len(out.OpenIDConnectProviderList)) + } + want := map[string]bool{ + OIDCProviderARN(acct, stripIssuerScheme(issuers[0])): true, + OIDCProviderARN(acct, stripIssuerScheme(issuers[1])): true, + } + for _, e := range out.OpenIDConnectProviderList { + if !want[aws.StringValue(e.Arn)] { + t.Fatalf("unexpected ARN %q", aws.StringValue(e.Arn)) + } + } +} + +func TestDeleteOpenIDConnectProvider(t *testing.T) { + svc := setupTestIAMService(t) + acct := "000000000001" + created, err := svc.CreateOpenIDConnectProvider(acct, &iam.CreateOpenIDConnectProviderInput{Url: aws.String(testIssuer)}) + if err != nil { + t.Fatalf("create: %v", err) + } + + if _, err := svc.DeleteOpenIDConnectProvider(acct, &iam.DeleteOpenIDConnectProviderInput{ + OpenIDConnectProviderArn: created.OpenIDConnectProviderArn, + }); err != nil { + t.Fatalf("delete: %v", err) + } + + // Second delete is NoSuchEntity. + _, err = svc.DeleteOpenIDConnectProvider(acct, &iam.DeleteOpenIDConnectProviderInput{ + OpenIDConnectProviderArn: created.OpenIDConnectProviderArn, + }) + if err == nil || err.Error() != awserrors.ErrorIAMNoSuchEntity { + t.Fatalf("second delete err = %v, want NoSuchEntity", err) + } +} + +func TestIssuerProviderARNRoundTrip(t *testing.T) { + arn := OIDCProviderARN("000000000001", stripIssuerScheme(testIssuer)) + got, err := issuerFromOIDCProviderARN(arn) + if err != nil { + t.Fatalf("reverse: %v", err) + } + if got != testIssuer { + t.Fatalf("round-trip issuer = %q, want %q", got, testIssuer) + } + if OIDCProviderKey(got) != OIDCProviderKey(testIssuer) { + t.Fatal("round-trip key mismatch") + } +} diff --git a/spinifex/handlers/iam/roles.go b/spinifex/handlers/iam/roles.go index 83bed719a..63601a690 100644 --- a/spinifex/handlers/iam/roles.go +++ b/spinifex/handlers/iam/roles.go @@ -69,13 +69,7 @@ func (s *IAMServiceImpl) CreateRole(accountID string, input *iam.CreateRoleInput MaxSessionDuration: maxSession, CreatedAt: time.Now().UTC().Format(time.RFC3339), AttachedPolicies: []string{}, - Tags: []Tag{}, - } - - for _, tag := range input.Tags { - if tag.Key != nil && tag.Value != nil { - role.Tags = append(role.Tags, Tag{Key: *tag.Key, Value: *tag.Value}) - } + Tags: copyTags(input.Tags), } data, err := json.Marshal(role) diff --git a/spinifex/handlers/iam/service.go b/spinifex/handlers/iam/service.go index 257e7a0e7..4fb5e969a 100644 --- a/spinifex/handlers/iam/service.go +++ b/spinifex/handlers/iam/service.go @@ -54,6 +54,14 @@ type IAMService interface { AddRoleToInstanceProfile(accountID string, input *iam.AddRoleToInstanceProfileInput) (*iam.AddRoleToInstanceProfileOutput, error) RemoveRoleFromInstanceProfile(accountID string, input *iam.RemoveRoleFromInstanceProfileInput) (*iam.RemoveRoleFromInstanceProfileOutput, error) + // OIDC identity-provider registry — account-scoped. Registers a cluster + // issuer as a trusted federated IdP so STS AssumeRoleWithWebIdentity will + // honour tokens it signs (IRSA). + CreateOpenIDConnectProvider(accountID string, input *iam.CreateOpenIDConnectProviderInput) (*iam.CreateOpenIDConnectProviderOutput, error) + GetOpenIDConnectProvider(accountID string, input *iam.GetOpenIDConnectProviderInput) (*iam.GetOpenIDConnectProviderOutput, error) + ListOpenIDConnectProviders(accountID string, input *iam.ListOpenIDConnectProvidersInput) (*iam.ListOpenIDConnectProvidersOutput, error) + DeleteOpenIDConnectProvider(accountID string, input *iam.DeleteOpenIDConnectProviderInput) (*iam.DeleteOpenIDConnectProviderOutput, error) + // ResolveInstanceProfile dereferences a RunInstancesInput.IamInstanceProfile // reference (name or ARN) to the canonical InstanceProfile record. Used by // EC2 paths only. Cross-account ARNs are rejected as a defence-in-depth diff --git a/spinifex/handlers/iam/service_impl.go b/spinifex/handlers/iam/service_impl.go index f205e2f42..23609d22b 100644 --- a/spinifex/handlers/iam/service_impl.go +++ b/spinifex/handlers/iam/service_impl.go @@ -87,6 +87,10 @@ type IAMServiceImpl struct { instanceProfilesBucket nats.KeyValue masterKey []byte decrypter *Decrypter + // replicas is the JetStream replication factor for lazily-created + // per-account buckets (the OIDC-provider registry). Matches the factor + // the eagerly-created buckets above were built with. + replicas int } var _ IAMService = (*IAMServiceImpl)(nil) @@ -189,6 +193,7 @@ func NewIAMServiceImpl(natsConn *nats.Conn, masterKey []byte, clusterSize int) ( instanceProfilesBucket: instanceProfilesBucket, masterKey: masterKey, decrypter: decrypter, + replicas: replicas, }, nil } @@ -207,6 +212,18 @@ func getOrCreateBucket(js nats.JetStreamContext, name string, history uint8, rep return kv, nil } +// copyTags converts SDK IAM tags into the stored Tag slice, skipping entries +// with a nil key or value. Returns a non-nil (possibly empty) slice. +func copyTags(tags []*iam.Tag) []Tag { + out := make([]Tag, 0, len(tags)) + for _, tag := range tags { + if tag.Key != nil && tag.Value != nil { + out = append(out, Tag{Key: *tag.Key, Value: *tag.Value}) + } + } + return out +} + // --------------------------------------------------------------------------- // User CRUD // --------------------------------------------------------------------------- @@ -239,16 +256,10 @@ func (s *IAMServiceImpl) CreateUser(accountID string, input *iam.CreateUserInput Path: path, CreatedAt: time.Now().UTC().Format(time.RFC3339), AccessKeys: []string{}, - Tags: []Tag{}, + Tags: copyTags(input.Tags), AttachedPolicies: []string{}, } - for _, tag := range input.Tags { - if tag.Key != nil && tag.Value != nil { - user.Tags = append(user.Tags, Tag{Key: *tag.Key, Value: *tag.Value}) - } - } - data, err := json.Marshal(user) if err != nil { return nil, fmt.Errorf("marshal user: %w", err) diff --git a/spinifex/handlers/imds/buckets.go b/spinifex/handlers/imds/buckets.go new file mode 100644 index 000000000..6e2b68fad --- /dev/null +++ b/spinifex/handlers/imds/buckets.go @@ -0,0 +1,72 @@ +package handlers_imds + +import ( + "fmt" + + "github.com/mulgadc/spinifex/spinifex/migrate" + "github.com/nats-io/nats.go" +) + +const ( + // KVBucketIMDSSubnetVeth is the canonical record that a subnet has an IMDS + // localport installed. Keyed by subnetID, the value carries the localport + // spec the BindManager replays to materialise host-side state on every chassis. + KVBucketIMDSSubnetVeth = "spinifex-network-imds-subnet-veth" + KVBucketIMDSSubnetVethVersion = 1 + + // KVBucketENIByVPCIP is a pure reverse index (vpcID/ip → eniID) so the IMDS + // handler can resolve a request's datapath-attested source IP to an ENI in + // one KV read instead of an O(N) scan of the ENI bucket. + KVBucketENIByVPCIP = "spinifex-network-eni-by-vpc-ip" + KVBucketENIByVPCIPVersion = 1 +) + +// InitBuckets opens (or creates) both IMDS KV buckets and runs any pending +// migrations. History is fixed at 1: both are write-once-per-key, so retained +// revisions only waste storage and lengthen tombstone lifetime. +func InitBuckets(js nats.JetStreamContext, replicas int) (subnetVeth, eniByIP nats.KeyValue, err error) { + subnetVeth, err = initIMDSBucket(js, KVBucketIMDSSubnetVeth, KVBucketIMDSSubnetVethVersion, replicas) + if err != nil { + return nil, nil, err + } + + eniByIP, err = initIMDSBucket(js, KVBucketENIByVPCIP, KVBucketENIByVPCIPVersion, replicas) + if err != nil { + return nil, nil, err + } + + return subnetVeth, eniByIP, nil +} + +// InitENIByIPBucket opens (or creates) just the eni-by-vpc-ip reverse-index +// bucket, for the ENI controller (in the daemon), which may start before the +// IMDS service and so needs an init path independent of InitBuckets. Idempotent. +func InitENIByIPBucket(js nats.JetStreamContext, replicas int) (nats.KeyValue, error) { + return initIMDSBucket(js, KVBucketENIByVPCIP, KVBucketENIByVPCIPVersion, replicas) +} + +// initIMDSBucket opens (or creates) a single IMDS KV bucket and runs any +// pending migrations. +func initIMDSBucket(js nats.JetStreamContext, bucket string, version, replicas int) (nats.KeyValue, error) { + if replicas < 1 { + replicas = 1 + } + + kv, err := js.CreateKeyValue(&nats.KeyValueConfig{ + Bucket: bucket, + History: 1, + Replicas: replicas, + }) + if err != nil { + kv, err = js.KeyValue(bucket) + if err != nil { + return nil, fmt.Errorf("open %s bucket: %w", bucket, err) + } + } + + if err := migrate.DefaultRegistry.RunKV(bucket, kv, version); err != nil { + return nil, fmt.Errorf("migrate %s: %w", bucket, err) + } + + return kv, nil +} diff --git a/spinifex/handlers/imds/buckets_test.go b/spinifex/handlers/imds/buckets_test.go new file mode 100644 index 000000000..4857cefb3 --- /dev/null +++ b/spinifex/handlers/imds/buckets_test.go @@ -0,0 +1,31 @@ +package handlers_imds + +import ( + "testing" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/require" +) + +func TestInitENIByIPBucket(t *testing.T) { + _, _, js := testutil.StartTestJetStream(t) + kv, err := InitENIByIPBucket(js, 1) + require.NoError(t, err) + require.NotNil(t, kv) +} + +// InitBuckets clamps replicas < 1 to 1, and a second call converges on the +// open-existing fallback rather than failing on the already-created bucket. +func TestInitBuckets_ReplicaClampAndIdempotent(t *testing.T) { + _, _, js := testutil.StartTestJetStream(t) + + subnetVeth, eniByIP, err := InitBuckets(js, 0) + require.NoError(t, err) + require.NotNil(t, subnetVeth) + require.NotNil(t, eniByIP) + + subnetVeth2, eniByIP2, err := InitBuckets(js, 1) + require.NoError(t, err) + require.NotNil(t, subnetVeth2) + require.NotNil(t, eniByIP2) +} diff --git a/spinifex/handlers/imds/constants.go b/spinifex/handlers/imds/constants.go new file mode 100644 index 000000000..d5e0e7ea9 --- /dev/null +++ b/spinifex/handlers/imds/constants.go @@ -0,0 +1,6 @@ +package handlers_imds + +// MetaDataServerIP is the link-local address the EC2 Instance Metadata Service is +// served on, identical to AWS. Exported so the network topology layer can claim it +// on the subnet-switch localport that answers IMDS over L2 for every guest. +const MetaDataServerIP = "169.254.169.254" diff --git a/spinifex/handlers/imds/credentials.go b/spinifex/handlers/imds/credentials.go new file mode 100644 index 000000000..1a57e01bf --- /dev/null +++ b/spinifex/handlers/imds/credentials.go @@ -0,0 +1,120 @@ +package handlers_imds + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/sts" +) + +const ( + // credDurationSeconds is the lifetime of instance-role credentials. 1 hour + // is the AWS default and matches the storage-backed STS v1 design. + credDurationSeconds = 3600 + + // credRefreshWindow is how far before expiry a cached credential is treated + // as stale and re-minted. AWS SDKs refresh at the same 5-minute mark. + credRefreshWindow = 5 * time.Minute +) + +// instanceCredential is the JSON body served at +// /latest/meta-data/iam/security-credentials/, byte-for-byte matching the +// AWS shape so unmodified SDK credential providers parse it. +type instanceCredential struct { + Code string `json:"Code"` + LastUpdated string `json:"LastUpdated"` + Type string `json:"Type"` + AccessKeyId string `json:"AccessKeyId"` + SecretAccessKey string `json:"SecretAccessKey"` + Token string `json:"Token"` + Expiration string `json:"Expiration"` + AccountId string `json:"AccountId"` +} + +// stsAssumer is the narrow slice of STSService the IMDS credential path needs: +// the in-process, EC2-service-principal AssumeRole entry point. Narrowed to an +// interface so the cred cache is unit-testable with a fake. +type stsAssumer interface { + AssumeRoleForInstance(accountID, roleARN, instanceID string, durationSeconds int64) (*sts.AssumeRoleOutput, error) +} + +// cachedCred holds a rendered credential body and the instant at which it should +// be re-minted (expiry minus the refresh window). +type cachedCred struct { + body []byte + refreshAt time.Time +} + +// credCache memoises minted instance-role credentials per (ENI, role) until the +// refresh window. It keeps STS off the hot path: the typical request returns a +// cached body, and only a near-expiry request triggers a fresh assume. +type credCache struct { + sts stsAssumer + mu sync.Mutex + entries map[string]cachedCred +} + +func newCredCache(assumer stsAssumer) *credCache { + return &credCache{sts: assumer, entries: make(map[string]cachedCred)} +} + +// get returns the credential JSON body for an instance's role, minting fresh +// credentials via STS when the cache is cold or within the refresh window. The +// cache key is (eniID, roleName) so two ENIs sharing a role don't collide. +func (c *credCache) get(eni *eniFacts, roleName, roleARN string, now time.Time) ([]byte, error) { + key := eni.eniID + "\x00" + roleName + + c.mu.Lock() + if entry, ok := c.entries[key]; ok && now.Before(entry.refreshAt) { + body := entry.body + c.mu.Unlock() + return body, nil + } + c.mu.Unlock() + + out, err := c.sts.AssumeRoleForInstance(eni.accountID, roleARN, eni.instanceID, credDurationSeconds) + if err != nil { + return nil, err + } + if out == nil || out.Credentials == nil { + return nil, fmt.Errorf("assume role for instance %s returned no credentials", eni.instanceID) + } + + expiration := aws.TimeValue(out.Credentials.Expiration) + body, err := json.Marshal(instanceCredential{ + Code: "Success", + LastUpdated: now.UTC().Format(time.RFC3339), + Type: "AWS-HMAC", + AccessKeyId: aws.StringValue(out.Credentials.AccessKeyId), + SecretAccessKey: aws.StringValue(out.Credentials.SecretAccessKey), + Token: aws.StringValue(out.Credentials.SessionToken), + Expiration: expiration.UTC().Format(time.RFC3339), + AccountId: eni.accountID, + }) + if err != nil { + return nil, fmt.Errorf("marshal instance credential: %w", err) + } + + c.mu.Lock() + c.entries[key] = cachedCred{body: body, refreshAt: expiration.Add(-credRefreshWindow)} + c.mu.Unlock() + + return body, nil +} + +// sweep drops fully-expired entries (past refreshAt + the refresh window, i.e. +// past the credential's actual expiry), bounding the cache against ENI churn: +// a terminated instance's entry is never overwritten and would otherwise leak. +// Evicting a live entry would only force a re-mint, so this is always safe. +func (c *credCache) sweep(now time.Time) { + c.mu.Lock() + defer c.mu.Unlock() + for key, entry := range c.entries { + if now.After(entry.refreshAt.Add(credRefreshWindow)) { + delete(c.entries, key) + } + } +} diff --git a/spinifex/handlers/imds/credentials_test.go b/spinifex/handlers/imds/credentials_test.go new file mode 100644 index 000000000..58d87007b --- /dev/null +++ b/spinifex/handlers/imds/credentials_test.go @@ -0,0 +1,139 @@ +package handlers_imds + +import ( + "encoding/json" + "errors" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// fakeAssumer is a programmable stsAssumer that records call counts. +type fakeAssumer struct { + out *sts.AssumeRoleOutput + err error + calls int +} + +func (f *fakeAssumer) AssumeRoleForInstance(_, _, _ string, _ int64) (*sts.AssumeRoleOutput, error) { + f.calls++ + return f.out, f.err +} + +func assumeOutput(akid string, exp time.Time) *sts.AssumeRoleOutput { + return &sts.AssumeRoleOutput{ + Credentials: &sts.Credentials{ + AccessKeyId: aws.String(akid), + SecretAccessKey: aws.String("secret"), + SessionToken: aws.String("token"), + Expiration: aws.Time(exp), + }, + } +} + +func TestCredCache_MintsAndRendersAWSShape(t *testing.T) { + now := time.Unix(1_700_000_000, 0).UTC() + exp := now.Add(time.Hour) + assumer := &fakeAssumer{out: assumeOutput("ASIAEXAMPLE", exp)} + cache := newCredCache(assumer) + + eni := &eniFacts{eniID: "eni-aaa", accountID: "111122223333", instanceID: "i-123"} + body, err := cache.get(eni, "app-role", "arn:aws:iam::111122223333:role/app-role", now) + require.NoError(t, err) + + var cred instanceCredential + require.NoError(t, json.Unmarshal(body, &cred)) + assert.Equal(t, "Success", cred.Code) + assert.Equal(t, "AWS-HMAC", cred.Type) + assert.Equal(t, "ASIAEXAMPLE", cred.AccessKeyId) + assert.Equal(t, "secret", cred.SecretAccessKey) + assert.Equal(t, "token", cred.Token) + assert.Equal(t, "111122223333", cred.AccountId) + assert.Equal(t, exp.Format(time.RFC3339), cred.Expiration) +} + +func TestCredCache_ServesCachedUntilRefreshWindow(t *testing.T) { + now := time.Unix(1_700_000_000, 0).UTC() + exp := now.Add(time.Hour) + assumer := &fakeAssumer{out: assumeOutput("ASIAEXAMPLE", exp)} + cache := newCredCache(assumer) + eni := &eniFacts{eniID: "eni-aaa", accountID: "111122223333", instanceID: "i-123"} + + _, err := cache.get(eni, "app-role", "arn:aws:iam::111122223333:role/app-role", now) + require.NoError(t, err) + // Still well outside the 5-minute refresh window → cached, no new mint. + _, err = cache.get(eni, "app-role", "arn:aws:iam::111122223333:role/app-role", now.Add(50*time.Minute)) + require.NoError(t, err) + assert.Equal(t, 1, assumer.calls, "second request inside the refresh window must hit the cache") +} + +func TestCredCache_RemintsInsideRefreshWindow(t *testing.T) { + now := time.Unix(1_700_000_000, 0).UTC() + exp := now.Add(time.Hour) + assumer := &fakeAssumer{out: assumeOutput("ASIAEXAMPLE", exp)} + cache := newCredCache(assumer) + eni := &eniFacts{eniID: "eni-aaa", accountID: "111122223333", instanceID: "i-123"} + + _, err := cache.get(eni, "app-role", "arn:aws:iam::111122223333:role/app-role", now) + require.NoError(t, err) + // 56 minutes in → within 5 min of expiry → re-mint. + assumer.out = assumeOutput("ASIAREFRESHED", now.Add(2*time.Hour)) + _, err = cache.get(eni, "app-role", "arn:aws:iam::111122223333:role/app-role", now.Add(56*time.Minute)) + require.NoError(t, err) + assert.Equal(t, 2, assumer.calls, "request inside the refresh window must re-mint") +} + +func TestCredCache_PropagatesAssumeError(t *testing.T) { + now := time.Unix(1_700_000_000, 0).UTC() + assumer := &fakeAssumer{err: errors.New("AccessDenied")} + cache := newCredCache(assumer) + eni := &eniFacts{eniID: "eni-aaa", accountID: "111122223333", instanceID: "i-123"} + + _, err := cache.get(eni, "app-role", "arn:aws:iam::111122223333:role/app-role", now) + require.Error(t, err) +} + +func TestCredCache_SweepEvictsExpiredOnly(t *testing.T) { + now := time.Unix(1_700_000_000, 0).UTC() + assumer := &fakeAssumer{out: assumeOutput("ASIAEXAMPLE", now.Add(time.Hour))} + cache := newCredCache(assumer) + + stale := &eniFacts{eniID: "eni-gone", accountID: "111122223333", instanceID: "i-old"} + live := &eniFacts{eniID: "eni-live", accountID: "111122223333", instanceID: "i-new"} + _, err := cache.get(stale, "app-role", "arn:aws:iam::111122223333:role/app-role", now) + require.NoError(t, err) + + // Mint the live entry an hour later so its expiry is still in the future + // when we sweep at the original expiry boundary. + assumer.out = assumeOutput("ASIALIVE", now.Add(2*time.Hour)) + _, err = cache.get(live, "app-role", "arn:aws:iam::111122223333:role/app-role", now.Add(time.Hour)) + require.NoError(t, err) + + // Sweep just past the stale entry's full expiry (mint + 1h). The live entry + // expires at mint + 2h, so it must survive. + cache.sweep(now.Add(time.Hour).Add(time.Second)) + + cache.mu.Lock() + _, staleExists := cache.entries[stale.eniID+"\x00"+"app-role"] + _, liveExists := cache.entries[live.eniID+"\x00"+"app-role"] + cache.mu.Unlock() + assert.False(t, staleExists, "expired entry must be swept") + assert.True(t, liveExists, "still-valid entry must survive the sweep") +} + +func TestCredCache_PerENIPerRoleKeying(t *testing.T) { + now := time.Unix(1_700_000_000, 0).UTC() + exp := now.Add(time.Hour) + assumer := &fakeAssumer{out: assumeOutput("ASIAEXAMPLE", exp)} + cache := newCredCache(assumer) + + a := &eniFacts{eniID: "eni-aaa", accountID: "111122223333", instanceID: "i-1"} + b := &eniFacts{eniID: "eni-bbb", accountID: "111122223333", instanceID: "i-2"} + _, _ = cache.get(a, "app-role", "arn:aws:iam::111122223333:role/app-role", now) + _, _ = cache.get(b, "app-role", "arn:aws:iam::111122223333:role/app-role", now) + assert.Equal(t, 2, assumer.calls, "distinct ENIs must not share a cache entry") +} diff --git a/spinifex/handlers/imds/instance_lookup.go b/spinifex/handlers/imds/instance_lookup.go new file mode 100644 index 000000000..123cc0096 --- /dev/null +++ b/spinifex/handlers/imds/instance_lookup.go @@ -0,0 +1,87 @@ +package handlers_imds + +import ( + "encoding/base64" + "fmt" + "log/slog" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + gateway_ec2_instance "github.com/mulgadc/spinifex/spinifex/gateway/ec2/instance" + "github.com/nats-io/nats.go" +) + +// natsInstanceLookup resolves the instance-only metadata fields via the same +// account-scoped NATS fan-out the gateway's DescribeInstances path uses, since +// the instance record lives in each daemon's in-memory manager, not a central KV. +type natsInstanceLookup struct { + nc *nats.Conn + expectedNodes int +} + +var _ instanceLookup = (*natsInstanceLookup)(nil) + +func (l *natsInstanceLookup) describe(accountID, instanceID string) (*instanceFacts, error) { + out, err := gateway_ec2_instance.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: []*string{aws.String(instanceID)}, + }, l.nc, l.expectedNodes, accountID) + if err != nil { + return nil, fmt.Errorf("describe instance %s: %w", instanceID, err) + } + + inst := firstInstance(out) + if inst == nil { + // The ENI references an instance the daemons no longer report (mid + // terminate, or not yet visible). Treated as a miss → 404. + return nil, nil + } + + facts := &instanceFacts{ + instanceType: aws.StringValue(inst.InstanceType), + imageID: aws.StringValue(inst.ImageId), + } + if inst.IamInstanceProfile != nil { + facts.iamInstanceProfileArn = aws.StringValue(inst.IamInstanceProfile.Arn) + } + + facts.userData = l.userData(accountID, instanceID) + return facts, nil +} + +// userData fetches the base64 user-data blob via DescribeInstanceAttribute and +// decodes it. A miss or decode failure yields nil — /latest/user-data then 404s, +// matching AWS for instances launched without user-data. +func (l *natsInstanceLookup) userData(accountID, instanceID string) []byte { + attr, err := gateway_ec2_instance.DescribeInstanceAttribute(&ec2.DescribeInstanceAttributeInput{ + InstanceId: aws.String(instanceID), + Attribute: aws.String("userData"), + }, l.nc, l.expectedNodes, accountID) + if err != nil || attr == nil || attr.UserData == nil || attr.UserData.Value == nil { + return nil + } + decoded, err := base64.StdEncoding.DecodeString(aws.StringValue(attr.UserData.Value)) + if err != nil { + slog.Warn("IMDS: failed to decode instance user-data", "instance_id", instanceID, "err", err) + return nil + } + return decoded +} + +// firstInstance returns the first instance in a DescribeInstances response, or +// nil when the response carries none. +func firstInstance(out *ec2.DescribeInstancesOutput) *ec2.Instance { + if out == nil { + return nil + } + for _, res := range out.Reservations { + if res == nil { + continue + } + for _, inst := range res.Instances { + if inst != nil { + return inst + } + } + } + return nil +} diff --git a/spinifex/handlers/imds/instance_lookup_test.go b/spinifex/handlers/imds/instance_lookup_test.go new file mode 100644 index 000000000..e68321635 --- /dev/null +++ b/spinifex/handlers/imds/instance_lookup_test.go @@ -0,0 +1,33 @@ +package handlers_imds + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFirstInstance_NilAndEmpty(t *testing.T) { + assert.Nil(t, firstInstance(nil)) + assert.Nil(t, firstInstance(&ec2.DescribeInstancesOutput{})) + assert.Nil(t, firstInstance(&ec2.DescribeInstancesOutput{ + Reservations: []*ec2.Reservation{nil, {Instances: []*ec2.Instance{nil}}}, + })) +} + +// firstInstance skips nil reservations and nil instance slots and returns the +// first concrete instance it finds. +func TestFirstInstance_SkipsNilsAndReturnsFirst(t *testing.T) { + out := &ec2.DescribeInstancesOutput{ + Reservations: []*ec2.Reservation{ + nil, + {Instances: []*ec2.Instance{nil}}, + {Instances: []*ec2.Instance{{InstanceId: aws.String("i-first")}, {InstanceId: aws.String("i-second")}}}, + }, + } + got := firstInstance(out) + require.NotNil(t, got) + assert.Equal(t, "i-first", aws.StringValue(got.InstanceId)) +} diff --git a/spinifex/handlers/imds/metadata.go b/spinifex/handlers/imds/metadata.go new file mode 100644 index 000000000..7dc9509e5 --- /dev/null +++ b/spinifex/handlers/imds/metadata.go @@ -0,0 +1,393 @@ +package handlers_imds + +import ( + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "strconv" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/mulgadc/spinifex/spinifex/awserrors" + "github.com/mulgadc/spinifex/spinifex/utils" +) + +// HTTP paths. IMDSv2 token issuance is a PUT; everything else is a token-gated +// GET under /latest/. +const ( + pathToken = "/latest/api/token" //nolint:gosec // URL path, not a credential + prefixMetaData = "/latest/meta-data/" + pathMetaDataRoot = "/latest/meta-data" + pathUserData = "/latest/user-data" + prefixSecurityCreds = "/latest/meta-data/iam/security-credentials/" //nolint:gosec // URL path, not a credential + pathSecurityCredsDir = "/latest/meta-data/iam/security-credentials" //nolint:gosec // URL path, not a credential + + hdrToken = "X-Aws-Ec2-Metadata-Token" //nolint:gosec // HTTP header name, not a credential + hdrTokenTTL = "X-Aws-Ec2-Metadata-Token-Ttl-Seconds" //nolint:gosec // HTTP header name, not a credential + + hdrForwardedFor = "X-Forwarded-For" +) + +// rejectForwarded enforces AWS IMDS's SSRF defence: any request carrying an +// X-Forwarded-For header is refused with 403 before token or identity checks, +// blocking the classic "trick a server-side app into relaying metadata" attack. +func rejectForwarded(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get(hdrForwardedFor) != "" { + w.WriteHeader(http.StatusForbidden) + return + } + next.ServeHTTP(w, r) + }) +} + +// handleToken serves PUT /latest/api/token. It binds a fresh token to the +// requesting ENI (resolved from the datapath-attested source IP) and returns it +// as a text/plain body, matching AWS IMDSv2. +func (s *IMDSServiceImpl) handleToken(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPut { + w.Header().Set("Allow", http.MethodPut) + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + ttl, ok := parseTokenTTL(r.Header.Get(hdrTokenTTL)) + if !ok { + w.WriteHeader(http.StatusBadRequest) + return + } + + eni := s.resolveCaller(r) + if eni == nil { + w.WriteHeader(http.StatusNotFound) + return + } + + token, err := s.tokens.issue(eni.eniID, ttl, s.now()) + if err != nil { + slog.Error("IMDS: token issuance failed", "eni_id", eni.eniID, "err", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "text/plain") + w.Header().Set(hdrTokenTTL, strconv.Itoa(int(ttl.Seconds()))) + _, _ = w.Write([]byte(token)) +} + +// handleMetadata serves every token-gated read path. It resolves the caller's +// ENI, enforces the IMDSv2 token, then dispatches on the request path. +func (s *IMDSServiceImpl) handleMetadata(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + w.Header().Set("Allow", http.MethodGet) + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + eni := s.resolveCaller(r) + if eni == nil { + w.WriteHeader(http.StatusNotFound) + return + } + + // IMDSv2: a valid, ENI-bound token is mandatory on every read. A missing, + // unknown, expired, or wrong-ENI token is an indistinguishable 401 so a + // guest cannot probe which tokens exist. + if !s.tokens.validate(r.Header.Get(hdrToken), eni.eniID, s.now()) { + w.WriteHeader(http.StatusUnauthorized) + return + } + + s.dispatch(w, r, eni) +} + +// resolveCaller maps the request's (vpcID-from-context, source-IP) to the owning +// ENI, or nil when no mapping exists (logged for operator triage). The listener +// identifies the subnet; its statically-resolved VPC is what keys the +// eni-by-vpc-ip index. A backend error is also surfaced as nil → the caller +// 404s, never 500s, matching AWS's opaque "eventually available" boot posture. +func (s *IMDSServiceImpl) resolveCaller(r *http.Request) *eniFacts { + vpcID, _ := r.Context().Value(ctxKeyVPCID).(string) + subnetID, _ := r.Context().Value(ctxKeySubnetID).(string) + srcIP := utils.ClientIP(r.RemoteAddr) + if vpcID == "" || srcIP == "" { + slog.Warn("IMDS: request missing VPC context or source IP", "vpc_id", vpcID, "subnet_id", subnetID, "remote_addr", r.RemoteAddr) + return nil + } + + eni, err := s.resolver.resolveENI(vpcID, srcIP) + if err != nil { + slog.Error("IMDS: ENI resolution failed", "vpc_id", vpcID, "subnet_id", subnetID, "src_ip", srcIP, "err", err) + return nil + } + if eni == nil { + slog.Warn("IMDS: no ENI for source IP", "vpc_id", vpcID, "subnet_id", subnetID, "src_ip", srcIP) + return nil + } + return eni +} + +// dispatch routes a token-validated GET to the right metadata producer. +func (s *IMDSServiceImpl) dispatch(w http.ResponseWriter, r *http.Request, eni *eniFacts) { + path := r.URL.Path + + // Credential fetch for a specific role: /iam/security-credentials/. + if strings.HasPrefix(path, prefixSecurityCreds) && len(path) > len(prefixSecurityCreds) { + s.serveRoleCredentials(w, eni, strings.TrimPrefix(path, prefixSecurityCreds)) + return + } + + switch path { + case pathMetaDataRoot, prefixMetaData: + writeText(w, "ami-id\nhostname\niam/\ninstance-id\ninstance-type\nlocal-hostname\nlocal-ipv4\nmac\nplacement/\npublic-ipv4\nsecurity-groups") + case prefixMetaData + "instance-id": + writeText(w, eni.instanceID) + case prefixMetaData + "local-ipv4": + writeText(w, eni.privateIP) + case prefixMetaData + "public-ipv4": + writeText(w, eni.publicIP) + case prefixMetaData + "mac": + writeText(w, eni.mac) + case prefixMetaData + "security-groups": + writeText(w, strings.Join(s.resolver.resolveSGNames(eni.accountID, eni.securityGroupIDs), "\n")) + case prefixMetaData + "hostname", prefixMetaData + "local-hostname": + writeText(w, synthHostname(eni.privateIP, regionFromAZ(eni.availabilityZone))) + case prefixMetaData + "placement", prefixMetaData + "placement/": + writeText(w, "availability-zone\nregion") + case prefixMetaData + "placement/availability-zone": + writeText(w, eni.availabilityZone) + case prefixMetaData + "placement/region": + writeText(w, regionFromAZ(eni.availabilityZone)) + case prefixMetaData + "instance-type": + s.serveInstanceField(w, eni, func(i *instanceFacts) string { return i.instanceType }) + case prefixMetaData + "ami-id": + s.serveInstanceField(w, eni, func(i *instanceFacts) string { return i.imageID }) + case prefixMetaData + "iam", prefixMetaData + "iam/": + writeText(w, "info\nsecurity-credentials/") + case prefixMetaData + "iam/info": + s.serveIAMInfo(w, eni) + case pathSecurityCredsDir, prefixSecurityCreds: + s.serveSecurityCredentialsList(w, eni) + case pathUserData: + s.serveUserData(w, eni) + default: + // /latest/dynamic/instance-identity/document and + // /latest/meta-data/network/interfaces/... are out of scope in v1. + w.WriteHeader(http.StatusNotFound) + } +} + +// serveInstanceField resolves the instance record and writes one of its +// string fields, 404ing when the instance is no longer visible. +func (s *IMDSServiceImpl) serveInstanceField(w http.ResponseWriter, eni *eniFacts, field func(*instanceFacts) string) { + inst := s.instanceFor(w, eni) + if inst == nil { + return + } + writeText(w, field(inst)) +} + +// serveUserData writes the instance's user-data, or 404 when there is none — +// matching AWS for instances launched without user-data. +func (s *IMDSServiceImpl) serveUserData(w http.ResponseWriter, eni *eniFacts) { + inst := s.instanceFor(w, eni) + if inst == nil { + return + } + if len(inst.userData) == 0 { + w.WriteHeader(http.StatusNotFound) + return + } + w.Header().Set("Content-Type", "text/plain") + _, _ = w.Write(inst.userData) +} + +// serveIAMInfo writes {InstanceProfileArn, InstanceProfileId}, 404 when the +// instance has no instance profile, or 500 when the profile can't be resolved. +func (s *IMDSServiceImpl) serveIAMInfo(w http.ResponseWriter, eni *eniFacts) { + profile, err := s.profileFor(eni) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + if profile == nil { + w.WriteHeader(http.StatusNotFound) + return + } + writeJSON(w, map[string]string{ + "InstanceProfileArn": profile.ARN, + "InstanceProfileId": profile.InstanceProfileID, + }) +} + +// serveSecurityCredentialsList writes the role name(s) under the profile, one +// per line. No profile/role is an empty 200 (matching AWS); a backend failure is +// a 500, never an empty 200 that would silently strip the instance's creds. +func (s *IMDSServiceImpl) serveSecurityCredentialsList(w http.ResponseWriter, eni *eniFacts) { + profile, err := s.profileFor(eni) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + if profile == nil || profile.RoleName == "" { + writeText(w, "") + return + } + writeText(w, profile.RoleName) +} + +// serveRoleCredentials mints (or serves cached) credentials for the named role. +// AWS only accepts the actual role name as the path parameter, not the profile +// name, so a mismatch is 404; a backend failure resolving the profile is 500. +func (s *IMDSServiceImpl) serveRoleCredentials(w http.ResponseWriter, eni *eniFacts, roleParam string) { + profile, err := s.profileFor(eni) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + if profile == nil || profile.RoleName == "" || roleParam != profile.RoleName { + w.WriteHeader(http.StatusNotFound) + return + } + + role, err := s.iam.GetRole(eni.accountID, &iam.GetRoleInput{RoleName: aws.String(profile.RoleName)}) + if err != nil || role == nil || role.Role == nil || role.Role.Arn == nil { + slog.Error("IMDS: resolve role ARN failed", "account_id", eni.accountID, "role", profile.RoleName, "err", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + body, err := s.creds.get(eni, profile.RoleName, *role.Role.Arn, s.now()) + if err != nil { + // AWS surfaces a trust-policy failure as a JSON body with Code:"Failed"; + // the SDK reports it as "EC2RoleProvider failed". Mirror that rather than + // a bare HTTP error so the SDK error message is AWS-accurate. + slog.Warn("IMDS: AssumeRoleForInstance failed", "account_id", eni.accountID, "role", profile.RoleName, "instance_id", eni.instanceID, "err", err) + writeJSON(w, map[string]string{ + "Code": "Failed", + "LastUpdated": s.now().UTC().Format(time.RFC3339), + "Message": "failed to assume instance role", + }) + return + } + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(body) +} + +// instanceFor resolves the instance record for an ENI, writing the appropriate +// HTTP error and returning nil on miss/failure so callers can early-return. +func (s *IMDSServiceImpl) instanceFor(w http.ResponseWriter, eni *eniFacts) *instanceFacts { + inst, err := s.resolver.resolveInstance(eni) + if err != nil { + slog.Error("IMDS: instance resolution failed", "instance_id", eni.instanceID, "err", err) + w.WriteHeader(http.StatusInternalServerError) + return nil + } + if inst == nil { + w.WriteHeader(http.StatusNotFound) + return nil + } + return inst +} + +// profileFor resolves the instance's IAM instance profile. Returns (profile, nil) +// when resolvable, (nil, nil) when genuinely absent (404/empty), (nil, err) on +// backend failure (500) — collapsing err into absent would silently drop creds. +func (s *IMDSServiceImpl) profileFor(eni *eniFacts) (*resolvedProfile, error) { + inst, err := s.resolver.resolveInstance(eni) + if err != nil { + slog.Error("IMDS: instance resolution failed", "instance_id", eni.instanceID, "err", err) + return nil, err + } + if inst == nil || inst.iamInstanceProfileArn == "" { + return nil, nil + } + profile, err := s.iam.ResolveInstanceProfile(eni.accountID, inst.iamInstanceProfileArn) + if err != nil { + if err.Error() == awserrors.ErrorIAMNoSuchEntity { + // The profile was deleted out from under the instance. AWS treats this + // as "no instance role", not a backend fault — NoSuchEntity is raised + // only for a genuinely absent record, never a transient. + return nil, nil + } + slog.Error("IMDS: resolve instance profile failed", "account_id", eni.accountID, "arn", inst.iamInstanceProfileArn, "err", err) + return nil, err + } + if profile == nil { + return nil, nil + } + return &resolvedProfile{ARN: profile.ARN, InstanceProfileID: profile.InstanceProfileID, RoleName: profile.RoleName}, nil +} + +// resolvedProfile is the slice of an IAM instance profile the metadata surface +// serves, decoupled from the handlers_iam type so this package needn't import +// it for the field access. +type resolvedProfile struct { + ARN string + InstanceProfileID string + RoleName string +} + +// parseTokenTTL validates the X-aws-ec2-metadata-token-ttl-seconds header. +func parseTokenTTL(raw string) (time.Duration, bool) { + if raw == "" { + return 0, false + } + n, err := strconv.Atoi(raw) + if err != nil || n < tokenTTLMin || n > tokenTTLMax { + return 0, false + } + return time.Duration(n) * time.Second, true +} + +// regionFromAZ derives the region from an availability-zone name by stripping +// the trailing AZ letter ("ap-southeast-2a" → "ap-southeast-2"). +func regionFromAZ(az string) string { + if az == "" { + return "" + } + last := az[len(az)-1] + if last >= 'a' && last <= 'z' { + return az[:len(az)-1] + } + return az +} + +// synthHostname builds the AWS-style internal hostname +// "ip-10-0-1-5..compute.internal" from a private IP and region. +func synthHostname(ip, region string) string { + if ip == "" || region == "" { + return "" + } + return fmt.Sprintf("ip-%s.%s.compute.internal", strings.ReplaceAll(ip, ".", "-"), region) +} + +func writeText(w http.ResponseWriter, body string) { + w.Header().Set("Content-Type", "text/plain") + _, _ = io.WriteString(w, body) +} + +func writeJSON(w http.ResponseWriter, v any) { + data, err := json.Marshal(v) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(data) +} + +// ctxKey is the unexported context-key type for values the bind manager threads +// into each request: the subnet the listener serves and that subnet's +// statically-resolved owning VPC (which keys the eni-by-vpc-ip index). +type ctxKey int + +const ( + ctxKeyVPCID ctxKey = iota + ctxKeySubnetID +) diff --git a/spinifex/handlers/imds/metadata_test.go b/spinifex/handlers/imds/metadata_test.go new file mode 100644 index 000000000..7f308eb14 --- /dev/null +++ b/spinifex/handlers/imds/metadata_test.go @@ -0,0 +1,418 @@ +package handlers_imds + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// ----- fakes ------------------------------------------------------------- + +type fakeResolver struct { + eni *eniFacts + eniErr error + inst *instanceFacts + instErr error + sgNames map[string]string // sg-id → group name; absent IDs fall back to the ID +} + +func (f *fakeResolver) resolveENI(_, _ string) (*eniFacts, error) { return f.eni, f.eniErr } +func (f *fakeResolver) resolveInstance(_ *eniFacts) (*instanceFacts, error) { + return f.inst, f.instErr +} + +func (f *fakeResolver) resolveSGNames(_ string, sgIDs []string) []string { + out := make([]string, len(sgIDs)) + for i, id := range sgIDs { + if name, ok := f.sgNames[id]; ok { + out[i] = name + } else { + out[i] = id + } + } + return out +} + +type fakeIAM struct { + profile *handlers_iam.InstanceProfile + profileErr error + roleARN string + roleErr error +} + +func (f *fakeIAM) ResolveInstanceProfile(_, _ string) (*handlers_iam.InstanceProfile, error) { + return f.profile, f.profileErr +} + +func (f *fakeIAM) GetRole(_ string, _ *iam.GetRoleInput) (*iam.GetRoleOutput, error) { + if f.roleErr != nil { + return nil, f.roleErr + } + return &iam.GetRoleOutput{Role: &iam.Role{Arn: aws.String(f.roleARN)}}, nil +} + +const ( + testVPC = "vpc-abc12345" + testIP = "10.0.1.5" +) + +func testENI() *eniFacts { + return &eniFacts{ + eniID: "eni-aaa", + accountID: "111122223333", + instanceID: "i-0123456789", + vpcID: testVPC, + subnetID: "subnet-1", + privateIP: testIP, + publicIP: "203.0.113.7", + mac: "02:11:22:33:44:55", + availabilityZone: "ap-southeast-2a", + securityGroupIDs: []string{"sg-1", "sg-2"}, + } +} + +// newTestService builds an IMDSServiceImpl with injected fakes and a fixed +// clock. The bind manager is left nil — the HTTP handler is exercised directly. +func newTestService(res eniResolver, fIAM profileLookup, assumer stsAssumer) (*IMDSServiceImpl, time.Time) { + now := time.Unix(1_700_000_000, 0).UTC() + return &IMDSServiceImpl{ + resolver: res, + tokens: newTokenStore(), + creds: newCredCache(assumer), + iam: fIAM, + now: func() time.Time { return now }, + }, now +} + +// get issues a token-gated GET with the VPC context + source IP a real listener +// would have threaded in, plus the supplied token header. +func get(t *testing.T, h http.Handler, path, token string) *httptest.ResponseRecorder { + t.Helper() + req := httptest.NewRequest(http.MethodGet, "http://"+MetaDataServerIP+path, nil) + req.RemoteAddr = testIP + ":50000" + req = req.WithContext(context.WithValue(req.Context(), ctxKeyVPCID, testVPC)) + if token != "" { + req.Header.Set(hdrToken, token) + } + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + return rec +} + +// issueToken performs the PUT /latest/api/token handshake and returns the token. +func issueToken(t *testing.T, h http.Handler) string { + t.Helper() + req := httptest.NewRequest(http.MethodPut, "http://"+MetaDataServerIP+pathToken, nil) + req.RemoteAddr = testIP + ":50000" + req = req.WithContext(context.WithValue(req.Context(), ctxKeyVPCID, testVPC)) + req.Header.Set(hdrTokenTTL, "60") + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Code) + require.NotEmpty(t, rec.Body.String()) + return rec.Body.String() +} + +// ----- token model ------------------------------------------------------ + +func TestHTTP_TokenlessGETRejected(t *testing.T) { + svc, _ := newTestService(&fakeResolver{eni: testENI()}, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + rec := get(t, h, prefixMetaData+"instance-id", "") + assert.Equal(t, http.StatusUnauthorized, rec.Code) + assert.Empty(t, rec.Body.String()) +} + +func TestHTTP_TokenPUTValidation(t *testing.T) { + svc, _ := newTestService(&fakeResolver{eni: testENI()}, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + + for _, ttl := range []string{"", "0", "21601", "notanumber"} { + req := httptest.NewRequest(http.MethodPut, "http://"+MetaDataServerIP+pathToken, nil) + req.RemoteAddr = testIP + ":50000" + req = req.WithContext(context.WithValue(req.Context(), ctxKeyVPCID, testVPC)) + if ttl != "" { + req.Header.Set(hdrTokenTTL, ttl) + } + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + assert.Equal(t, http.StatusBadRequest, rec.Code, "ttl=%q", ttl) + } +} + +func TestHTTP_TokenGETMethodNotAllowed(t *testing.T) { + svc, _ := newTestService(&fakeResolver{eni: testENI()}, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + req := httptest.NewRequest(http.MethodGet, "http://"+MetaDataServerIP+pathToken, nil) + req.RemoteAddr = testIP + ":50000" + req = req.WithContext(context.WithValue(req.Context(), ctxKeyVPCID, testVPC)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + assert.Equal(t, http.StatusMethodNotAllowed, rec.Code) +} + +// TestHTTP_ForwardedForRejected pins AWS IMDS's SSRF defence: any request +// carrying X-Forwarded-For is refused with 403 regardless of token or identity, +// on both the read path and token issuance. +func TestHTTP_ForwardedForRejected(t *testing.T) { + svc, _ := newTestService(&fakeResolver{eni: testENI()}, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + + // A valid token + resolvable ENI is still refused once X-Forwarded-For is set. + token := issueToken(t, h) + req := httptest.NewRequest(http.MethodGet, "http://"+MetaDataServerIP+prefixMetaData+"instance-id", nil) + req.RemoteAddr = testIP + ":50000" + req = req.WithContext(context.WithValue(req.Context(), ctxKeyVPCID, testVPC)) + req.Header.Set(hdrToken, token) + req.Header.Set(hdrForwardedFor, "10.0.0.99") + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + assert.Equal(t, http.StatusForbidden, rec.Code) + + // Token issuance is refused too. + put := httptest.NewRequest(http.MethodPut, "http://"+MetaDataServerIP+pathToken, nil) + put.RemoteAddr = testIP + ":50000" + put = put.WithContext(context.WithValue(put.Context(), ctxKeyVPCID, testVPC)) + put.Header.Set(hdrTokenTTL, "60") + put.Header.Set(hdrForwardedFor, "10.0.0.99") + putRec := httptest.NewRecorder() + h.ServeHTTP(putRec, put) + assert.Equal(t, http.StatusForbidden, putRec.Code) +} + +func TestHTTP_ENIMissReturns404(t *testing.T) { + svc, _ := newTestService(&fakeResolver{eni: nil}, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + + // Token PUT and metadata GET both 404 when the source IP maps to no ENI. + req := httptest.NewRequest(http.MethodPut, "http://"+MetaDataServerIP+pathToken, nil) + req.RemoteAddr = testIP + ":50000" + req = req.WithContext(context.WithValue(req.Context(), ctxKeyVPCID, testVPC)) + req.Header.Set(hdrTokenTTL, "60") + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + assert.Equal(t, http.StatusNotFound, rec.Code) +} + +// ----- metadata surface -------------------------------------------------- + +func TestHTTP_MetadataPaths(t *testing.T) { + res := &fakeResolver{ + eni: testENI(), + inst: &instanceFacts{instanceType: "t3.micro", imageID: "ami-12345", userData: []byte("#!/bin/sh\necho hi")}, + sgNames: map[string]string{"sg-1": "web-sg", "sg-2": "db-sg"}, + } + svc, _ := newTestService(res, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + + cases := []struct{ path, want string }{ + {prefixMetaData + "instance-id", "i-0123456789"}, + {prefixMetaData + "instance-type", "t3.micro"}, + {prefixMetaData + "ami-id", "ami-12345"}, + {prefixMetaData + "local-ipv4", "10.0.1.5"}, + {prefixMetaData + "public-ipv4", "203.0.113.7"}, + {prefixMetaData + "mac", "02:11:22:33:44:55"}, + {prefixMetaData + "placement/availability-zone", "ap-southeast-2a"}, + {prefixMetaData + "placement/region", "ap-southeast-2"}, + {prefixMetaData + "security-groups", "web-sg\ndb-sg"}, + {prefixMetaData + "hostname", "ip-10-0-1-5.ap-southeast-2.compute.internal"}, + {prefixMetaData + "local-hostname", "ip-10-0-1-5.ap-southeast-2.compute.internal"}, + {pathUserData, "#!/bin/sh\necho hi"}, + } + for _, c := range cases { + rec := get(t, h, c.path, token) + assert.Equal(t, http.StatusOK, rec.Code, "path=%s", c.path) + assert.Equal(t, c.want, rec.Body.String(), "path=%s", c.path) + } +} + +func TestHTTP_DirectoryListing(t *testing.T) { + svc, _ := newTestService(&fakeResolver{eni: testENI()}, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, pathMetaDataRoot, token) + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), "instance-id") + assert.Contains(t, rec.Body.String(), "iam/") +} + +func TestHTTP_UserDataAbsent404(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{}} + svc, _ := newTestService(res, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, pathUserData, token) + assert.Equal(t, http.StatusNotFound, rec.Code) +} + +func TestHTTP_OutOfScopePaths404(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{}} + svc, _ := newTestService(res, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + for _, p := range []string{ + "/latest/dynamic/instance-identity/document", + prefixMetaData + "network/interfaces/macs", + prefixMetaData + "nonsense", + } { + rec := get(t, h, p, token) + assert.Equal(t, http.StatusNotFound, rec.Code, "path=%s", p) + } +} + +// ----- IAM / credentials ------------------------------------------------- + +func profileFixture() *handlers_iam.InstanceProfile { + return &handlers_iam.InstanceProfile{ + InstanceProfileName: "app-profile", + InstanceProfileID: "AIPAEXAMPLE", + AccountID: "111122223333", + ARN: "arn:aws:iam::111122223333:instance-profile/app-profile", + RoleName: "app-role", + } +} + +func TestHTTP_IAMInfo(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{iamInstanceProfileArn: "arn:aws:iam::111122223333:instance-profile/app-profile"}} + svc, _ := newTestService(res, &fakeIAM{profile: profileFixture()}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + + rec := get(t, h, prefixMetaData+"iam/info", token) + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), "arn:aws:iam::111122223333:instance-profile/app-profile") + assert.Contains(t, rec.Body.String(), "AIPAEXAMPLE") +} + +func TestHTTP_IAMInfoNoProfile404(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{}} // no profile ARN + svc, _ := newTestService(res, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, prefixMetaData+"iam/info", token) + assert.Equal(t, http.StatusNotFound, rec.Code) +} + +func TestHTTP_SecurityCredentialsList(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{iamInstanceProfileArn: "arn:aws:iam::111122223333:instance-profile/app-profile"}} + svc, _ := newTestService(res, &fakeIAM{profile: profileFixture()}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, pathSecurityCredsDir, token) + assert.Equal(t, http.StatusOK, rec.Code) + assert.Equal(t, "app-role", rec.Body.String()) +} + +func TestHTTP_SecurityCredentialsListNoRoleEmpty(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{}} + svc, _ := newTestService(res, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, pathSecurityCredsDir, token) + assert.Equal(t, http.StatusOK, rec.Code) + assert.Empty(t, rec.Body.String()) +} + +// A backend failure resolving the instance must surface as 500, never an empty +// 200 — otherwise a transient hiccup is indistinguishable from "instance has no +// role" and the SDK silently proceeds unauthenticated. +func TestHTTP_SecurityCredentialsListBackendError500(t *testing.T) { + res := &fakeResolver{eni: testENI(), instErr: errors.New("backend unavailable")} + svc, _ := newTestService(res, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, pathSecurityCredsDir, token) + assert.Equal(t, http.StatusInternalServerError, rec.Code) +} + +// A profile deleted out from under a running instance (the ARN dereferences to +// NoSuchEntity) is "no instance role", not a backend fault: iam/info 404s, the +// creds list is an empty 200, and the role path 404s — matching AWS. Only a +// genuine backend error (TestHTTP_*BackendError500) is a 500. +func TestHTTP_IAMInfoDeletedProfile404(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{iamInstanceProfileArn: "arn:aws:iam::111122223333:instance-profile/gone"}} + svc, _ := newTestService(res, &fakeIAM{profileErr: errors.New(awserrors.ErrorIAMNoSuchEntity)}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, prefixMetaData+"iam/info", token) + assert.Equal(t, http.StatusNotFound, rec.Code) +} + +func TestHTTP_SecurityCredentialsListDeletedProfileEmpty(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{iamInstanceProfileArn: "arn:aws:iam::111122223333:instance-profile/gone"}} + svc, _ := newTestService(res, &fakeIAM{profileErr: errors.New(awserrors.ErrorIAMNoSuchEntity)}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, pathSecurityCredsDir, token) + assert.Equal(t, http.StatusOK, rec.Code) + assert.Empty(t, rec.Body.String()) +} + +func TestHTTP_RoleCredentialsDeletedProfile404(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{iamInstanceProfileArn: "arn:aws:iam::111122223333:instance-profile/gone"}} + svc, _ := newTestService(res, &fakeIAM{profileErr: errors.New(awserrors.ErrorIAMNoSuchEntity)}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, prefixSecurityCreds+"app-role", token) + assert.Equal(t, http.StatusNotFound, rec.Code) +} + +func TestHTTP_IAMInfoBackendError500(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{iamInstanceProfileArn: "arn:aws:iam::111122223333:instance-profile/app-profile"}} + svc, _ := newTestService(res, &fakeIAM{profileErr: errors.New("iam unavailable")}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, prefixMetaData+"iam/info", token) + assert.Equal(t, http.StatusInternalServerError, rec.Code) +} + +func TestHTTP_RoleCredentialsBackendError500(t *testing.T) { + res := &fakeResolver{eni: testENI(), instErr: errors.New("backend unavailable")} + svc, _ := newTestService(res, &fakeIAM{}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + rec := get(t, h, prefixSecurityCreds+"app-role", token) + assert.Equal(t, http.StatusInternalServerError, rec.Code) +} + +func TestHTTP_RoleCredentials(t *testing.T) { + now := time.Unix(1_700_000_000, 0).UTC() + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{iamInstanceProfileArn: "arn:aws:iam::111122223333:instance-profile/app-profile"}} + assumer := &fakeAssumer{out: &sts.AssumeRoleOutput{Credentials: &sts.Credentials{ + AccessKeyId: aws.String("ASIAEXAMPLE"), + SecretAccessKey: aws.String("secret"), + SessionToken: aws.String("token"), + Expiration: aws.Time(now.Add(time.Hour)), + }}} + svc, _ := newTestService(res, &fakeIAM{profile: profileFixture(), roleARN: "arn:aws:iam::111122223333:role/app-role"}, assumer) + h := svc.httpHandler() + token := issueToken(t, h) + + rec := get(t, h, prefixSecurityCreds+"app-role", token) + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), "ASIAEXAMPLE") + assert.Contains(t, rec.Body.String(), `"Code":"Success"`) +} + +func TestHTTP_RoleCredentialsWrongRole404(t *testing.T) { + res := &fakeResolver{eni: testENI(), inst: &instanceFacts{iamInstanceProfileArn: "arn:aws:iam::111122223333:instance-profile/app-profile"}} + svc, _ := newTestService(res, &fakeIAM{profile: profileFixture(), roleARN: "arn:aws:iam::111122223333:role/app-role"}, &fakeAssumer{}) + h := svc.httpHandler() + token := issueToken(t, h) + + // AWS only accepts the actual role name, never the profile name. + rec := get(t, h, prefixSecurityCreds+"app-profile", token) + assert.Equal(t, http.StatusNotFound, rec.Code) +} diff --git a/spinifex/handlers/imds/nats_clients.go b/spinifex/handlers/imds/nats_clients.go new file mode 100644 index 000000000..adbf15362 --- /dev/null +++ b/spinifex/handlers/imds/nats_clients.go @@ -0,0 +1,134 @@ +package handlers_imds + +import ( + "sync" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/aws/aws-sdk-go/service/sts" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" +) + +const ( + // imdsRPCTimeout bounds each internal control-plane round-trip to awsgw. + imdsRPCTimeout = 10 * time.Second + + // profileCacheTTL memoises profile/role lookups. Both mappings are effectively + // static, so a short TTL keeps the iam/* GETs off a NATS round-trip per request. + profileCacheTTL = 5 * time.Minute +) + +// NATSSTSAssumer is the NATS-backed stsAssumer. It mints instance-role credentials +// via awsgw's SubjectAssumeRoleForInstance responder, keeping no local cache — +// credCache already memoises minted credentials, so STS stays off the hot path. +type NATSSTSAssumer struct { + nc *nats.Conn +} + +var _ stsAssumer = (*NATSSTSAssumer)(nil) + +// NewNATSSTSAssumer constructs a NATSSTSAssumer over nc. +func NewNATSSTSAssumer(nc *nats.Conn) *NATSSTSAssumer { + return &NATSSTSAssumer{nc: nc} +} + +func (a *NATSSTSAssumer) AssumeRoleForInstance(accountID, roleARN, instanceID string, durationSeconds int64) (*sts.AssumeRoleOutput, error) { + return utils.NATSRequest[sts.AssumeRoleOutput](a.nc, handlers_sts.SubjectAssumeRoleForInstance, handlers_sts.AssumeRoleForInstanceRequest{ + AccountID: accountID, + RoleARN: roleARN, + InstanceID: instanceID, + DurationSeconds: durationSeconds, + }, imdsRPCTimeout, accountID) +} + +// NATSProfileLookup is the NATS-backed profileLookup. It resolves instance +// profiles and roles via awsgw's IAM responders, fronted by a short-TTL cache +// so repeat iam/* GETs don't take a round-trip each. +type NATSProfileLookup struct { + nc *nats.Conn + profiles *ttlCache[*handlers_iam.InstanceProfile] + roles *ttlCache[*iam.GetRoleOutput] +} + +var _ profileLookup = (*NATSProfileLookup)(nil) + +// NewNATSProfileLookup constructs a NATSProfileLookup over nc. +func NewNATSProfileLookup(nc *nats.Conn) *NATSProfileLookup { + return &NATSProfileLookup{ + nc: nc, + profiles: newTTLCache[*handlers_iam.InstanceProfile](profileCacheTTL), + roles: newTTLCache[*iam.GetRoleOutput](profileCacheTTL), + } +} + +func (p *NATSProfileLookup) ResolveInstanceProfile(accountID, nameOrARN string) (*handlers_iam.InstanceProfile, error) { + key := accountID + "\x00" + nameOrARN + if v, ok := p.profiles.get(key); ok { + return v, nil + } + out, err := utils.NATSRequest[handlers_iam.InstanceProfile](p.nc, handlers_iam.SubjectResolveInstanceProfile, handlers_iam.ResolveInstanceProfileRequest{ + AccountID: accountID, + NameOrARN: nameOrARN, + }, imdsRPCTimeout, accountID) + if err != nil { + return nil, err + } + p.profiles.put(key, out) + return out, nil +} + +func (p *NATSProfileLookup) GetRole(accountID string, input *iam.GetRoleInput) (*iam.GetRoleOutput, error) { + key := accountID + "\x00" + aws.StringValue(input.RoleName) + if v, ok := p.roles.get(key); ok { + return v, nil + } + out, err := utils.NATSRequest[iam.GetRoleOutput](p.nc, handlers_iam.SubjectGetRole, handlers_iam.GetRoleRequest{ + AccountID: accountID, + Input: input, + }, imdsRPCTimeout, accountID) + if err != nil { + return nil, err + } + p.roles.put(key, out) + return out, nil +} + +// ttlCache is a minimal concurrency-safe map with per-entry expiry, used to +// keep effectively-static IAM lookups off the NATS round-trip. now is a field +// so tests can drive expiry deterministically. +type ttlCache[V any] struct { + mu sync.Mutex + ttl time.Duration + now func() time.Time + m map[string]ttlCacheEntry[V] +} + +type ttlCacheEntry[V any] struct { + val V + expiresAt time.Time +} + +func newTTLCache[V any](ttl time.Duration) *ttlCache[V] { + return &ttlCache[V]{ttl: ttl, now: time.Now, m: make(map[string]ttlCacheEntry[V])} +} + +func (c *ttlCache[V]) get(key string) (V, bool) { + c.mu.Lock() + defer c.mu.Unlock() + entry, ok := c.m[key] + if !ok || c.now().After(entry.expiresAt) { + var zero V + return zero, false + } + return entry.val, true +} + +func (c *ttlCache[V]) put(key string, v V) { + c.mu.Lock() + defer c.mu.Unlock() + c.m[key] = ttlCacheEntry[V]{val: v, expiresAt: c.now().Add(c.ttl)} +} diff --git a/spinifex/handlers/imds/nats_clients_test.go b/spinifex/handlers/imds/nats_clients_test.go new file mode 100644 index 000000000..1eaa71e56 --- /dev/null +++ b/spinifex/handlers/imds/nats_clients_test.go @@ -0,0 +1,136 @@ +package handlers_imds + +import ( + "sync/atomic" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/aws/aws-sdk-go/service/sts" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const imdsTestAccountID = "123456789012" + +// serveCounted subscribes fn to subject and returns a counter of how many +// requests the responder actually handled — the assertion hook for the TTL +// cache (a cache hit must not reach the responder). +func serveCounted[I any, O any](t *testing.T, nc *nats.Conn, subject string, fn func(*I) (*O, error)) *int32 { + t.Helper() + var calls int32 + sub, err := nc.Subscribe(subject, func(msg *nats.Msg) { + atomic.AddInt32(&calls, 1) + utils.ServeNATSRequest(msg, fn) + }) + require.NoError(t, err) + t.Cleanup(func() { _ = sub.Unsubscribe() }) + return &calls +} + +func TestNATSSTSAssumer_RoundTrip(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + + var gotReq handlers_sts.AssumeRoleForInstanceRequest + calls := serveCounted(t, nc, handlers_sts.SubjectAssumeRoleForInstance, + func(req *handlers_sts.AssumeRoleForInstanceRequest) (*sts.AssumeRoleOutput, error) { + gotReq = *req + return &sts.AssumeRoleOutput{Credentials: &sts.Credentials{ + AccessKeyId: aws.String("ASIATESTKEY"), + SecretAccessKey: aws.String("secret"), + SessionToken: aws.String("token"), + Expiration: aws.Time(time.Unix(1700000000, 0).UTC()), + }}, nil + }) + + assumer := NewNATSSTSAssumer(nc) + out, err := assumer.AssumeRoleForInstance(imdsTestAccountID, "arn:aws:iam::123456789012:role/app", "i-abc", 3600) + require.NoError(t, err) + require.NotNil(t, out.Credentials) + + assert.Equal(t, "ASIATESTKEY", aws.StringValue(out.Credentials.AccessKeyId)) + assert.Equal(t, int32(1), atomic.LoadInt32(calls)) + // The request payload must round-trip every field the responder needs. + assert.Equal(t, handlers_sts.AssumeRoleForInstanceRequest{ + AccountID: imdsTestAccountID, + RoleARN: "arn:aws:iam::123456789012:role/app", + InstanceID: "i-abc", + DurationSeconds: 3600, + }, gotReq) +} + +func TestNATSProfileLookup_CachesProfile(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + + calls := serveCounted(t, nc, handlers_iam.SubjectResolveInstanceProfile, + func(req *handlers_iam.ResolveInstanceProfileRequest) (*handlers_iam.InstanceProfile, error) { + return &handlers_iam.InstanceProfile{ + InstanceProfileName: "app", + AccountID: req.AccountID, + RoleName: "app-role", + }, nil + }) + + lookup := NewNATSProfileLookup(nc) + for range 3 { + prof, err := lookup.ResolveInstanceProfile(imdsTestAccountID, "app") + require.NoError(t, err) + assert.Equal(t, "app-role", prof.RoleName) + } + // Three lookups, one round-trip: the TTL cache suppressed the repeats. + assert.Equal(t, int32(1), atomic.LoadInt32(calls)) +} + +func TestNATSProfileLookup_CachesRole(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + + calls := serveCounted(t, nc, handlers_iam.SubjectGetRole, + func(req *handlers_iam.GetRoleRequest) (*iam.GetRoleOutput, error) { + return &iam.GetRoleOutput{Role: &iam.Role{ + RoleName: req.Input.RoleName, + Arn: aws.String("arn:aws:iam::123456789012:role/app-role"), + }}, nil + }) + + lookup := NewNATSProfileLookup(nc) + for range 3 { + out, err := lookup.GetRole(imdsTestAccountID, &iam.GetRoleInput{RoleName: aws.String("app-role")}) + require.NoError(t, err) + assert.Equal(t, "arn:aws:iam::123456789012:role/app-role", aws.StringValue(out.Role.Arn)) + } + assert.Equal(t, int32(1), atomic.LoadInt32(calls)) +} + +func TestNATSProfileLookup_PropagatesError(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + + serveCounted(t, nc, handlers_iam.SubjectResolveInstanceProfile, + func(_ *handlers_iam.ResolveInstanceProfileRequest) (*handlers_iam.InstanceProfile, error) { + return nil, assert.AnError + }) + + lookup := NewNATSProfileLookup(nc) + _, err := lookup.ResolveInstanceProfile(imdsTestAccountID, "missing") + require.Error(t, err) +} + +func TestTTLCache_ExpiresEntries(t *testing.T) { + now := time.Unix(1700000000, 0) + c := newTTLCache[string](time.Minute) + c.now = func() time.Time { return now } + + c.put("k", "v") + got, ok := c.get("k") + require.True(t, ok) + assert.Equal(t, "v", got) + + now = now.Add(2 * time.Minute) // past the TTL + _, ok = c.get("k") + assert.False(t, ok) +} diff --git a/spinifex/handlers/imds/resolver.go b/spinifex/handlers/imds/resolver.go new file mode 100644 index 000000000..8400d7f13 --- /dev/null +++ b/spinifex/handlers/imds/resolver.go @@ -0,0 +1,189 @@ +package handlers_imds + +import ( + "encoding/json" + "errors" + "fmt" + "log/slog" + + "github.com/nats-io/nats.go" +) + +// kvBucketENIs is the ENI source-of-truth bucket (handlers_ec2_vpc.KVBucketENIs), +// duplicated as a literal rather than imported because importing handlers/ec2/vpc +// back would close an import cycle (the veth_store record type does likewise). +const kvBucketENIs = "spinifex-vpc-enis" + +// kvBucketSecurityGroups is the security-group source-of-truth bucket +// (handlers_ec2_vpc.KVBucketSecurityGroups). Duplicated as a literal for the +// same import-cycle reason as kvBucketENIs. +const kvBucketSecurityGroups = "spinifex-vpc-security-groups" + +// eniFacts is the subset of an ENIRecord the IMDS metadata surface serves +// directly, plus the account ID recovered from the ENI bucket key. Everything +// here is read live off the ENI record, so a change (e.g. a re-associated EIP) +// is reflected on the next request. +type eniFacts struct { + eniID string + accountID string + instanceID string + vpcID string + subnetID string + privateIP string + publicIP string + mac string + availabilityZone string + securityGroupIDs []string +} + +// instanceFacts carries the four metadata fields not present on the ENI record. +// They live in the daemon's in-memory instance manager, so they are fetched via +// the account-scoped DescribeInstances fan-out behind instanceLookup. +type instanceFacts struct { + instanceType string + imageID string + iamInstanceProfileArn string + userData []byte +} + +// instanceLookup resolves the instance-only metadata fields by instance ID. It +// is an interface so the metadata handlers stay unit-testable without a live +// NATS cluster; the production implementation (natsInstanceLookup) fans out +// DescribeInstances + DescribeInstanceAttribute. +type instanceLookup interface { + describe(accountID, instanceID string) (*instanceFacts, error) +} + +// eniIndexValue mirrors the stored shape of a spinifex-network-eni-by-vpc-ip +// row (handlers_ec2_vpc.eniByIPValue). Duplicated for the same cycle reason as +// kvBucketENIs. Both fields are the ENI's immutable identity; everything mutable +// is read live off the ENI record. +type eniIndexValue struct { + ENIId string `json:"eni_id"` + AccountID string `json:"account_id"` +} + +// sgNameRecord is the slice of handlers_ec2_vpc.SecurityGroupRecord the metadata +// surface needs: the human-readable group name AWS serves at +// /latest/meta-data/security-groups (not the sg-* ID). +type sgNameRecord struct { + GroupName string `json:"group_name"` +} + +// eniRecord is the subset of handlers_ec2_vpc.ENIRecord the resolver reads. The +// full record is owned by the ENI controller; only these fields feed IMDS. +type eniRecord struct { + NetworkInterfaceId string `json:"network_interface_id"` + SubnetId string `json:"subnet_id"` + VpcId string `json:"vpc_id"` + AvailabilityZone string `json:"availability_zone"` + PrivateIpAddress string `json:"private_ip_address"` + MacAddress string `json:"mac_address"` + InstanceId string `json:"instance_id,omitempty"` + PublicIpAddress string `json:"public_ip_address,omitempty"` + SecurityGroupIds []string `json:"security_group_ids,omitempty"` +} + +// metadataResolver maps a datapath-attested (vpcID, srcIP) to the ENI + instance +// facts the metadata surface serves. The chain is: +// +// (vpcID, ip) → {eniID, accountID} via the eni-by-vpc-ip reverse index +// (account, eniID) → ENIRecord via a direct KV Get +// (account, instanceID) → facts via the account-scoped DescribeInstances fan-out +// +// The index carries the ENI's immutable identity so the account resolves in one +// Get; every mutable field is read live off the record, so there's no staleness. +type metadataResolver struct { + index nats.KeyValue // spinifex-network-eni-by-vpc-ip + eniKV nats.KeyValue // spinifex-vpc-enis + sgKV nats.KeyValue // spinifex-vpc-security-groups (nil-safe: degrades to IDs) + lookup instanceLookup +} + +var _ eniResolver = (*metadataResolver)(nil) + +// resolveENI returns the ENI facts for a request's (vpcID, srcIP), or (nil, nil) +// when no mapping exists — the caller maps a miss to 404, matching AWS's +// "eventually available during boot" posture. A non-nil error is reserved for +// genuine backend failures. +func (r *metadataResolver) resolveENI(vpcID, srcIP string) (*eniFacts, error) { + entry, err := r.index.Get(vpcID + "/" + srcIP) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, nil + } + return nil, fmt.Errorf("get eni-by-ip index %s/%s: %w", vpcID, srcIP, err) + } + + var idx eniIndexValue + if err := json.Unmarshal(entry.Value(), &idx); err != nil { + return nil, fmt.Errorf("unmarshal eni-by-ip index %s/%s: %w", vpcID, srcIP, err) + } + if idx.ENIId == "" || idx.AccountID == "" { + return nil, nil + } + + raw, err := r.eniKV.Get(idx.AccountID + "." + idx.ENIId) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, nil + } + return nil, fmt.Errorf("get eni record %s.%s: %w", idx.AccountID, idx.ENIId, err) + } + + var rec eniRecord + if err := json.Unmarshal(raw.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal eni record %s: %w", idx.ENIId, err) + } + + return &eniFacts{ + eniID: rec.NetworkInterfaceId, + accountID: idx.AccountID, + instanceID: rec.InstanceId, + vpcID: rec.VpcId, + subnetID: rec.SubnetId, + privateIP: rec.PrivateIpAddress, + publicIP: rec.PublicIpAddress, + mac: rec.MacAddress, + availabilityZone: rec.AvailabilityZone, + securityGroupIDs: rec.SecurityGroupIds, + }, nil +} + +// resolveInstance fetches the instance-only metadata fields for an ENI's +// attached instance. Returns (nil, nil) when the ENI has no attached instance. +func (r *metadataResolver) resolveInstance(eni *eniFacts) (*instanceFacts, error) { + if eni.instanceID == "" { + return nil, nil + } + return r.lookup.describe(eni.accountID, eni.instanceID) +} + +// resolveSGNames maps security-group IDs to the group names AWS serves at +// /latest/meta-data/security-groups. Best-effort and order-preserving: a missing +// record or nil sgKV falls back to the raw ID rather than failing the request. +func (r *metadataResolver) resolveSGNames(accountID string, sgIDs []string) []string { + names := make([]string, len(sgIDs)) + for i, id := range sgIDs { + names[i] = id + if r.sgKV == nil { + continue + } + raw, err := r.sgKV.Get(accountID + "." + id) + if err != nil { + if !errors.Is(err, nats.ErrKeyNotFound) { + slog.Warn("IMDS: security-group name lookup failed", "account_id", accountID, "sg_id", id, "err", err) + } + continue + } + var rec sgNameRecord + if err := json.Unmarshal(raw.Value(), &rec); err != nil { + slog.Warn("IMDS: security-group unmarshal failed", "sg_id", id, "err", err) + continue + } + if rec.GroupName != "" { + names[i] = rec.GroupName + } + } + return names +} diff --git a/spinifex/handlers/imds/resolver_test.go b/spinifex/handlers/imds/resolver_test.go new file mode 100644 index 000000000..5577570d7 --- /dev/null +++ b/spinifex/handlers/imds/resolver_test.go @@ -0,0 +1,190 @@ +package handlers_imds + +import ( + "encoding/json" + "errors" + "testing" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// fakeLookup is a programmable instanceLookup so resolver tests never need the +// account-scoped instance fan-out. +type fakeLookup struct { + facts *instanceFacts + err error + calls int +} + +func (f *fakeLookup) describe(_, _ string) (*instanceFacts, error) { + f.calls++ + return f.facts, f.err +} + +// newTestResolver wires a metadataResolver onto a live test JetStream with the +// three buckets it reads (eni-by-vpc-ip index, ENI source-of-truth, SG names) +// created empty, plus a fake instance lookup the caller can program. +func newTestResolver(t *testing.T) (*metadataResolver, *fakeLookup) { + t.Helper() + _, _, js := testutil.StartTestJetStream(t) + index, err := InitENIByIPBucket(js, 1) + require.NoError(t, err) + eniKV, err := js.CreateKeyValue(&nats.KeyValueConfig{Bucket: kvBucketENIs, History: 1}) + require.NoError(t, err) + sgKV, err := js.CreateKeyValue(&nats.KeyValueConfig{Bucket: kvBucketSecurityGroups, History: 1}) + require.NoError(t, err) + + lookup := &fakeLookup{} + return &metadataResolver{index: index, eniKV: eniKV, sgKV: sgKV, lookup: lookup}, lookup +} + +func putJSON(t *testing.T, kv nats.KeyValue, key string, v any) { + t.Helper() + data, err := json.Marshal(v) + require.NoError(t, err) + _, err = kv.Put(key, data) + require.NoError(t, err) +} + +// seedENI writes a complete index → ENI chain for (testVPC, testIP) and returns +// the resolver ready to serve it. +func seedENI(t *testing.T, r *metadataResolver) { + t.Helper() + putJSON(t, r.index, testVPC+"/"+testIP, eniIndexValue{ENIId: "eni-aaa", AccountID: "111122223333"}) + putJSON(t, r.eniKV, "111122223333.eni-aaa", eniRecord{ + NetworkInterfaceId: "eni-aaa", + SubnetId: "subnet-1", + VpcId: testVPC, + AvailabilityZone: "ap-southeast-2a", + PrivateIpAddress: testIP, + MacAddress: "02:11:22:33:44:55", + InstanceId: "i-0123456789", + PublicIpAddress: "203.0.113.7", + SecurityGroupIds: []string{"sg-1", "sg-2"}, + }) +} + +func TestResolveENI_Hit(t *testing.T) { + r, _ := newTestResolver(t) + seedENI(t, r) + + eni, err := r.resolveENI(testVPC, testIP) + require.NoError(t, err) + require.NotNil(t, eni) + assert.Equal(t, "eni-aaa", eni.eniID) + assert.Equal(t, "111122223333", eni.accountID) + assert.Equal(t, "i-0123456789", eni.instanceID) + assert.Equal(t, testVPC, eni.vpcID) + assert.Equal(t, "subnet-1", eni.subnetID) + assert.Equal(t, testIP, eni.privateIP) + assert.Equal(t, "203.0.113.7", eni.publicIP) + assert.Equal(t, "02:11:22:33:44:55", eni.mac) + assert.Equal(t, "ap-southeast-2a", eni.availabilityZone) + assert.Equal(t, []string{"sg-1", "sg-2"}, eni.securityGroupIDs) +} + +// A source IP with no reverse-index row is a miss, not an error — the caller +// maps it to 404 per AWS's "eventually available during boot" posture. +func TestResolveENI_IndexMissIsNilNil(t *testing.T) { + r, _ := newTestResolver(t) + eni, err := r.resolveENI(testVPC, "10.9.9.9") + require.NoError(t, err) + assert.Nil(t, eni) +} + +func TestResolveENI_IndexBadJSONErrors(t *testing.T) { + r, _ := newTestResolver(t) + _, err := r.index.Put(testVPC+"/"+testIP, []byte("not json")) + require.NoError(t, err) + + _, err = r.resolveENI(testVPC, testIP) + require.Error(t, err) +} + +// An index row missing either identity field is treated as a miss rather than +// being chased into a malformed ENI Get. +func TestResolveENI_EmptyIndexFieldsIsNilNil(t *testing.T) { + r, _ := newTestResolver(t) + putJSON(t, r.index, testVPC+"/"+testIP, eniIndexValue{ENIId: "eni-aaa"}) // no account + eni, err := r.resolveENI(testVPC, testIP) + require.NoError(t, err) + assert.Nil(t, eni) +} + +// A dangling index pointing at an ENI record that no longer exists is a miss, +// matching a mid-teardown race. +func TestResolveENI_ENIRecordMissIsNilNil(t *testing.T) { + r, _ := newTestResolver(t) + putJSON(t, r.index, testVPC+"/"+testIP, eniIndexValue{ENIId: "eni-gone", AccountID: "111122223333"}) + eni, err := r.resolveENI(testVPC, testIP) + require.NoError(t, err) + assert.Nil(t, eni) +} + +func TestResolveENI_ENIRecordBadJSONErrors(t *testing.T) { + r, _ := newTestResolver(t) + putJSON(t, r.index, testVPC+"/"+testIP, eniIndexValue{ENIId: "eni-aaa", AccountID: "111122223333"}) + _, err := r.eniKV.Put("111122223333.eni-aaa", []byte("not json")) + require.NoError(t, err) + + _, err = r.resolveENI(testVPC, testIP) + require.Error(t, err) +} + +func TestResolveInstance_NoAttachedInstance(t *testing.T) { + r, lookup := newTestResolver(t) + inst, err := r.resolveInstance(&eniFacts{instanceID: ""}) + require.NoError(t, err) + assert.Nil(t, inst) + assert.Equal(t, 0, lookup.calls, "lookup must be skipped when no instance is attached") +} + +func TestResolveInstance_DelegatesToLookup(t *testing.T) { + r, lookup := newTestResolver(t) + lookup.facts = &instanceFacts{instanceType: "t3.micro", imageID: "ami-1"} + + inst, err := r.resolveInstance(&eniFacts{accountID: "111122223333", instanceID: "i-1"}) + require.NoError(t, err) + require.NotNil(t, inst) + assert.Equal(t, "t3.micro", inst.instanceType) + assert.Equal(t, 1, lookup.calls) +} + +func TestResolveInstance_LookupErrorPropagates(t *testing.T) { + r, lookup := newTestResolver(t) + lookup.err = errors.New("fan-out failed") + _, err := r.resolveInstance(&eniFacts{accountID: "111122223333", instanceID: "i-1"}) + require.Error(t, err) +} + +// A nil SG bucket (not yet up at start) must degrade the endpoint to raw IDs +// rather than failing the request. +func TestResolveSGNames_NilBucketDegradesToIDs(t *testing.T) { + r, _ := newTestResolver(t) + r.sgKV = nil + got := r.resolveSGNames("111122223333", []string{"sg-1", "sg-2"}) + assert.Equal(t, []string{"sg-1", "sg-2"}, got) +} + +// Names resolve where a record exists; a missing record or one with an empty +// name falls back to the ID, and order is preserved throughout. +func TestResolveSGNames_ResolvesAndFallsBack(t *testing.T) { + r, _ := newTestResolver(t) + putJSON(t, r.sgKV, "111122223333.sg-1", sgNameRecord{GroupName: "web-sg"}) + putJSON(t, r.sgKV, "111122223333.sg-3", sgNameRecord{GroupName: ""}) // empty → ID + + got := r.resolveSGNames("111122223333", []string{"sg-1", "sg-2", "sg-3"}) + assert.Equal(t, []string{"web-sg", "sg-2", "sg-3"}, got) +} + +func TestResolveSGNames_BadJSONFallsBackToID(t *testing.T) { + r, _ := newTestResolver(t) + _, err := r.sgKV.Put("111122223333.sg-1", []byte("not json")) + require.NoError(t, err) + + got := r.resolveSGNames("111122223333", []string{"sg-1"}) + assert.Equal(t, []string{"sg-1"}, got) +} diff --git a/spinifex/handlers/imds/server.go b/spinifex/handlers/imds/server.go new file mode 100644 index 000000000..edc2bf9e5 --- /dev/null +++ b/spinifex/handlers/imds/server.go @@ -0,0 +1,346 @@ +package handlers_imds + +import ( + "context" + "errors" + "fmt" + "log/slog" + "net" + "net/http" + "net/netip" + "os" + "runtime" + "sync" + "syscall" + "time" + + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" + "golang.org/x/sys/unix" +) + +// ensureVethFunc / removeVethFunc are the per-subnet host veth lifecycle hooks, +// injected (not imported) to avoid an import cycle via network/host. vpcd passes +// host.EnsureIMDSVeth / host.RemoveIMDSVeth; tests pass root-free fakes. ensure +// takes the subnet CIDR (added on-link in the netns so the reply resolves the +// guest by ARP) and returns the per-subnet netns the listener must be created in +// plus the host-end veth name (now living inside that netns). +type ensureVethFunc func(ctx context.Context, subnetID string, cidr netip.Prefix) (netnsName, hostEnd string, err error) +type removeVethFunc func(ctx context.Context, subnetID string) error + +// listenFunc binds a TCP listener on 169.254.169.254:80 inside the subnet's netns, +// scoped to its host veth. Swappable in tests so bind-manager logic runs without +// CAP_NET_ADMIN or a real netns. An empty netnsName binds in the current netns. +type listenFunc func(ctx context.Context, netnsName, hostEnd string) (net.Listener, error) + +// activeBinding is the per-subnet realised state: a listener bound to the subnet's +// host veth and the http.Server serving it. +type activeBinding struct { + listener net.Listener + server *http.Server +} + +// bindManager realises the per-chassis IMDS listener stack. It runs on every +// chassis and eagerly materialises a veth + OVS port + listener for every subnet in +// the subnet-veth bucket — lifecycle is "watch the bucket, ensure local state". +type bindManager struct { + kv nats.KeyValue // spinifex-network-imds-subnet-veth + store *VethStore // reads the subnet CIDR the host on-link route needs + handler http.Handler + ensureVeth ensureVethFunc + removeVeth removeVethFunc + listen listenFunc + + mu sync.Mutex + active map[string]*activeBinding // subnetID → binding +} + +func newBindManager(kv nats.KeyValue, handler http.Handler, ensure ensureVethFunc, remove removeVethFunc, listen listenFunc) *bindManager { + return &bindManager{ + kv: kv, + store: NewVethStore(kv), + handler: handler, + ensureVeth: ensure, + removeVeth: remove, + listen: listen, + active: make(map[string]*activeBinding), + } +} + +// sync reads the full subnet-veth bucket and binds a listener for every entry. Run +// once at startup; idempotent, so a re-sync after a transient error is safe. +func (b *bindManager) sync(ctx context.Context) error { + keys, err := b.kv.Keys() + if err != nil { + if errors.Is(err, nats.ErrNoKeysFound) { + return nil + } + return fmt.Errorf("list subnet-veth keys: %w", err) + } + for _, subnetID := range keys { + if subnetID == utils.VersionKey { + continue // schema-version marker written by migrate.RunKV, not a subnet + } + vpcID, cidr, err := b.subnetBinding(subnetID) + if err != nil { + slog.Error("IMDS: resolve subnet binding during sync", "subnet_id", subnetID, "err", err) + continue + } + if err := b.bind(ctx, subnetID, vpcID, cidr); err != nil { + slog.Error("IMDS: bind failed during sync", "subnet_id", subnetID, "err", err) + } + } + return nil +} + +// subnetBinding reads the persisted veth record for subnetID and returns the two +// facts bind needs: the owning VPC (the subnet→VPC static lookup — the listener +// identifies the subnet, the eni-by-vpc-ip index is keyed vpcID/ip) and the +// subnet CIDR (added on-link in the netns so the reply resolves the guest by +// ARP). A record missing either field is malformed; bind cannot proceed without +// them, and the reconciler re-publishes a complete record to converge. +func (b *bindManager) subnetBinding(subnetID string) (vpcID string, cidr netip.Prefix, err error) { + rec, err := b.store.Get(subnetID) + if err != nil { + return "", netip.Prefix{}, err + } + if rec == nil { + return "", netip.Prefix{}, fmt.Errorf("no veth record for %s", subnetID) + } + if rec.VPCID == "" { + return "", netip.Prefix{}, fmt.Errorf("veth record for %s has no vpc_id", subnetID) + } + cidr, err = netip.ParsePrefix(rec.SubnetCIDR) + if err != nil { + return "", netip.Prefix{}, fmt.Errorf("parse subnet cidr %q: %w", rec.SubnetCIDR, err) + } + return rec.VPCID, cidr, nil +} + +// watch reacts to subnet-veth bucket changes for the life of ctx: a Put binds the +// subnet (if not already bound), a Delete unbinds it and tears down the veth. Both +// transitions are idempotent. +func (b *bindManager) watch(ctx context.Context) { + watcher, err := b.kv.WatchAll(nats.Context(ctx)) + if err != nil { + slog.Error("IMDS: failed to start subnet-veth watcher", "err", err) + return + } + defer func() { _ = watcher.Stop() }() + + for { + select { + case <-ctx.Done(): + return + case entry, ok := <-watcher.Updates(): + if !ok { + return + } + if entry == nil { + // nil marks the end of the initial replay; sync() already + // covered those, so there is nothing extra to do. + continue + } + if entry.Key() == utils.VersionKey { + continue // schema-version marker written by migrate.RunKV, not a subnet + } + switch entry.Operation() { + case nats.KeyValuePut: + vpcID, cidr, err := b.subnetBinding(entry.Key()) + if err != nil { + slog.Error("IMDS: resolve subnet binding on watch", "subnet_id", entry.Key(), "err", err) + continue + } + if err := b.bind(ctx, entry.Key(), vpcID, cidr); err != nil { + slog.Error("IMDS: bind failed on watch", "subnet_id", entry.Key(), "err", err) + } + case nats.KeyValueDelete, nats.KeyValuePurge: + b.unbind(ctx, entry.Key()) + } + } + } +} + +// bind ensures the subnet's host veth (with the subnet CIDR on-link for the reply +// path) then opens a device-scoped listener for subnetID. vpcID is the subnet's +// statically-resolved owning VPC, threaded into every request so the handler can +// key the eni-by-vpc-ip index. A no-op if already bound. +func (b *bindManager) bind(ctx context.Context, subnetID, vpcID string, cidr netip.Prefix) error { + b.mu.Lock() + if _, ok := b.active[subnetID]; ok { + b.mu.Unlock() + return nil + } + b.mu.Unlock() + + netnsName, hostEnd, err := b.ensureVeth(ctx, subnetID, cidr) + if err != nil { + return fmt.Errorf("ensure veth: %w", err) + } + + listener, err := b.listen(ctx, netnsName, hostEnd) + if err != nil { + return fmt.Errorf("listen on %s in netns %s: %w", hostEnd, netnsName, err) + } + + server := &http.Server{ + Handler: b.handler, + ReadHeaderTimeout: 10 * time.Second, + // Tag every request from this listener with the subnet it serves and that + // subnet's owning VPC. The handler pairs the VPC with the datapath-attested + // source IP to resolve the ENI; the subnet ID rides along for triage. + BaseContext: func(net.Listener) context.Context { + c := context.WithValue(ctx, ctxKeyVPCID, vpcID) + return context.WithValue(c, ctxKeySubnetID, subnetID) + }, + } + + b.mu.Lock() + // Re-check under lock in case a concurrent bind won the race. + if _, ok := b.active[subnetID]; ok { + b.mu.Unlock() + _ = listener.Close() + return nil + } + b.active[subnetID] = &activeBinding{listener: listener, server: server} + b.mu.Unlock() + + go func() { + if err := server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { + slog.Error("IMDS: listener serve exited", "subnet_id", subnetID, "host_end", hostEnd, "err", err) + } + }() + + slog.Info("IMDS: listener bound", "subnet_id", subnetID, "vpc_id", vpcID, "netns", netnsName, "host_end", hostEnd, "addr", listener.Addr().String()) + return nil +} + +// unbind closes the subnet's listener and removes its host veth. Idempotent. +func (b *bindManager) unbind(ctx context.Context, subnetID string) { + b.mu.Lock() + binding := b.active[subnetID] + delete(b.active, subnetID) + b.mu.Unlock() + + if binding != nil { + if err := binding.server.Close(); err != nil { + slog.Warn("IMDS: listener close failed", "subnet_id", subnetID, "err", err) + } + } + if err := b.removeVeth(ctx, subnetID); err != nil { + slog.Warn("IMDS: veth removal failed", "subnet_id", subnetID, "err", err) + } + slog.Info("IMDS: listener unbound", "subnet_id", subnetID) +} + +// shutdown closes every active listener. The host veths are intentionally left +// in place: they are cheap, idempotent to re-ensure on the next start, and +// tearing them down on a clean vpcd restart would needlessly disrupt any other +// chassis-local consumer. +func (b *bindManager) shutdown() { + b.mu.Lock() + defer b.mu.Unlock() + for subnetID, binding := range b.active { + if err := binding.server.Close(); err != nil { + slog.Warn("IMDS: listener close failed during shutdown", "subnet_id", subnetID, "err", err) + } + delete(b.active, subnetID) + } +} + +// bindLocalListener opens a TCP listener on 169.254.169.254:80 inside the subnet's +// netns, where the host-end veth carries 169.254.169.254/30 with the subnet CIDR +// on-link — so the host-served reply resolves the guest by ARP over the localport +// on the guest's own L2 switch (SO_BINDTODEVICE alone in the root netns could not +// route the SYN-ACK). The netns also isolates subnets whose CIDRs overlap, since +// each is its own routing domain. The socket must be *created* in the netns, so we +// enter it on a locked thread for the socket call and restore the root netns before +// serving. +// SO_BINDTODEVICE (belt-and-braces — there is only one veth in the netns) and +// SO_REUSEADDR (fast rebind on vpcd restart) are still set on the fd. +func bindLocalListener(ctx context.Context, netnsName, hostEnd string) (net.Listener, error) { + lc := net.ListenConfig{ + Control: func(_, _ string, c syscall.RawConn) error { + var sockErr error + if err := c.Control(func(fd uintptr) { + if sockErr = unix.SetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_BINDTODEVICE, hostEnd); sockErr != nil { + return + } + sockErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) + }); err != nil { + return err + } + return sockErr + }, + } + + bind := func() (net.Listener, error) { + return lc.Listen(ctx, "tcp4", net.JoinHostPort(MetaDataServerIP, "80")) + } + + if netnsName == "" { + // No netns (tests, or a degraded host): bind in the current netns. + return bind() + } + + var ln net.Listener + err := inNetns(netnsName, func() error { + var e error + ln, e = bind() + return e + }) + if err != nil { + return nil, err + } + return ln, nil +} + +// inNetns runs fn on a dedicated OS thread switched into the named network +// namespace, then restores the root netns. fn's socket-creating syscalls happen +// in the target netns, so the listener fd belongs to it; once created, the fd is +// served normally from any thread. If the root netns cannot be restored the +// thread is poisoned, so the goroutine returns while still locked and the Go +// runtime retires the thread rather than returning it to the pool. +func inNetns(netnsName string, fn func() error) error { + const netnsDir = "/var/run/netns/" + errCh := make(chan error, 1) + + go func() { + runtime.LockOSThread() + + orig, err := os.Open("/proc/thread-self/ns/net") + if err != nil { + runtime.UnlockOSThread() + errCh <- fmt.Errorf("open current netns: %w", err) + return + } + defer func() { _ = orig.Close() }() + + target, err := os.Open(netnsDir + netnsName) + if err != nil { + runtime.UnlockOSThread() + errCh <- fmt.Errorf("open netns %s: %w", netnsName, err) + return + } + defer func() { _ = target.Close() }() + + if err := unix.Setns(int(target.Fd()), unix.CLONE_NEWNET); err != nil { + runtime.UnlockOSThread() + errCh <- fmt.Errorf("setns into %s: %w", netnsName, err) + return + } + + fnErr := fn() + + if err := unix.Setns(int(orig.Fd()), unix.CLONE_NEWNET); err != nil { + // Cannot return to the root netns — leave the thread locked so the + // runtime destroys it when this goroutine exits. + errCh <- errors.Join(fnErr, fmt.Errorf("restore root netns: %w", err)) + return + } + runtime.UnlockOSThread() + errCh <- fnErr + }() + + return <-errCh +} diff --git a/spinifex/handlers/imds/server_test.go b/spinifex/handlers/imds/server_test.go new file mode 100644 index 000000000..5fd500999 --- /dev/null +++ b/spinifex/handlers/imds/server_test.go @@ -0,0 +1,139 @@ +package handlers_imds + +import ( + "context" + "io" + "net" + "net/http" + "net/netip" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// loopbackListen returns a listenFunc backed by a real 127.0.0.1 listener so +// bind-manager logic runs end-to-end (including http.Server.Serve and the +// per-listener BaseContext) without root or the link-local address. It records +// the bound address per host-end so the test can dial it. +func loopbackListen(addrs *sync.Map) listenFunc { + return func(ctx context.Context, netnsName, hostEnd string) (net.Listener, error) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } + addrs.Store(hostEnd, ln.Addr().String()) + return ln, nil + } +} + +func TestBindManager_BindServesWithVPCContext(t *testing.T) { + const ( + hostEnd = "imds-h-abc12345" + subnetID = "subnet-abc12345" + vpcID = "vpc-abc12345" + ) + var ensured, removed atomic.Int32 + var ensuredKey, removedKey atomic.Value + ensure := func(_ context.Context, key string, _ netip.Prefix) (string, string, error) { + ensured.Add(1) + ensuredKey.Store(key) + return "", hostEnd, nil + } + remove := func(_ context.Context, key string) error { + removed.Add(1) + removedKey.Store(key) + return nil + } + + // Echo the VPC + subnet the bind manager threaded into the request context. + // The VPC keys the eni-by-vpc-ip index — the (VPC-ID, source-IP) → identity + // path the security model relies on; the subnet rides along for triage. + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctxVPC, _ := r.Context().Value(ctxKeyVPCID).(string) + ctxSubnet, _ := r.Context().Value(ctxKeySubnetID).(string) + _, _ = io.WriteString(w, ctxVPC+"|"+ctxSubnet) + }) + + var addrs sync.Map + bm := newBindManager(nil, handler, ensure, remove, loopbackListen(&addrs)) + ctx := context.Background() + cidr := netip.MustParsePrefix("10.211.0.0/16") + + require.NoError(t, bm.bind(ctx, subnetID, vpcID, cidr)) + assert.Equal(t, int32(1), ensured.Load()) + assert.Equal(t, subnetID, ensuredKey.Load(), "veth lifecycle is keyed by subnet") + + // Second bind of the same key is a no-op (idempotent), no extra veth ensure. + require.NoError(t, bm.bind(ctx, subnetID, vpcID, cidr)) + assert.Equal(t, int32(1), ensured.Load()) + + raw, ok := addrs.Load(hostEnd) + require.True(t, ok) + addr, ok := raw.(string) + require.True(t, ok) + + resp, err := http.Get("http://" + addr + prefixMetaData) + require.NoError(t, err) + body, _ := io.ReadAll(resp.Body) + _ = resp.Body.Close() + assert.Equal(t, vpcID+"|"+subnetID, string(body), "handler must see the listener's VPC and subnet") + + // Unbind closes the listener and tears down the veth, keyed by subnet. + bm.unbind(ctx, subnetID) + assert.Equal(t, int32(1), removed.Load()) + assert.Equal(t, subnetID, removedKey.Load(), "veth removal is keyed by subnet") + + client := http.Client{Timeout: 500 * time.Millisecond} + _, err = client.Get("http://" + addr + prefixMetaData) + assert.Error(t, err, "listener must be closed after unbind") +} + +func TestBindManager_BindPropagatesVethError(t *testing.T) { + ensure := func(_ context.Context, _ string, _ netip.Prefix) (string, string, error) { + return "", "", errEnsureFailed + } + bm := newBindManager(nil, http.NewServeMux(), ensure, func(context.Context, string) error { return nil }, loopbackListen(&sync.Map{})) + err := bm.bind(context.Background(), "subnet-x", "vpc-x", netip.MustParsePrefix("10.211.0.0/16")) + require.Error(t, err) +} + +var errEnsureFailed = io.ErrUnexpectedEOF + +// TestBindManager_SyncSkipsVersionKey guards that the schema-version marker +// migrate.RunKV stamps into the subnet-veth bucket is not mistaken for a subnet +// ID and made to bring up a bogus veth + listener. +func TestBindManager_SyncSkipsVersionKey(t *testing.T) { + _, _, js := testutil.StartTestJetStream(t) + subnetVeth, _, err := InitBuckets(js, 1) + require.NoError(t, err) + + // InitBuckets → migrate.RunKV stamps utils.VersionKey; add one real subnet + // record (sync resolves its CIDR from the record before binding). + require.NoError(t, NewVethStore(subnetVeth).Put(SubnetVethRecord{ + SubnetID: "subnet-abcdef12", + VPCID: "vpc-abcdef12", + SubnetCIDR: "10.211.0.0/16", + })) + + var bound sync.Map + ensure := func(_ context.Context, subnetID string, _ netip.Prefix) (string, string, error) { + bound.Store(subnetID, true) + return "imds-" + subnetID, "imds-h-" + subnetID, nil + } + remove := func(context.Context, string) error { return nil } + bm := newBindManager(subnetVeth, http.NewServeMux(), ensure, remove, loopbackListen(&sync.Map{})) + + require.NoError(t, bm.sync(context.Background())) + defer bm.shutdown() + + _, versionBound := bound.Load(utils.VersionKey) + assert.False(t, versionBound, "_version marker must not be bound as a subnet") + _, subnetBound := bound.Load("subnet-abcdef12") + assert.True(t, subnetBound, "real subnet entry must be bound") +} diff --git a/spinifex/handlers/imds/service.go b/spinifex/handlers/imds/service.go new file mode 100644 index 000000000..0f3c6df92 --- /dev/null +++ b/spinifex/handlers/imds/service.go @@ -0,0 +1,161 @@ +package handlers_imds + +import ( + "context" + "errors" + "fmt" + "log/slog" + "net/http" + "time" + + "github.com/aws/aws-sdk-go/service/iam" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/nats-io/nats.go" +) + +// tokenSweepInterval is how often abandoned/expired IMDSv2 tokens are swept. +const tokenSweepInterval = time.Minute + +// profileLookup is the slice of IAMService the IMDS credential + iam/* paths +// need: dereference an instance-profile ARN to its record, and resolve a role +// name to its canonical ARN. Narrowed to an interface for unit testing. +type profileLookup interface { + ResolveInstanceProfile(accountID, nameOrARN string) (*handlers_iam.InstanceProfile, error) + GetRole(accountID string, input *iam.GetRoleInput) (*iam.GetRoleOutput, error) +} + +// IMDSService is the host-served EC2 Instance Metadata Service. It runs in +// vpcd on every chassis, serving 169.254.169.254 to local guest VMs over +// per-subnet veths. Run owns the listener lifecycle and blocks until ctx is done. +type IMDSService interface { + Run(ctx context.Context) error +} + +var _ IMDSService = (*IMDSServiceImpl)(nil) + +// eniResolver maps a datapath-attested (vpcID, srcIP) to ENI + instance facts. +// An interface so the HTTP handlers are unit-testable without a live KV; +// metadataResolver is the production implementation. +type eniResolver interface { + resolveENI(vpcID, srcIP string) (*eniFacts, error) + resolveInstance(eni *eniFacts) (*instanceFacts, error) + resolveSGNames(accountID string, sgIDs []string) []string +} + +// IMDSServiceImpl is the in-process IMDS implementation. It is not part of the +// HTTPS gateway dispatch surface — it owns its own per-VPC listener stack. +type IMDSServiceImpl struct { + resolver eniResolver + tokens *tokenStore + creds *credCache + iam profileLookup + bind *bindManager + now func() time.Time +} + +// NewIMDSServiceImpl wires the IMDS service. natsConn backs the KV reads and the +// account-scoped instance fan-out (sized by expectedNodes); ensureVeth/removeVeth +// are injected rather than imported to avoid an import cycle via network/host. +func NewIMDSServiceImpl(natsConn *nats.Conn, sts stsAssumer, iamSvc profileLookup, expectedNodes int, ensureVeth ensureVethFunc, removeVeth removeVethFunc) (*IMDSServiceImpl, error) { + if natsConn == nil { + return nil, errors.New("nil NATS connection") + } + if sts == nil { + return nil, errors.New("nil STS service") + } + if iamSvc == nil { + return nil, errors.New("nil IAM service") + } + if ensureVeth == nil || removeVeth == nil { + return nil, errors.New("nil veth lifecycle hooks") + } + + js, err := natsConn.JetStream() + if err != nil { + return nil, fmt.Errorf("get JetStream context: %w", err) + } + + indexKV, err := js.KeyValue(KVBucketENIByVPCIP) + if err != nil { + return nil, fmt.Errorf("open %s bucket: %w", KVBucketENIByVPCIP, err) + } + eniKV, err := js.KeyValue(kvBucketENIs) + if err != nil { + return nil, fmt.Errorf("open %s bucket: %w", kvBucketENIs, err) + } + vethKV, err := js.KeyValue(KVBucketIMDSSubnetVeth) + if err != nil { + return nil, fmt.Errorf("open %s bucket: %w", KVBucketIMDSSubnetVeth, err) + } + + // The SG bucket is owned by the VPC service and only feeds the non-critical + // security-groups path. Open it best-effort: if it isn't up yet, IMDS still + // starts and resolveSGNames degrades to serving raw IDs. + sgKV, err := js.KeyValue(kvBucketSecurityGroups) + if err != nil { + slog.Warn("IMDS: security-group bucket unavailable, /security-groups will serve IDs", "bucket", kvBucketSecurityGroups, "err", err) + sgKV = nil + } + + svc := &IMDSServiceImpl{ + resolver: &metadataResolver{ + index: indexKV, + eniKV: eniKV, + sgKV: sgKV, + lookup: &natsInstanceLookup{nc: natsConn, expectedNodes: expectedNodes}, + }, + tokens: newTokenStore(), + creds: newCredCache(sts), + iam: iamSvc, + now: time.Now, + } + svc.bind = newBindManager(vethKV, svc.httpHandler(), ensureVeth, removeVeth, bindLocalListener) + + slog.Info("IMDS service initialized", + "index_bucket", KVBucketENIByVPCIP, + "veth_bucket", KVBucketIMDSSubnetVeth) + return svc, nil +} + +// httpHandler builds the shared mux used by every per-VPC listener. The token +// PUT is the only path reachable without a token; everything else is gated. +func (s *IMDSServiceImpl) httpHandler() http.Handler { + mux := http.NewServeMux() + mux.HandleFunc(pathToken, s.handleToken) + mux.HandleFunc("/", s.handleMetadata) + return rejectForwarded(mux) +} + +// Run starts the bind manager (sync + watch) and the token-sweep ticker, then +// blocks until ctx is cancelled. The initial sync failing is logged but not +// fatal — other vpcd services must not have their readiness gated on IMDS +// plumbing, and SDKs retry while it converges. +func (s *IMDSServiceImpl) Run(ctx context.Context) error { + if err := s.bind.sync(ctx); err != nil { + slog.Warn("IMDS: initial bind sync failed, continuing", "err", err) + } + go s.bind.watch(ctx) + go s.sweepExpired(ctx) + + <-ctx.Done() + s.bind.shutdown() + return ctx.Err() +} + +// sweepExpired evicts expired IMDSv2 tokens and stale credential-cache entries +// on a ticker for the life of ctx. Both stores are in-memory and accrete on VM +// churn, so a single sweep keeps them bounded without a per-store goroutine. +func (s *IMDSServiceImpl) sweepExpired(ctx context.Context) { + ticker := time.NewTicker(tokenSweepInterval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + now := s.now() + s.tokens.sweep(now) + s.creds.sweep(now) + } + } +} diff --git a/spinifex/handlers/imds/token.go b/spinifex/handlers/imds/token.go new file mode 100644 index 000000000..5dd30280f --- /dev/null +++ b/spinifex/handlers/imds/token.go @@ -0,0 +1,85 @@ +package handlers_imds + +import ( + "crypto/rand" + "encoding/base64" + "sync" + "time" +) + +const ( + // tokenTTLMin / tokenTTLMax bound the X-aws-ec2-metadata-token-ttl-seconds + // header, matching AWS (1 second to 6 hours). + tokenTTLMin = 1 + tokenTTLMax = 21600 + + // tokenBytes is the random token length before base64url encoding. + tokenBytes = 32 +) + +// tokenEntry binds an issued IMDSv2 token to the ENI that requested it (not the +// IP — so the session survives a DHCP-renew IP change but dies on ENI detach) +// and its expiry. +type tokenEntry struct { + eniID string + expiresAt time.Time +} + +// tokenStore holds outstanding IMDSv2 tokens in memory, per process. Tokens are +// a CSRF defence, not a security artefact, so they are never persisted; a +// vpcd restart drops them and SDKs reissue transparently. +type tokenStore struct { + mu sync.Mutex + tokens map[string]tokenEntry +} + +func newTokenStore() *tokenStore { + return &tokenStore{tokens: make(map[string]tokenEntry)} +} + +// issue mints a fresh token bound to eniID, valid for ttl from now. +func (s *tokenStore) issue(eniID string, ttl time.Duration, now time.Time) (string, error) { + buf := make([]byte, tokenBytes) + if _, err := rand.Read(buf); err != nil { + return "", err + } + token := base64.RawURLEncoding.EncodeToString(buf) + + s.mu.Lock() + s.tokens[token] = tokenEntry{eniID: eniID, expiresAt: now.Add(ttl)} + s.mu.Unlock() + return token, nil +} + +// validate reports whether token is currently valid AND was issued to +// expectedENI. A wrong-ENI token is rejected exactly like an unknown one so the +// caller cannot probe which tokens exist. Expired tokens are evicted lazily. +func (s *tokenStore) validate(token, expectedENI string, now time.Time) bool { + if token == "" { + return false + } + s.mu.Lock() + defer s.mu.Unlock() + + entry, ok := s.tokens[token] + if !ok { + return false + } + if now.After(entry.expiresAt) { + delete(s.tokens, token) + return false + } + return entry.eniID == expectedENI +} + +// sweep deletes every token that expired at or before now. Called on a 1-minute +// ticker so abandoned tokens (an SDK that issues then never reads) don't leak. +func (s *tokenStore) sweep(now time.Time) { + s.mu.Lock() + defer s.mu.Unlock() + for token, entry := range s.tokens { + if now.After(entry.expiresAt) { + delete(s.tokens, token) + } + } +} diff --git a/spinifex/handlers/imds/token_test.go b/spinifex/handlers/imds/token_test.go new file mode 100644 index 000000000..7dde4b500 --- /dev/null +++ b/spinifex/handlers/imds/token_test.go @@ -0,0 +1,95 @@ +package handlers_imds + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTokenStore_IssueAndValidate(t *testing.T) { + s := newTokenStore() + now := time.Unix(1_700_000_000, 0) + + token, err := s.issue("eni-aaa", 60*time.Second, now) + require.NoError(t, err) + require.NotEmpty(t, token) + + assert.True(t, s.validate(token, "eni-aaa", now), "fresh token for its ENI must validate") + assert.True(t, s.validate(token, "eni-aaa", now.Add(59*time.Second)), "token valid until TTL") +} + +func TestTokenStore_WrongENIRejected(t *testing.T) { + s := newTokenStore() + now := time.Unix(1_700_000_000, 0) + token, err := s.issue("eni-aaa", 60*time.Second, now) + require.NoError(t, err) + + // A token issued to eni-aaa must not validate for eni-bbb — otherwise a + // guest could replay a peer's token to read the peer's session. + assert.False(t, s.validate(token, "eni-bbb", now)) +} + +func TestTokenStore_ExpiredRejectedAndEvicted(t *testing.T) { + s := newTokenStore() + now := time.Unix(1_700_000_000, 0) + token, err := s.issue("eni-aaa", 1*time.Second, now) + require.NoError(t, err) + + assert.False(t, s.validate(token, "eni-aaa", now.Add(2*time.Second)), "expired token must be rejected") + // Lazy eviction: the failed validate above dropped it, so even at a valid + // time it no longer exists. + assert.False(t, s.validate(token, "eni-aaa", now)) +} + +func TestTokenStore_UnknownAndEmptyRejected(t *testing.T) { + s := newTokenStore() + now := time.Unix(1_700_000_000, 0) + assert.False(t, s.validate("", "eni-aaa", now)) + assert.False(t, s.validate("never-issued", "eni-aaa", now)) +} + +func TestTokenStore_Sweep(t *testing.T) { + s := newTokenStore() + now := time.Unix(1_700_000_000, 0) + live, _ := s.issue("eni-live", 3600*time.Second, now) + dead, _ := s.issue("eni-dead", 1*time.Second, now) + + s.sweep(now.Add(2 * time.Second)) + + assert.True(t, s.validate(live, "eni-live", now.Add(2*time.Second)), "live token survives sweep") + assert.False(t, s.validate(dead, "eni-dead", now), "expired token swept") +} + +func TestTokenStore_IssueUnique(t *testing.T) { + s := newTokenStore() + now := time.Unix(1_700_000_000, 0) + a, _ := s.issue("eni-aaa", 60*time.Second, now) + b, _ := s.issue("eni-aaa", 60*time.Second, now) + assert.NotEqual(t, a, b, "each issued token must be distinct") +} + +func TestParseTokenTTL(t *testing.T) { + cases := []struct { + in string + ok bool + secs int + }{ + {"", false, 0}, + {"abc", false, 0}, + {"0", false, 0}, + {"21601", false, 0}, + {"-5", false, 0}, + {"1", true, 1}, + {"21600", true, 21600}, + {"60", true, 60}, + } + for _, c := range cases { + d, ok := parseTokenTTL(c.in) + assert.Equal(t, c.ok, ok, "ttl=%q", c.in) + if ok { + assert.Equal(t, c.secs, int(d.Seconds()), "ttl=%q", c.in) + } + } +} diff --git a/spinifex/handlers/imds/veth_store.go b/spinifex/handlers/imds/veth_store.go new file mode 100644 index 000000000..4e574457c --- /dev/null +++ b/spinifex/handlers/imds/veth_store.go @@ -0,0 +1,75 @@ +package handlers_imds + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/nats-io/nats.go" +) + +// SubnetVethRecord is the persisted per-subnet IMDS plumbing record +// (KVBucketIMDSSubnetVeth, keyed by subnet ID): the signal that a subnet's IMDS +// localport is installed, replayed by each chassis's BindManager. It carries the +// LSP MAC and the subnet CIDR the host can't re-derive, plus the owning VPC for +// the subnet→VPC reverse lookup. +type SubnetVethRecord struct { + SubnetID string `json:"subnet_id"` + ShortSubnetID string `json:"short_subnet_id"` + VPCID string `json:"vpc_id"` + IMDSPortMAC string `json:"imds_port_mac"` + SubnetCIDR string `json:"subnet_cidr"` + CreatedAt string `json:"created_at"` +} + +// VethStore is the persistence surface for SubnetVethRecord rows in +// KVBucketIMDSSubnetVeth. Backed by a NATS JetStream KV bucket opened by +// InitBuckets in production; tests inject the handle via NewVethStore. +type VethStore struct { + kv nats.KeyValue +} + +// NewVethStore wraps an already-opened subnet-veth KV bucket. +func NewVethStore(kv nats.KeyValue) *VethStore { return &VethStore{kv: kv} } + +// Get returns the record for subnetID, or (nil, nil) when absent. +func (s *VethStore) Get(subnetID string) (*SubnetVethRecord, error) { + raw, err := s.kv.Get(subnetID) + if err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil, nil + } + return nil, fmt.Errorf("get imds veth record %q: %w", subnetID, err) + } + var rec SubnetVethRecord + if err := json.Unmarshal(raw.Value(), &rec); err != nil { + return nil, fmt.Errorf("unmarshal imds veth record %q: %w", subnetID, err) + } + return &rec, nil +} + +// Put writes the record keyed by its SubnetID, overwriting any prior entry. +func (s *VethStore) Put(rec SubnetVethRecord) error { + if rec.SubnetID == "" { + return errors.New("imds veth store put: subnet_id is empty") + } + data, err := json.Marshal(rec) + if err != nil { + return fmt.Errorf("marshal imds veth record: %w", err) + } + if _, err := s.kv.Put(rec.SubnetID, data); err != nil { + return fmt.Errorf("put imds veth record %q: %w", rec.SubnetID, err) + } + return nil +} + +// Delete removes the record. Idempotent: a missing key returns nil. +func (s *VethStore) Delete(subnetID string) error { + if err := s.kv.Delete(subnetID); err != nil { + if errors.Is(err, nats.ErrKeyNotFound) { + return nil + } + return fmt.Errorf("delete imds veth record %q: %w", subnetID, err) + } + return nil +} diff --git a/spinifex/handlers/imds/veth_store_test.go b/spinifex/handlers/imds/veth_store_test.go new file mode 100644 index 000000000..65fad3404 --- /dev/null +++ b/spinifex/handlers/imds/veth_store_test.go @@ -0,0 +1,58 @@ +package handlers_imds + +import ( + "testing" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestVethStore(t *testing.T) *VethStore { + t.Helper() + _, _, js := testutil.StartTestJetStream(t) + subnetVeth, _, err := InitBuckets(js, 1) + require.NoError(t, err) + return NewVethStore(subnetVeth) +} + +func TestVethStore_PutGetRoundTrip(t *testing.T) { + s := newTestVethStore(t) + rec := SubnetVethRecord{ + SubnetID: "subnet-abc12345", + ShortSubnetID: "abc12345", + VPCID: "vpc-abc12345", + IMDSPortMAC: "02:aa:bb:cc:dd:ee", + SubnetCIDR: "10.0.1.0/24", + CreatedAt: "2026-05-29T00:00:00Z", + } + require.NoError(t, s.Put(rec)) + + got, err := s.Get("subnet-abc12345") + require.NoError(t, err) + require.NotNil(t, got) + assert.Equal(t, rec, *got) +} + +func TestVethStore_GetAbsentReturnsNil(t *testing.T) { + s := newTestVethStore(t) + got, err := s.Get("subnet-nope") + require.NoError(t, err) + assert.Nil(t, got) +} + +func TestVethStore_PutRejectsEmptySubnetID(t *testing.T) { + s := newTestVethStore(t) + require.Error(t, s.Put(SubnetVethRecord{ShortSubnetID: "abc12345"})) +} + +func TestVethStore_DeleteIsIdempotent(t *testing.T) { + s := newTestVethStore(t) + require.NoError(t, s.Delete("subnet-missing")) + + require.NoError(t, s.Put(SubnetVethRecord{SubnetID: "subnet-1"})) + require.NoError(t, s.Delete("subnet-1")) + got, err := s.Get("subnet-1") + require.NoError(t, err) + assert.Nil(t, got) +} diff --git a/spinifex/handlers/sts/assume_role.go b/spinifex/handlers/sts/assume_role.go index 79d387870..2014ff4e6 100644 --- a/spinifex/handlers/sts/assume_role.go +++ b/spinifex/handlers/sts/assume_role.go @@ -11,6 +11,7 @@ import ( "fmt" "log/slog" "regexp" + "slices" "strings" "time" @@ -28,6 +29,21 @@ const ( stsActionWildcard = "sts:*" globalWildcard = "*" + // principalTypeAssumedRole is the SessionCredential.PrincipalType for a role + // session — the default kind, also assumed when a stored value is empty. + principalTypeAssumedRole = "assumed-role" + + // principalTypeUser is the SessionCredential.PrincipalType for a session + // minted by GetSessionToken, which resolves back to the calling IAM user. + // Mirrors the gateway's principalTypeUser so the ASIA resolve path can map + // the stored value straight onto the user principal context. + principalTypeUser = "user" + + // ec2ServicePrincipal is the synthetic caller used by AssumeRoleForInstance — the + // only service principal that matches a trust policy in v1. The HTTPS AssumeRole + // path supplies an empty principalSource, so service principals never match there. + ec2ServicePrincipal = "ec2.amazonaws.com" + minDurationSeconds int64 = 900 maxDurationSeconds int64 = 43200 defaultDurationSeconds int64 = 3600 @@ -72,7 +88,62 @@ func (s *STSServiceImpl) AssumeRole(callerAccountID, callerARN, callerIdentity s return nil, errors.New(awserrors.ErrorInvalidParameterValue) } - roleAccountID, roleName, err := parseRoleARN(*input.RoleArn) + duration := int64(0) + if input.DurationSeconds != nil { + duration = *input.DurationSeconds + } + + out, err := s.assumeRoleForCaller(callerAccountID, callerARN, "", *input.RoleArn, sessionName, aws.StringValue(input.SourceIdentity), duration) + if err != nil { + return nil, err + } + + slog.Info("AssumeRole success", + "caller_arn", callerARN, + "caller_identity", callerIdentity, + "role_arn", aws.StringValue(input.RoleArn), + "session_name", sessionName, + "akid", aws.StringValue(out.Credentials.AccessKeyId), + "expires_at", aws.TimeValue(out.Credentials.Expiration), + "source_identity", aws.StringValue(input.SourceIdentity), + "external_id", aws.StringValue(input.ExternalId), + ) + + return out, nil +} + +// AssumeRoleForInstance is the in-process IMDS entry point, not reachable over HTTPS. +// The caller is synthesised as the EC2 service principal (principalSource = +// ec2.amazonaws.com); the session name is the instanceID, yielding an AWS-style ARN. +func (s *STSServiceImpl) AssumeRoleForInstance(accountID, roleARN, instanceID string, durationSeconds int64) (*sts.AssumeRoleOutput, error) { + if accountID == "" || roleARN == "" || instanceID == "" { + return nil, errors.New(awserrors.ErrorMissingParameter) + } + if !roleSessionNameRegex.MatchString(instanceID) { + return nil, errors.New(awserrors.ErrorValidationError) + } + + out, err := s.assumeRoleForCaller(accountID, "", ec2ServicePrincipal, roleARN, instanceID, "", durationSeconds) + if err != nil { + return nil, err + } + + slog.Info("AssumeRoleForInstance success", + "account_id", accountID, + "instance_id", instanceID, + "role_arn", roleARN, + "akid", aws.StringValue(out.Credentials.AccessKeyId), + "expires_at", aws.TimeValue(out.Credentials.Expiration), + ) + + return out, nil +} + +// assumeRoleForCaller is the shared core of AssumeRole (HTTPS) and AssumeRoleForInstance +// (IMDS): resolves the role, validates the duration (0 → default), evaluates the trust +// policy, and mints credentials. principalSource is "" for HTTPS, ec2.amazonaws.com for IMDS. +func (s *STSServiceImpl) assumeRoleForCaller(callerAccountID, callerARN, principalSource, roleARN, sessionName, sourceIdentity string, requestedDuration int64) (*sts.AssumeRoleOutput, error) { + roleAccountID, roleName, err := parseRoleARN(roleARN) if err != nil { return nil, errors.New(awserrors.ErrorValidationError) } @@ -90,9 +161,9 @@ func (s *STSServiceImpl) AssumeRole(callerAccountID, callerARN, callerIdentity s } role := roleOut.Role - duration := defaultDurationSeconds - if input.DurationSeconds != nil { - duration = *input.DurationSeconds + duration := requestedDuration + if duration == 0 { + duration = defaultDurationSeconds } effectiveMax := aws.Int64Value(role.MaxSessionDuration) if effectiveMax == 0 { @@ -105,26 +176,16 @@ func (s *STSServiceImpl) AssumeRole(callerAccountID, callerARN, callerIdentity s return nil, errors.New(awserrors.ErrorValidationError) } - if err := evalTrustPolicy(aws.StringValue(role.AssumeRolePolicyDocument), callerARN); err != nil { + if err := evalTrustPolicy(aws.StringValue(role.AssumeRolePolicyDocument), callerARN, principalSource); err != nil { return nil, err } - cred, plainSecret, plainToken, err := s.mintSessionCredential(role, roleAccountID, sessionName, aws.StringValue(input.SourceIdentity), duration) + env := assumedRoleEnvelope(role, roleAccountID, sessionName, sourceIdentity) + cred, plainSecret, plainToken, err := s.mintSession(env, duration) if err != nil { return nil, err } - slog.Info("AssumeRole success", - "caller_arn", callerARN, - "caller_identity", callerIdentity, - "role_arn", aws.StringValue(role.Arn), - "session_name", sessionName, - "akid", cred.AccessKeyID, - "expires_at", cred.ExpiresAt, - "source_identity", aws.StringValue(input.SourceIdentity), - "external_id", aws.StringValue(input.ExternalId), - ) - return &sts.AssumeRoleOutput{ Credentials: &sts.Credentials{ AccessKeyId: aws.String(cred.AccessKeyID), @@ -172,7 +233,7 @@ func parseRoleARN(arnStr string) (accountID, name string, err error) { // pass scans every Allow statement (returning nil on the first match). A // single-pass "return on first Allow" loop would silently skip a later Deny // and is a real authorisation bug. -func evalTrustPolicy(docJSON, callerARN string) error { +func evalTrustPolicy(docJSON, callerARN, principalSource string) error { doc, err := handlers_iam.ValidateTrustPolicyDocument(docJSON) if err != nil { // Stored docs were validated at CreateRole / UpdateAssumeRolePolicy @@ -187,7 +248,7 @@ func evalTrustPolicy(docJSON, callerARN string) error { if !matchTrustAction(stmt.Action) { continue } - match, err := matchTrustPrincipal(stmt.Principal, callerARN) + match, err := matchTrustPrincipal(stmt.Principal, callerARN, principalSource) if err != nil { return err } @@ -203,7 +264,7 @@ func evalTrustPolicy(docJSON, callerARN string) error { if !matchTrustAction(stmt.Action) { continue } - match, err := matchTrustPrincipal(stmt.Principal, callerARN) + match, err := matchTrustPrincipal(stmt.Principal, callerARN, principalSource) if err != nil { return err } @@ -224,11 +285,10 @@ func matchTrustAction(actions []string) bool { return false } -// matchTrustPrincipal evaluates each top-level Principal key independently — -// AWS semantics treat the map as an OR across keys, and unsupported keys -// (Service, Federated) skip at the entry level rather than failing the whole -// statement. -func matchTrustPrincipal(raw json.RawMessage, callerARN string) (bool, error) { +// matchTrustPrincipal evaluates each top-level Principal key independently (AWS +// treats the map as an OR). AWS matches callerARN, Service matches principalSource +// (set only by the IMDS path); unsupported keys (Federated) skip, not fail. +func matchTrustPrincipal(raw json.RawMessage, callerARN, principalSource string) (bool, error) { if len(raw) == 0 { return false, nil } @@ -242,11 +302,45 @@ func matchTrustPrincipal(raw json.RawMessage, callerARN string) (bool, error) { if err := json.Unmarshal(raw, &m); err != nil { return false, fmt.Errorf("unmarshal Principal: %w", err) } - awsRaw, ok := m["AWS"] - if !ok { + if awsRaw, ok := m["AWS"]; ok { + match, err := matchAWSPrincipal(awsRaw, callerARN) + if err != nil { + return false, err + } + if match { + return true, nil + } + } + if svcRaw, ok := m["Service"]; ok { + match, err := matchServicePrincipal(svcRaw, principalSource) + if err != nil { + return false, err + } + if match { + return true, nil + } + } + return false, nil +} + +// matchServicePrincipal matches a Service principal against the synthesised caller. +// v1 trusts only the EC2 service principal: any other principalSource (including +// the empty one used by the HTTPS path) is rejected up front, so unsupported +// service principals are denied even if a future caller synthesised one. +// Expanding the whitelist (ecs-tasks, lambda) is a one-line change to this guard. +func matchServicePrincipal(raw json.RawMessage, principalSource string) (bool, error) { + if principalSource != ec2ServicePrincipal { return false, nil } - return matchAWSPrincipal(awsRaw, callerARN) + var single string + if err := json.Unmarshal(raw, &single); err == nil { + return single == principalSource, nil + } + var arr []string + if err := json.Unmarshal(raw, &arr); err != nil { + return false, fmt.Errorf("Principal.Service must be string or array: %w", err) + } + return slices.Contains(arr, principalSource), nil } func matchAWSPrincipal(raw json.RawMessage, callerARN string) (bool, error) { @@ -340,11 +434,48 @@ func lastPathSegment(s string) string { return s } -// mintSessionCredential generates a fresh ASIA AKID, secret, and session -// token, encrypts the secret with the IAM master key, HMACs the session -// token, and persists the record. Retries on AKID collision; the birthday -// bound for 64-bit AKID entropy makes that practically unreachable. -func (s *STSServiceImpl) mintSessionCredential(role *iam.Role, roleAccountID, sessionName, sourceIdentity string, duration int64) (*SessionCredential, string, string, error) { +// sessionEnvelope is the resolved identity a session credential is minted for. +// It decouples mintSession from AssumeRole's *iam.Role so GetSessionToken can +// mint a user-bound session: the role paths fill the assumed-role fields, the +// user path sets PrincipalType "user" + SessionName = the IAM user name and +// leaves the assumed-role fields empty. +type sessionEnvelope struct { + PrincipalType string + AccountID string + SessionName string + SourceIdentity string + + // Assumed-role-only; empty for a user (GetSessionToken) envelope. + AssumedRoleARN string + UnderlyingRoleARN string + RoleID string + AssumedRoleID string +} + +// assumedRoleEnvelope derives the session envelope for a role session, shared by +// AssumeRole, AssumeRoleForInstance, and AssumeRoleWithWebIdentity. It owns the +// assumed-role ARN / AssumedRoleId construction so every role path stays +// consistent. +func assumedRoleEnvelope(role *iam.Role, roleAccountID, sessionName, sourceIdentity string) sessionEnvelope { + roleID := aws.StringValue(role.RoleId) + roleName := aws.StringValue(role.RoleName) + return sessionEnvelope{ + PrincipalType: principalTypeAssumedRole, + AccountID: roleAccountID, + SessionName: sessionName, + SourceIdentity: sourceIdentity, + AssumedRoleARN: fmt.Sprintf("arn:aws:sts::%s:assumed-role/%s/%s", roleAccountID, roleName, sessionName), + UnderlyingRoleARN: aws.StringValue(role.Arn), + RoleID: roleID, + AssumedRoleID: fmt.Sprintf("%s:%s", roleID, sessionName), + } +} + +// mintSession generates a fresh ASIA AKID, secret, and session token, encrypts +// the secret with the IAM master key, HMACs the session token, and persists the +// record described by env. Retries on AKID collision; the birthday bound for +// 64-bit AKID entropy makes that practically unreachable. +func (s *STSServiceImpl) mintSession(env sessionEnvelope, duration int64) (*SessionCredential, string, string, error) { plainSecret, err := generateRandomBase64(sessionSecretBytes) if err != nil { return nil, "", "", err @@ -363,10 +494,6 @@ func (s *STSServiceImpl) mintSessionCredential(role *iam.Role, roleAccountID, se now := time.Now().UTC() expiresAt := now.Add(time.Duration(duration) * time.Second) - roleID := aws.StringValue(role.RoleId) - roleName := aws.StringValue(role.RoleName) - underlyingRoleARN := aws.StringValue(role.Arn) - for range mintMaxAttempts { akid, err := generateSessionAKID() if err != nil { @@ -376,13 +503,14 @@ func (s *STSServiceImpl) mintSessionCredential(role *iam.Role, roleAccountID, se AccessKeyID: akid, SecretEncrypted: secretEncrypted, SessionTokenHMAC: tokenHMAC, - AccountID: roleAccountID, - AssumedRoleARN: fmt.Sprintf("arn:aws:sts::%s:assumed-role/%s/%s", roleAccountID, roleName, sessionName), - UnderlyingRoleARN: underlyingRoleARN, - RoleID: roleID, - AssumedRoleID: fmt.Sprintf("%s:%s", roleID, sessionName), - SessionName: sessionName, - SourceIdentity: sourceIdentity, + AccountID: env.AccountID, + PrincipalType: env.PrincipalType, + AssumedRoleARN: env.AssumedRoleARN, + UnderlyingRoleARN: env.UnderlyingRoleARN, + RoleID: env.RoleID, + AssumedRoleID: env.AssumedRoleID, + SessionName: env.SessionName, + SourceIdentity: env.SourceIdentity, ExpiresAt: expiresAt, CreatedAt: now, } diff --git a/spinifex/handlers/sts/assume_role_test.go b/spinifex/handlers/sts/assume_role_test.go index b3345930f..50c59d7cb 100644 --- a/spinifex/handlers/sts/assume_role_test.go +++ b/spinifex/handlers/sts/assume_role_test.go @@ -20,9 +20,10 @@ import ( ) const ( - testCallerAccountID = utils.GlobalAccountID - testCallerUserName = "alice" - testCrossAccountID = "999999999999" + testCallerAccountID = utils.GlobalAccountID + testCallerUserName = "alice" + testCallerAccessKeyID = "AKIAIOSFODNN7EXAMPLE" + testCrossAccountID = "999999999999" ) func testCallerARN() string { @@ -557,6 +558,109 @@ func TestAssumeRole_ServicePrincipal_NeverMatchesInV1(t *testing.T) { assert.Equal(t, awserrors.ErrorAccessDenied, err.Error()) } +// ----- AssumeRoleForInstance (IMDS in-process path) ---------------------- + +const testInstanceID = "i-0123456789abcdef0" + +func trustPolicyAllowingEC2Service() string { + return `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}]}` +} + +func TestAssumeRoleForInstance_EC2ServicePrincipal_Allowed(t *testing.T) { + svc, _ := newTestSetup(t) + role := createRoleInAccount(t, svc, testCallerAccountID, "app-role", trustPolicyAllowingEC2Service()) + + out, err := svc.AssumeRoleForInstance(testCallerAccountID, *role.Arn, testInstanceID, defaultDurationSeconds) + require.NoError(t, err) + require.NotNil(t, out) + require.NotNil(t, out.Credentials) + + akid := aws.StringValue(out.Credentials.AccessKeyId) + assert.True(t, strings.HasPrefix(akid, SessionAccessKeyIDPrefix)) + assert.Len(t, akid, 20) + + // The session name is the instance ID, matching AWS. + assert.Equal(t, fmt.Sprintf("arn:aws:sts::%s:assumed-role/app-role/%s", testCallerAccountID, testInstanceID), + aws.StringValue(out.AssumedRoleUser.Arn)) + + stored, err := svc.LookupSessionCredential(akid) + require.NoError(t, err) + require.NotNil(t, stored) + assert.Equal(t, testInstanceID, stored.SessionName) + assert.Equal(t, testCallerAccountID, stored.AccountID) +} + +// Service principals other than ec2.amazonaws.com are not whitelisted in v1, so +// even though the caller is synthesised as the EC2 service principal, a +// lambda.amazonaws.com trust statement must not match. +func TestAssumeRoleForInstance_NonWhitelistedService_Denied(t *testing.T) { + svc, _ := newTestSetup(t) + policy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}` + role := createRoleInAccount(t, svc, testCallerAccountID, "lambda-role", policy) + + _, err := svc.AssumeRoleForInstance(testCallerAccountID, *role.Arn, testInstanceID, defaultDurationSeconds) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorAccessDenied, err.Error()) +} + +// A role that only trusts an AWS principal (no Service entry) must not be +// assumable by an instance — the synthesised EC2 caller has no ARN to match. +func TestAssumeRoleForInstance_AWSOnlyPrincipal_Denied(t *testing.T) { + svc, _ := newTestSetup(t) + role := createRoleInAccount(t, svc, testCallerAccountID, "user-role", trustPolicyAllowingUser(testCallerARN())) + + _, err := svc.AssumeRoleForInstance(testCallerAccountID, *role.Arn, testInstanceID, defaultDurationSeconds) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorAccessDenied, err.Error()) +} + +// Regression: the EC2 service principal must remain unreachable over the HTTPS +// AssumeRole path. A role trusting only ec2.amazonaws.com is never assumable by +// a normal SigV4 caller, no matter what — the security boundary between the two +// entry points. +func TestAssumeRoleForInstance_EC2ServiceUnreachableViaHTTPS(t *testing.T) { + svc, _ := newTestSetup(t) + role := createRoleInAccount(t, svc, testCallerAccountID, "ec2-only", trustPolicyAllowingEC2Service()) + + _, err := svc.AssumeRole(testCallerAccountID, testCallerARN(), testCallerUserName, + basicAssumeRoleInput(*role.Arn, "sess")) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorAccessDenied, err.Error()) +} + +func TestAssumeRoleForInstance_RejectsMissingFields(t *testing.T) { + svc, _ := newTestSetup(t) + role := createRoleInAccount(t, svc, testCallerAccountID, "fields", trustPolicyAllowingEC2Service()) + + cases := []struct { + name string + accountID string + roleARN string + instanceID string + }{ + {"missing_account", "", *role.Arn, testInstanceID}, + {"missing_role_arn", testCallerAccountID, "", testInstanceID}, + {"missing_instance_id", testCallerAccountID, *role.Arn, ""}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, err := svc.AssumeRoleForInstance(tc.accountID, tc.roleARN, tc.instanceID, defaultDurationSeconds) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorMissingParameter, err.Error()) + }) + } +} + +func TestAssumeRoleForInstance_CrossAccountRoleMiss_AccessDenied(t *testing.T) { + svc, _ := newTestSetup(t) + // Instance's account differs from the role's account and the role is + // missing — masked to AccessDenied to prevent cross-account enumeration. + _, err := svc.AssumeRoleForInstance(testCallerAccountID, + fmt.Sprintf("arn:aws:iam::%s:role/ghost", testCrossAccountID), testInstanceID, defaultDurationSeconds) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorAccessDenied, err.Error()) +} + func TestAssumeRole_RetriesOnAKIDCollision(t *testing.T) { svc, _ := newTestSetup(t) caller := testCallerARN() @@ -643,7 +747,7 @@ func TestEvalTrustPolicy_CorruptDocReturnsError(t *testing.T) { // Stored docs are validated upstream; reaching evalTrustPolicy with a // malformed doc indicates corruption — must fail closed and NOT collapse // to AccessDenied (which would hide the corruption from operators). - err := evalTrustPolicy(`{not json`, testCallerARN()) + err := evalTrustPolicy(`{not json`, testCallerARN(), "") require.Error(t, err) assert.NotEqual(t, awserrors.ErrorAccessDenied, err.Error(), "corrupt doc must not be reported as AccessDenied — operators need to see the corruption signal") diff --git a/spinifex/handlers/sts/assume_role_with_web_identity.go b/spinifex/handlers/sts/assume_role_with_web_identity.go index d1499b497..12de89ea3 100644 --- a/spinifex/handlers/sts/assume_role_with_web_identity.go +++ b/spinifex/handlers/sts/assume_role_with_web_identity.go @@ -130,7 +130,8 @@ func (s *STSServiceImpl) AssumeRoleWithWebIdentity(input *sts.AssumeRoleWithWebI return nil, err } - cred, plainSecret, plainToken, err := s.mintSessionCredential(role, roleAccountID, sessionName, claims.Subject, duration) + env := assumedRoleEnvelope(role, roleAccountID, sessionName, claims.Subject) + cred, plainSecret, plainToken, err := s.mintSession(env, duration) if err != nil { return nil, err } diff --git a/spinifex/handlers/sts/assume_role_with_web_identity_test.go b/spinifex/handlers/sts/assume_role_with_web_identity_test.go index 367aec23e..5669d1257 100644 --- a/spinifex/handlers/sts/assume_role_with_web_identity_test.go +++ b/spinifex/handlers/sts/assume_role_with_web_identity_test.go @@ -56,8 +56,8 @@ func newWebIdentityFixture(t *testing.T, svc *STSServiceImpl, accountID string) Y: base64.RawURLEncoding.EncodeToString(priv.Y.Bytes()), }}} - issuer := fmt.Sprintf("https://oidc.eks.%s.%s/%s/%s", - testWebRegion, testWebSuffix, accountID, testWebClusterName) + issuer := fmt.Sprintf("https://gw.%s/oidc/eks/%s/%s/%s", + testWebSuffix, testWebRegion, accountID, testWebClusterName) issuerHostPath := strings.TrimPrefix(issuer, "https://") federatedARN := handlers_iam.OIDCProviderARN(accountID, issuerHostPath) @@ -346,8 +346,8 @@ func TestAssumeRoleWithWebIdentity_ProviderNotRegistered(t *testing.T) { priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) require.NoError(t, err) const kid = "unregistered" - issuer := fmt.Sprintf("https://oidc.eks.%s.%s/%s/%s", - testWebRegion, testWebSuffix, testCallerAccountID, "unregistered-cluster") + issuer := fmt.Sprintf("https://gw.%s/oidc/eks/%s/%s/%s", + testWebSuffix, testWebRegion, testCallerAccountID, "unregistered-cluster") issuerHostPath := strings.TrimPrefix(issuer, "https://") federatedARN := handlers_iam.OIDCProviderARN(testCallerAccountID, issuerHostPath) diff --git a/spinifex/handlers/sts/get_session_token.go b/spinifex/handlers/sts/get_session_token.go new file mode 100644 index 000000000..fa762387d --- /dev/null +++ b/spinifex/handlers/sts/get_session_token.go @@ -0,0 +1,117 @@ +package handlers_sts + +import ( + "errors" + "log/slog" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/mulgadc/spinifex/spinifex/awserrors" +) + +const ( + // GetSessionToken duration bounds differ from AssumeRole: AWS permits a + // session up to 36h (vs. the 12h role-session ceiling) and defaults to 12h. + // The floor is the STS-wide minimum (minDurationSeconds, 900s). + getSessionTokenMaxDuration int64 = 129600 // 36h + getSessionTokenDefaultDuration int64 = 43200 // 12h +) + +// GetSessionToken exchanges the calling IAM user's long-lived credentials for +// short-lived ASIA session credentials bound to the SAME user identity. Unlike +// AssumeRole, the resulting session resolves back to arn:aws:iam::A:user/N and +// IAM policy is evaluated against that user — see the user branch of the SigV4 +// ASIA resolve path. The caller identity is resolved by the gateway and passed +// in as plain strings to keep the handler unit-testable. +// +// AWS forbids calling GetSessionToken with temporary credentials, so only a +// long-lived user principal (AKIA → principalType "user") is accepted. Assumed +// -role callers are rejected by principal type; session callers — including a +// GetSessionToken session, which resolves back to principalType "user" — are +// rejected by their ASIA access-key prefix. Root is a long-lived user and, per +// AWS, may call GetSessionToken. +func (s *STSServiceImpl) GetSessionToken(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID string, input *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) { + if input == nil { + // `aws sts get-session-token` with no arguments sends an empty body; + // an absent input is the common case, not an error. + input = &sts.GetSessionTokenInput{} + } + + if callerPrincipalType != principalTypeUser { + slog.Warn("GetSessionToken denied: caller is not a long-lived user principal", + "account_id", callerAccountID, "principal_type", callerPrincipalType) + return nil, errors.New(awserrors.ErrorAccessDenied) + } + if strings.HasPrefix(callerAccessKeyID, SessionAccessKeyIDPrefix) { + // A GetSessionToken session resolves back to principalType "user", so the + // check above cannot catch it — the ASIA prefix is the only signal that + // the caller holds temporary credentials. Rejecting it stops a session + // from minting a fresh session and rolling its lifetime forward forever. + slog.Warn("GetSessionToken denied: caller is using temporary (session) credentials", + "account_id", callerAccountID, "akid", callerAccessKeyID) + return nil, errors.New(awserrors.ErrorAccessDenied) + } + if callerAccountID == "" || callerUserName == "" { + // A user principal with no account or name is a mis-wired caller, not a + // client error — fail loud rather than mint a session with an empty name. + slog.Error("GetSessionToken: user principal missing account or name", + "account_id", callerAccountID, "user_name", callerUserName) + return nil, errors.New(awserrors.ErrorInternalError) + } + + // MFA (SerialNumber/TokenCode) is out of scope: reject rather than silently + // ignore so a caller never believes an MFA condition was enforced. + if aws.StringValue(input.SerialNumber) != "" || aws.StringValue(input.TokenCode) != "" { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + + duration := getSessionTokenDefaultDuration + if input.DurationSeconds != nil { + duration = clampGetSessionTokenDuration(*input.DurationSeconds) + } + + cred, plainSecret, plainToken, err := s.mintSession(userEnvelope(callerAccountID, callerUserName), duration) + if err != nil { + return nil, err + } + + slog.Info("GetSessionToken success", + "account_id", callerAccountID, + "user_name", callerUserName, + "akid", cred.AccessKeyID, + "expires_at", cred.ExpiresAt, + ) + + return &sts.GetSessionTokenOutput{ + Credentials: &sts.Credentials{ + AccessKeyId: aws.String(cred.AccessKeyID), + SecretAccessKey: aws.String(plainSecret), + SessionToken: aws.String(plainToken), + Expiration: aws.Time(cred.ExpiresAt), + }, + }, nil +} + +// clampGetSessionTokenDuration coerces a requested duration into the permitted +// [900, 129600] window. A nil DurationSeconds is handled by the caller (default +// 12h); this only sees an explicit value, which AWS clamps rather than rejects. +func clampGetSessionTokenDuration(requested int64) int64 { + if requested < minDurationSeconds { + return minDurationSeconds + } + if requested > getSessionTokenMaxDuration { + return getSessionTokenMaxDuration + } + return requested +} + +// userEnvelope is the session envelope for a GetSessionToken user session: +// PrincipalType "user", SessionName = the IAM user name, no assumed-role fields. +func userEnvelope(accountID, userName string) sessionEnvelope { + return sessionEnvelope{ + PrincipalType: principalTypeUser, + AccountID: accountID, + SessionName: userName, + } +} diff --git a/spinifex/handlers/sts/get_session_token_test.go b/spinifex/handlers/sts/get_session_token_test.go new file mode 100644 index 000000000..4750bcc9e --- /dev/null +++ b/spinifex/handlers/sts/get_session_token_test.go @@ -0,0 +1,167 @@ +package handlers_sts + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "strings" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetSessionToken_HappyPath_MintsUserSession(t *testing.T) { + svc, _ := newTestSetup(t) + + out, err := svc.GetSessionToken(testCallerAccountID, testCallerUserName, principalTypeUser, testCallerAccessKeyID, + &sts.GetSessionTokenInput{}) + require.NoError(t, err) + require.NotNil(t, out) + require.NotNil(t, out.Credentials) + + akid := aws.StringValue(out.Credentials.AccessKeyId) + secret := aws.StringValue(out.Credentials.SecretAccessKey) + token := aws.StringValue(out.Credentials.SessionToken) + + assert.True(t, strings.HasPrefix(akid, SessionAccessKeyIDPrefix)) + assert.Len(t, akid, 20) + assert.NotEmpty(t, secret) + assert.NotEmpty(t, token) + assert.True(t, out.Credentials.Expiration.After(time.Now().UTC())) + + stored, err := svc.LookupSessionCredential(akid) + require.NoError(t, err) + require.NotNil(t, stored) + + // The defining property of a GetSessionToken session: it is user-bound, so + // PrincipalType is "user", SessionName is the IAM user name, and every + // assumed-role field is empty. resolveSessionAKID relies on exactly this. + assert.Equal(t, principalTypeUser, stored.PrincipalType) + assert.Equal(t, testCallerUserName, stored.SessionName) + assert.Equal(t, testCallerAccountID, stored.AccountID) + assert.Empty(t, stored.AssumedRoleARN) + assert.Empty(t, stored.UnderlyingRoleARN) + assert.Empty(t, stored.RoleID) + assert.Empty(t, stored.AssumedRoleID) + assert.Empty(t, stored.SourceIdentity) + + // The wire token must HMAC to the persisted record, and the persisted secret + // must decrypt back — same minting invariants as AssumeRole. + mac := hmac.New(sha256.New, svc.masterKey) + mac.Write([]byte(token)) + expected := base64.StdEncoding.EncodeToString(mac.Sum(nil)) + assert.Equal(t, expected, stored.SessionTokenHMAC) + assert.NotEqual(t, token, stored.SessionTokenHMAC, "raw token must not be persisted") + + decrypted, err := handlers_iam.DecryptSecret(stored.SecretEncrypted, svc.masterKey) + require.NoError(t, err) + assert.Equal(t, secret, decrypted) +} + +func TestGetSessionToken_NilInput_DefaultsToTwelveHours(t *testing.T) { + svc, _ := newTestSetup(t) + + out, err := svc.GetSessionToken(testCallerAccountID, testCallerUserName, principalTypeUser, testCallerAccessKeyID, nil) + require.NoError(t, err) + require.NotNil(t, out.Credentials) + + assertStoredDuration(t, svc, out, getSessionTokenDefaultDuration) +} + +func TestGetSessionToken_DurationClamp(t *testing.T) { + svc, _ := newTestSetup(t) + + cases := []struct { + name string + requested int64 + want int64 + }{ + {"below_minimum_clamps_up", minDurationSeconds - 1, minDurationSeconds}, + {"at_minimum", minDurationSeconds, minDurationSeconds}, + {"in_range", 7200, 7200}, + {"at_maximum", getSessionTokenMaxDuration, getSessionTokenMaxDuration}, + {"above_maximum_clamps_down", getSessionTokenMaxDuration + 1, getSessionTokenMaxDuration}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + out, err := svc.GetSessionToken(testCallerAccountID, testCallerUserName, principalTypeUser, testCallerAccessKeyID, + &sts.GetSessionTokenInput{DurationSeconds: aws.Int64(tc.requested)}) + require.NoError(t, err) + assertStoredDuration(t, svc, out, tc.want) + }) + } +} + +func TestGetSessionToken_RejectsNonUserAndSessionCallers(t *testing.T) { + svc, _ := newTestSetup(t) + + // Only a long-lived user (AKIA + principalType "user") may call + // GetSessionToken. Assumed-role callers are denied by principal type; + // session callers are denied by their ASIA access-key prefix — including a + // GetSessionToken session, which resolves back to principalType "user" and + // would otherwise roll its own lifetime forward indefinitely. + cases := []struct { + name string + principalType string + accessKeyID string + }{ + {"assumed_role_session", principalTypeAssumedRole, "ASIAEXAMPLEAAAAAAAAA"}, + {"user_session", principalTypeUser, "ASIAEXAMPLEAAAAAAAAA"}, + {"empty_principal_type", "", testCallerAccessKeyID}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + out, err := svc.GetSessionToken(testCallerAccountID, testCallerUserName, tc.principalType, tc.accessKeyID, + &sts.GetSessionTokenInput{}) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorAccessDenied, err.Error()) + assert.Nil(t, out) + }) + } +} + +func TestGetSessionToken_RejectsMFAParameters(t *testing.T) { + svc, _ := newTestSetup(t) + + cases := []struct { + name string + input *sts.GetSessionTokenInput + }{ + {"serial_number", &sts.GetSessionTokenInput{SerialNumber: aws.String("arn:aws:iam::000000000000:mfa/alice")}}, + {"token_code", &sts.GetSessionTokenInput{TokenCode: aws.String("123456")}}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + out, err := svc.GetSessionToken(testCallerAccountID, testCallerUserName, principalTypeUser, testCallerAccessKeyID, tc.input) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorInvalidParameterValue, err.Error()) + assert.Nil(t, out) + }) + } +} + +func TestGetSessionToken_RejectsMissingUserName(t *testing.T) { + svc, _ := newTestSetup(t) + + out, err := svc.GetSessionToken(testCallerAccountID, "", principalTypeUser, testCallerAccessKeyID, &sts.GetSessionTokenInput{}) + require.Error(t, err) + assert.Equal(t, awserrors.ErrorInternalError, err.Error()) + assert.Nil(t, out) +} + +// assertStoredDuration looks up the minted credential and asserts the persisted +// lifetime (ExpiresAt - CreatedAt) equals the expected number of seconds. This +// reads the clamp through the stored record rather than re-deriving it. +func assertStoredDuration(t *testing.T, svc *STSServiceImpl, out *sts.GetSessionTokenOutput, wantSeconds int64) { + t.Helper() + stored, err := svc.LookupSessionCredential(aws.StringValue(out.Credentials.AccessKeyId)) + require.NoError(t, err) + require.NotNil(t, stored) + assert.Equal(t, time.Duration(wantSeconds)*time.Second, stored.ExpiresAt.Sub(stored.CreatedAt)) +} diff --git a/spinifex/handlers/sts/imds_rpc.go b/spinifex/handlers/sts/imds_rpc.go new file mode 100644 index 000000000..7bb54e265 --- /dev/null +++ b/spinifex/handlers/sts/imds_rpc.go @@ -0,0 +1,36 @@ +package handlers_sts + +import ( + "github.com/aws/aws-sdk-go/service/sts" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" +) + +// imdsResponderQueue load-balances the IMDS control-plane RPCs across every +// awsgw in the cluster, matching the cluster-wide worker convention. +const imdsResponderQueue = "spinifex-workers" + +// SubjectAssumeRoleForInstance is the internal, ACL-gated subject the IMDS handler +// (in vpcd) calls to mint instance-role credentials. STS keeps the IAM master key +// in-process inside awsgw; only this narrow RPC is surfaced, never to a guest. +const SubjectAssumeRoleForInstance = "imds.sts.assume_role_for_instance" + +// AssumeRoleForInstanceRequest is the SubjectAssumeRoleForInstance payload. The +// account ID is data here (resolved from the requesting ENI), not an +// authenticated caller identity. +type AssumeRoleForInstanceRequest struct { + AccountID string `json:"account_id"` + RoleARN string `json:"role_arn"` + InstanceID string `json:"instance_id"` + DurationSeconds int64 `json:"duration_seconds"` +} + +// SubscribeIMDSResponder wires the AssumeRoleForInstance responder onto nc, +// queue-grouped so any awsgw can answer. +func (s *STSServiceImpl) SubscribeIMDSResponder(nc *nats.Conn) (*nats.Subscription, error) { + return nc.QueueSubscribe(SubjectAssumeRoleForInstance, imdsResponderQueue, func(msg *nats.Msg) { + utils.ServeNATSRequest(msg, func(req *AssumeRoleForInstanceRequest) (*sts.AssumeRoleOutput, error) { + return s.AssumeRoleForInstance(req.AccountID, req.RoleARN, req.InstanceID, req.DurationSeconds) + }) + }) +} diff --git a/spinifex/handlers/sts/invariants_test.go b/spinifex/handlers/sts/invariants_test.go index abce32a2b..65016e657 100644 --- a/spinifex/handlers/sts/invariants_test.go +++ b/spinifex/handlers/sts/invariants_test.go @@ -49,6 +49,39 @@ func TestInvariant_SessionCredentialsBucket_AcceptsASIAPrefix(t *testing.T) { require.NoError(t, putSessionCredential(bucket, cred)) } +// User sessions (minted by GetSessionToken) write to the same bucket through +// the same putSessionCredential helper as assumed-role sessions, so the +// ASIA-prefix invariant must be principal-type-agnostic. This pins that: a +// PrincipalType="user" credential is accepted iff its AKID is ASIA-prefixed, +// exactly like a role session — the writer guard must never special-case the +// principal type, or a user session could land in the wrong bucket and bypass +// the ASIA dispatch path's invariants. +func TestInvariant_SessionCredentialsBucket_UserSessionPrefixEnforced(t *testing.T) { + bucket := setupBucket(t) + + userCred := func(akid string) *SessionCredential { + now := time.Now().UTC() + return &SessionCredential{ + AccessKeyID: akid, + SecretEncrypted: "ciphertext-base64", + SessionTokenHMAC: "hmac-base64", + AccountID: "000000000000", + PrincipalType: principalTypeUser, + SessionName: "alice", + ExpiresAt: now.Add(time.Hour), + CreatedAt: now, + } + } + + // A non-ASIA user AKID is rejected at the writer, same as any other prefix. + err := putSessionCredential(bucket, userCred("AKIA0123456789ABCDEF")) + require.Error(t, err, "putSessionCredential accepted a non-ASIA user session") + assert.Contains(t, err.Error(), SessionAccessKeyIDPrefix) + + // An ASIA user AKID is accepted, just like an assumed-role session. + require.NoError(t, putSessionCredential(bucket, userCred(SessionAccessKeyIDPrefix+"USERSESSION00001"))) +} + // Cross-cluster anti-replay invariant. A presigned sts:GetCallerIdentity URL // signed under cluster-A's name MUST NOT verify when presented to cluster-B. // The defence is the X-K8s-Aws-Id header participating in SigV4 diff --git a/spinifex/handlers/sts/oidc_jwks.go b/spinifex/handlers/sts/oidc_jwks.go index 32bddb6a5..175b745cd 100644 --- a/spinifex/handlers/sts/oidc_jwks.go +++ b/spinifex/handlers/sts/oidc_jwks.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net/url" + "slices" "strings" handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" @@ -47,15 +48,34 @@ func (s *JWKS) FindByKID(kid string) *JWK { return nil } -// ParseEKSIssuerURL extracts the (accountID, clusterName) pair encoded in an -// EKS OIDC issuer URL of the form: +// eksIssuerPathSegments is the fixed path layout of a spinifex EKS OIDC issuer: // -// https://oidc.eks.{region}.{suffix}/{accountID}/{clusterName} +// oidc / eks / {region} / {accountID} / {clusterName} +const ( + eksIssuerPathSegments = 5 + eksIssuerSegOIDC = 0 + eksIssuerSegEKS = 1 + eksIssuerSegRegion = 2 + eksIssuerSegAccountID = 3 + eksIssuerSegCluster = 4 +) + +// ParseEKSIssuerURL extracts the (accountID, clusterName) pair encoded in a +// spinifex EKS OIDC issuer URL of the form: // -// Returns an error for any URL that does not match that exact two-segment -// path structure under an `oidc.eks.` host prefix — defensive parsing prevents -// a maliciously crafted `iss` claim from steering the JWKS lookup at an -// attacker-controlled cluster bucket. +// https://{gateway-host}/oidc/eks/{region}/{accountID}/{clusterName} +// +// This matches the URL ClusterOIDCIssuer (handlers/eks) emits and that awsgw +// serves /.well-known/openid-configuration + /keys under. The host is the +// awsgw gateway (an IP:port on bare-metal/on-prem, not an `oidc.eks.*` vhost), +// so host trust is established by the IAM OIDC-provider registration — the full +// issuer string is the registry key in verifyOIDCProviderRegistered — and by +// the JWKS signature check, not by a host name pattern. +// +// Returns an error for any URL that does not match this exact 5-segment path +// structure: defensive parsing pins accountID and clusterName at fixed +// positions so a maliciously crafted `iss` claim cannot steer the JWKS lookup +// at a different cluster bucket. func ParseEKSIssuerURL(issuer string) (accountID, clusterName string, err error) { u, err := url.Parse(issuer) if err != nil { @@ -64,14 +84,18 @@ func ParseEKSIssuerURL(issuer string) (accountID, clusterName string, err error) if u.Scheme != "https" { return "", "", errors.New("issuer URL must use https scheme") } - if !strings.HasPrefix(u.Host, "oidc.eks.") { - return "", "", errors.New("issuer host must start with oidc.eks") + if u.Host == "" { + return "", "", errors.New("issuer URL missing host") } segments := strings.Split(strings.Trim(u.Path, "/"), "/") - if len(segments) != 2 || segments[0] == "" || segments[1] == "" { - return "", "", errors.New("issuer path must be /{accountID}/{clusterName}") + if len(segments) != eksIssuerPathSegments || + segments[eksIssuerSegOIDC] != "oidc" || segments[eksIssuerSegEKS] != "eks" { + return "", "", errors.New("issuer path must be /oidc/eks/{region}/{accountID}/{clusterName}") } - return segments[0], segments[1], nil + if slices.Contains(segments, "") { + return "", "", errors.New("issuer path has empty segment") + } + return segments[eksIssuerSegAccountID], segments[eksIssuerSegCluster], nil } // FetchClusterJWKS reads the JWKS document for an EKS cluster from the diff --git a/spinifex/handlers/sts/oidc_jwks_test.go b/spinifex/handlers/sts/oidc_jwks_test.go index d3a465861..c4ec3b5e4 100644 --- a/spinifex/handlers/sts/oidc_jwks_test.go +++ b/spinifex/handlers/sts/oidc_jwks_test.go @@ -12,7 +12,20 @@ import ( ) func TestParseEKSIssuerURL_Valid(t *testing.T) { - accountID, clusterName, err := ParseEKSIssuerURL("https://oidc.eks.au-mel-1.mulga.local/123456789012/my-cluster") + accountID, clusterName, err := ParseEKSIssuerURL("https://10.0.0.1:9999/oidc/eks/au-mel-1/123456789012/my-cluster") + require.NoError(t, err) + assert.Equal(t, "123456789012", accountID) + assert.Equal(t, "my-cluster", clusterName) +} + +// Cross-component contract: a token whose iss is built by ClusterOIDCIssuer +// must parse cleanly via ParseEKSIssuerURL and resolve to the same +// (accountID, clusterName). This is the collision that bead 165.11 fixes. +func TestParseEKSIssuerURL_RoundTripsClusterOIDCIssuer(t *testing.T) { + issuer, err := handlers_eks.ClusterOIDCIssuer("https://10.0.0.1:9999", "au-mel-1", "123456789012", "my-cluster") + require.NoError(t, err) + + accountID, clusterName, err := ParseEKSIssuerURL(issuer) require.NoError(t, err) assert.Equal(t, "123456789012", accountID) assert.Equal(t, "my-cluster", clusterName) @@ -23,13 +36,15 @@ func TestParseEKSIssuerURL_Invalid(t *testing.T) { name string issuer string }{ - {"http scheme", "http://oidc.eks.au-mel-1.mulga.local/123456789012/c1"}, - {"wrong host prefix", "https://attacker.example.com/123456789012/c1"}, - {"single segment", "https://oidc.eks.au-mel-1.mulga.local/123456789012"}, - {"three segments", "https://oidc.eks.au-mel-1.mulga.local/123/c/extra"}, - {"empty cluster segment", "https://oidc.eks.au-mel-1.mulga.local/123456789012/"}, - {"empty account segment", "https://oidc.eks.au-mel-1.mulga.local//c1"}, - {"empty path", "https://oidc.eks.au-mel-1.mulga.local"}, + {"http scheme", "http://10.0.0.1:9999/oidc/eks/au-mel-1/123456789012/c1"}, + {"missing host", "https:///oidc/eks/au-mel-1/123456789012/c1"}, + {"wrong path prefix", "https://10.0.0.1:9999/foo/eks/au-mel-1/123456789012/c1"}, + {"too few segments", "https://10.0.0.1:9999/oidc/eks/au-mel-1/123456789012"}, + {"too many segments", "https://10.0.0.1:9999/oidc/eks/au-mel-1/123456789012/c1/extra"}, + {"empty account segment", "https://10.0.0.1:9999/oidc/eks/au-mel-1//c1"}, + {"empty cluster segment", "https://10.0.0.1:9999/oidc/eks/au-mel-1/123456789012/"}, + {"empty path", "https://10.0.0.1:9999"}, + {"legacy aws two-segment shape", "https://oidc.eks.au-mel-1.mulga.local/123456789012/c1"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/spinifex/handlers/sts/presigned_url_verify.go b/spinifex/handlers/sts/presigned_url_verify.go index 1414149b3..c605c58f4 100644 --- a/spinifex/handlers/sts/presigned_url_verify.go +++ b/spinifex/handlers/sts/presigned_url_verify.go @@ -143,7 +143,11 @@ func (s *STSServiceImpl) VerifyPresignedGetCallerIdentity(presignedURL, expected canonicalQuery, canonicalHeaders, signedHeadersList, - "UNSIGNED-PAYLOAD", + // botocore / aws-sdk presign `sts:GetCallerIdentity` (a non-S3, empty-body + // request) with the SHA256 of the empty string as the payload hash. + // "UNSIGNED-PAYLOAD" is an S3-only convention and would mismatch every + // real `aws eks get-token`. + emptyStringSHA256, }, "\n") stringToSign := strings.Join([]string{ @@ -360,6 +364,10 @@ func hexSHA256(s string) string { return hex.EncodeToString(sum[:]) } +// emptyStringSHA256 is the SigV4 payload hash for an empty body — the value +// botocore/aws-sdk use when presigning non-S3 requests like GetCallerIdentity. +var emptyStringSHA256 = hexSHA256("") + func hmacSHA256(key []byte, data string) []byte { mac := hmac.New(sha256.New, key) mac.Write([]byte(data)) diff --git a/spinifex/handlers/sts/presigned_url_verify_test.go b/spinifex/handlers/sts/presigned_url_verify_test.go index ed0b5af05..e7c05b7dd 100644 --- a/spinifex/handlers/sts/presigned_url_verify_test.go +++ b/spinifex/handlers/sts/presigned_url_verify_test.go @@ -2,6 +2,7 @@ package handlers_sts import ( "encoding/hex" + "net/http" "net/url" "sort" "strconv" @@ -10,6 +11,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/aws/aws-sdk-go/service/iam" "github.com/mulgadc/spinifex/spinifex/awserrors" "github.com/stretchr/testify/assert" @@ -52,7 +55,7 @@ func presignTestURL(t *testing.T, accessKeyID, secret, clusterName string, signe canonHeaders := "host:" + host + "\n" + "x-k8s-aws-id:" + clusterName + "\n" signedHeadersList := "host;x-k8s-aws-id" canonRequest := strings.Join([]string{ - "GET", "/", canonQ, canonHeaders, signedHeadersList, "UNSIGNED-PAYLOAD", + "GET", "/", canonQ, canonHeaders, signedHeadersList, emptyStringSHA256, }, "\n") stringToSign := strings.Join([]string{ @@ -204,7 +207,7 @@ func TestVerifyPresignedGetCallerIdentity_MissingXK8sAwsIdInSignedHeaders(t *tes canonQ := canonicalQueryStringForTest(q) canonHeaders := "host:" + testPresignedHost + "\n" canonReq := strings.Join([]string{ - "GET", "/", canonQ, canonHeaders, "host", "UNSIGNED-PAYLOAD", + "GET", "/", canonQ, canonHeaders, "host", emptyStringSHA256, }, "\n") stringToSign := strings.Join([]string{ "AWS4-HMAC-SHA256", amzDate, credentialScope, hexSHA256(canonReq), @@ -383,3 +386,38 @@ func TestDeriveSigningKey_KnownVector(t *testing.T) { const wantHex = "f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d" assert.Equal(t, wantHex, hex.EncodeToString(key)) } + +// TestVerifyPresignedGetCallerIdentity_RealSDKPresign signs the URL with the +// actual aws-sdk-go v4 presigner — the same canonicalisation `aws eks +// get-token` uses — rather than the hand-rolled presignTestURL. This is the +// faithful end-to-end guard: it caught the payload-hash bug (verify used the +// S3-only "UNSIGNED-PAYLOAD" instead of the empty-string SHA256 that botocore +// signs non-S3 requests with), which every hand-rolled test masked by using +// the same wrong constant. +func TestVerifyPresignedGetCallerIdentity_RealSDKPresign(t *testing.T) { + svc, _ := newTestSetup(t) + akid, secret := seedAccessKey(t, svc, testCallerAccountID, "carol") + + signedAt := time.Now().UTC().Truncate(time.Second) + withFrozenTime(t, signedAt) + + const cluster = "prod-cluster" + req, err := http.NewRequest(http.MethodGet, + "https://"+testPresignedHost+"/?Action=GetCallerIdentity&Version=2011-06-15", nil) + require.NoError(t, err) + req.Header.Set("X-K8s-Aws-Id", cluster) + + signer := v4.NewSigner(credentials.NewStaticCredentials(akid, secret, "")) + _, err = signer.Presign(req, nil, "sts", testPresignedRegion, 900*time.Second, signedAt) + require.NoError(t, err) + + got, err := svc.VerifyPresignedGetCallerIdentity(req.URL.String(), cluster) + require.NoError(t, err) + require.NotNil(t, got) + assert.Equal(t, cluster, got.XK8sAwsID) + assert.Contains(t, got.ARN, ":user/carol") + + // Anti-replay: the SDK-signed URL must NOT verify against a different cluster. + _, err = svc.VerifyPresignedGetCallerIdentity(req.URL.String(), "other-cluster") + require.Error(t, err) +} diff --git a/spinifex/handlers/sts/service.go b/spinifex/handlers/sts/service.go index 3a65d502f..acc65f8d3 100644 --- a/spinifex/handlers/sts/service.go +++ b/spinifex/handlers/sts/service.go @@ -17,6 +17,11 @@ type STSService interface { // strings to keep the handler unit-testable. AssumeRole(callerAccountID, callerARN, callerIdentity string, input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) + // AssumeRoleForInstance mints role-bound temporary credentials for an EC2 instance. + // It is the in-process IMDS entry point, NOT reachable over HTTPS: the caller is the + // synthesised EC2 service principal (trust must allow Service ec2.amazonaws.com). + AssumeRoleForInstance(accountID, roleARN, instanceID string, durationSeconds int64) (*sts.AssumeRoleOutput, error) + // AssumeRoleWithWebIdentity exchanges an OIDC ID token (typically a // projected K8s ServiceAccount token signed by an EKS cluster's // per-cluster signing key) for short-lived AWS credentials bound to the @@ -29,6 +34,14 @@ type STSService interface { // gateway does not gate it with checkPolicy. GetCallerIdentity(callerAccountID, callerARN, callerUserID string, input *sts.GetCallerIdentityInput) (*sts.GetCallerIdentityOutput, error) + // GetSessionToken exchanges the calling IAM user's long-lived credentials for + // short-lived session credentials bound to the SAME user identity — resolving + // back to arn:aws:iam::A:user/N, unlike AssumeRole. callerPrincipalType and + // callerAccessKeyID let the handler enforce that only a long-lived user + // (never a session) may call it. The gateway resolves these from the SigV4 + // context. + GetSessionToken(callerAccountID, callerUserName, callerPrincipalType, callerAccessKeyID string, input *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) + // VerifyPresignedGetCallerIdentity validates a SigV4-presigned URL for // the sts:GetCallerIdentity action — the token shape produced by `aws // eks get-token` — and resolves the calling principal. The EKS token diff --git a/spinifex/handlers/sts/session_credentials.go b/spinifex/handlers/sts/session_credentials.go index 29914eac9..8023cba09 100644 --- a/spinifex/handlers/sts/session_credentials.go +++ b/spinifex/handlers/sts/session_credentials.go @@ -43,10 +43,22 @@ const ( // persisted; only an HMAC of it is stored, so a bucket read by a process that // lacks the master key cannot recover the token. type SessionCredential struct { - AccessKeyID string `json:"access_key_id"` - SecretEncrypted string `json:"secret_encrypted"` - SessionTokenHMAC string `json:"session_token_hmac"` - AccountID string `json:"account_id"` + AccessKeyID string `json:"access_key_id"` + SecretEncrypted string `json:"secret_encrypted"` + SessionTokenHMAC string `json:"session_token_hmac"` + AccountID string `json:"account_id"` + + // PrincipalType records what kind of identity this session represents: + // "assumed-role" (minted by AssumeRole) or "user" (minted by + // GetSessionToken, resolving back to the calling IAM user). It governs how + // the SigV4 ASIA path rebuilds the caller identity and evaluates policy. + // + // Backward compat: an empty/absent value is treated as "assumed-role". + // Records are write-once and expire within hours (default 12h, max 36h), + // so no bucket-version bump or data migration is needed — the empty-string + // default covers any in-flight records minted before this field existed. + PrincipalType string `json:"principal_type,omitempty"` + AssumedRoleARN string `json:"assumed_role_arn"` UnderlyingRoleARN string `json:"underlying_role_arn"` RoleID string `json:"role_id"` diff --git a/spinifex/handlers/sysinstance/launcher.go b/spinifex/handlers/sysinstance/launcher.go new file mode 100644 index 000000000..7e6cb1f52 --- /dev/null +++ b/spinifex/handlers/sysinstance/launcher.go @@ -0,0 +1,158 @@ +// Package sysinstance holds the boot-agnostic contract for launching +// system-managed VMs. System instances are hidden from customer +// DescribeInstances calls — BootDirect VMs (LB) via system-account ownership, +// BootAMI VMs (EKS control plane, owned by the customer account so their ENI +// can live in the customer VPC) via their ManagedBy tag. Two boot styles are +// supported: +// +// - BootDirect: a direct-boot microVM (bundled vmlinuz+initramfs, per-VM +// config delivered via QEMU fw_cfg blobs; no AMI, volume, or cloud-init). +// Used by the ELBv2 LB agent. +// - BootAMI: a full AMI boot (root volume cloned from an AMI, cloud-init +// user-data + network-config seed, q35 machine). Used by the EKS K3s +// control-plane VM. +// +// The types live here (not in handlers/elbv2) so both ELBv2 and EKS can launch +// system instances without an eks→elbv2 dependency. handlers/elbv2 re-exports +// them as type aliases for source compatibility. +package sysinstance + +import "errors" + +// ErrSystemInstanceNotFound is returned by TerminateSystemInstance when the +// target instance is unknown to the daemon — already terminated, or never +// present on this node. Teardown callers treat it as idempotent success so a +// retried delete (e.g. EKS DeleteCluster after the VM already drained) does not +// wedge forever on a VM that is legitimately gone. +var ErrSystemInstanceNotFound = errors.New("sysinstance: instance not found") + +// BootMode selects how a system instance boots. +type BootMode string + +const ( + // BootDirect is the direct-boot microVM path (fw_cfg-delivered config). + BootDirect BootMode = "direct" + // BootAMI is the AMI + cloud-init path (root volume + seed ISO). + BootAMI BootMode = "ami" +) + +// SystemInstanceLauncher is the subset of daemon functionality needed to launch +// and terminate system-managed VMs. Defined here to avoid a circular import +// between the launching handlers (elbv2, eks) and daemon. +type SystemInstanceLauncher interface { + // LaunchSystemInstance creates and starts a system-managed VM. The VM is + // assigned to the system account and not visible to customers. Returns the + // instance ID and private IP once the VM is running. + LaunchSystemInstance(input *SystemInstanceInput) (*SystemInstanceOutput, error) + + // TerminateSystemInstance stops and cleans up a system-managed VM. + TerminateSystemInstance(instanceID string) error +} + +// SystemInstanceInput describes a system VM to launch. The BootMode field +// selects which subset of fields is consumed: +// +// - BootDirect uses NICs, LBAgentEnv, CACert (fw_cfg blobs); ImageID/UserData +// are ignored. +// - BootAMI uses ImageID and UserData (cloud-init); NICs/LBAgentEnv/CACert are +// ignored. The mgmt NIC is allocated by the daemon and rendered into the +// cloud-init network-config from the VM record. +// +// JSON tags are explicit because this struct crosses the +// system.LaunchInstance.* NATS subject root when a launch is handed off to the +// cluster — see daemon/daemon_system_dispatch.go. +type SystemInstanceInput struct { + // BootMode selects the boot style. Empty defaults to BootDirect for + // backward compatibility with the original ELBv2-only contract. + BootMode BootMode `json:"boot_mode,omitempty"` + + // ManagedBy is the spinifex:managed-by tag value stamped on the instance so + // the UI/customer listings hide it (e.g. "elbv2", "eks"). + ManagedBy string `json:"managed_by,omitempty"` + + InstanceType string `json:"instance_type"` // e.g. "sys.micro" + SubnetID string `json:"subnet_id"` // VPC subnet for networking + + // ENI fields — the VM always uses a pre-created ENI (the primary ENI). + ENIID string `json:"eni_id"` + ENIMac string `json:"eni_mac"` + ENIIP string `json:"eni_ip"` + + // ExtraENIs lists additional pre-created ENIs to attach alongside the + // primary ENI (e.g. multi-subnet ALBs). + ExtraENIs []ExtraENIInput `json:"extra_enis,omitempty"` + + // Scheme is the ALB scheme ("internet-facing" or "internal"). BootDirect + // only; ignored for BootAMI. + Scheme string `json:"scheme,omitempty"` + + // AccountID is the owner account of the primary ENI. Required so the daemon + // can look up and update the ENI record (which is keyed by account). + AccountID string `json:"account_id"` + + // HostfwdPorts forwards additional guest ports from the host via the QEMU + // user-mode dev NIC (dev_networking mode only). + HostfwdPorts []int `json:"hostfwd_ports,omitempty"` + + // NICs defines the network interfaces for a BootDirect microVM. Index 0 is + // the primary VPC ENI, index 1 the mgmt NIC, index 2+ extra ENIs. + NICs []NICConfig `json:"nics,omitempty"` + + // LBAgentEnv is a KEY=value blob written to /etc/conf.d/lb-agent inside a + // BootDirect guest via fw_cfg. + LBAgentEnv string `json:"lb_agent_env,omitempty"` + + // CACert holds PEM CA bytes delivered to a BootDirect guest via fw_cfg. + CACert string `json:"ca_cert,omitempty"` + + // ImageID is the AMI to clone the root volume from (BootAMI only). + ImageID string `json:"image_id,omitempty"` + + // UserData is the raw (un-encoded) cloud-init user-data for a BootAMI guest. + // The launcher base64-encodes it for the RunInstances path. + UserData string `json:"user_data,omitempty"` +} + +// ExtraENIInput describes an additional pre-created ENI to attach to a system +// instance. The primary ENI is still passed via ENIID/ENIMac/ENIIP. +type ExtraENIInput struct { + ENIID string `json:"eni_id"` + ENIMac string `json:"eni_mac"` + ENIIP string `json:"eni_ip"` + SubnetID string `json:"subnet_id"` +} + +// NICConfig describes a single network interface for a BootDirect microVM. +// Index 0 is the primary VPC ENI, index 1 the management NIC, index 2+ extra. +type NICConfig struct { + MAC string `json:"mac"` // e.g. "02:0a:01:23:45:67" + CIDR string `json:"cidr"` // e.g. "10.0.1.5/24" + Gateway string `json:"gateway,omitempty"` // e.g. "10.0.1.1"; empty for mgmt NIC + IsDefault bool `json:"is_default"` // true for exactly one NIC (primary VPC ENI) + RouteDst string `json:"route_dst,omitempty"` // specific host route dst, e.g. "10.20.0.5/32" + RouteVia string `json:"route_via,omitempty"` // next-hop for RouteDst +} + +// RecoveryContext carries the per-VM fields needed to reconstruct a +// SystemInstanceInput during host-reboot recovery without importing the vm +// package. MgmtMAC / MgmtIP are the values the daemon already allocated on a +// prior launch; the service injects them back into NIC[1] rather than +// re-allocating from the mgmt IPAM. +type RecoveryContext struct { + InstanceID string + InstanceType string + ENIMac string + MgmtMAC string + MgmtIP string +} + +// SystemInstanceOutput contains the result of a successful launch. +type SystemInstanceOutput struct { + InstanceID string `json:"instance_id"` // e.g. "i-xxxxx" + PrivateIP string `json:"private_ip"` // VPC private IP + PublicIP string `json:"public_ip,omitempty"` // only for internet-facing scheme + MgmtIP string `json:"mgmt_ip,omitempty"` // br-mgmt NIC IP, reachable from the host + + // HostfwdMap maps guest port → host port for any forwarded ports. + HostfwdMap map[int]int `json:"hostfwd_map,omitempty"` +} diff --git a/spinifex/lbagent/agent.go b/spinifex/lbagent/agent.go index 4fffb401b..648004f43 100644 --- a/spinifex/lbagent/agent.go +++ b/spinifex/lbagent/agent.go @@ -5,7 +5,9 @@ package lbagent import ( "bytes" + "crypto/sha256" "crypto/tls" + "encoding/hex" "encoding/xml" "fmt" "io" @@ -20,8 +22,7 @@ import ( "syscall" "time" - "github.com/aws/aws-sdk-go/aws/credentials" - v4 "github.com/aws/aws-sdk-go/aws/signer/v4" + "github.com/mulgadc/predastore/auth" "github.com/mulgadc/spinifex/internal/tlsconfig" ) @@ -30,10 +31,33 @@ const ( DefaultConfigPath = "/etc/haproxy/haproxy.cfg" DefaultPIDPath = "/run/haproxy.pid" + // CertDir is the fixed directory holding TLS certificate PEM files for + // HTTPS listeners. The daemon renders `ssl crt` paths under this dir; the + // agent writes the delivered PEM files here (0600) before reload and + // rejects any delivered path that escapes it. + CertDir = "/etc/haproxy/certs" + + // Nginx paths for the NLB (L4) data plane. NLBs render an nginx `stream` + // config because HAProxy cannot load-balance UDP; ALBs keep using the + // HAProxy paths above. NginxCertDir holds TLS PEM files for TLS listeners + // (referenced by both ssl_certificate and ssl_certificate_key). + NginxConfigPath = "/etc/nginx/nginx.conf" + NginxPIDPath = "/run/nginx.pid" + NginxCertDir = "/etc/nginx/certs" + // pollInterval is how often the agent sends a heartbeat. pollInterval = 5 * time.Second ) +// Data-plane engines, duplicated from handlers/elbv2 to avoid an import cycle +// (handlers/elbv2 imports this package for path constants). The agent runs +// HAProxy for ALBs and nginx `stream` for NLBs, selected by the GetLBConfig +// response. +const ( + EngineHAProxy = "haproxy" + EngineNginx = "nginx" +) + // Agent manages HAProxy configuration inside an LB VM by polling the gateway. type Agent struct { lbID string @@ -41,18 +65,25 @@ type Agent struct { region string // SigV4 signing region configPath string pidPath string + certDir string // dir for TLS cert PEM files; defaults to CertDir (overridable in tests) socketPath string // HAProxy stats socket - signer *v4.Signer - client *http.Client + accessKey string + secretKey string + client *http.Client localConfigHash string + engine string // data-plane engine of the last applied config + healthTargets []HealthTarget // active probe targets (nginx/NLB only) stopCh chan struct{} - // For testing: override the reload function. - reloadFn func(configPath, pidPath string) error + // For testing: override the reload functions (HAProxy / nginx). + reloadFn func(configPath, pidPath string) error + reloadNginxFn func(configPath, pidPath string) error // For testing: override the stats query function. statsFn func(socketPath string) ([]ServerStatus, error) + // For testing: override the active health prober (nginx/NLB). + probeFn func(targets []HealthTarget) []ServerStatus } // New creates a new LB agent for the given load balancer. @@ -70,9 +101,6 @@ func New(lbID, gatewayURL, accessKey, secretKey, region string) (*Agent, error) return nil, fmt.Errorf("region is required") } - creds := credentials.NewStaticCredentials(accessKey, secretKey, "") - signer := v4.NewSigner(creds) - // Use system CA trust store (CA cert injected via cloud-init ca_certs). client := &http.Client{ Timeout: 10 * time.Second, @@ -87,23 +115,27 @@ func New(lbID, gatewayURL, accessKey, secretKey, region string) (*Agent, error) } return &Agent{ - lbID: lbID, - gatewayURL: strings.TrimRight(gatewayURL, "/"), - region: region, - configPath: DefaultConfigPath, - pidPath: DefaultPIDPath, - socketPath: fmt.Sprintf("/tmp/spinifex-haproxy/lb-%s.sock", lbID), - signer: signer, - client: client, - stopCh: make(chan struct{}), - reloadFn: reloadHAProxy, - statsFn: queryHAProxyStats, + lbID: lbID, + gatewayURL: strings.TrimRight(gatewayURL, "/"), + region: region, + configPath: DefaultConfigPath, + pidPath: DefaultPIDPath, + certDir: CertDir, + socketPath: fmt.Sprintf("/tmp/spinifex-haproxy/lb-%s.sock", lbID), + accessKey: accessKey, + secretKey: secretKey, + client: client, + stopCh: make(chan struct{}), + reloadFn: reloadHAProxy, + reloadNginxFn: reloadNginx, + statsFn: queryHAProxyStats, + probeFn: probeHealthTargets, }, nil } // Start runs the poll loop. It blocks until Stop is called. func (a *Agent) Start() error { - slog.Info("Agent started", "lbId", a.lbID, "gateway", a.gatewayURL) + slog.Info("Agent started", "lbId", a.lbID, "gateway", a.gatewayURL, "region", a.region) // Run first tick immediately. a.tick() @@ -133,14 +165,25 @@ func (a *Agent) Stop() { // tick runs one heartbeat cycle: send health, check config hash, fetch if changed. func (a *Agent) tick() { - servers, err := a.statsFn(a.socketPath) - if err != nil { - slog.Warn("HAProxy stats unavailable", "err", err) + // nginx (NLB) has no per-server stats socket — the agent actively probes + // the delivered health targets instead of reading HAProxy stats. + var servers []ServerStatus + if a.engine == EngineNginx { + servers = a.probeFn(a.healthTargets) + } else { + var statsErr error + servers, statsErr = a.statsFn(a.socketPath) + if statsErr != nil { + slog.Warn("HAProxy stats unavailable", "err", statsErr) + } } resp, err := a.sendHeartbeat(servers) if err != nil { - slog.Error("Heartbeat failed", "err", err) + // Include the dial target so the serial console disambiguates a + // transport failure (never reached AWSGW — "send request: dial ...") + // from an HTTP-level rejection ("gateway returned NNN: ..."). + slog.Error("Heartbeat failed", "err", err, "gateway", a.gatewayURL, "region", a.region) return } @@ -162,11 +205,30 @@ type heartbeatResponse struct { ConfigHash string `xml:"LBAgentHeartbeatResult>ConfigHash"` } +// certFile is one delivered TLS certificate PEM parsed from the GetLBConfig +// response. Path is the absolute destination under CertDir. +type certFile struct { + Path string `xml:"Path"` + PEM string `xml:"PEM"` +} + // configResponse is the parsed XML response from GetLBConfig. type configResponse struct { - XMLName xml.Name `xml:"GetLBConfigResponse"` - ConfigText string `xml:"GetLBConfigResult>ConfigText"` - ConfigHash string `xml:"GetLBConfigResult>ConfigHash"` + XMLName xml.Name `xml:"GetLBConfigResponse"` + ConfigText string `xml:"GetLBConfigResult>ConfigText"` + ConfigHash string `xml:"GetLBConfigResult>ConfigHash"` + Engine string `xml:"GetLBConfigResult>Engine"` + CertFiles []certFile `xml:"GetLBConfigResult>CertFiles>member"` + HealthTargets []HealthTarget `xml:"GetLBConfigResult>HealthTargets>member"` +} + +// enginePaths returns the data-plane file paths and reload function for the +// given engine, defaulting to HAProxy when the gateway omits Engine. +func (a *Agent) enginePaths(engine string) (configPath, pidPath, certDir string, reload func(string, string) error) { + if engine == EngineNginx { + return NginxConfigPath, NginxPIDPath, NginxCertDir, a.reloadNginxFn + } + return a.configPath, a.pidPath, a.certDir, a.reloadFn } // sendHeartbeat sends a heartbeat with health report to the gateway. @@ -216,16 +278,31 @@ func (a *Agent) fetchAndApplyConfig() error { return fmt.Errorf("empty config returned") } - if err := WriteConfig(a.configPath, resp.ConfigText); err != nil { + engine := resp.Engine + if engine == "" { + engine = EngineHAProxy + } + configPath, pidPath, certDir, reload := a.enginePaths(engine) + + // Cert files must land before the config that references them (via + // `ssl crt` / `ssl_certificate`), otherwise the engine fails to + // start/reload on a missing path. + if err := a.writeCertFiles(certDir, resp.CertFiles); err != nil { + return fmt.Errorf("write cert files: %w", err) + } + + if err := WriteConfig(configPath, resp.ConfigText); err != nil { return fmt.Errorf("write config: %w", err) } - if err := a.reloadFn(a.configPath, a.pidPath); err != nil { - return fmt.Errorf("reload haproxy: %w", err) + if err := reload(configPath, pidPath); err != nil { + return fmt.Errorf("reload %s: %w", engine, err) } + a.engine = engine + a.healthTargets = resp.HealthTargets a.localConfigHash = resp.ConfigHash - slog.Info("Config applied", "hash", resp.ConfigHash) + slog.Info("Config applied", "engine", engine, "hash", resp.ConfigHash, "healthTargets", len(resp.HealthTargets)) return nil } @@ -238,8 +315,9 @@ func (a *Agent) signedPost(params url.Values) ([]byte, error) { } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - _, err = a.signer.Sign(req, bytes.NewReader([]byte(body)), "elasticloadbalancing", a.region, time.Now()) - if err != nil { + sum := sha256.Sum256([]byte(body)) + payloadHash := hex.EncodeToString(sum[:]) + if err := auth.SignReq(req, a.accessKey, a.secretKey, payloadHash, "elasticloadbalancing", a.region); err != nil { return nil, fmt.Errorf("sign request: %w", err) } @@ -261,6 +339,34 @@ func (a *Agent) signedPost(params url.Values) ([]byte, error) { return respBody, nil } +// writeCertFiles writes each delivered TLS certificate PEM to its path 0600, +// creating certDir as needed. Every path is constrained to certDir — a +// delivered path that escapes it (traversal, absolute elsewhere) is rejected +// rather than written, so a malformed/hostile response can't drop files across +// the filesystem. +func (a *Agent) writeCertFiles(certDir string, certs []certFile) error { + if len(certs) == 0 { + return nil + } + if err := os.MkdirAll(certDir, 0o750); err != nil { + return fmt.Errorf("create cert dir: %w", err) + } + for _, c := range certs { + clean := filepath.Clean(c.Path) + if clean != filepath.Join(certDir, filepath.Base(clean)) { + return fmt.Errorf("cert path %q escapes %s", c.Path, certDir) + } + if c.PEM == "" { + return fmt.Errorf("cert %q has empty PEM", c.Path) + } + if err := os.WriteFile(clean, []byte(c.PEM), 0o600); err != nil { + return fmt.Errorf("write cert %q: %w", clean, err) + } + slog.Info("Wrote TLS cert", "path", clean, "bytes", len(c.PEM)) + } + return nil +} + // WriteConfig atomically writes an HAProxy config file. // It writes to a temp file first, then renames for atomicity. func WriteConfig(path, content string) error { @@ -306,6 +412,56 @@ func reloadHAProxy(configPath, pidPath string) error { return nil } +// reloadNginx validates then starts or reloads nginx for the NLB data plane. +// `nginx -t` fails closed before any reload, mirroring HAProxy's +// validate-before-bind guarantee (a bad config never replaces a good one). +func reloadNginx(configPath, pidPath string) error { + // On a stripped Alpine microvm initramfs the nginx runtime prefix dirs may + // be absent, so `nginx -t`/start fails closed on a missing pid dir or temp + // path — and the agent never enters nginx mode, never probes, and reports + // empty health (NLB targets stuck at 0/N). Pre-create them, mirroring + // reloadHAProxy's socket-dir pre-create. + for _, dir := range []string{filepath.Dir(pidPath), "/var/lib/nginx", "/var/lib/nginx/tmp", "/var/log/nginx"} { + _ = os.MkdirAll(dir, 0o750) + } + + test := exec.Command("nginx", "-t", "-c", configPath) + if out, err := test.CombinedOutput(); err != nil { + return fmt.Errorf("nginx -t: %s: %w", strings.TrimSpace(string(out)), err) + } + + var cmd *exec.Cmd + if readPID(pidPath) > 0 { + // Master running: signal a graceful reload. + cmd = exec.Command("nginx", "-s", "reload", "-c", configPath) + } else { + // Fresh start (nginx daemonizes; pid path comes from the config). + cmd = exec.Command("nginx", "-c", configPath) + } + + // nginx daemonizes on start, and the rendered config uses `error_log stderr`, + // so the forked master and workers inherit and keep this command's stderr + // open. CombinedOutput pipes stderr and blocks on Wait until EOF — which the + // long-lived daemon never sends — hanging the agent here forever before it + // ever reports health, leaving NLB targets stuck at 0/N. Capture to a temp + // file instead: Wait then blocks only on the short-lived foreground process, + // which exits once it has daemonized. + logFile, err := os.CreateTemp("", "nginx-reload-*.log") + if err != nil { + return fmt.Errorf("create nginx log: %w", err) + } + defer os.Remove(logFile.Name()) + defer logFile.Close() + cmd.Stdout = logFile + cmd.Stderr = logFile + + if err := cmd.Run(); err != nil { + out, _ := os.ReadFile(logFile.Name()) + return fmt.Errorf("nginx: %s: %w", strings.TrimSpace(string(out)), err) + } + return nil +} + // readPID reads the HAProxy PID from the PID file. Returns 0 if unavailable. func readPID(pidPath string) int { data, err := os.ReadFile(pidPath) diff --git a/spinifex/lbagent/agent_certs_test.go b/spinifex/lbagent/agent_certs_test.go new file mode 100644 index 000000000..8c343c16a --- /dev/null +++ b/spinifex/lbagent/agent_certs_test.go @@ -0,0 +1,63 @@ +package lbagent + +import ( + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newCertTestAgent(t *testing.T) *Agent { + t.Helper() + a, err := New("lb-1", "https://gw.local", "AK", "SK", "us-east-1") + require.NoError(t, err) + a.certDir = t.TempDir() + return a +} + +func TestWriteCertFiles_WritesPEM0600(t *testing.T) { + a := newCertTestAgent(t) + path := filepath.Join(a.certDir, "lb-1-lst.pem") + + err := a.writeCertFiles(a.certDir, []certFile{{Path: path, PEM: "LEAF\nKEY\n"}}) + require.NoError(t, err) + + data, err := os.ReadFile(path) + require.NoError(t, err) + assert.Equal(t, "LEAF\nKEY\n", string(data)) + + if runtime.GOOS != "windows" { + fi, err := os.Stat(path) + require.NoError(t, err) + assert.Equal(t, os.FileMode(0o600), fi.Mode().Perm()) + } +} + +func TestWriteCertFiles_Empty(t *testing.T) { + a := newCertTestAgent(t) + assert.NoError(t, a.writeCertFiles(a.certDir, nil)) +} + +func TestWriteCertFiles_RejectsTraversal(t *testing.T) { + a := newCertTestAgent(t) + cases := []string{ + filepath.Join(a.certDir, "../escape.pem"), + "/etc/passwd", + filepath.Join(a.certDir, "sub/dir/x.pem"), + } + for _, p := range cases { + err := a.writeCertFiles(a.certDir, []certFile{{Path: p, PEM: "X"}}) + require.Error(t, err, "path %q must be rejected", p) + assert.Contains(t, err.Error(), "escapes") + } +} + +func TestWriteCertFiles_RejectsEmptyPEM(t *testing.T) { + a := newCertTestAgent(t) + err := a.writeCertFiles(a.certDir, []certFile{{Path: filepath.Join(a.certDir, "x.pem"), PEM: ""}}) + require.Error(t, err) + assert.Contains(t, err.Error(), "empty PEM") +} diff --git a/spinifex/lbagent/agent_test.go b/spinifex/lbagent/agent_test.go index 54540087e..38125975c 100644 --- a/spinifex/lbagent/agent_test.go +++ b/spinifex/lbagent/agent_test.go @@ -1,14 +1,20 @@ package lbagent import ( + "crypto/sha256" + "encoding/hex" "fmt" + "io" "net/http" "net/http/httptest" + "net/url" "os" "path/filepath" "sync/atomic" "testing" "time" + + "github.com/mulgadc/predastore/auth" ) func TestNew(t *testing.T) { @@ -68,6 +74,53 @@ func TestNew_SocketPath(t *testing.T) { } } +// TestSignedPost_ProducesVerifiableSignature confirms the migrated signing path +// (predastore/auth.SignReq over aws-sdk-go-v2) produces a request the gateway's +// SigV4 verifier accepts, including a body-hash that matches X-Amz-Content-Sha256. +func TestSignedPost_ProducesVerifiableSignature(t *testing.T) { + const ( + accessKey = "AKIAIOSFODNN7EXAMPLE" + secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + region = "us-east-1" + ) + + var parsed bool + var verifyErr error + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + sum := sha256.Sum256(body) + req, err := auth.ParseReq(r) + if err != nil { + verifyErr = err + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + parsed = true + verifyErr = req.Verify(secretKey, "elasticloadbalancing", region, + auth.WithBodyHash(hex.EncodeToString(sum[:]))) + fmt.Fprint(w, "ok") + })) + defer srv.Close() + + agent, err := New("lb-sig", srv.URL, accessKey, secretKey, region) + if err != nil { + t.Fatalf("New: %v", err) + } + + if _, err := agent.signedPost(url.Values{ + "Action": {"LBAgentHeartbeat"}, + "LoadBalancerId": {"lb-sig"}, + }); err != nil { + t.Fatalf("signedPost: %v", err) + } + if !parsed { + t.Fatal("gateway did not parse a SigV4 Authorization header") + } + if verifyErr != nil { + t.Fatalf("signed request failed server-side verification: %v", verifyErr) + } +} + // fakeGateway returns a test server that responds to LBAgentHeartbeat and GetLBConfig. func fakeGateway(t *testing.T, configHash, configText, status string) *httptest.Server { t.Helper() @@ -300,6 +353,100 @@ func TestHeartbeat_StatsError(t *testing.T) { agent.tick() } +func TestEnginePaths_Nginx(t *testing.T) { + agent := newTestAgent(t, "http://example.invalid") + var nginxCalled, haproxyCalled bool + agent.reloadNginxFn = func(_, _ string) error { nginxCalled = true; return nil } + agent.reloadFn = func(_, _ string) error { haproxyCalled = true; return nil } + + cfg, pid, certDir, reload := agent.enginePaths(EngineNginx) + if cfg != NginxConfigPath || pid != NginxPIDPath || certDir != NginxCertDir { + t.Errorf("nginx paths = %q,%q,%q want %q,%q,%q", cfg, pid, certDir, NginxConfigPath, NginxPIDPath, NginxCertDir) + } + _ = reload("", "") + if !nginxCalled || haproxyCalled { + t.Errorf("nginx engine routed to wrong reload (nginx=%v haproxy=%v)", nginxCalled, haproxyCalled) + } +} + +func TestEnginePaths_HAProxyDefault(t *testing.T) { + agent := newTestAgent(t, "http://example.invalid") + var nginxCalled, haproxyCalled bool + agent.reloadNginxFn = func(_, _ string) error { nginxCalled = true; return nil } + agent.reloadFn = func(_, _ string) error { haproxyCalled = true; return nil } + + // Both explicit haproxy and an empty engine fall through to HAProxy. + for _, engine := range []string{EngineHAProxy, ""} { + cfg, pid, certDir, reload := agent.enginePaths(engine) + if cfg != agent.configPath || pid != agent.pidPath || certDir != agent.certDir { + t.Errorf("engine %q paths = %q,%q,%q want haproxy defaults", engine, cfg, pid, certDir) + } + _ = reload("", "") + } + if nginxCalled || !haproxyCalled { + t.Errorf("haproxy/empty engine routed to wrong reload (nginx=%v haproxy=%v)", nginxCalled, haproxyCalled) + } +} + +func TestTick_NginxSkipsStats(t *testing.T) { + gw := fakeGateway(t, "h1", "", "active") + defer gw.Close() + + agent := newTestAgent(t, gw.URL) + agent.localConfigHash = "h1" + agent.engine = EngineNginx // stats poll is HAProxy-only + statsCalled := false + agent.statsFn = func(_ string) ([]ServerStatus, error) { + statsCalled = true + return nil, nil + } + + agent.tick() + + if statsCalled { + t.Error("nginx engine must not poll HAProxy stats") + } +} + +func TestTick_NginxProbesHealthTargets(t *testing.T) { + var receivedServer, receivedStatus string + gw := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + if r.FormValue("Action") == "LBAgentHeartbeat" { + receivedServer = r.FormValue("Servers.member.1.Server") + receivedStatus = r.FormValue("Servers.member.1.Status") + fmt.Fprint(w, `activeh1`) + return + } + http.Error(w, "unexpected", http.StatusBadRequest) + })) + defer gw.Close() + + agent := newTestAgent(t, gw.URL) + agent.localConfigHash = "h1" // match to avoid config fetch + agent.engine = EngineNginx + agent.healthTargets = []HealthTarget{{ServerName: "srv_i-web1", Address: "10.0.0.5:80", Protocol: "TCP"}} + statsCalled := false + agent.statsFn = func(_ string) ([]ServerStatus, error) { statsCalled = true; return nil, nil } + probedTargets := 0 + agent.probeFn = func(targets []HealthTarget) []ServerStatus { + probedTargets = len(targets) + return []ServerStatus{{Server: "srv_i-web1", Status: "UP"}} + } + + agent.tick() + + if statsCalled { + t.Error("nginx engine must not poll HAProxy stats") + } + if probedTargets != 1 { + t.Errorf("probeFn saw %d targets, want 1", probedTargets) + } + if receivedServer != "srv_i-web1" || receivedStatus != "UP" { + t.Errorf("heartbeat server/status = %q/%q, want srv_i-web1/UP", receivedServer, receivedStatus) + } +} + func TestHeartbeat_EmptyConfigHash(t *testing.T) { // Gateway returns empty config hash (no config stored yet) gw := fakeGateway(t, "", "", "provisioning") diff --git a/spinifex/lbagent/healthcheck.go b/spinifex/lbagent/healthcheck.go new file mode 100644 index 000000000..94ad0371e --- /dev/null +++ b/spinifex/lbagent/healthcheck.go @@ -0,0 +1,94 @@ +package lbagent + +import ( + "crypto/tls" + "fmt" + "log/slog" + "net" + "net/http" + "strings" + "time" +) + +// HealthTarget is one backend the agent must actively health-check, delivered +// by GetLBConfig for the nginx (NLB) data plane. OSS nginx `stream` has no +// active upstream probing, so the agent probes each target every tick and +// reports the result. ServerName is the daemon-computed name the health checker +// keys on — echoed back verbatim so the agent needs no ELBv2 naming logic. +type HealthTarget struct { + ServerName string `xml:"ServerName"` + Address string `xml:"Address"` // ip:port to probe + Protocol string `xml:"Protocol"` // TCP | HTTP | HTTPS + Path string `xml:"Path"` // HTTP(S) path +} + +// probeTimeout bounds each individual target probe. +const probeTimeout = 3 * time.Second + +// probeHealthTargets actively probes every delivered health target and returns +// one ServerStatus per target (Status "UP"/"DOWN"). The Server field carries +// the daemon-supplied ServerName so the daemon health checker matches it +// directly. An empty target list yields a nil report (the daemon early-returns +// on empty reports, leaving health untouched). +func probeHealthTargets(targets []HealthTarget) []ServerStatus { + if len(targets) == 0 { + return nil + } + out := make([]ServerStatus, 0, len(targets)) + for _, t := range targets { + status := "DOWN" + if probeOne(t) { + status = "UP" + } + out = append(out, ServerStatus{Server: t.ServerName, Status: status}) + } + return out +} + +// probeOne performs a single health check against one target. TCP succeeds on a +// completed dial; HTTP/HTTPS succeeds on a response status < 400 (HTTPS skips +// certificate verification, matching how AWS NLB HTTPS health checks treat the +// target's self-signed cert). +func probeOne(t HealthTarget) bool { + switch strings.ToUpper(t.Protocol) { + case "HTTP", "HTTPS": + return probeHTTP(t) + default: // TCP and anything else falls back to a connect probe + conn, err := net.DialTimeout("tcp", t.Address, probeTimeout) + if err != nil { + slog.Warn("TCP probe failed", "addr", t.Address, "err", err) + return false + } + _ = conn.Close() + return true + } +} + +// probeHTTP issues a GET to the target's health-check path and reports whether +// the response status is below 400. +func probeHTTP(t HealthTarget) bool { + scheme := "http" + client := &http.Client{Timeout: probeTimeout} + if strings.EqualFold(t.Protocol, "HTTPS") { + scheme = "https" + client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // target uses a self-signed cert; NLB HTTPS checks don't verify it + } + } + + path := t.Path + if path == "" { + path = "/" + } else if !strings.HasPrefix(path, "/") { + path = "/" + path + } + + url := fmt.Sprintf("%s://%s%s", scheme, t.Address, path) + resp, err := client.Get(url) //nolint:noctx // short fixed-timeout probe + if err != nil { + slog.Warn("HTTP probe failed", "url", url, "err", err) + return false + } + defer resp.Body.Close() + return resp.StatusCode < 400 +} diff --git a/spinifex/lbagent/healthcheck_test.go b/spinifex/lbagent/healthcheck_test.go new file mode 100644 index 000000000..2a237be06 --- /dev/null +++ b/spinifex/lbagent/healthcheck_test.go @@ -0,0 +1,96 @@ +package lbagent + +import ( + "net" + "net/http" + "net/http/httptest" + "testing" +) + +func TestProbeHealthTargets_Empty(t *testing.T) { + if got := probeHealthTargets(nil); got != nil { + t.Errorf("empty targets = %v, want nil (daemon early-returns on empty report)", got) + } +} + +func TestProbeOne_TCP(t *testing.T) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("listen: %v", err) + } + defer ln.Close() + go func() { + for { + c, err := ln.Accept() + if err != nil { + return + } + c.Close() + } + }() + + up := HealthTarget{ServerName: "srv_i-up", Address: ln.Addr().String(), Protocol: "TCP"} + if !probeOne(up) { + t.Error("open TCP port probed DOWN, want UP") + } + + // Closed port: pick the listener's port after closing it. + addr := ln.Addr().String() + ln.Close() + down := HealthTarget{ServerName: "srv_i-down", Address: addr, Protocol: "TCP"} + if probeOne(down) { + t.Error("closed TCP port probed UP, want DOWN") + } +} + +func TestProbeOne_HTTP(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/healthz" { + w.WriteHeader(http.StatusOK) + return + } + w.WriteHeader(http.StatusInternalServerError) + })) + defer srv.Close() + + addr := srv.Listener.Addr().String() + up := HealthTarget{ServerName: "srv_i-h", Address: addr, Protocol: "HTTP", Path: "/healthz"} + if !probeOne(up) { + t.Error("200 health path probed DOWN, want UP") + } + + down := HealthTarget{ServerName: "srv_i-h", Address: addr, Protocol: "HTTP", Path: "/bad"} + if probeOne(down) { + t.Error("500 health path probed UP, want DOWN") + } +} + +func TestProbeHealthTargets_ReportsServerName(t *testing.T) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("listen: %v", err) + } + defer ln.Close() + go func() { + for { + c, err := ln.Accept() + if err != nil { + return + } + c.Close() + } + }() + + out := probeHealthTargets([]HealthTarget{ + {ServerName: "srv_i-abc", Address: ln.Addr().String(), Protocol: "TCP"}, + }) + if len(out) != 1 { + t.Fatalf("got %d statuses, want 1", len(out)) + } + if out[0].Server != "srv_i-abc" { + t.Errorf("Server = %q, want %q (daemon health checker keys on it)", out[0].Server, "srv_i-abc") + } + if out[0].Status != "UP" { + t.Errorf("Status = %q, want UP", out[0].Status) + } +} diff --git a/spinifex/network/external/eip.go b/spinifex/network/external/eip.go index 03e0fc8f5..567f1b556 100644 --- a/spinifex/network/external/eip.go +++ b/spinifex/network/external/eip.go @@ -12,7 +12,7 @@ import ( // that adds the flows-barrier so callers see the EIP reachable on return. type EIPManager interface { AttachEIP(ctx context.Context, eip policy.EIPSpec) error - DetachEIP(ctx context.Context, vpcID, logicalIP string) error + DetachEIP(ctx context.Context, vpcID, externalIP, logicalIP string) error } type eipManager struct { @@ -44,6 +44,6 @@ func (m *eipManager) AttachEIP(ctx context.Context, eip policy.EIPSpec) error { return nil } -func (m *eipManager) DetachEIP(ctx context.Context, vpcID, logicalIP string) error { - return m.nat.DeleteEIP(ctx, vpcID, logicalIP) +func (m *eipManager) DetachEIP(ctx context.Context, vpcID, externalIP, logicalIP string) error { + return m.nat.DeleteEIP(ctx, vpcID, externalIP, logicalIP) } diff --git a/spinifex/network/external/eip_test.go b/spinifex/network/external/eip_test.go index a838953be..ba128425f 100644 --- a/spinifex/network/external/eip_test.go +++ b/spinifex/network/external/eip_test.go @@ -41,7 +41,7 @@ func TestEIPManager_AttachAndDetach(t *testing.T) { require.NotNil(t, found) assert.Equal(t, 1, barrierCalls) - require.NoError(t, mgr.DetachEIP(ctx, "vpc-1", "10.0.0.5")) + require.NoError(t, mgr.DetachEIP(ctx, "vpc-1", "1.2.3.4", "10.0.0.5")) for _, n := range m.NATs { assert.NotEqual(t, "10.0.0.5", n.LogicalIP) } @@ -59,5 +59,5 @@ func TestEIPManager_DetachIdempotent(t *testing.T) { require.NoError(t, m.CreateLogicalRouter(ctx, &nbdb.LogicalRouter{Name: topology.VPCRouter("vpc-1")})) nm, _ := policy.NewNATManager(m, policy.NATModeDistributed) mgr, _ := NewEIPManager(nm, nil) - require.NoError(t, mgr.DetachEIP(ctx, "vpc-1", "10.0.0.99")) + require.NoError(t, mgr.DetachEIP(ctx, "vpc-1", "9.9.9.9", "10.0.0.99")) } diff --git a/spinifex/network/external/igw.go b/spinifex/network/external/igw.go index 7c9375ae9..62075025b 100644 --- a/spinifex/network/external/igw.go +++ b/spinifex/network/external/igw.go @@ -47,6 +47,15 @@ type IGWManager interface { EnsureSubnetEgressDrop(ctx context.Context, vpcID, subnetID string, prefix netip.Prefix) error // RemoveSubnetEgressDrop is the inverse. Idempotent. RemoveSubnetEgressDrop(ctx context.Context, vpcID, subnetID string, prefix netip.Prefix) error + // EnsureSystemInstanceEgress wires egress-only internet access for a + // single system instance (e.g. an EKS K3s server VM) sourced from + // instanceIP in subnetID, SNAT'd to externalIP. Installs a /32 reroute + // policy at SystemInstanceEgressPriority (above the drop gate so it works + // even on a drop-gated subnet) plus a plain snat (no DNAT, no inbound). + // Requires AttachIGW to have run first. Idempotent. + EnsureSystemInstanceEgress(ctx context.Context, vpcID, subnetID, instanceIP, externalIP string) error + // RemoveSystemInstanceEgress is the inverse. Idempotent. + RemoveSystemInstanceEgress(ctx context.Context, vpcID, subnetID, instanceIP, externalIP string) error } // IGWManagerConfig is the construction-time bag for igwManager. @@ -289,7 +298,7 @@ func (m *igwManager) RemoveSubnetEgress(ctx context.Context, vpcID, subnetID str if vpcID == "" || subnetID == "" { return errors.New("RemoveSubnetEgress: vpcID and subnetID required") } - return m.routes.DeleteSubnetEgress(ctx, vpcID, subnetID, prefix, m.vpcExcludeCIDRs(ctx, vpcID)) + return m.routes.DeleteSubnetEgress(ctx, vpcID, subnetID, prefix, m.rerouteExcludeCIDRs(ctx, vpcID)) } // EnsureNATGatewaySubnetEgress is the NATGW priority sibling of @@ -306,7 +315,7 @@ func (m *igwManager) RemoveNATGatewaySubnetEgress(ctx context.Context, vpcID, su if vpcID == "" || subnetID == "" { return errors.New("RemoveNATGatewaySubnetEgress: vpcID and subnetID required") } - return m.routes.DeleteSubnetEgress(ctx, vpcID, subnetID, prefix, m.vpcExcludeCIDRs(ctx, vpcID)) + return m.routes.DeleteSubnetEgress(ctx, vpcID, subnetID, prefix, m.rerouteExcludeCIDRs(ctx, vpcID)) } // EnsureSubnetEgressDrop installs a drop policy at SubnetEgressPriorityDrop @@ -352,23 +361,85 @@ func (m *igwManager) ensureSubnetEgressAtPriority(ctx context.Context, vpcID, su Nexthop: nexthop, OutputPort: topology.GatewayRouterPort(vpcID), Priority: priority, - ExcludeCIDRs: m.vpcExcludeCIDRs(ctx, vpcID), + ExcludeCIDRs: m.rerouteExcludeCIDRs(ctx, vpcID), }) } +// EnsureSystemInstanceEgress installs an egress-only path for one system +// instance: a /32 reroute policy out the gateway port plus a plain snat +// (instanceIP -> externalIP). The reroute sits above the subnet drop gate so +// it works even where the instance's subnet is otherwise private; where the +// subnet already has a 1000 reroute it is harmless (same nexthop). The snat is +// not dnat_and_snat, so the instance is never reachable inbound. +func (m *igwManager) EnsureSystemInstanceEgress(ctx context.Context, vpcID, subnetID, instanceIP, externalIP string) error { + if vpcID == "" || subnetID == "" { + return errors.New("EnsureSystemInstanceEgress: vpcID and subnetID required") + } + srcIP, err := netip.ParseAddr(instanceIP) + if err != nil { + return fmt.Errorf("EnsureSystemInstanceEgress: parse instance IP %q: %w", instanceIP, err) + } + if externalIP == "" { + return errors.New("EnsureSystemInstanceEgress: externalIP required") + } + + _, nexthop, _, err := m.resolveGatewayNetwork(ctx, vpcID) + if err != nil { + return fmt.Errorf("resolve gateway network for %s: %w", vpcID, err) + } + if nexthop == "" { + return fmt.Errorf("EnsureSystemInstanceEgress: no gateway nexthop for %s (IGW not attached?)", vpcID) + } + + if err := m.routes.AddSystemInstanceEgress(ctx, vpcID, policy.SystemInstanceEgressSpec{ + SubnetID: subnetID, + SrcIP: srcIP, + Prefix: defaultRoutePrefix, + Nexthop: nexthop, + OutputPort: topology.GatewayRouterPort(vpcID), + ExcludeCIDRs: m.vpcExcludeCIDRs(ctx, vpcID), + }); err != nil { + return fmt.Errorf("add system instance egress reroute: %w", err) + } + if err := m.nat.AddSystemInstanceSNAT(ctx, vpcID, instanceIP+"/32", externalIP); err != nil { + return fmt.Errorf("add system instance egress snat: %w", err) + } + return nil +} + +// RemoveSystemInstanceEgress deletes the policy + snat installed by +// EnsureSystemInstanceEgress. Idempotent. +func (m *igwManager) RemoveSystemInstanceEgress(ctx context.Context, vpcID, subnetID, instanceIP, externalIP string) error { + if vpcID == "" || subnetID == "" { + return errors.New("RemoveSystemInstanceEgress: vpcID and subnetID required") + } + srcIP, err := netip.ParseAddr(instanceIP) + if err != nil { + return fmt.Errorf("RemoveSystemInstanceEgress: parse instance IP %q: %w", instanceIP, err) + } + var firstErr error + if err := m.routes.DeleteSystemInstanceEgress(ctx, vpcID, subnetID, srcIP, defaultRoutePrefix, m.vpcExcludeCIDRs(ctx, vpcID)); err != nil { + firstErr = fmt.Errorf("delete system instance egress reroute: %w", err) + } + if err := m.nat.DeleteSystemInstanceSNAT(ctx, vpcID, instanceIP+"/32"); err != nil && firstErr == nil { + firstErr = fmt.Errorf("delete system instance egress snat: %w", err) + } + return firstErr +} + // linkLocalCIDR / multicastCIDR are appended to drop-policy excludes so -// link-local (DHCP, metadata, 169.254.169.254) and multicast destinations -// are not killed by the per-subnet gate. +// link-local (DHCP and friends — traffic that should never egress the gateway) +// and multicast destinations are not killed by the per-subnet gate. var ( - linkLocalCIDR = netip.MustParsePrefix("169.254.0.0/16") - multicastCIDR = netip.MustParsePrefix("224.0.0.0/4") + linkLocalCIDR = netip.MustParsePrefix("169.254.0.0/16") + multicastCIDR = netip.MustParsePrefix("224.0.0.0/4") + defaultRoutePrefix = netip.MustParsePrefix("0.0.0.0/0") ) // dropExcludeCIDRs returns the exclusion list for a per-subnet drop policy: -// VPC CIDR (if discoverable) plus link-local and multicast. Reroute policies -// only need the VPC CIDR exclusion because their match scope is narrower; the -// drop policy is a default-deny within the subnet's egress so it must spare -// any class the platform legitimately uses outside the VPC. +// VPC CIDR (if discoverable) plus link-local and multicast. The drop policy is +// a default-deny within the subnet's egress so it must spare any class the +// platform legitimately uses outside the VPC. func (m *igwManager) dropExcludeCIDRs(ctx context.Context, vpcID string) []netip.Prefix { excludes := []netip.Prefix{linkLocalCIDR, multicastCIDR} if vpc := m.vpcExcludeCIDRs(ctx, vpcID); len(vpc) > 0 { @@ -377,6 +448,18 @@ func (m *igwManager) dropExcludeCIDRs(ctx context.Context, vpcID string) []netip return excludes } +// rerouteExcludeCIDRs returns the exclusion list for a per-subnet egress +// reroute policy: VPC CIDR (if discoverable) plus link-local. The reroute +// matches 0.0.0.0/0 — the widest possible scope — so it would otherwise divert +// link-local (169.254.0.0/16) out the gateway, where it has no business going. +// Multicast is not excluded here (unlike the drop policy): a reroute only +// diverts traffic that already has somewhere to go, so leaking multicast to the +// gateway is harmless, whereas the drop policy would kill it outright. +func (m *igwManager) rerouteExcludeCIDRs(ctx context.Context, vpcID string) []netip.Prefix { + excludes := m.vpcExcludeCIDRs(ctx, vpcID) + return append(excludes, linkLocalCIDR) +} + // vpcExcludeCIDRs fetches the VPC's primary CIDR from the LR ExternalIDs // label so per-subnet egress reroute policies skip in-VPC destinations. // Returns nil on lookup failure or unset label — caller installs a policy diff --git a/spinifex/network/external/igw_test.go b/spinifex/network/external/igw_test.go index 1a6802ded..1e46e85d9 100644 --- a/spinifex/network/external/igw_test.go +++ b/spinifex/network/external/igw_test.go @@ -375,6 +375,114 @@ func TestRemoveNATGatewaySubnetEgress_PropagatesDeleteError(t *testing.T) { require.Error(t, err) } +// TestEnsureSystemInstanceEgress installs the /32 reroute plus the snat-only +// NAT, and asserts no inbound dnat_and_snat row exists (egress-only). +func TestEnsureSystemInstanceEgress(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedVPCRouter(t, m, "vpc-1", "10.0.0.0/16") + pool := &ExternalPoolConfig{Name: "p", Gateway: "192.168.1.1", PrefixLen: 24} + mgr, _ := newTestIGWManager(t, m, policy.NATModeDistributed, pool, LinkLocalAllocator{}, []string{"chassis-a"}) + + require.NoError(t, mgr.AttachIGW(ctx, IGWSpec{VPCID: "vpc-1", InternetGatewayID: "igw-1"})) + require.NoError(t, mgr.EnsureSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "10.0.4.10", "203.0.113.7")) + + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + require.Len(t, policies, 1) + assert.Equal(t, policy.SystemInstanceEgressPriority, policies[0].Priority) + assert.Contains(t, policies[0].Match, "ip4.src == 10.0.4.10/32") + + var snat, dnat *nbdb.NAT + for _, n := range m.NATs { + switch n.Type { + case "snat": + snat = n + case "dnat_and_snat": + dnat = n + } + } + require.NotNil(t, snat, "egress snat must be installed") + assert.Equal(t, "203.0.113.7", snat.ExternalIP) + assert.Equal(t, "10.0.4.10/32", snat.LogicalIP) + assert.Nil(t, dnat, "egress-only: no inbound dnat_and_snat may be installed") +} + +// TestEnsureSystemInstanceEgress_IsIdempotent re-runs install; row counts stay 1. +func TestEnsureSystemInstanceEgress_IsIdempotent(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedVPCRouter(t, m, "vpc-1", "10.0.0.0/16") + pool := &ExternalPoolConfig{Name: "p", Gateway: "192.168.1.1", PrefixLen: 24} + mgr, _ := newTestIGWManager(t, m, policy.NATModeDistributed, pool, LinkLocalAllocator{}, nil) + + require.NoError(t, mgr.AttachIGW(ctx, IGWSpec{VPCID: "vpc-1", InternetGatewayID: "igw-1"})) + require.NoError(t, mgr.EnsureSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "10.0.4.10", "203.0.113.7")) + require.NoError(t, mgr.EnsureSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "10.0.4.10", "203.0.113.7")) + + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + assert.Len(t, policies, 1) + snatCount := 0 + for _, n := range m.NATs { + if n.Type == "snat" { + snatCount++ + } + } + assert.Equal(t, 1, snatCount) +} + +// TestRemoveSystemInstanceEgress tears down both the reroute and the snat and +// is idempotent on a second call. +func TestRemoveSystemInstanceEgress(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedVPCRouter(t, m, "vpc-1", "10.0.0.0/16") + pool := &ExternalPoolConfig{Name: "p", Gateway: "192.168.1.1", PrefixLen: 24} + mgr, _ := newTestIGWManager(t, m, policy.NATModeDistributed, pool, LinkLocalAllocator{}, nil) + + require.NoError(t, mgr.AttachIGW(ctx, IGWSpec{VPCID: "vpc-1", InternetGatewayID: "igw-1"})) + require.NoError(t, mgr.EnsureSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "10.0.4.10", "203.0.113.7")) + require.NoError(t, mgr.RemoveSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "10.0.4.10", "203.0.113.7")) + + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + assert.Empty(t, policies) + for _, n := range m.NATs { + assert.NotEqual(t, "snat", n.Type, "snat must be removed") + } + + require.NoError(t, mgr.RemoveSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "10.0.4.10", "203.0.113.7")) +} + +// TestSystemInstanceEgress_RejectsBadArgs guards validation for malformed events. +func TestSystemInstanceEgress_RejectsBadArgs(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedVPCRouter(t, m, "vpc-1", "10.0.0.0/16") + pool := &ExternalPoolConfig{Name: "p", Gateway: "192.168.1.1", PrefixLen: 24} + mgr, _ := newTestIGWManager(t, m, policy.NATModeDistributed, pool, LinkLocalAllocator{}, nil) + require.NoError(t, mgr.AttachIGW(ctx, IGWSpec{VPCID: "vpc-1", InternetGatewayID: "igw-1"})) + + require.Error(t, mgr.EnsureSystemInstanceEgress(ctx, "", "subnet-k3s", "10.0.4.10", "203.0.113.7")) + require.Error(t, mgr.EnsureSystemInstanceEgress(ctx, "vpc-1", "", "10.0.4.10", "203.0.113.7")) + require.Error(t, mgr.EnsureSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "not-an-ip", "203.0.113.7")) + require.Error(t, mgr.EnsureSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "10.0.4.10", "")) +} + +// TestEnsureSystemInstanceEgress_RequiresAttachedIGW asserts a clear error when +// no gateway nexthop exists — without it the reroute would be unreachable. +func TestEnsureSystemInstanceEgress_RequiresAttachedIGW(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedVPCRouter(t, m, "vpc-1", "10.0.0.0/16") + pool := &ExternalPoolConfig{Name: "p", PrefixLen: 24} // no Gateway → no centralized nexthop + mgr, _ := newTestIGWManager(t, m, policy.NATModeCentralized, pool, failingAllocator{}, nil) + + err := mgr.EnsureSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", "10.0.4.10", "203.0.113.7") + require.Error(t, err) +} + // TestRemoveSubnetEgress_RejectsEmptyArgs covers the IGW-priority sibling of // the NATGW empty-arg guard. Both vpcID and subnetID are mandatory. func TestRemoveSubnetEgress_RejectsEmptyArgs(t *testing.T) { diff --git a/spinifex/network/external/imds_datapath_contract_test.go b/spinifex/network/external/imds_datapath_contract_test.go new file mode 100644 index 000000000..d126cfdb1 --- /dev/null +++ b/spinifex/network/external/imds_datapath_contract_test.go @@ -0,0 +1,246 @@ +package external + +import ( + "context" + "net/netip" + "sort" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + handlers_imds "github.com/mulgadc/spinifex/spinifex/handlers/imds" + "github.com/mulgadc/spinifex/spinifex/network/ovn/mock" + "github.com/mulgadc/spinifex/spinifex/network/policy" + "github.com/mulgadc/spinifex/spinifex/network/topology" +) + +// IMDS datapath contract — desk-speed oracle. +// +// Every IMDS datapath bug in the v1 grind (the egress-reroute hijack of the +// /32, the multi-hop reply break) lived inside the VPC logical-router pipeline. +// The L2 datapath takes IMDS off the router entirely: the localport that claims +// 169.254.169.254 sits directly on the guest's subnet switch, so the request is +// answered over a single L2 hop and never enters lr_in_*. This test asserts that +// structural property — the localport is on the subnet LS and NO IMDS object +// (LRP, /32 route, dedicated switch) exists on the VPC router — for two VPCs that +// share the *identical* CIDR (the overlapping-CIDR case per-subnet identity is +// built for). +// +// The LR-pipeline simulator below is retained for TestIMDSDatapathContract_OracleHasTeeth, +// which proves a broad lr_in_policy reroute WOULD have hijacked IMDS had it ever +// been routed — documenting exactly why moving IMDS to L2 removes the bug class. + +const imdsContractCIDR = "10.211.0.0/16" + +func TestIMDSDatapathContract_AnsweredAtL2_OverlappingCIDRs(t *testing.T) { + ctx := context.Background() + m := mock.New() + + // Two VPCs on the IDENTICAL CIDR, each with internet egress (the broad + // 0.0.0.0/0 reroute + drop that historically threatened IMDS) and a subnet + // switch carrying the IMDS localport. + vpcs := []struct{ vpcID, subnetID string }{ + {"vpc-aaaa1111", "subnet-aaaa1111"}, + {"vpc-bbbb2222", "subnet-bbbb2222"}, + } + + imdsMgr, _ := newIMDSManager(t, m) + igwMgr, _ := newTestIGWManager(t, m, policy.NATModeDistributed, + &ExternalPoolConfig{Name: "p", Gateway: "192.168.1.1", PrefixLen: 24}, + LinkLocalAllocator{}, []string{"chassis-a"}) + + defaultRoute := netip.MustParsePrefix("0.0.0.0/0") + + for _, v := range vpcs { + seedVPCRouter(t, m, v.vpcID, imdsContractCIDR) + seedSubnetSwitch(t, m, v.subnetID) + require.NoError(t, igwMgr.AttachIGW(ctx, IGWSpec{VPCID: v.vpcID, InternetGatewayID: "igw-" + v.vpcID})) + // The broad 0.0.0.0/0 egress reroute + drop — the pipeline stages that + // hijacked / killed the IMDS /32 in v1 before #19 excluded link-local. + require.NoError(t, igwMgr.EnsureSubnetEgress(ctx, v.vpcID, v.subnetID, defaultRoute)) + require.NoError(t, igwMgr.EnsureSubnetEgressDrop(ctx, v.vpcID, v.subnetID, defaultRoute)) + // The IMDS localport on the subnet switch. + _, err := imdsMgr.EnsureForSubnet(ctx, v.subnetID, v.vpcID, netip.MustParsePrefix(imdsContractCIDR)) + require.NoError(t, err) + } + + for _, v := range vpcs { + assertIMDSAnsweredAtL2(t, m, v.vpcID, v.subnetID) + } +} + +// TestIMDSDatapathContract_OracleHasTeeth proves the LR-pipeline simulator +// actually detects the regression it documents: a 0.0.0.0/0 egress reroute that +// does NOT exclude link-local (the pre-#19 bug) must be reported as diverting +// IMDS. Under L2 this can no longer reach IMDS — but the simulator keeps the +// historical hazard legible and the oracle non-vacuous. +func TestIMDSDatapathContract_OracleHasTeeth(t *testing.T) { + ctx := context.Background() + m := mock.New() + const vpcID, subnetID = "vpc-cccc3333", "subnet-cccc3333" + seedVPCRouter(t, m, vpcID, imdsContractCIDR) + + rm := policy.NewRouteManager(m) + gw := "169.254.0.2" + // Reroute excluding ONLY the VPC CIDR — link-local omitted, reproducing the + // bug #19 fixed. IMDS (169.254.169.254) is in 0.0.0.0/0 and not in the VPC + // CIDR, so this policy would hijack it had IMDS ever been routed. + require.NoError(t, rm.AddSubnetEgress(ctx, vpcID, policy.SubnetEgressSpec{ + SubnetID: subnetID, + Prefix: netip.MustParsePrefix("0.0.0.0/0"), + Nexthop: gw, + OutputPort: topology.GatewayRouterPort(vpcID), + Priority: policy.SubnetEgressPriorityIGW, + ExcludeCIDRs: []netip.Prefix{netip.MustParsePrefix(imdsContractCIDR)}, + })) + + res := resolveEgress(t, m, vpcID, subnetID, handlers_imds.MetaDataServerIP) + assert.True(t, res.diverted, + "simulator must detect a link-local-unaware reroute as hijacking IMDS; if this fails the oracle is blind") +} + +// assertIMDSAnsweredAtL2 is the contract: 169.254.169.254 is claimed by a +// localport on the guest's subnet switch (so it is answered over one L2 hop), +// and NO IMDS object exists on the VPC router (so the LR pipeline cannot shadow +// it — the entire v1 bug class is structurally impossible). +func assertIMDSAnsweredAtL2(t *testing.T, m *mock.Client, vpcID, subnetID string) { + t.Helper() + spec := IMDSSpecForSubnet(subnetID) + + lsp, err := m.GetLogicalSwitchPort(context.Background(), spec.LSPName) + require.NoErrorf(t, err, "IMDS contract (%s): no localport on the subnet switch %s", vpcID, spec.LSName) + assert.Equalf(t, "localport", lsp.Type, + "IMDS contract (%s): the IMDS port must be a localport so every chassis self-serves it over L2", vpcID) + assert.Emptyf(t, lsp.PortSecurity, + "IMDS contract (%s): the IMDS localport must carry no port_security (the host sources the frames)", vpcID) + assert.Equalf(t, []string{spec.LSPMAC + " " + handlers_imds.MetaDataServerIP}, lsp.Addresses, + "IMDS contract (%s): the localport must claim %s on the subnet switch", vpcID, handlers_imds.MetaDataServerIP) + + // No IMDS object on the router: the request never enters lr_in_*. + assert.Nilf(t, findIMDSRoute(m, handlers_imds.MetaDataServerIP+"/32"), + "IMDS contract (%s): no %s/32 static route may exist — IMDS must not transit the VPC router", + vpcID, handlers_imds.MetaDataServerIP) + for name := range m.RouterPorts { + assert.Falsef(t, strings.HasPrefix(name, "imds-"), + "IMDS contract (%s): found IMDS LRP %q — IMDS must not transit the VPC router", vpcID, name) + } +} + +// --- minimal OVN LR ingress-pipeline model (used by OracleHasTeeth) ----------- + +type egressResult struct { + diverted bool // a reroute lr_in_policy matched + dropped bool // a drop lr_in_policy matched + outputPort string // lr_in_ip_routing winner's output port (empty if a policy already decided) + routePrefix string // lr_in_ip_routing winner's prefix +} + +// resolveEgress simulates the VPC LR ingress pipeline for a packet arriving on +// the subnet LRP destined to dstIP. +func resolveEgress(t *testing.T, m *mock.Client, vpcID, subnetID, dstIP string) egressResult { + t.Helper() + return resolveEgressFrom(t, m, vpcID, topology.SubnetRouterPort(subnetID), dstIP) +} + +// resolveEgressFrom models OVN's two ingress stages in order: +// +// lr_in_policy — Logical_Router_Policy, highest Priority first, first +// applicable wins (reroute => diverted, drop => dropped). +// lr_in_ip_routing — longest-prefix-match over static routes. +func resolveEgressFrom(t *testing.T, m *mock.Client, vpcID, inport, dstIP string) egressResult { + t.Helper() + dst, err := netip.ParseAddr(dstIP) + require.NoError(t, err) + router := topology.VPCRouter(vpcID) + + // Stage 1: lr_in_policy. + policies, err := m.ListLogicalRouterPolicies(context.Background(), router) + require.NoError(t, err) + sort.SliceStable(policies, func(i, j int) bool { return policies[i].Priority > policies[j].Priority }) + for _, p := range policies { + mt, ok := parsePolicyMatch(p.Match) + if !ok || !mt.applies(inport, dst) { + continue + } + switch p.Action { + case "reroute": + return egressResult{diverted: true} + case "drop": + return egressResult{dropped: true} + case "allow": + // falls through to routing + } + break + } + + // Stage 2: lr_in_ip_routing (longest-prefix-match). + lr, err := m.GetLogicalRouter(context.Background(), router) + require.NoError(t, err) + var best egressResult + bestBits := -1 + for _, uuid := range lr.StaticRoutes { + r := m.StaticRoutes[uuid] + if r == nil { + continue + } + pfx, err := netip.ParsePrefix(r.IPPrefix) + if err != nil || !pfx.Contains(dst) { + continue + } + if pfx.Bits() > bestBits { + bestBits = pfx.Bits() + out := "" + if r.OutputPort != nil { + out = *r.OutputPort + } + best = egressResult{outputPort: out, routePrefix: r.IPPrefix} + } + } + return best +} + +type policyMatch struct { + inport string // "" => no inport constraint + dst netip.Prefix + excludes []netip.Prefix +} + +func (mt policyMatch) applies(inport string, dst netip.Addr) bool { + if mt.inport != "" && mt.inport != inport { + return false + } + if mt.dst.IsValid() && !mt.dst.Contains(dst) { + return false + } + for _, ex := range mt.excludes { + if ex.Contains(dst) { + return false + } + } + return true +} + +// parsePolicyMatch parses the match strings produced by subnetEgressMatch: +// +// inport == "rtr-subnet-x" && ip4.dst == 0.0.0.0/0 && ip4.dst != 10.0.0.0/16 && ip4.dst != 169.254.0.0/16 +func parsePolicyMatch(match string) (policyMatch, bool) { + var mt policyMatch + for clause := range strings.SplitSeq(match, "&&") { + clause = strings.TrimSpace(clause) + switch { + case strings.HasPrefix(clause, "inport =="): + mt.inport = strings.Trim(strings.TrimSpace(strings.TrimPrefix(clause, "inport ==")), `"`) + case strings.HasPrefix(clause, "ip4.dst =="): + if p, err := netip.ParsePrefix(strings.TrimSpace(strings.TrimPrefix(clause, "ip4.dst =="))); err == nil { + mt.dst = p + } + case strings.HasPrefix(clause, "ip4.dst !="): + if p, err := netip.ParsePrefix(strings.TrimSpace(strings.TrimPrefix(clause, "ip4.dst !="))); err == nil { + mt.excludes = append(mt.excludes, p) + } + } + } + return mt, true +} diff --git a/spinifex/network/external/imds_topology.go b/spinifex/network/external/imds_topology.go new file mode 100644 index 000000000..7d65af0a1 --- /dev/null +++ b/spinifex/network/external/imds_topology.go @@ -0,0 +1,157 @@ +package external + +import ( + "context" + "errors" + "fmt" + "log/slog" + "net/netip" + "time" + + handlers_imds "github.com/mulgadc/spinifex/spinifex/handlers/imds" + "github.com/mulgadc/spinifex/spinifex/network/host" + "github.com/mulgadc/spinifex/spinifex/network/ovn" + "github.com/mulgadc/spinifex/spinifex/network/ovn/nbdb" + "github.com/mulgadc/spinifex/spinifex/network/topology" + "github.com/mulgadc/spinifex/spinifex/utils" +) + +// IMDSSubnetSpec is the resolved set of OVN + host names for a subnet's IMDS +// localport. Every field is a deterministic function of the subnet ID, so +// callers can rebuild it from the subnet ID alone (BindManager does this per +// bucket entry). +type IMDSSubnetSpec struct { + SubnetID string + ShortSubnetID string // last 8 chars of SubnetID + LSName string // subnet-{subnetID} — the guest's own switch + LSPName string // imds-port-{subnetID} (localport; host veth binds here) + LSPMAC string // 02:.. — HashMAC("imds-"+subnetID) + OVSEndName string // imds-o-{shortSubnetID} + HostEndName string // imds-h-{shortSubnetID} +} + +// IMDSTopologyManager installs and removes the per-subnet localport that claims +// 169.254.169.254 directly on the guest's subnet switch, so metadata is reached +// over a single L2 hop on the guest's own broadcast domain — no router in the +// path. Idempotent. +type IMDSTopologyManager interface { + EnsureForSubnet(ctx context.Context, subnetID, vpcID string, cidr netip.Prefix) (IMDSSubnetSpec, error) + RemoveForSubnet(ctx context.Context, subnetID string) error +} + +type imdsTopologyManager struct { + ovn ovn.Client + store *handlers_imds.VethStore +} + +var _ IMDSTopologyManager = (*imdsTopologyManager)(nil) + +// NewIMDSTopologyManager constructs an IMDSTopologyManager. store is the +// subnet-veth KV bucket the installer publishes records to. +func NewIMDSTopologyManager(client ovn.Client, store *handlers_imds.VethStore) (IMDSTopologyManager, error) { + switch { + case client == nil: + return nil, errors.New("IMDSTopologyManager: OVN client required") + case store == nil: + return nil, errors.New("IMDSTopologyManager: VethStore required") + } + return &imdsTopologyManager{ovn: client, store: store}, nil +} + +// IMDSSpecForSubnet returns the deterministic name/MAC set for a subnet's IMDS +// localport without touching OVN or the bucket. +func IMDSSpecForSubnet(subnetID string) IMDSSubnetSpec { + return IMDSSubnetSpec{ + SubnetID: subnetID, + ShortSubnetID: host.IMDSShortSubnetID(subnetID), + LSName: topology.SubnetSwitch(subnetID), + LSPName: topology.IMDSPort(subnetID), + LSPMAC: utils.HashMAC("imds-" + subnetID), + OVSEndName: host.IMDSOVSPortName(subnetID), + HostEndName: host.IMDSHostVethName(subnetID), + } +} + +// EnsureForSubnet installs the IMDS localport on subnet-{subnetID} and publishes +// the subnet-veth record. Idempotent: a present record short-circuits, and the +// localport is created only when absent so a lost record still converges. The +// subnet switch must already exist (the subnet lifecycle owns it). cidr is +// persisted in the record so the host reply path resolves the guest on-link; +// vpcID is persisted so the IMDS handler can resolve the subnet→VPC mapping the +// eni-by-vpc-ip index needs (the index stays keyed vpcID/ip). +func (m *imdsTopologyManager) EnsureForSubnet(ctx context.Context, subnetID, vpcID string, cidr netip.Prefix) (IMDSSubnetSpec, error) { + if subnetID == "" { + return IMDSSubnetSpec{}, errors.New("EnsureForSubnet: subnetID required") + } + if vpcID == "" { + return IMDSSubnetSpec{}, errors.New("EnsureForSubnet: vpcID required") + } + if !cidr.IsValid() { + return IMDSSubnetSpec{}, errors.New("EnsureForSubnet: cidr required") + } + spec := IMDSSpecForSubnet(subnetID) + + // The bucket record is the publish gate: present means the localport is + // installed and BindManagers have (or will) materialise host state. + rec, err := m.store.Get(subnetID) + if err != nil { + return IMDSSubnetSpec{}, fmt.Errorf("read imds veth record for %s: %w", subnetID, err) + } + if rec != nil { + return spec, nil + } + + extIDs := map[string]string{"spinifex:subnet_id": subnetID, "spinifex:role": "imds"} + + // Host-owned localport on the guest's subnet switch: no port_security (the + // host sources 169.254.169.254 frames, and port_security would make + // ovn-controller drop reply frames from the host veth's MAC) and no SG + // membership (so the guest's SG ACLs never match it). localport binds on + // every chassis. + if _, err := m.ovn.GetLogicalSwitchPort(ctx, spec.LSPName); err != nil { + if cErr := m.ovn.CreateLogicalSwitchPort(ctx, spec.LSName, &nbdb.LogicalSwitchPort{ + Name: spec.LSPName, + Type: "localport", + Addresses: []string{spec.LSPMAC + " " + handlers_imds.MetaDataServerIP}, + ExternalIDs: extIDs, + }); cErr != nil { + return IMDSSubnetSpec{}, fmt.Errorf("create imds localport %s on %s: %w", spec.LSPName, spec.LSName, cErr) + } + } + + if err := m.store.Put(handlers_imds.SubnetVethRecord{ + SubnetID: subnetID, + ShortSubnetID: spec.ShortSubnetID, + VPCID: vpcID, + IMDSPortMAC: spec.LSPMAC, + SubnetCIDR: cidr.String(), + CreatedAt: time.Now().UTC().Format(time.RFC3339), + }); err != nil { + return IMDSSubnetSpec{}, fmt.Errorf("publish imds veth record for %s: %w", subnetID, err) + } + + slog.Info("external: installed IMDS localport", + "subnet_id", subnetID, "subnet_switch", spec.LSName, "imds_port", spec.LSPName) + return spec, nil +} + +// RemoveForSubnet deletes the IMDS localport and the subnet-veth record. The +// subnet switch is left in place (the subnet lifecycle owns it). Idempotent and +// drift-tolerant: a missing localport is logged and skipped so the record delete +// always runs. Must run before the subnet switch goes away. +func (m *imdsTopologyManager) RemoveForSubnet(ctx context.Context, subnetID string) error { + if subnetID == "" { + return errors.New("RemoveForSubnet: subnetID required") + } + spec := IMDSSpecForSubnet(subnetID) + + if err := m.ovn.DeleteLogicalSwitchPort(ctx, spec.LSName, spec.LSPName); err != nil { + slog.Warn("external: delete imds localport failed", "port", spec.LSPName, "err", err) + } + if err := m.store.Delete(subnetID); err != nil { + return fmt.Errorf("delete imds veth record for %s: %w", subnetID, err) + } + + slog.Info("external: removed IMDS localport", "subnet_id", subnetID) + return nil +} diff --git a/spinifex/network/external/imds_topology_test.go b/spinifex/network/external/imds_topology_test.go new file mode 100644 index 000000000..7cfeaa5ad --- /dev/null +++ b/spinifex/network/external/imds_topology_test.go @@ -0,0 +1,194 @@ +package external + +import ( + "context" + "net/netip" + "slices" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + handlers_imds "github.com/mulgadc/spinifex/spinifex/handlers/imds" + "github.com/mulgadc/spinifex/spinifex/network/ovn/mock" + "github.com/mulgadc/spinifex/spinifex/network/ovn/nbdb" + "github.com/mulgadc/spinifex/spinifex/network/topology" + "github.com/mulgadc/spinifex/spinifex/testutil" +) + +const ( + imdsTestSubnetID = "subnet-0a1b2c3d4e5f6789" + imdsTestVPCID = "vpc-0a1b2c3d4e5f6789" + imdsTestCIDR = "10.0.1.0/24" +) + +func newIMDSManager(t *testing.T, m *mock.Client) (IMDSTopologyManager, *handlers_imds.VethStore) { + t.Helper() + _, _, js := testutil.StartTestJetStream(t) + subnetVeth, _, err := handlers_imds.InitBuckets(js, 1) + require.NoError(t, err) + store := handlers_imds.NewVethStore(subnetVeth) + mgr, err := NewIMDSTopologyManager(m, store) + require.NoError(t, err) + return mgr, store +} + +// seedSubnetSwitch creates the subnet logical switch the IMDS localport attaches +// to. The subnet lifecycle owns the switch; EnsureForSubnet only adds the localport. +func seedSubnetSwitch(t *testing.T, m *mock.Client, subnetID string) { + t.Helper() + _, err := m.EnsureLogicalSwitch(context.Background(), &nbdb.LogicalSwitch{ + Name: topology.SubnetSwitch(subnetID), + ExternalIDs: map[string]string{"spinifex:subnet_id": subnetID}, + }) + require.NoError(t, err) +} + +func findIMDSRoute(m *mock.Client, prefix string) *nbdb.LogicalRouterStaticRoute { + for _, r := range m.StaticRoutes { + if r.IPPrefix == prefix { + return r + } + } + return nil +} + +func TestNewIMDSTopologyManager_RejectsMissingDeps(t *testing.T) { + m := mock.New() + _, _, js := testutil.StartTestJetStream(t) + subnetVeth, _, err := handlers_imds.InitBuckets(js, 1) + require.NoError(t, err) + store := handlers_imds.NewVethStore(subnetVeth) + + _, err = NewIMDSTopologyManager(nil, store) + require.Error(t, err) + _, err = NewIMDSTopologyManager(m, nil) + require.Error(t, err) +} + +func TestIMDSEnsureForSubnet_InstallsLocalportOnSubnetLS(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedSubnetSwitch(t, m, imdsTestSubnetID) + mgr, store := newIMDSManager(t, m) + + spec, err := mgr.EnsureForSubnet(ctx, imdsTestSubnetID, imdsTestVPCID, netip.MustParsePrefix(imdsTestCIDR)) + require.NoError(t, err) + + // Resolved names are deterministic functions of the subnet ID. + assert.Equal(t, "subnet-"+imdsTestSubnetID, spec.LSName) + assert.Equal(t, "imds-port-"+imdsTestSubnetID, spec.LSPName) + assert.Equal(t, "4e5f6789", spec.ShortSubnetID) + assert.Equal(t, "imds-o-4e5f6789", spec.OVSEndName) + assert.Equal(t, "imds-h-4e5f6789", spec.HostEndName) + + // The localport is created directly on the guest's subnet switch — no IMDS + // switch, LRP, router-LSP, or static route exists. + assert.Len(t, m.Switches, 1, "only the subnet switch should exist; no dedicated IMDS switch") + assert.Empty(t, m.RouterPorts, "no IMDS LRP under the L2 datapath") + assert.Empty(t, m.StaticRoutes, "no 169.254.169.254/32 static route under the L2 datapath") + assert.Nil(t, findIMDSRoute(m, handlers_imds.MetaDataServerIP+"/32")) + + // Host-owned localport: claims the MetaData IP, no port_security, no SG. + lsp, err := m.GetLogicalSwitchPort(ctx, spec.LSPName) + require.NoError(t, err) + assert.Equal(t, "localport", lsp.Type) + assert.Empty(t, lsp.PortSecurity) + assert.Equal(t, []string{spec.LSPMAC + " " + handlers_imds.MetaDataServerIP}, lsp.Addresses) + + // ...and it is a member of the subnet switch. + ls, err := m.GetLogicalSwitch(ctx, spec.LSName) + require.NoError(t, err) + assert.True(t, slices.Contains(ls.Ports, lsp.UUID), "localport must be a port on the subnet switch") + + // Record published to the bucket, keyed by subnet, carrying the owning VPC + // (the subnet→VPC static lookup the IMDS handler needs) and the CIDR. + rec, err := store.Get(imdsTestSubnetID) + require.NoError(t, err) + require.NotNil(t, rec) + assert.Equal(t, spec.ShortSubnetID, rec.ShortSubnetID) + assert.Equal(t, imdsTestVPCID, rec.VPCID) + assert.Equal(t, spec.LSPMAC, rec.IMDSPortMAC) + assert.Equal(t, imdsTestCIDR, rec.SubnetCIDR) + assert.NotEmpty(t, rec.CreatedAt) +} + +func TestIMDSEnsureForSubnet_RequiresCIDR(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedSubnetSwitch(t, m, imdsTestSubnetID) + mgr, _ := newIMDSManager(t, m) + + _, err := mgr.EnsureForSubnet(ctx, imdsTestSubnetID, imdsTestVPCID, netip.Prefix{}) + require.Error(t, err) +} + +func TestIMDSEnsureForSubnet_RequiresVPCID(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedSubnetSwitch(t, m, imdsTestSubnetID) + mgr, _ := newIMDSManager(t, m) + + // vpcID is persisted in the record for the IMDS handler's subnet→VPC lookup; + // without it the eni-by-vpc-ip index can't be keyed, so install must fail loud. + _, err := mgr.EnsureForSubnet(ctx, imdsTestSubnetID, "", netip.MustParsePrefix(imdsTestCIDR)) + require.Error(t, err) +} + +func TestIMDSEnsureForSubnet_Idempotent(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedSubnetSwitch(t, m, "subnet-1") + mgr, _ := newIMDSManager(t, m) + + for range 3 { + _, err := mgr.EnsureForSubnet(ctx, "subnet-1", "vpc-1", netip.MustParsePrefix(imdsTestCIDR)) + require.NoError(t, err) + } + + // Exactly one switch (the subnet) and exactly one localport. + assert.Len(t, m.Switches, 1) + assert.Len(t, m.Ports, 1) + lsp, err := m.GetLogicalSwitchPort(ctx, "imds-port-subnet-1") + require.NoError(t, err) + assert.Equal(t, "localport", lsp.Type) +} + +func TestIMDSEnsureForSubnet_MissingSwitchErrors(t *testing.T) { + ctx := context.Background() + m := mock.New() + // No subnet switch seeded — the localport create has nowhere to land. + mgr, store := newIMDSManager(t, m) + + _, err := mgr.EnsureForSubnet(ctx, "subnet-missing", "vpc-1", netip.MustParsePrefix(imdsTestCIDR)) + require.Error(t, err) + + // No record published when install fails. + rec, err := store.Get("subnet-missing") + require.NoError(t, err) + assert.Nil(t, rec) +} + +func TestIMDSRemoveForSubnet_TearsDownLocalportKeepsSwitch(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedSubnetSwitch(t, m, "subnet-1") + mgr, store := newIMDSManager(t, m) + + spec, err := mgr.EnsureForSubnet(ctx, "subnet-1", "vpc-1", netip.MustParsePrefix(imdsTestCIDR)) + require.NoError(t, err) + + require.NoError(t, mgr.RemoveForSubnet(ctx, "subnet-1")) + + // Localport and record gone; the subnet switch survives (subnet lifecycle owns it). + _, err = m.GetLogicalSwitchPort(ctx, spec.LSPName) + require.Error(t, err) + _, err = m.GetLogicalSwitch(ctx, spec.LSName) + require.NoError(t, err) + rec, err := store.Get("subnet-1") + require.NoError(t, err) + assert.Nil(t, rec) + + // Second remove is a no-op. + require.NoError(t, mgr.RemoveForSubnet(ctx, "subnet-1")) +} diff --git a/spinifex/network/external/invariants_test.go b/spinifex/network/external/invariants_test.go new file mode 100644 index 000000000..e0f89001e --- /dev/null +++ b/spinifex/network/external/invariants_test.go @@ -0,0 +1,110 @@ +package external + +import ( + "context" + "fmt" + "net/netip" + "slices" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + handlers_imds "github.com/mulgadc/spinifex/spinifex/handlers/imds" + "github.com/mulgadc/spinifex/spinifex/network/ovn/mock" + "github.com/mulgadc/spinifex/spinifex/network/policy" + "github.com/mulgadc/spinifex/spinifex/network/topology" +) + +// IMDS-datapath invariants. These guard the OVN topology +// the IMDS handler trusts as a security + availability boundary. A regression +// in either is a privilege-escalation or single-point-of-failure vector, so the +// failure messages quote the offending design clause verbatim. + +// TestI2_IMDSLocalportOnSubnetSwitchNoPortSecurity asserts the host-owned +// imds-port-{subnetID} LSP is a localport on the guest's subnet switch with no +// port_security. The host — not a guest — sources 169.254.169.254 frames on this +// port, so port_security would be actively harmful (it pins the allowed src MAC +// to the LSP's claimed MAC, and ovn-controller would drop reply frames egressing +// from the host veth's MAC). A regular (non-localport) LSP would bind to a single +// chassis, defeating the per-chassis self-serve design. Living on the subnet +// switch is the L2 datapath itself: the guest reaches metadata in one hop on its +// own broadcast domain, with no router in the path to shadow it. +func TestI2_IMDSLocalportOnSubnetSwitchNoPortSecurity(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedSubnetSwitch(t, m, imdsTestSubnetID) + mgr, _ := newIMDSManager(t, m) + + spec, err := mgr.EnsureForSubnet(ctx, imdsTestSubnetID, imdsTestVPCID, netip.MustParsePrefix(imdsTestCIDR)) + require.NoError(t, err) + + lsp, err := m.GetLogicalSwitchPort(ctx, spec.LSPName) + require.NoError(t, err) + + if lsp.Type != "localport" { + t.Errorf("imds LSP %s Type = %q, want \"localport\": a regular LSP binds to "+ + "one chassis only, forcing IMDS through Geneve and creating a single point "+ + "of failure per subnet", spec.LSPName, lsp.Type) + } + if len(lsp.PortSecurity) != 0 { + t.Errorf("imds LSP %s PortSecurity = %v, want empty: port_security set on the "+ + "host-owned LSP would cause ovn-controller to drop reply frames from the "+ + "host veth's MAC", spec.LSPName, lsp.PortSecurity) + } + assert.Equal(t, []string{spec.LSPMAC + " " + handlers_imds.MetaDataServerIP}, lsp.Addresses, + "the localport must claim %s on the subnet switch", handlers_imds.MetaDataServerIP) + + // It must be a port on the subnet switch — that is what makes the datapath L2. + ls, err := m.GetLogicalSwitch(ctx, spec.LSName) + require.NoError(t, err) + if !slices.Contains(ls.Ports, lsp.UUID) { + t.Errorf("imds localport %s is not a port on subnet switch %s: the L2 datapath "+ + "requires the localport on the guest's own broadcast domain, not a dedicated "+ + "IMDS switch reached through the VPC router", spec.LSPName, spec.LSName) + } +} + +// TestI4_SubnetEgressRerouteMustExcludeLinkLocal asserts every per-subnet +// internet-egress reroute policy excludes 169.254.0.0/16. The reroute matches +// 0.0.0.0/0 — the widest possible scope — so without this exclusion it would +// SNAT-reroute link-local traffic out the WAN. Link-local is host-scoped by +// definition and must never egress to the provider network. Both the IGW- and +// NATGW-priority reroutes carry the gate, so both must spare link-local. A +// drifted exclude list silently leaks 169.254.0.0/16 onto the internet. +func TestI4_SubnetEgressRerouteMustExcludeLinkLocal(t *testing.T) { + linkLocal := fmt.Sprintf("ip4.dst != %s", linkLocalCIDR.String()) + + cases := []struct { + name string + install func(mgr IGWManager, ctx context.Context) error + }{ + {"IGW", func(mgr IGWManager, ctx context.Context) error { + return mgr.EnsureSubnetEgress(ctx, "vpc-1", "subnet-pub", netip.MustParsePrefix("0.0.0.0/0")) + }}, + {"NATGW", func(mgr IGWManager, ctx context.Context) error { + return mgr.EnsureNATGatewaySubnetEgress(ctx, "vpc-1", "subnet-priv", netip.MustParsePrefix("0.0.0.0/0")) + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedVPCRouter(t, m, "vpc-1", "10.0.0.0/16") + pool := &ExternalPoolConfig{Name: "p", Gateway: "192.168.1.1", PrefixLen: 24} + mgr, _ := newTestIGWManager(t, m, policy.NATModeDistributed, pool, LinkLocalAllocator{}, []string{"chassis-a"}) + + require.NoError(t, mgr.AttachIGW(ctx, IGWSpec{VPCID: "vpc-1", InternetGatewayID: "igw-1"})) + require.NoError(t, tc.install(mgr, ctx)) + + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + require.Len(t, policies, 1) + assert.Contains(t, policies[0].Match, linkLocal, + "%s egress reroute %q does not exclude %s — it will SNAT-reroute "+ + "link-local traffic out the WAN; link-local must never egress", + tc.name, policies[0].Match, linkLocalCIDR) + }) + } +} diff --git a/spinifex/network/external/static_pool.go b/spinifex/network/external/static_pool.go index cdcf2a513..bc00d13c8 100644 --- a/spinifex/network/external/static_pool.go +++ b/spinifex/network/external/static_pool.go @@ -52,6 +52,12 @@ type PoolRecord struct { GwLrpRangeStart string `json:"gw_lrp_range_start,omitempty"` GwLrpRangeEnd string `json:"gw_lrp_range_end,omitempty"` Allocated map[string]ExternalIPAllocation `json:"allocated"` + // Cursor is the last address handed out. Allocation resumes just past it + // and wraps at RangeEnd, so a released IP is not reused until the cursor + // cycles the whole range — by which time the host ARP cache for the prior + // owner has decayed and its OVN gateway state is gone. Empty on a fresh + // pool (older records unmarshal with no cursor) → first cycle is lowest-free. + Cursor string `json:"cursor,omitempty"` } // StaticPoolAllocator implements Allocator backed by a NATS JetStream KV @@ -114,6 +120,7 @@ func (a *StaticPoolAllocator) Allocate(_ context.Context, req AllocateRequest) ( ENIId: req.ENIID, InstanceId: req.InstanceID, } + record.Cursor = ip data, err := json.Marshal(record) if err != nil { @@ -274,8 +281,12 @@ func (a *StaticPoolAllocator) getRecord(poolName string) (*PoolRecord, uint64, e } // nextAvailableIP picks the next unallocated address in the pool's range, -// skipping addresses inside [GwLrpRangeStart, GwLrpRangeEnd]. Carved from -// the pre-Q1 handlers/ec2/vpc.nextAvailableExternalIP. +// skipping addresses inside [GwLrpRangeStart, GwLrpRangeEnd]. Allocation +// resumes one past record.Cursor and wraps at RangeEnd so a just-released IP +// is not handed straight back — it only becomes a candidate again after the +// cursor cycles the whole range. An empty cursor (fresh pool, or an older KV +// record) starts at RangeStart, preserving lowest-free order for the first +// cycle. Carved from the pre-Q1 handlers/ec2/vpc.nextAvailableExternalIP. func nextAvailableIP(record *PoolRecord) (string, error) { startIP := net.ParseIP(record.RangeStart).To4() endIP := net.ParseIP(record.RangeEnd).To4() @@ -297,16 +308,28 @@ func nextAvailableIP(record *PoolRecord) (string, error) { startInt := ipv4ToUint32(startIP) endInt := ipv4ToUint32(endIP) + if endInt < startInt { + return "", fmt.Errorf("invalid IP range: %s - %s", record.RangeStart, record.RangeEnd) + } + total := endInt - startInt + 1 - for i := startInt; ; i++ { - if !hasGwLrp || i < gwLrpStart || i > gwLrpEnd { - candidate := uint32ToIPv4(i).String() - if _, taken := record.Allocated[candidate]; !taken { - return candidate, nil - } + // Resume just past the cursor; empty/out-of-range cursor → start of range. + offset := uint32(0) + if c := net.ParseIP(record.Cursor).To4(); c != nil { + ci := ipv4ToUint32(c) + if ci >= startInt && ci <= endInt { + offset = ((ci - startInt) + 1) % total } - if i == endInt { - break + } + + for k := range total { + i := startInt + (offset+k)%total + if hasGwLrp && i >= gwLrpStart && i <= gwLrpEnd { + continue + } + candidate := uint32ToIPv4(i).String() + if _, taken := record.Allocated[candidate]; !taken { + return candidate, nil } } diff --git a/spinifex/network/external/static_pool_test.go b/spinifex/network/external/static_pool_test.go index 2c5196c94..fd85992b8 100644 --- a/spinifex/network/external/static_pool_test.go +++ b/spinifex/network/external/static_pool_test.go @@ -62,20 +62,38 @@ func TestStaticPool_GatewayReserved(t *testing.T) { assert.ErrorContains(t, err, "cannot release gateway IP") } -func TestStaticPool_Release(t *testing.T) { +// TestStaticPool_NoInstantReuseAfterRelease pins the round-robin reuse policy: +// a released IP must not be handed straight back (the recycle that left a +// freshly-reassigned EIP unreachable from the same host — siv-246). It is only +// reusable after the cursor cycles the range. +func TestStaticPool_NoInstantReuseAfterRelease(t *testing.T) { a := newStaticAllocator(t, []ExternalPoolConfig{wanPool()}) ctx := context.Background() ip1, err := a.Allocate(ctx, AllocateRequest{PoolName: "wan", Purpose: "eni-public", ENIID: "eni-1"}) require.NoError(t, err) + require.Equal(t, "192.168.1.151", ip1.String()) _, err = a.Allocate(ctx, AllocateRequest{PoolName: "wan", Purpose: "eni-public", ENIID: "eni-2"}) require.NoError(t, err) + // Release the first IP; the very next allocation must NOT reuse it. require.NoError(t, a.Release(ctx, "wan", ip1)) - ip3, err := a.Allocate(ctx, AllocateRequest{PoolName: "wan", Purpose: "eni-public", ENIID: "eni-3"}) require.NoError(t, err) - assert.Equal(t, ip1, ip3) + assert.NotEqual(t, ip1, ip3, "released IP must not be reused immediately") + assert.Equal(t, "192.168.1.153", ip3.String(), "allocation resumes past the cursor") + + // The released IP becomes reusable only after the cursor wraps the range. + var reused bool + for range 10 { + ip, err := a.Allocate(ctx, AllocateRequest{PoolName: "wan"}) + require.NoError(t, err) + if ip == ip1 { + reused = true + break + } + } + assert.True(t, reused, "released IP reused only after a full cycle") } func TestStaticPool_Exhaustion(t *testing.T) { diff --git a/spinifex/network/host/imds_names.go b/spinifex/network/host/imds_names.go new file mode 100644 index 000000000..fea55b4c5 --- /dev/null +++ b/spinifex/network/host/imds_names.go @@ -0,0 +1,36 @@ +package host + +// IMDS per-subnet veth naming. The names live here (not in network/topology) so +// the IMDS BindManager can derive them without importing topology, which would +// form an import cycle (topology imports handlers/imds for MetaDataServerIP). + +// IMDSShortSubnetID is the last 8 chars of the subnet ID. Linux IFNAMSIZ caps +// interface names at 15 chars, so the IMDS veth names key off this short form. +// Deterministic and stable: every chassis serving IMDS for subnet X derives the +// same names. +func IMDSShortSubnetID(subnetID string) string { + if len(subnetID) <= 8 { + return subnetID + } + return subnetID[len(subnetID)-8:] +} + +// IMDSOVSPortName is the br-int side of the per-subnet IMDS veth pair, bound to the +// imds-port LSP via external_ids:iface-id. The 7-char prefix keeps "imds-o-" + the +// 8-char short subnet ID at exactly IFNAMSIZ-1 (15); longer fails `ip link add`. +func IMDSOVSPortName(subnetID string) string { return "imds-o-" + IMDSShortSubnetID(subnetID) } + +// IMDSHostVethName is the host-end side of the per-subnet IMDS veth pair — the +// device the IMDS listener binds to via SO_BINDTODEVICE. Same 7-char prefix +// budget as IMDSOVSPortName: "imds-h-" + 8-char short subnet ID = 15 chars. It +// lives inside the subnet's netns (see IMDSNetnsName), not the root netns. +func IMDSHostVethName(subnetID string) string { return "imds-h-" + IMDSShortSubnetID(subnetID) } + +// IMDSNetnsName is the per-subnet network namespace the IMDS host-end veth and its +// listener live in. The netns gives the reply path a real L3 next-hop — the host +// end carries 169.254.169.254/30 with the subnet CIDR on-link, so the reply to the +// guest resolves by ARP over the localport — and structurally isolates subnets +// with overlapping CIDRs, since each netns is its own routing domain. Netns names +// are filesystem-scoped, not IFNAMSIZ-bound, so "imds-" + 8-char short subnet ID +// (13 chars) is comfortably within budget. +func IMDSNetnsName(subnetID string) string { return "imds-" + IMDSShortSubnetID(subnetID) } diff --git a/spinifex/network/host/imds_netns_unit_test.go b/spinifex/network/host/imds_netns_unit_test.go new file mode 100644 index 000000000..47fd99cb3 --- /dev/null +++ b/spinifex/network/host/imds_netns_unit_test.go @@ -0,0 +1,97 @@ +package host + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +// repoRoot resolves the spinifex submodule root from this test file's location +// (spinifex/network/host/ → three levels up) so the systemd-unit invariants can +// read build/ artifacts regardless of the test's working directory. +func repoRoot(t *testing.T) string { + t.Helper() + _, file, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("runtime.Caller failed") + } + root := filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..", "..")) + if _, err := os.Stat(filepath.Join(root, "build", "systemd")); err != nil { + t.Fatalf("could not locate build/systemd from %s: %v", root, err) + } + return root +} + +func readUnit(t *testing.T, root, rel string) string { + t.Helper() + b, err := os.ReadFile(filepath.Join(root, rel)) + if err != nil { + t.Fatalf("read %s: %v", rel, err) + } + return string(b) +} + +// TestIMDS_NetnsHostMountNSInvariant pins the host-mount-namespace contract for +// per-VPC IMDS netns (see spinifex-vpcd.service + bead js-133): vpcd creates the +// netns via `ip netns add` and binds the listener via in-process setns, so the +// /run/netns/ bind-mount must live in the HOST mount namespace — host-visible +// for diagnostics and durable across a vpcd restart. Any filesystem-sandbox +// directive forks a PRIVATE mount ns that traps the bind-mount inside vpcd, so the +// host sees only a stale handle that fails setns(2) EINVAL (CI runs 26920490454, +// 26922587471). The earlier MountFlags=shared + spinifex-netns.service shared-mount +// workaround was proven insufficient and removed; vpcd now simply shares the host +// mount ns. Guard that shape: re-adding any mount-ns directive reintroduces the bug. +func TestIMDS_NetnsHostMountNSInvariant(t *testing.T) { + root := repoRoot(t) + + vpcd := readUnit(t, root, "build/systemd/spinifex-vpcd.service") + target := readUnit(t, root, "build/systemd/spinifex.target") + + // No filesystem-sandbox directive may be set on vpcd: each forks a private + // mount namespace and re-traps the netns bind-mount. + for _, banned := range []string{ + "ProtectSystem", "ProtectHome", "PrivateTmp", "ProtectKernelTunables", + "ProtectKernelModules", "ProtectKernelLogs", "ProtectControlGroups", + "ProtectProc", "ReadOnlyPaths", "ReadWritePaths", "MountFlags", + } { + if directiveSet(vpcd, banned) { + t.Errorf("spinifex-vpcd.service must share the HOST mount ns but sets %s= — a private mount ns traps /run/netns/ and host setns(2) fails EINVAL", banned) + } + } + + // vpcd still needs CAP_SYS_ADMIN for setns(CLONE_NEWNET) and the net+mnt + // namespace allowance for the in-netns `ip` plumbing. + mustContain(t, "spinifex-vpcd.service", vpcd, "CAP_SYS_ADMIN") + mustContain(t, "spinifex-vpcd.service", vpcd, "RestrictNamespaces=net mnt") + + // The removed shared-mount workaround must not reappear by reference. + if strings.Contains(vpcd, "spinifex-netns.service") && !strings.Contains(vpcd, "# ") { + t.Errorf("spinifex-vpcd.service must not depend on spinifex-netns.service (removed)") + } + if directiveSet(target, "Wants") && strings.Contains(target, "spinifex-netns.service") { + t.Errorf("spinifex.target must not pull in spinifex-netns.service (removed)") + } + if _, err := os.Stat(filepath.Join(root, "build", "systemd", "spinifex-netns.service")); err == nil { + t.Errorf("build/systemd/spinifex-netns.service must be removed — the shared-mount workaround was proven insufficient") + } +} + +// directiveSet reports whether key is set as an actual unit directive (a line +// `key=...`), ignoring comment prose that merely names it. +func directiveSet(body, key string) bool { + for line := range strings.SplitSeq(body, "\n") { + if strings.HasPrefix(strings.TrimSpace(line), key+"=") { + return true + } + } + return false +} + +func mustContain(t *testing.T, name, body, want string) { + t.Helper() + if !strings.Contains(body, want) { + t.Errorf("%s: missing %q", name, want) + } +} diff --git a/spinifex/network/host/imds_veth.go b/spinifex/network/host/imds_veth.go new file mode 100644 index 000000000..94a8679b5 --- /dev/null +++ b/spinifex/network/host/imds_veth.go @@ -0,0 +1,220 @@ +package host + +import ( + "context" + "fmt" + "log/slog" + "net/netip" + "strings" + + "github.com/mulgadc/spinifex/spinifex/utils" +) + +// imdsNetnsAddr is the host/listener side of the IMDS address, assigned to the +// veth host end inside the per-subnet netns. .254 is MetaDataServerIP; the /30 is +// kept (no longer load-bearing for a next-hop) and the reply path resolves the +// guest via the subnet CIDR added on-link out the same veth. +const imdsNetnsAddr = "169.254.169.254/30" + +// imdsPortLSPName is the OVN logical-switch-port the OVS-side veth binds to via +// external_ids:iface-id. It mirrors topology.IMDSPort but is duplicated here to +// avoid an import cycle (host ← handlers/imds ← topology). +func imdsPortLSPName(subnetID string) string { return "imds-port-" + subnetID } + +// imdsLSPMAC is the logical MAC OVN advertises for 169.254.169.254 (the localport +// address). The netns veth host end MUST carry this exact MAC: OVN resolves .254 +// to it and delivers IMDS request frames with dl_dst set to it, so a kernel-random +// veth MAC makes the netns drop every frame not-for-me and the listener never sees +// a SYN. Mirrors external.IMDSSpecForSubnet's LSPMAC, duplicated here (like +// imdsPortLSPName) to avoid the import cycle. +func imdsLSPMAC(subnetID string) string { return utils.HashMAC("imds-" + subnetID) } + +// EnsureIMDSVeth idempotently creates the per-subnet IMDS veth pair, moves the host +// end into a dedicated netns with 169.254.169.254/30 + the subnet CIDR on-link, and +// attaches the OVS end to br-int so ovn-controller binds the localport here. The +// netns gives the host-served reply a real L3 next-hop (SO_BINDTODEVICE alone cannot +// route the SYN-ACK): the on-link subnet route makes the guest directly reachable, so +// the reply resolves by ARP over the localport on the guest's own L2 switch. The netns +// also isolates overlapping subnet CIDRs into separate routing domains. Returns the +// netns name and host-end name the listener enters and binds. +func EnsureIMDSVeth(ctx context.Context, subnetID string, cidr netip.Prefix) (netnsName, hostEndName string, err error) { + if !cidr.IsValid() { + return "", "", fmt.Errorf("EnsureIMDSVeth: subnet %s requires a valid CIDR", subnetID) + } + ovsEnd := IMDSOVSPortName(subnetID) + hostEnd := IMDSHostVethName(subnetID) + netns := IMDSNetnsName(subnetID) + + // Idempotency probe: the full plumbing (veth pair, netns, host-end address + + // route) exists from a prior boot only when the OVS end is a port on br-int + // AND the netns is enterable. A live OVS port over a stale netns (name + // present but setns(2) fails EINVAL — e.g. a crash between umount and unlink, + // or a tmpfs remount across reboot) leaves the listener permanently + // unbindable, so that case must NOT short-circuit: tear the inconsistent + // plumbing down and rebuild it below. + if imdsOVSPortOnBrInt(ovsEnd) { + if netnsEnterable(netns) { + slog.Debug("IMDS veth already present", "subnet", subnetID, "ovs_end", ovsEnd, "host_end", hostEnd, "netns", netns) + return netns, hostEnd, nil + } + slog.Warn("IMDS netns unenterable behind a live OVS port (stale handle), rebuilding plumbing", + "subnet", subnetID, "netns", netns, "ovs_end", ovsEnd) + if err := removeIMDSPlumbing(ovsEnd, hostEnd, netns); err != nil { + slog.Warn("Failed to tear down stale IMDS plumbing before rebuild", "subnet", subnetID, "err", err) + } + } + + if err := ensureNetns(netns); err != nil { + return "", "", err + } + + if out, err := utils.SudoCommand("ip", "link", "add", ovsEnd, "type", "veth", "peer", "name", hostEnd).CombinedOutput(); err != nil { + if cleanErr := removeIMDSPlumbing(ovsEnd, hostEnd, netns); cleanErr != nil { + slog.Warn("Failed to clean up IMDS plumbing after veth-create failure", "subnet", subnetID, "err", cleanErr) + } + return "", "", fmt.Errorf("create IMDS veth pair %s/%s: %s: %w", ovsEnd, hostEnd, strings.TrimSpace(string(out)), err) + } + + if err := configureIMDSNetns(netns, hostEnd, ovsEnd, imdsLSPMAC(subnetID), cidr); err != nil { + if cleanErr := removeIMDSPlumbing(ovsEnd, hostEnd, netns); cleanErr != nil { + slog.Warn("Failed to clean up IMDS plumbing after netns config failure", "subnet", subnetID, "err", cleanErr) + } + return "", "", err + } + + ifaceID := imdsPortLSPName(subnetID) + if out, err := utils.SudoCommand("ovs-vsctl", "add-port", "br-int", ovsEnd, + "--", "set", "Interface", ovsEnd, "external_ids:iface-id="+ifaceID).CombinedOutput(); err != nil { + if cleanErr := removeIMDSPlumbing(ovsEnd, hostEnd, netns); cleanErr != nil { + slog.Warn("Failed to clean up IMDS plumbing after OVS failure", "subnet", subnetID, "err", cleanErr) + } + return "", "", fmt.Errorf("add IMDS veth %s to br-int: %s: %w", ovsEnd, strings.TrimSpace(string(out)), err) + } + + slog.Info("IMDS veth plumbing complete", "subnet", subnetID, "ovs_end", ovsEnd, "host_end", hostEnd, "netns", netns, "iface_id", ifaceID) + return netns, hostEnd, nil +} + +// configureIMDSNetns moves the host end into the netns, sets its MAC to the +// localport's logical MAC, brings it (and lo) up, and assigns the IMDS address + +// the subnet CIDR on-link so the host-served reply resolves the guest by ARP over +// the localport (a single L2 hop on the guest's own switch — no router next-hop). +// The OVS end stays in the root netns for ovn-controller to bind. The MAC set is +// the request-path contract: OVN delivers frames with dl_dst = the localport MAC, +// so the veth host end must own that MAC or the netns silently drops them. The +// addr/route adds tolerate "File exists" so a re-run after a partial failure +// converges rather than wedging. +func configureIMDSNetns(netns, hostEnd, ovsEnd, hostEndMAC string, cidr netip.Prefix) error { + if out, err := utils.SudoCommand("ip", "link", "set", ovsEnd, "up").CombinedOutput(); err != nil { + return fmt.Errorf("bring up IMDS OVS end %s: %s: %w", ovsEnd, strings.TrimSpace(string(out)), err) + } + if out, err := utils.SudoCommand("ip", "link", "set", hostEnd, "netns", netns).CombinedOutput(); err != nil { + return fmt.Errorf("move IMDS host end %s into netns %s: %s: %w", hostEnd, netns, strings.TrimSpace(string(out)), err) + } + if out, err := utils.SudoCommand("ip", "-n", netns, "link", "set", hostEnd, "address", hostEndMAC).CombinedOutput(); err != nil { + return fmt.Errorf("set IMDS host end %s MAC %s in netns %s: %s: %w", hostEnd, hostEndMAC, netns, strings.TrimSpace(string(out)), err) + } + for _, dev := range []string{"lo", hostEnd} { + if out, err := utils.SudoCommand("ip", "-n", netns, "link", "set", dev, "up").CombinedOutput(); err != nil { + return fmt.Errorf("bring up %s in netns %s: %s: %w", dev, netns, strings.TrimSpace(string(out)), err) + } + } + if err := ipNetnsTolerate(netns, "File exists", "addr", "add", imdsNetnsAddr, "dev", hostEnd); err != nil { + return err + } + return ipNetnsTolerate(netns, "File exists", "route", "add", cidr.String(), "dev", hostEnd) +} + +// ensureNetns creates the netns, treating "already exists" as success — but +// only when the pre-existing handle is actually enterable. A stale +// /run/netns/ bind-mount (name resolves yet setns(2) fails EINVAL) returns +// "File exists" from `ip netns add` while being unusable, which would leave the +// IMDS listener permanently unbindable; that handle is torn down and recreated. +func ensureNetns(netns string) error { + out, err := utils.SudoCommand("ip", "netns", "add", netns).CombinedOutput() + if err == nil { + return nil + } + if !strings.Contains(string(out), "File exists") { + return fmt.Errorf("create IMDS netns %s: %s: %w", netns, strings.TrimSpace(string(out)), err) + } + if netnsEnterable(netns) { + return nil + } + slog.Warn("IMDS netns present but unenterable (stale handle), recreating", "netns", netns) + if out, err := utils.SudoCommand("ip", "netns", "del", netns).CombinedOutput(); err != nil { + if msg := strings.TrimSpace(string(out)); !strings.Contains(msg, "No such file") { + return fmt.Errorf("delete stale IMDS netns %s: %s: %w", netns, msg, err) + } + } + if out, err := utils.SudoCommand("ip", "netns", "add", netns).CombinedOutput(); err != nil { + return fmt.Errorf("recreate IMDS netns %s: %s: %w", netns, strings.TrimSpace(string(out)), err) + } + return nil +} + +// imdsOVSPortOnBrInt reports whether the IMDS OVS-end veth is currently a port +// on br-int — the cheap signal that prior plumbing exists. +func imdsOVSPortOnBrInt(ovsEnd string) bool { + out, err := utils.SudoCommand("ovs-vsctl", "port-to-br", ovsEnd).CombinedOutput() + return err == nil && strings.TrimSpace(string(out)) == "br-int" +} + +// netnsEnterable reports whether netns can be entered via setns(2). A stale +// bind-mount resolves by name but fails EINVAL; `ip -n link show lo` +// performs the setns and surfaces it. A truly-absent netns also reports false, +// which is the correct signal for both callers (recreate / rebuild). +func netnsEnterable(netns string) bool { + return utils.SudoCommand("ip", "-n", netns, "link", "show", "lo").Run() == nil +} + +// ipNetnsTolerate runs `ip -n `, treating output containing +// tolerate as success (for idempotent re-runs of addr/route adds). +func ipNetnsTolerate(netns, tolerate string, args ...string) error { + full := append([]string{"-n", netns}, args...) + if out, err := utils.SudoCommand("ip", full...).CombinedOutput(); err != nil { + if strings.Contains(string(out), tolerate) { + return nil + } + return fmt.Errorf("ip %s: %s: %w", strings.Join(full, " "), strings.TrimSpace(string(out)), err) + } + return nil +} + +// RemoveIMDSVeth detaches the OVS port, deletes the netns (which destroys the +// host end and thus the veth pair), and clears any root-netns remnant. Idempotent: +// safe to call for a subnet that never had a veth on this chassis. +func RemoveIMDSVeth(ctx context.Context, subnetID string) error { + return removeIMDSPlumbing(IMDSOVSPortName(subnetID), IMDSHostVethName(subnetID), IMDSNetnsName(subnetID)) +} + +// removeIMDSPlumbing tears down the OVS port, the netns, and any leftover veth. +// Deleting the netns destroys the host end (and its peer); the trailing link del +// covers the partial state where the host end was never moved into the netns. +func removeIMDSPlumbing(ovsEnd, hostEnd, netns string) error { + if out, err := utils.SudoCommand("ovs-vsctl", "--if-exists", "del-port", ovsEnd).CombinedOutput(); err != nil { + slog.Warn("Failed to remove IMDS veth from OVS", "ovs_end", ovsEnd, "err", err, "out", strings.TrimSpace(string(out))) + } + + if out, err := utils.SudoCommand("ip", "netns", "del", netns).CombinedOutput(); err != nil { + msg := strings.TrimSpace(string(out)) + // "No such file or directory" — already gone. Anything else is logged + // but not fatal: the link del below still runs. + if !strings.Contains(msg, "No such file") { + slog.Warn("Failed to delete IMDS netns", "netns", netns, "err", err, "out", msg) + } + } + + if out, err := utils.SudoCommand("ip", "link", "del", ovsEnd).CombinedOutput(); err != nil { + // "Cannot find device" — the pair was already destroyed with the netns. + msg := strings.TrimSpace(string(out)) + if strings.Contains(msg, "Cannot find device") { + slog.Debug("IMDS veth already absent", "ovs_end", ovsEnd) + return nil + } + return fmt.Errorf("delete IMDS veth %s: %s: %w", ovsEnd, msg, err) + } + + slog.Info("IMDS veth removed", "ovs_end", ovsEnd, "host_end", hostEnd, "netns", netns) + return nil +} diff --git a/spinifex/network/host/imds_veth_test.go b/spinifex/network/host/imds_veth_test.go new file mode 100644 index 000000000..55f846b07 --- /dev/null +++ b/spinifex/network/host/imds_veth_test.go @@ -0,0 +1,354 @@ +package host + +import ( + "context" + "net/netip" + "os/exec" + "slices" + "strings" + "testing" + + "github.com/mulgadc/spinifex/spinifex/utils" +) + +const testSubnetID = "subnet-0123456789abcdef" // short form: "89abcdef" + +var testSubnetCIDR = netip.MustParsePrefix("10.211.0.0/16") + +func recordSudo(t *testing.T, fail func(name string, args []string) *exec.Cmd) *[][]string { + t.Helper() + var calls [][]string + t.Cleanup(utils.SetSudoCommandForTest(func(name string, args ...string) *exec.Cmd { + calls = append(calls, append([]string{name}, args...)) + if fail != nil { + if c := fail(name, args); c != nil { + return c + } + } + return exec.Command("/bin/true") + })) + return &calls +} + +func TestEnsureIMDSVeth_HappyPath(t *testing.T) { + // Probe must report "not a port" so creation proceeds. + calls := recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "port-to-br" { + return exec.Command("/bin/false") + } + return nil + }) + + netns, hostEnd, err := EnsureIMDSVeth(context.Background(), testSubnetID, testSubnetCIDR) + if err != nil { + t.Fatalf("EnsureIMDSVeth: %v", err) + } + if netns != "imds-89abcdef" { + t.Errorf("netns = %q, want imds-89abcdef", netns) + } + if hostEnd != "imds-h-89abcdef" { + t.Errorf("hostEnd = %q, want imds-h-89abcdef", hostEnd) + } + + want := [][]string{ + {"ovs-vsctl", "port-to-br", "imds-o-89abcdef"}, + {"ip", "netns", "add", "imds-89abcdef"}, + {"ip", "link", "add", "imds-o-89abcdef", "type", "veth", "peer", "name", "imds-h-89abcdef"}, + {"ip", "link", "set", "imds-o-89abcdef", "up"}, + {"ip", "link", "set", "imds-h-89abcdef", "netns", "imds-89abcdef"}, + {"ip", "-n", "imds-89abcdef", "link", "set", "imds-h-89abcdef", "address", utils.HashMAC("imds-" + testSubnetID)}, + {"ip", "-n", "imds-89abcdef", "link", "set", "lo", "up"}, + {"ip", "-n", "imds-89abcdef", "link", "set", "imds-h-89abcdef", "up"}, + {"ip", "-n", "imds-89abcdef", "addr", "add", "169.254.169.254/30", "dev", "imds-h-89abcdef"}, + {"ip", "-n", "imds-89abcdef", "route", "add", "10.211.0.0/16", "dev", "imds-h-89abcdef"}, + {"ovs-vsctl", "add-port", "br-int", "imds-o-89abcdef", + "--", "set", "Interface", "imds-o-89abcdef", + "external_ids:iface-id=imds-port-" + testSubnetID}, + } + if len(*calls) != len(want) { + t.Fatalf("got %d calls, want %d: %v", len(*calls), len(want), *calls) + } + for i := range want { + if !slices.Equal((*calls)[i], want[i]) { + t.Errorf("call %d = %v, want %v", i, (*calls)[i], want[i]) + } + } +} + +func TestEnsureIMDSVeth_Idempotent(t *testing.T) { + // Probe reports the OVS end is already on br-int and the netns is enterable + // (default mock = /bin/true) → early return, no mutation. + calls := recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "port-to-br" { + return exec.Command("echo", "br-int") + } + return nil + }) + + netns, hostEnd, err := EnsureIMDSVeth(context.Background(), testSubnetID, testSubnetCIDR) + if err != nil { + t.Fatalf("EnsureIMDSVeth: %v", err) + } + if netns != "imds-89abcdef" { + t.Errorf("netns = %q, want imds-89abcdef", netns) + } + if hostEnd != "imds-h-89abcdef" { + t.Errorf("hostEnd = %q, want imds-h-89abcdef", hostEnd) + } + want := [][]string{ + {"ovs-vsctl", "port-to-br", "imds-o-89abcdef"}, + {"ip", "-n", "imds-89abcdef", "link", "show", "lo"}, + } + if len(*calls) != len(want) { + t.Fatalf("expected only the two probe calls, got %v", *calls) + } + for i := range want { + if !slices.Equal((*calls)[i], want[i]) { + t.Errorf("call %d = %v, want %v", i, (*calls)[i], want[i]) + } + } +} + +// TestEnsureIMDSVeth_StaleNetnsBehindLivePort_Rebuilds guards Fix #2: a live +// OVS port over an unenterable netns (stale /run/netns handle, setns EINVAL) +// must NOT short-circuit. The inconsistent plumbing is torn down and rebuilt so +// the IMDS listener becomes bindable again, rather than wedging forever. +func TestEnsureIMDSVeth_StaleNetnsBehindLivePort_Rebuilds(t *testing.T) { + calls := recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "port-to-br" { + return exec.Command("echo", "br-int") + } + // Netns enterability probe fails with the kernel's EINVAL message. + if name == "ip" && len(args) >= 5 && args[0] == "-n" && args[2] == "link" && args[3] == "show" { + return exec.Command("sh", "-c", `echo 'setting the network namespace "imds-89abcdef" failed: Invalid argument' >&2; exit 1`) + } + return nil + }) + + if _, _, err := EnsureIMDSVeth(context.Background(), testSubnetID, testSubnetCIDR); err != nil { + t.Fatalf("EnsureIMDSVeth: %v", err) + } + + // Teardown (removeIMDSPlumbing) must run before the rebuild, and the rebuild + // must re-add the OVS port — proving we did not short-circuit. + var sawDelPort, sawNetnsDel, sawAddPort bool + for _, c := range *calls { + if c[0] == "ovs-vsctl" && slices.Contains(c, "del-port") { + sawDelPort = true + } + if c[0] == "ip" && len(c) >= 3 && c[1] == "netns" && c[2] == "del" { + sawNetnsDel = true + } + if c[0] == "ovs-vsctl" && slices.Contains(c, "add-port") { + sawAddPort = true + } + } + if !sawDelPort || !sawNetnsDel || !sawAddPort { + t.Errorf("expected stale plumbing torn down and rebuilt (del-port=%v, netns del=%v, add-port=%v); calls=%v", + sawDelPort, sawNetnsDel, sawAddPort, *calls) + } +} + +// TestEnsureIMDSVeth_StaleNetnsRecreated guards Fix #1: when `ip netns add` +// reports "File exists" but the handle is unenterable, ensureNetns deletes the +// stale handle and recreates it, then plumbing proceeds to completion. +func TestEnsureIMDSVeth_StaleNetnsRecreated(t *testing.T) { + netnsAdds := 0 + calls := recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "port-to-br" { + return exec.Command("/bin/false") + } + if name == "ip" && len(args) >= 2 && args[0] == "netns" && args[1] == "add" { + netnsAdds++ + if netnsAdds == 1 { + // First add: name already present as a stale handle. + return exec.Command("sh", "-c", "echo 'Cannot create namespace file: File exists' >&2; exit 1") + } + return nil // recreate succeeds + } + // Enterability probe fails → stale handle detected. + if name == "ip" && len(args) >= 5 && args[0] == "-n" && args[2] == "link" && args[3] == "show" { + return exec.Command("sh", "-c", `echo 'setting the network namespace "imds-89abcdef" failed: Invalid argument' >&2; exit 1`) + } + return nil + }) + + if _, _, err := EnsureIMDSVeth(context.Background(), testSubnetID, testSubnetCIDR); err != nil { + t.Fatalf("EnsureIMDSVeth: %v", err) + } + + // Stale handle deleted then re-added, and the rebuild ran to the add-port. + var sawNetnsDel, sawAddPort bool + for _, c := range *calls { + if c[0] == "ip" && len(c) >= 3 && c[1] == "netns" && c[2] == "del" { + sawNetnsDel = true + } + if c[0] == "ovs-vsctl" && slices.Contains(c, "add-port") { + sawAddPort = true + } + } + if !sawNetnsDel { + t.Errorf("expected stale netns to be deleted before recreate; calls=%v", *calls) + } + if !sawAddPort { + t.Errorf("expected plumbing to complete after recreate; calls=%v", *calls) + } + if netnsAdds != 2 { + t.Errorf("expected two netns add attempts (stale + recreate), got %d", netnsAdds) + } +} + +// TestEnsureIMDSVeth_NetnsExistsTolerated guards idempotent re-runs after a +// partial prior attempt: `ip netns add` reporting "File exists" must not abort. +func TestEnsureIMDSVeth_NetnsExistsTolerated(t *testing.T) { + recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "port-to-br" { + return exec.Command("/bin/false") + } + if name == "ip" && len(args) >= 2 && args[0] == "netns" && args[1] == "add" { + return exec.Command("sh", "-c", "echo 'Cannot create namespace file: File exists' >&2; exit 1") + } + return nil + }) + + if _, _, err := EnsureIMDSVeth(context.Background(), testSubnetID, testSubnetCIDR); err != nil { + t.Fatalf("expected nil for pre-existing netns, got %v", err) + } +} + +func TestEnsureIMDSVeth_AddPortFailureCleansUp(t *testing.T) { + calls := recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "port-to-br" { + return exec.Command("/bin/false") + } + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "add-port" { + return exec.Command("/bin/false") + } + return nil + }) + + _, _, err := EnsureIMDSVeth(context.Background(), testSubnetID, testSubnetCIDR) + if err == nil || !strings.Contains(err.Error(), "add IMDS veth") { + t.Fatalf("expected add-port error, got %v", err) + } + // Cleanup must have run: del-port, netns del, then link del. + var sawDelPort, sawNetnsDel, sawLinkDel bool + for _, c := range *calls { + if c[0] == "ovs-vsctl" && slices.Contains(c, "del-port") { + sawDelPort = true + } + if c[0] == "ip" && len(c) >= 3 && c[1] == "netns" && c[2] == "del" { + sawNetnsDel = true + } + if c[0] == "ip" && len(c) >= 3 && c[1] == "link" && c[2] == "del" { + sawLinkDel = true + } + } + if !sawDelPort || !sawNetnsDel || !sawLinkDel { + t.Errorf("expected cleanup (del-port=%v, netns del=%v, link del=%v); calls=%v", sawDelPort, sawNetnsDel, sawLinkDel, *calls) + } +} + +func TestEnsureIMDSVeth_LinkAddFailureSurfaces(t *testing.T) { + recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "port-to-br" { + return exec.Command("/bin/false") + } + if name == "ip" && len(args) >= 3 && args[0] == "link" && args[1] == "add" { + return exec.Command("/bin/false") + } + return nil + }) + + _, _, err := EnsureIMDSVeth(context.Background(), testSubnetID, testSubnetCIDR) + if err == nil || !strings.Contains(err.Error(), "create IMDS veth pair") { + t.Fatalf("expected create error, got %v", err) + } +} + +// TestEnsureIMDSVeth_AddrFailureCleansUp asserts a failure assigning the IMDS +// address inside the netns surfaces and tears the half-built plumbing down. +func TestEnsureIMDSVeth_AddrFailureCleansUp(t *testing.T) { + calls := recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ovs-vsctl" && len(args) >= 1 && args[0] == "port-to-br" { + return exec.Command("/bin/false") + } + if name == "ip" && len(args) >= 4 && args[0] == "-n" && args[2] == "addr" && args[3] == "add" { + return exec.Command("/bin/false") + } + return nil + }) + + _, _, err := EnsureIMDSVeth(context.Background(), testSubnetID, testSubnetCIDR) + if err == nil || !strings.Contains(err.Error(), "addr add") { + t.Fatalf("expected addr-add error, got %v", err) + } + var sawNetnsDel bool + for _, c := range *calls { + if c[0] == "ip" && len(c) >= 3 && c[1] == "netns" && c[2] == "del" { + sawNetnsDel = true + } + } + if !sawNetnsDel { + t.Errorf("expected netns cleanup after addr failure; calls=%v", *calls) + } +} + +func TestRemoveIMDSVeth(t *testing.T) { + calls := recordSudo(t, nil) + + if err := RemoveIMDSVeth(context.Background(), testSubnetID); err != nil { + t.Fatalf("RemoveIMDSVeth: %v", err) + } + + want := [][]string{ + {"ovs-vsctl", "--if-exists", "del-port", "imds-o-89abcdef"}, + {"ip", "netns", "del", "imds-89abcdef"}, + {"ip", "link", "del", "imds-o-89abcdef"}, + } + if len(*calls) != len(want) { + t.Fatalf("got %d calls, want %d: %v", len(*calls), len(want), *calls) + } + for i := range want { + if !slices.Equal((*calls)[i], want[i]) { + t.Errorf("call %d = %v, want %v", i, (*calls)[i], want[i]) + } + } +} + +func TestRemoveIMDSVeth_MissingDeviceIsNotError(t *testing.T) { + recordSudo(t, func(name string, args []string) *exec.Cmd { + if name == "ip" && len(args) >= 2 && args[0] == "netns" && args[1] == "del" { + return exec.Command("sh", "-c", "echo 'Cannot remove namespace file \"/var/run/netns/imds-89abcdef\": No such file or directory' >&2; exit 1") + } + if name == "ip" && len(args) >= 2 && args[0] == "link" && args[1] == "del" { + // Emit the kernel's "absent device" message on stderr and fail. + return exec.Command("sh", "-c", "echo 'Cannot find device \"imds-o-89abcdef\"' >&2; exit 1") + } + return nil + }) + + if err := RemoveIMDSVeth(context.Background(), testSubnetID); err != nil { + t.Fatalf("expected nil for absent device, got %v", err) + } +} + +// TestIMDSVethNamesWithinIFNAMSIZ guards the load-bearing constraint that both +// veth-end names fit Linux's IFNAMSIZ-1 (15 chars). A longer name makes +// `ip link add` fail with "name too long" for every subnet, silently taking IMDS +// offline cluster-wide — and the SudoCommand mock would never catch it. +func TestIMDSVethNamesWithinIFNAMSIZ(t *testing.T) { + const ifnamsizMax = 15 + for _, subnetID := range []string{ + "subnet-0123456789abcdef", + "subnet-0a1b2c3d4e5f6a7b8", + "subnet-deadbeef", + "short", + } { + for _, name := range []string{IMDSOVSPortName(subnetID), IMDSHostVethName(subnetID)} { + if len(name) > ifnamsizMax { + t.Errorf("veth name %q (%d chars) exceeds IFNAMSIZ-1 (%d) for subnet %q", name, len(name), ifnamsizMax, subnetID) + } + } + } +} diff --git a/spinifex/network/host/neigh.go b/spinifex/network/host/neigh.go new file mode 100644 index 000000000..de403fd42 --- /dev/null +++ b/spinifex/network/host/neigh.go @@ -0,0 +1,63 @@ +package host + +import ( + "context" + "fmt" + "strings" +) + +// FlushNeigh removes the kernel neighbour (ARP) entry for ip on dev so the next +// packet to ip re-resolves L2. Wraps `ip neigh flush to dev `, which +// is idempotent — flushing zero entries still succeeds. +// +// inject-garp-independent ARP refresh for EIP recycle: ovn-controller emits its +// automatic GARP only on LSP binding-chassis migration, and the explicit +// `ovn-appctl inject-garp` refresh (see InjectGARP) is unavailable on OVN builds +// without that appctl. Without either, a same-chassis external_ip rebind leaves +// the host neighbour cache pointed at the prior owner's MAC until the kernel ARP +// timeout (60-300s). Flushing the entry on EIP attach and detach forces a fresh +// resolution against the current owner. +// +// L0 method (ADR-0006 S2) — only network/host/ may shell out to host tools. +// Best-effort: callers must treat errors as warnings, not failures. +func FlushNeigh(ctx context.Context, runner Runner, dev, ip string) error { + if dev == "" || ip == "" { + return fmt.Errorf("host.FlushNeigh: dev and ip required") + } + if runner == nil { + runner = NewExecRunner() + } + out, err := runner.Run(ctx, "ip", "neigh", "flush", "to", ip, "dev", dev) + if err != nil { + return fmt.Errorf("ip neigh flush to %s dev %s: %s: %w", ip, dev, strings.TrimSpace(string(out)), err) + } + return nil +} + +// ReplaceNeigh installs (or overwrites) the kernel neighbour entry mapping ip to +// mac on dev in NUD_REACHABLE state. Wraps `ip neigh replace lladdr +// dev nud reachable`. +// +// This is the deterministic counterpart to FlushNeigh for EIP attach: when an +// external IP is recycled onto a new owner, OVN advertises the new MAC only via +// a GARP it emits on LSP binding-chassis migration — a same-chassis rebind +// emits none, and `ovn-appctl inject-garp` is absent on older OVN builds, so no +// node answers the host's re-ARP. Flushing then waits for an ARP reply that +// never comes; programming the known external_mac directly skips the round-trip, +// and inbound traffic refreshes the entry from there on. +// +// L0 method (ADR-0006 S2) — only network/host/ may shell out to host tools. +// Best-effort: callers must treat errors as warnings, not failures. +func ReplaceNeigh(ctx context.Context, runner Runner, dev, ip, mac string) error { + if dev == "" || ip == "" || mac == "" { + return fmt.Errorf("host.ReplaceNeigh: dev, ip and mac required") + } + if runner == nil { + runner = NewExecRunner() + } + out, err := runner.Run(ctx, "ip", "neigh", "replace", ip, "lladdr", mac, "dev", dev, "nud", "reachable") + if err != nil { + return fmt.Errorf("ip neigh replace %s lladdr %s dev %s: %s: %w", ip, mac, dev, strings.TrimSpace(string(out)), err) + } + return nil +} diff --git a/spinifex/network/host/neigh_test.go b/spinifex/network/host/neigh_test.go new file mode 100644 index 000000000..3309c0402 --- /dev/null +++ b/spinifex/network/host/neigh_test.go @@ -0,0 +1,73 @@ +package host + +import ( + "context" + "errors" + "strings" + "testing" +) + +func TestFlushNeigh_ShellOutShape(t *testing.T) { + r := &recordingRunner{} + if err := FlushNeigh(context.Background(), r, "br-wan", "192.168.0.231"); err != nil { + t.Fatalf("FlushNeigh: %v", err) + } + if len(r.args) != 1 { + t.Fatalf("expected 1 call, got %d", len(r.args)) + } + got := strings.Join(r.args[0], " ") + want := "ip neigh flush to 192.168.0.231 dev br-wan" + if got != want { + t.Fatalf("argv mismatch\n got: %s\nwant: %s", got, want) + } +} + +func TestFlushNeigh_MissingArgs(t *testing.T) { + if err := FlushNeigh(context.Background(), &recordingRunner{}, "", "192.168.0.231"); err == nil { + t.Fatal("expected error for empty dev") + } + if err := FlushNeigh(context.Background(), &recordingRunner{}, "br-wan", ""); err == nil { + t.Fatal("expected error for empty ip") + } +} + +func TestFlushNeigh_PropagatesRunnerError(t *testing.T) { + r := &recordingRunner{out: []byte("Cannot find device \"br-wan\""), err: errors.New("exit 1")} + if err := FlushNeigh(context.Background(), r, "br-wan", "192.168.0.231"); err == nil { + t.Fatal("expected error") + } +} + +func TestReplaceNeigh_ShellOutShape(t *testing.T) { + r := &recordingRunner{} + if err := ReplaceNeigh(context.Background(), r, "br-wan", "192.168.0.231", "02:00:0a:d2:01:04"); err != nil { + t.Fatalf("ReplaceNeigh: %v", err) + } + if len(r.args) != 1 { + t.Fatalf("expected 1 call, got %d", len(r.args)) + } + got := strings.Join(r.args[0], " ") + want := "ip neigh replace 192.168.0.231 lladdr 02:00:0a:d2:01:04 dev br-wan nud reachable" + if got != want { + t.Fatalf("argv mismatch\n got: %s\nwant: %s", got, want) + } +} + +func TestReplaceNeigh_MissingArgs(t *testing.T) { + if err := ReplaceNeigh(context.Background(), &recordingRunner{}, "", "192.168.0.231", "02:00:0a:d2:01:04"); err == nil { + t.Fatal("expected error for empty dev") + } + if err := ReplaceNeigh(context.Background(), &recordingRunner{}, "br-wan", "", "02:00:0a:d2:01:04"); err == nil { + t.Fatal("expected error for empty ip") + } + if err := ReplaceNeigh(context.Background(), &recordingRunner{}, "br-wan", "192.168.0.231", ""); err == nil { + t.Fatal("expected error for empty mac") + } +} + +func TestReplaceNeigh_PropagatesRunnerError(t *testing.T) { + r := &recordingRunner{out: []byte("Cannot find device \"br-wan\""), err: errors.New("exit 1")} + if err := ReplaceNeigh(context.Background(), r, "br-wan", "192.168.0.231", "02:00:0a:d2:01:04"); err == nil { + t.Fatal("expected error") + } +} diff --git a/spinifex/network/policy/nat.go b/spinifex/network/policy/nat.go index dd6d82993..40e8541a5 100644 --- a/spinifex/network/policy/nat.go +++ b/spinifex/network/policy/nat.go @@ -36,8 +36,10 @@ type NATManager interface { // same ExternalIP on any router first (pool reuse across VPCs). AddEIP(ctx context.Context, eip EIPSpec) error - // DeleteEIP removes the rule by LogicalIP; idempotent. - DeleteEIP(ctx context.Context, vpcID, logicalIP string) error + // DeleteEIP removes the rule by LogicalIP and flushes the host ARP entry + // for ExternalIP so a recycled IP is not shadowed by the stale MAC; + // idempotent. + DeleteEIP(ctx context.Context, vpcID, externalIP, logicalIP string) error // AddNATGateway installs the (snat, SubnetCIDR) rule; rejects overlap. AddNATGateway(ctx context.Context, gw NATGWSpec) error @@ -51,6 +53,16 @@ type NATManager interface { // DeleteSNAT removes the IGW default-outbound snat for vpcCIDR. DeleteSNAT(ctx context.Context, vpcID, vpcCIDR string) error + + // AddSystemInstanceSNAT installs an egress-only snat rewriting a single + // system instance's /32 (logicalIP) to externalIP. Unlike AddEIP it is a + // plain snat, not dnat_and_snat, so there is no inbound path to the + // instance. Idempotent; tagged role=system-instance-egress. + AddSystemInstanceSNAT(ctx context.Context, vpcID, logicalIP, externalIP string) error + + // DeleteSystemInstanceSNAT removes the egress-only snat by logicalIP; + // idempotent. + DeleteSystemInstanceSNAT(ctx context.Context, vpcID, logicalIP string) error } // FlowsBarrier blocks until ovn-northd has compiled NB → SB and every @@ -69,6 +81,27 @@ type FlowsBarrier func() error // Best-effort: implementations return errors but callers warn and proceed. type GARPEmitter func(EIPSpec) error +// NeighFlusher invalidates the host neighbour (ARP) entry for externalIP so the +// next dial re-resolves L2. Called on both EIP attach and detach: it is the +// inject-garp-independent path that keeps a recycled external IP reachable on +// OVN builds where the GARPEmitter (ovn-appctl inject-garp) is a no-op, +// preventing the host ARP cache from pointing at the prior owner's MAC until +// the kernel ARP timeout expires (60-300s). +// +// Best-effort: implementations return errors but callers warn and proceed. +type NeighFlusher func(externalIP string) error + +// NeighPrimer installs the host neighbour entry for a distributed EIP directly, +// mapping ExternalIP to its external_mac. Preferred over NeighFlusher on attach +// when the MAC is known (distributed mode): a flush merely re-arms ARP, but on +// OVN builds without inject-garp no node answers the host's re-ARP for a +// same-chassis recycled IP, so the entry would just decay back to FAILED. +// Programming the known MAC makes the recycled IP reachable without an ARP +// round-trip. +// +// Best-effort: implementations return errors but callers warn and proceed. +type NeighPrimer func(eip EIPSpec) error + type Option func(*natManager) // WithFlowsBarrier injects the post-write flow-install barrier so callers @@ -92,11 +125,37 @@ func WithGARPEmitter(g GARPEmitter) Option { } } +// WithNeighFlusher injects the host neighbour-flush hook fired on EIP detach (and +// on attach when the external_mac is unknown). Without it, recycled external IPs +// rely solely on the GARPEmitter, which is a no-op on OVN builds lacking +// inject-garp. +func WithNeighFlusher(f NeighFlusher) Option { + return func(m *natManager) { + if f != nil { + m.neigh = f + } + } +} + +// WithNeighPrimer injects the host neighbour-prime hook fired on EIP attach in +// distributed mode (external_mac known). Preferred over the flusher there: it +// programs the recycled IP's MAC directly instead of waiting on an ARP reply +// that no node sends when inject-garp is unavailable. +func WithNeighPrimer(p NeighPrimer) Option { + return func(m *natManager) { + if p != nil { + m.neighPrime = p + } + } +} + type natManager struct { - ovn ovn.Client - mode NATMode - barrier FlowsBarrier - garp GARPEmitter + ovn ovn.Client + mode NATMode + barrier FlowsBarrier + garp GARPEmitter + neigh NeighFlusher + neighPrime NeighPrimer } var _ NATManager = (*natManager)(nil) @@ -108,10 +167,12 @@ func NewNATManager(client ovn.Client, mode NATMode, opts ...Option) (NATManager, return nil, fmt.Errorf("NAT mode is unknown; resolve from host.Wiring.UplinkMode()") } m := &natManager{ - ovn: client, - mode: mode, - barrier: func() error { return nil }, - garp: func(EIPSpec) error { return nil }, + ovn: client, + mode: mode, + barrier: func() error { return nil }, + garp: func(EIPSpec) error { return nil }, + neigh: func(string) error { return nil }, + neighPrime: func(EIPSpec) error { return nil }, } for _, opt := range opts { opt(m) @@ -170,16 +231,34 @@ func (m *natManager) AddEIP(ctx context.Context, eip EIPSpec) error { if err := m.garp(eip); err != nil { slog.Warn("policy: AddEIP gratuitous-ARP emission failed", "external_ip", eip.ExternalIP, "logical_ip", eip.LogicalIP, "err", err) } + // Distributed mode: the external_mac is known, so program the host + // neighbour entry directly instead of flushing and waiting on an ARP reply + // no node sends when inject-garp is unavailable. Fall back to a flush when + // the MAC is absent (centralised shape). + if distributed { + if err := m.neighPrime(eip); err != nil { + slog.Warn("policy: AddEIP neighbour prime failed", "external_ip", eip.ExternalIP, "logical_ip", eip.LogicalIP, "err", err) + } + } else if err := m.neigh(eip.ExternalIP); err != nil { + slog.Warn("policy: AddEIP neighbour flush failed", "external_ip", eip.ExternalIP, "logical_ip", eip.LogicalIP, "err", err) + } return nil } -func (m *natManager) DeleteEIP(ctx context.Context, vpcID, logicalIP string) error { +func (m *natManager) DeleteEIP(ctx context.Context, vpcID, externalIP, logicalIP string) error { router := topology.VPCRouter(vpcID) if err := m.ovn.DeleteNAT(ctx, router, "dnat_and_snat", logicalIP); err != nil { - if errors.Is(err, ovn.ErrNATNotFound) { - return nil + if !errors.Is(err, ovn.ErrNATNotFound) { + return fmt.Errorf("delete dnat_and_snat %s on %s: %w", logicalIP, router, err) + } + } + // Flush the host ARP entry for the released IP so the next owner is not + // shadowed by this rule's stale MAC. Best-effort, and safe on the + // not-found path (the entry may still be cached from a prior binding). + if externalIP != "" { + if err := m.neigh(externalIP); err != nil { + slog.Warn("policy: DeleteEIP neighbour flush failed", "external_ip", externalIP, "logical_ip", logicalIP, "err", err) } - return fmt.Errorf("delete dnat_and_snat %s on %s: %w", logicalIP, router, err) } return nil } @@ -246,3 +325,48 @@ func (m *natManager) DeleteSNAT(ctx context.Context, vpcID, vpcCIDR string) erro } return nil } + +func (m *natManager) AddSystemInstanceSNAT(ctx context.Context, vpcID, logicalIP, externalIP string) error { + router := topology.VPCRouter(vpcID) + snatRule := &nbdb.NAT{ + Type: "snat", + ExternalIP: externalIP, + LogicalIP: logicalIP, + ExternalIDs: map[string]string{ + "spinifex:vpc_id": vpcID, + "spinifex:public_ip": externalIP, + "spinifex:role": "system-instance-egress", + }, + } + // Skip when the existing row already matches; avoids the duplicate-append + // on a re-published add (the snat is keyed by its unique pool external IP). + if existing, err := m.ovn.FindNATByExternalIP(ctx, "snat", externalIP); err != nil { + slog.Warn("policy: AddSystemInstanceSNAT idempotency lookup failed", "external_ip", externalIP, "err", err) + } else if existing != nil && existing.LogicalIP == logicalIP { + slog.Info("policy: AddSystemInstanceSNAT idempotent skip — rule already current", + "router", router, "external_ip", externalIP, "logical_ip", logicalIP) + return nil + } + + if err := m.ovn.AddNAT(ctx, router, snatRule); err != nil { + return fmt.Errorf("add system-instance snat %s -> %s on %s: %w", logicalIP, externalIP, router, err) + } + // Block until SB + chassis have the SNAT flow; otherwise first egress + // packets from the instance drop. + if err := m.barrier(); err != nil { + slog.Warn("policy: AddSystemInstanceSNAT flows barrier failed", + "logical_ip", logicalIP, "external_ip", externalIP, "err", err) + } + return nil +} + +func (m *natManager) DeleteSystemInstanceSNAT(ctx context.Context, vpcID, logicalIP string) error { + router := topology.VPCRouter(vpcID) + if err := m.ovn.DeleteNAT(ctx, router, "snat", logicalIP); err != nil { + if errors.Is(err, ovn.ErrNATNotFound) { + return nil + } + return fmt.Errorf("delete system-instance snat %s on %s: %w", logicalIP, router, err) + } + return nil +} diff --git a/spinifex/network/policy/nat_test.go b/spinifex/network/policy/nat_test.go index 9b065d832..9720899d5 100644 --- a/spinifex/network/policy/nat_test.go +++ b/spinifex/network/policy/nat_test.go @@ -140,6 +140,78 @@ func TestNATManager_AddEIP_GARP_FailureNonFatal(t *testing.T) { assert.NotNil(t, findNAT(m, "dnat_and_snat", "10.0.0.5"), "NAT row must persist despite GARP failure") } +func TestNATManager_NeighPrime_OnDistributedAttach_FlushOnDetach(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + var primed []EIPSpec + var flushed []string + nm, err := NewNATManager(m, NATModeDistributed, + WithNeighPrimer(func(eip EIPSpec) error { + primed = append(primed, eip) + return nil + }), + WithNeighFlusher(func(externalIP string) error { + flushed = append(flushed, externalIP) + return nil + })) + require.NoError(t, err) + + spec := EIPSpec{ + VPCID: "vpc-1", ExternalIP: "1.2.3.4", LogicalIP: "10.0.0.5", + PortName: "port-eni-abc", MAC: "aa:bb:cc:dd:ee:ff", + } + require.NoError(t, nm.AddEIP(ctx, spec)) + require.NoError(t, nm.DeleteEIP(ctx, "vpc-1", "1.2.3.4", "10.0.0.5")) + + require.Equal(t, []EIPSpec{spec}, primed, + "distributed attach must prime the host neighbour with the external_mac, not flush") + assert.Equal(t, []string{"1.2.3.4"}, flushed, + "detach must still flush the released external IP") +} + +func TestNATManager_NeighFlush_OnCentralizedAttach(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + var primed []EIPSpec + var flushed []string + nm, err := NewNATManager(m, NATModeCentralized, + WithNeighPrimer(func(eip EIPSpec) error { + primed = append(primed, eip) + return nil + }), + WithNeighFlusher(func(externalIP string) error { + flushed = append(flushed, externalIP) + return nil + })) + require.NoError(t, err) + + require.NoError(t, nm.AddEIP(ctx, EIPSpec{ + VPCID: "vpc-1", ExternalIP: "1.2.3.4", LogicalIP: "10.0.0.5", + })) + assert.Empty(t, primed, "centralized attach has no external_mac to prime") + assert.Equal(t, []string{"1.2.3.4"}, flushed, + "centralized attach must fall back to a neighbour flush") +} + +func TestNATManager_NeighHooks_FailureNonFatal(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + nm, err := NewNATManager(m, NATModeDistributed, + WithNeighPrimer(func(EIPSpec) error { return assert.AnError }), + WithNeighFlusher(func(string) error { return assert.AnError })) + require.NoError(t, err) + + require.NoError(t, nm.AddEIP(ctx, EIPSpec{ + VPCID: "vpc-1", ExternalIP: "1.2.3.4", LogicalIP: "10.0.0.5", + PortName: "port-eni-abc", MAC: "aa:bb:cc:dd:ee:ff", + }), "neigh prime failure must not propagate from AddEIP") + require.NoError(t, nm.DeleteEIP(ctx, "vpc-1", "1.2.3.4", "10.0.0.5"), + "neigh flush failure must not propagate from DeleteEIP") +} + func TestNATManager_AddEIP_NoBarrier_Default(t *testing.T) { ctx := context.Background() m := mock.New() @@ -237,7 +309,7 @@ func TestNATManager_DeleteEIP_IdempotentOnMissing(t *testing.T) { nm, _ := NewNATManager(m, NATModeDistributed) // No prior AddEIP — delete should still succeed. - require.NoError(t, nm.DeleteEIP(ctx, "vpc-1", "10.0.0.5")) + require.NoError(t, nm.DeleteEIP(ctx, "vpc-1", "1.2.3.4", "10.0.0.5")) } func TestNATManager_AddNATGateway_FlowsBarrier_Fires(t *testing.T) { @@ -342,7 +414,7 @@ func TestNATManager_DeleteEIP_CrossRouterIsolation(t *testing.T) { PortName: "port-b", MAC: "bb:bb:bb:bb:bb:bb", })) - require.NoError(t, nm.DeleteEIP(ctx, "vpc-a", "10.0.0.5")) + require.NoError(t, nm.DeleteEIP(ctx, "vpc-a", "1.1.1.1", "10.0.0.5")) var survived *nbdb.NAT for _, n := range m.NATs { @@ -370,3 +442,23 @@ func TestNATManager_AddSNAT_AndDelete(t *testing.T) { assert.Nil(t, findNAT(m, "snat", "10.0.0.0/16")) require.NoError(t, nm.DeleteSNAT(ctx, "vpc-1", "10.0.0.0/16")) } + +func TestNATManager_AddSystemInstanceSNAT_AndDelete(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + nm, _ := NewNATManager(m, NATModeDistributed) + + require.NoError(t, nm.AddSystemInstanceSNAT(ctx, "vpc-1", "172.31.4.10/32", "1.2.3.4")) + got := findNAT(m, "snat", "172.31.4.10/32") + require.NotNil(t, got) + assert.Equal(t, "1.2.3.4", got.ExternalIP) + assert.Equal(t, "system-instance-egress", got.ExternalIDs["spinifex:role"]) + // snat-only: no dnat_and_snat row, so the instance stays unreachable inbound. + assert.Nil(t, findNAT(m, "dnat_and_snat", "172.31.4.10/32")) + + require.NoError(t, nm.DeleteSystemInstanceSNAT(ctx, "vpc-1", "172.31.4.10/32")) + assert.Nil(t, findNAT(m, "snat", "172.31.4.10/32")) + // Idempotent on a missing rule. + require.NoError(t, nm.DeleteSystemInstanceSNAT(ctx, "vpc-1", "172.31.4.10/32")) +} diff --git a/spinifex/network/policy/route.go b/spinifex/network/policy/route.go index 7893e8595..fcf5a0fac 100644 --- a/spinifex/network/policy/route.go +++ b/spinifex/network/policy/route.go @@ -24,6 +24,13 @@ const ( SubnetEgressPriorityDrop = 1100 SubnetEgressPriorityIGW = 1000 SubnetEgressPriorityNATGW = 900 + + // SystemInstanceEgressPriority sits above the drop gate (1100) so a single + // system instance (e.g. an EKS K3s server VM) egresses even when its subnet + // is otherwise drop-gated. The policy is scoped to the instance's /32 + // source, so peer instances in the same subnet are unaffected — they still + // hit the 1100 drop (or the subnet's own 1000 reroute where one exists). + SystemInstanceEgressPriority = 1200 ) // RouteSpec is a static route on a VPC's LogicalRouter. OutputPort is @@ -52,6 +59,20 @@ type SubnetEgressSpec struct { ExcludeCIDRs []netip.Prefix } +// SystemInstanceEgressSpec describes a per-instance egress override installed +// as an OVN Logical_Router_Policy at SystemInstanceEgressPriority. The match +// adds an `ip4.src == /32` clause to the subnet-egress shape so the +// reroute is confined to a single system instance; peer instances in the same +// subnet are untouched. Action is "reroute" via Nexthop out OutputPort. +type SystemInstanceEgressSpec struct { + SubnetID string + SrcIP netip.Addr + Prefix netip.Prefix + Nexthop string + OutputPort string + ExcludeCIDRs []netip.Prefix +} + // RouteManager owns static routes on VPC LogicalRouters. Adds are // idempotent (no-op on match, delete-then-add on drift) so the reconciler // can replay safely. @@ -64,6 +85,8 @@ type RouteManager interface { DeleteSubnetEgress(ctx context.Context, vpcID, subnetID string, prefix netip.Prefix, excludeCIDRs []netip.Prefix) error AddSubnetEgressDrop(ctx context.Context, vpcID string, spec SubnetEgressDropSpec) error DeleteSubnetEgressDrop(ctx context.Context, vpcID, subnetID string, prefix netip.Prefix, excludeCIDRs []netip.Prefix) error + AddSystemInstanceEgress(ctx context.Context, vpcID string, spec SystemInstanceEgressSpec) error + DeleteSystemInstanceEgress(ctx context.Context, vpcID, subnetID string, srcIP netip.Addr, prefix netip.Prefix, excludeCIDRs []netip.Prefix) error } // SubnetEgressDropSpec describes a per-subnet egress DROP override installed @@ -263,6 +286,62 @@ func (m *routeManager) DeleteSubnetEgressDrop(ctx context.Context, vpcID, subnet return nil } +func (m *routeManager) AddSystemInstanceEgress(ctx context.Context, vpcID string, spec SystemInstanceEgressSpec) error { + if spec.SubnetID == "" { + return fmt.Errorf("system instance egress: SubnetID required") + } + if !spec.SrcIP.IsValid() { + return fmt.Errorf("system instance egress: SrcIP required") + } + if spec.OutputPort == "" { + return fmt.Errorf("system instance egress: OutputPort required (ovn-northd drops policy reroute otherwise)") + } + router := topology.VPCRouter(vpcID) + match := systemInstanceEgressMatch(spec.SubnetID, spec.SrcIP, spec.Prefix, spec.ExcludeCIDRs) + + existing, err := m.ovn.FindLogicalRouterPolicy(ctx, router, SystemInstanceEgressPriority, match) + if err != nil { + return fmt.Errorf("find LR policy %q on %s: %w", match, router, err) + } + if existing != nil { + if existing.Action == "reroute" && existing.Nexthop != nil && *existing.Nexthop == spec.Nexthop && + existing.ExternalIDs["spinifex:output_port"] == spec.OutputPort { + return nil + } + if err := m.ovn.DeleteLogicalRouterPolicy(ctx, router, SystemInstanceEgressPriority, match); err != nil { + return fmt.Errorf("delete drifted LR policy %q on %s: %w", match, router, err) + } + } + + nexthop := spec.Nexthop + row := &nbdb.LogicalRouterPolicy{ + Priority: SystemInstanceEgressPriority, + Match: match, + Action: "reroute", + Nexthop: &nexthop, + Options: map[string]string{}, + ExternalIDs: map[string]string{ + "spinifex:subnet": spec.SubnetID, + "spinifex:src_ip": spec.SrcIP.String(), + "spinifex:output_port": spec.OutputPort, + "spinifex:role": "system-instance-egress", + }, + } + if err := m.ovn.AddLogicalRouterPolicy(ctx, router, row); err != nil { + return fmt.Errorf("add LR policy %q -> %s on %s: %w", match, spec.Nexthop, router, err) + } + return nil +} + +func (m *routeManager) DeleteSystemInstanceEgress(ctx context.Context, vpcID, subnetID string, srcIP netip.Addr, prefix netip.Prefix, excludeCIDRs []netip.Prefix) error { + router := topology.VPCRouter(vpcID) + match := systemInstanceEgressMatch(subnetID, srcIP, prefix, excludeCIDRs) + if err := m.ovn.DeleteLogicalRouterPolicy(ctx, router, SystemInstanceEgressPriority, match); err != nil { + return fmt.Errorf("delete system instance egress LR policy %q on %s: %w", match, router, err) + } + return nil +} + func subnetEgressMatch(subnetID string, prefix netip.Prefix, excludeCIDRs []netip.Prefix) string { match := fmt.Sprintf(`inport == %q && ip4.dst == %s`, topology.SubnetRouterPort(subnetID), prefix.String()) for _, ex := range excludeCIDRs { @@ -274,6 +353,18 @@ func subnetEgressMatch(subnetID string, prefix netip.Prefix, excludeCIDRs []neti return match } +func systemInstanceEgressMatch(subnetID string, srcIP netip.Addr, prefix netip.Prefix, excludeCIDRs []netip.Prefix) string { + match := fmt.Sprintf(`inport == %q && ip4.src == %s/32 && ip4.dst == %s`, + topology.SubnetRouterPort(subnetID), srcIP.String(), prefix.String()) + for _, ex := range excludeCIDRs { + if !ex.IsValid() { + continue + } + match += fmt.Sprintf(` && ip4.dst != %s`, ex.String()) + } + return match +} + func policyMatches(existing *nbdb.LogicalRouterPolicy, want SubnetEgressSpec) bool { if existing.Action != "reroute" { return false diff --git a/spinifex/network/policy/route_test.go b/spinifex/network/policy/route_test.go index 6fa5a4a33..0ba25dfe0 100644 --- a/spinifex/network/policy/route_test.go +++ b/spinifex/network/policy/route_test.go @@ -131,6 +131,128 @@ func TestRouteManager_AddSubnetEgress_InstallsScopedPolicy(t *testing.T) { assert.Equal(t, "gw-vpc-1", p.ExternalIDs["spinifex:output_port"]) } +func TestRouteManager_AddSystemInstanceEgress_InstallsScopedPolicy(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + rm := NewRouteManager(m) + + require.NoError(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", SystemInstanceEgressSpec{ + SubnetID: "subnet-k3s", + SrcIP: netip.MustParseAddr("172.31.4.10"), + Prefix: netip.MustParsePrefix("0.0.0.0/0"), + Nexthop: "192.168.1.1", + OutputPort: "gw-vpc-1", + })) + + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + require.Len(t, policies, 1) + p := policies[0] + assert.Equal(t, "reroute", p.Action) + assert.Equal(t, SystemInstanceEgressPriority, p.Priority) + require.NotNil(t, p.Nexthop) + assert.Equal(t, "192.168.1.1", *p.Nexthop) + // Scoped to the single instance /32 source so peers in the subnet are untouched. + assert.Contains(t, p.Match, "ip4.src == 172.31.4.10/32") + assert.Contains(t, p.Match, topology.SubnetRouterPort("subnet-k3s")) + assert.Equal(t, "172.31.4.10", p.ExternalIDs["spinifex:src_ip"]) + assert.Equal(t, "system-instance-egress", p.ExternalIDs["spinifex:role"]) +} + +func TestRouteManager_AddSystemInstanceEgress_IsIdempotent(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + rm := NewRouteManager(m) + + spec := SystemInstanceEgressSpec{ + SubnetID: "subnet-k3s", + SrcIP: netip.MustParseAddr("172.31.4.10"), + Prefix: netip.MustParsePrefix("0.0.0.0/0"), + Nexthop: "192.168.1.1", + OutputPort: "gw-vpc-1", + } + require.NoError(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", spec)) + require.NoError(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", spec)) + require.NoError(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", spec)) + + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + assert.Len(t, policies, 1) +} + +func TestRouteManager_AddSystemInstanceEgress_DriftReplacesNexthop(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + rm := NewRouteManager(m) + + spec := SystemInstanceEgressSpec{ + SubnetID: "subnet-k3s", + SrcIP: netip.MustParseAddr("172.31.4.10"), + Prefix: netip.MustParsePrefix("0.0.0.0/0"), + Nexthop: "192.168.1.1", + OutputPort: "gw-vpc-1", + } + require.NoError(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", spec)) + spec.Nexthop = "192.168.1.254" + require.NoError(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", spec)) + + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + require.Len(t, policies, 1) + require.NotNil(t, policies[0].Nexthop) + assert.Equal(t, "192.168.1.254", *policies[0].Nexthop) +} + +func TestRouteManager_AddSystemInstanceEgress_RejectsMissingFields(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + rm := NewRouteManager(m) + + valid := SystemInstanceEgressSpec{ + SubnetID: "subnet-k3s", + SrcIP: netip.MustParseAddr("172.31.4.10"), + Prefix: netip.MustParsePrefix("0.0.0.0/0"), + Nexthop: "192.168.1.1", + OutputPort: "gw-vpc-1", + } + + noSubnet := valid + noSubnet.SubnetID = "" + assert.Error(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", noSubnet)) + + noSrc := valid + noSrc.SrcIP = netip.Addr{} + assert.Error(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", noSrc)) + + noPort := valid + noPort.OutputPort = "" + assert.Error(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", noPort)) +} + +func TestRouteManager_DeleteSystemInstanceEgress_RemovesAndIdempotent(t *testing.T) { + ctx := context.Background() + m := mock.New() + seedRouter(t, m, "vpc-1") + rm := NewRouteManager(m) + + src := netip.MustParseAddr("172.31.4.10") + prefix := netip.MustParsePrefix("0.0.0.0/0") + require.NoError(t, rm.AddSystemInstanceEgress(ctx, "vpc-1", SystemInstanceEgressSpec{ + SubnetID: "subnet-k3s", SrcIP: src, Prefix: prefix, Nexthop: "192.168.1.1", OutputPort: "gw-vpc-1", + })) + + require.NoError(t, rm.DeleteSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", src, prefix, nil)) + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + assert.Empty(t, policies) + // Idempotent on a missing policy. + require.NoError(t, rm.DeleteSystemInstanceEgress(ctx, "vpc-1", "subnet-k3s", src, prefix, nil)) +} + func TestRouteManager_AddSubnetEgress_IsIdempotent(t *testing.T) { ctx := context.Background() m := mock.New() diff --git a/spinifex/network/reconcile/apply.go b/spinifex/network/reconcile/apply.go index ff904e45f..31eeaf9fb 100644 --- a/spinifex/network/reconcile/apply.go +++ b/spinifex/network/reconcile/apply.go @@ -15,22 +15,21 @@ import ( func (r *reconciler) applyVPCs(ctx context.Context, intent IntentState, actual ActualState) { for vpcID, spec := range intent.VPCs { routerName := topology.VPCRouter(vpcID) - if _, ok := actual.Routers[routerName]; ok { - continue + if _, ok := actual.Routers[routerName]; !ok { + lr := &nbdb.LogicalRouter{ + Name: routerName, + ExternalIDs: map[string]string{ + "spinifex:vpc_id": vpcID, + "spinifex:cidr": spec.CIDR.String(), + }, + } + if _, err := r.ovn.EnsureLogicalRouter(ctx, lr); err != nil { + slog.Error("reconcile/apply: ensure VPC router failed", "vpc_id", vpcID, "err", err) + continue + } + actual.Routers[routerName] = struct{}{} + slog.Info("reconcile/apply: ensured VPC router", "vpc_id", vpcID, "router", routerName) } - lr := &nbdb.LogicalRouter{ - Name: routerName, - ExternalIDs: map[string]string{ - "spinifex:vpc_id": vpcID, - "spinifex:cidr": spec.CIDR.String(), - }, - } - if _, err := r.ovn.EnsureLogicalRouter(ctx, lr); err != nil { - slog.Error("reconcile/apply: ensure VPC router failed", "vpc_id", vpcID, "err", err) - continue - } - actual.Routers[routerName] = struct{}{} - slog.Info("reconcile/apply: ensured VPC router", "vpc_id", vpcID, "router", routerName) } } @@ -88,7 +87,9 @@ func (r *reconciler) applySubnets(ctx context.Context, intent IntentState, actua Name: switchRouterPortName, Type: "router", Addresses: []string{"router"}, - Options: map[string]string{"router-port": routerPortName}, + Options: map[string]string{ + "router-port": routerPortName, + }, ExternalIDs: map[string]string{ "spinifex:subnet_id": subnetID, "spinifex:vpc_id": spec.VPCID, @@ -100,6 +101,14 @@ func (r *reconciler) applySubnets(ctx context.Context, intent IntentState, actua } } + // The IMDS localport rides on the subnet switch (idempotent via the + // subnet-veth bucket gate); the switch must exist first. + if r.imds != nil { + if _, err := r.imds.EnsureForSubnet(ctx, subnetID, spec.VPCID, spec.CIDR); err != nil { + slog.Error("reconcile/apply: IMDS EnsureForSubnet failed", "subnet_id", subnetID, "err", err) + } + } + if existing, err := r.ovn.FindDHCPOptionsByExternalID(ctx, "spinifex:subnet_id", subnetID); err != nil || existing == nil { opts := &nbdb.DHCPOptions{ CIDR: spec.CIDR.String(), diff --git a/spinifex/network/reconcile/invariants_test.go b/spinifex/network/reconcile/invariants_test.go new file mode 100644 index 000000000..8afa80b77 --- /dev/null +++ b/spinifex/network/reconcile/invariants_test.go @@ -0,0 +1,85 @@ +package reconcile + +import ( + "context" + "net" + "net/netip" + "testing" + + "github.com/mulgadc/spinifex/spinifex/network/ovn/mock" + "github.com/mulgadc/spinifex/spinifex/network/topology" +) + +// IMDS-datapath invariants. A guest LSP is created by two +// independent paths — the live topology manager (EnsurePort) and the reconciler +// (applyPorts) — and the per-subnet DHCPOptions row likewise. Both paths are +// exercised here so neither can drift away from the contract the IMDS handler +// trusts. These live in the reconcile package because it is the only network +// package that can import both topology (the live path) and itself (the +// reconciler path) without a cycle. + +// TestI1_GuestLSPMustHavePortSecurity asserts that every guest-attached LSP, +// created by either path, carries port_security equal to its addresses +// (" "). This is the load-bearing security boundary: ovn-controller +// drops any frame whose eth.src/ip4.src doesn't match port_security at the +// ingress LSP, so a compromised guest cannot forge a peer's source IP. Without +// it the IMDS (VPC-ID, source-IP) → ENI mapping is forgeable. +func TestI1_GuestLSPMustHavePortSecurity(t *testing.T) { + ctx := context.Background() + + // Live path: topology.EnsurePort. + t.Run("live", func(t *testing.T) { + m := mock.New() + mgr := topology.NewLiveManager(m) + vpc := topology.VPCSpec{VPCID: "vpc-a", CIDR: netip.MustParsePrefix("10.0.0.0/16")} + sub := topology.SubnetSpec{SubnetID: "subnet-a", VPCID: "vpc-a", CIDR: netip.MustParsePrefix("10.0.1.0/24")} + mac, _ := net.ParseMAC("02:00:00:00:00:01") + port := topology.PortSpec{ + PortID: "eni-a", SubnetID: "subnet-a", VPCID: "vpc-a", + PrivateIP: netip.MustParseAddr("10.0.1.5"), MAC: mac, + } + if err := mgr.EnsureVPC(ctx, vpc); err != nil { + t.Fatalf("EnsureVPC: %v", err) + } + if err := mgr.EnsureSubnet(ctx, sub); err != nil { + t.Fatalf("EnsureSubnet: %v", err) + } + if err := mgr.EnsurePort(ctx, port); err != nil { + t.Fatalf("EnsurePort: %v", err) + } + assertPortSecurity(t, m, topology.Port(port.PortID), "02:00:00:00:00:01 10.0.1.5") + }) + + // Reconciler path: applyPorts. + t.Run("reconciler", func(t *testing.T) { + rec, m := newTestReconciler(t) + intent := freshIntent(t) + if err := rec.Reconcile(ctx, intent); err != nil { + t.Fatalf("Reconcile: %v", err) + } + port := intent.Ports["eni-a"] + want := port.MAC.String() + " " + port.PrivateIP.String() + assertPortSecurity(t, m, topology.Port(port.PortID), want) + }) +} + +// assertPortSecurity fails with the ADR clause when the LSP lacks port_security +// equal to its addresses. +func assertPortSecurity(t *testing.T, m *mock.Client, portName, want string) { + t.Helper() + lsp, err := m.GetLogicalSwitchPort(context.Background(), portName) + if err != nil { + t.Fatalf("guest LSP %s missing: %v", portName, err) + } + if len(lsp.PortSecurity) != 1 || lsp.PortSecurity[0] != want { + t.Errorf("guest LSP %s PortSecurity = %v, want [%q]: guest LSPs without "+ + "port_security allow source-IP spoofing, breaking the IMDS "+ + "(VPC-ID, source-IP) → ENI mapping", portName, lsp.PortSecurity, want) + } + // port_security must mirror addresses exactly, or the enforced identity and + // the advertised identity diverge. + if len(lsp.Addresses) != 1 || lsp.Addresses[0] != lsp.PortSecurity[0] { + t.Errorf("guest LSP %s Addresses %v != PortSecurity %v: enforced and advertised "+ + "identity must match", portName, lsp.Addresses, lsp.PortSecurity) + } +} diff --git a/spinifex/network/reconcile/lock.go b/spinifex/network/reconcile/lock.go index 07da0f5c1..37b4b5c19 100644 --- a/spinifex/network/reconcile/lock.go +++ b/spinifex/network/reconcile/lock.go @@ -1,6 +1,7 @@ package reconcile import ( + "errors" "log/slog" "time" @@ -32,11 +33,18 @@ func AcquireLeader(nc *nats.Conn, holder string) (func(), bool) { ) deadline := time.Now().Add(leaderRetryFor) for { - kv, err = js.CreateKeyValue(&nats.KeyValueConfig{ - Bucket: reconcileLeaderBucket, - History: 1, - TTL: reconcileLeaderTTL, - }) + // Get-or-create: CreateKeyValue alone returns "stream name already in + // use" once the bucket exists, so every reconcile after the first would + // hit the deadline and never elect. Attach to the existing bucket first, + // create only when it is genuinely absent. + kv, err = js.KeyValue(reconcileLeaderBucket) + if errors.Is(err, nats.ErrBucketNotFound) { + kv, err = js.CreateKeyValue(&nats.KeyValueConfig{ + Bucket: reconcileLeaderBucket, + History: 1, + TTL: reconcileLeaderTTL, + }) + } if err == nil { break } diff --git a/spinifex/network/reconcile/lock_test.go b/spinifex/network/reconcile/lock_test.go new file mode 100644 index 000000000..2c2a48d44 --- /dev/null +++ b/spinifex/network/reconcile/lock_test.go @@ -0,0 +1,35 @@ +package reconcile + +import ( + "testing" + + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestAcquireLeader_GetOrCreateAcrossCalls pins the get-or-create fix: the +// first acquire creates the bucket and wins, a second acquire while the lock is +// held must attach to the now-existing bucket (not hang on "stream name already +// in use") and lose cleanly, and once the winner releases a later acquire wins +// again. +func TestAcquireLeader_GetOrCreateAcrossCalls(t *testing.T) { + _, nc, _ := testutil.StartTestJetStream(t) + + release, ok := AcquireLeader(nc, "node-1") + require.True(t, ok, "first acquire must win and create the bucket") + require.NotNil(t, release) + + // Bucket now exists; the second acquire must reach the Create-key contention + // path (and lose) rather than dead-ending on CreateKeyValue. + loserRelease, ok := AcquireLeader(nc, "node-2") + assert.False(t, ok, "second acquire must lose while the lock is held") + assert.Nil(t, loserRelease) + + // Releasing frees the key so a subsequent acquire can take over. + release() + release2, ok := AcquireLeader(nc, "node-3") + require.True(t, ok, "acquire after release must win") + require.NotNil(t, release2) + release2() +} diff --git a/spinifex/network/reconcile/reconcile.go b/spinifex/network/reconcile/reconcile.go index 02946b490..37078949a 100644 --- a/spinifex/network/reconcile/reconcile.go +++ b/spinifex/network/reconcile/reconcile.go @@ -36,7 +36,11 @@ type Config struct { Routes policy.RouteManager IGW external.IGWManager Topology topology.Manager - LocalAZ string + // IMDS installs per-VPC IMDS OVN topology during the VPC apply pass. + // Optional: nil skips IMDS plumbing (focused reconcile tests leave it + // unset; production always wires it). + IMDS external.IMDSTopologyManager + LocalAZ string // NodeHostname is the holder identity for leader-election CAS. NodeHostname string // Chassis is the SBDB-discovered chassis list for gateway LRP rebinding. @@ -54,6 +58,7 @@ type reconciler struct { routes policy.RouteManager igw external.IGWManager topology topology.Manager + imds external.IMDSTopologyManager localAZ string host string chassis []string @@ -92,6 +97,7 @@ func New(cfg Config) (Reconciler, error) { routes: cfg.Routes, igw: cfg.IGW, topology: cfg.Topology, + imds: cfg.IMDS, localAZ: cfg.LocalAZ, host: cfg.NodeHostname, chassis: cfg.Chassis, diff --git a/spinifex/network/subscribers/events.go b/spinifex/network/subscribers/events.go index de3bb0442..6e12c8198 100644 --- a/spinifex/network/subscribers/events.go +++ b/spinifex/network/subscribers/events.go @@ -19,6 +19,8 @@ const ( TopicDeleteIGWRoute = "vpc.delete-igw-route" TopicGateSubnetEgress = "vpc.gate-subnet-egress" TopicUngateSubnetEgress = "vpc.ungate-subnet-egress" + TopicAddSystemEgress = "vpc.add-system-egress" + TopicDeleteSystemEgress = "vpc.delete-system-egress" TopicCreateSG = "vpc.create-sg" TopicDeleteSG = "vpc.delete-sg" TopicUpdateSG = "vpc.update-sg" @@ -113,6 +115,19 @@ type SubnetEgressUngateEvent struct { DestinationCidr string `json:"destination_cidr"` } +// SystemEgressEvent is published on vpc.add-system-egress / +// vpc.delete-system-egress to give a single hidden system instance (e.g. an +// EKS K3s server VM) egress-only internet access. The subscriber installs (or +// removes) a /32 reroute policy at SystemInstanceEgressPriority plus a plain +// snat (InstanceIp -> ExternalIp) — no DNAT, so the instance stays unreachable +// inbound. +type SystemEgressEvent struct { + VpcId string `json:"vpc_id"` + SubnetId string `json:"subnet_id"` + InstanceIp string `json:"instance_ip"` + ExternalIp string `json:"external_ip"` +} + // SGRule mirrors handlers/ec2/vpc.SGRule on the wire (kept local to avoid // the handler import). type SGRule struct { diff --git a/spinifex/network/subscribers/subscribers.go b/spinifex/network/subscribers/subscribers.go index fc3368bc5..ccd785b4e 100644 --- a/spinifex/network/subscribers/subscribers.go +++ b/spinifex/network/subscribers/subscribers.go @@ -23,15 +23,20 @@ type Subscriber struct { eip external.EIPManager natgw external.NATGWManager igw external.IGWManager + imds external.IMDSTopologyManager } -// Config: all fields required. +// Config: all fields required except IMDS. type Config struct { Topology topology.Manager SG policy.SecurityGroupManager EIP external.EIPManager NATGW external.NATGWManager IGW external.IGWManager + // IMDS installs per-VPC IMDS OVN topology on vpc.create / removes it on + // vpc.delete. Optional: nil disables IMDS plumbing (focused topology tests + // leave it unset; production always wires it). + IMDS external.IMDSTopologyManager } // New constructs a Subscriber, returning an error when any manager is nil. @@ -54,6 +59,7 @@ func New(cfg Config) (*Subscriber, error) { eip: cfg.EIP, natgw: cfg.NATGW, igw: cfg.IGW, + imds: cfg.IMDS, }, nil } @@ -82,6 +88,8 @@ func (s *Subscriber) Subscribe(nc *nats.Conn) ([]*nats.Subscription, error) { {TopicDeleteIGWRoute, s.handleDeleteIGWRoute}, {TopicGateSubnetEgress, s.handleGateSubnetEgress}, {TopicUngateSubnetEgress, s.handleUngateSubnetEgress}, + {TopicAddSystemEgress, s.handleAddSystemEgress}, + {TopicDeleteSystemEgress, s.handleDeleteSystemEgress}, {TopicCreateSG, s.handleCreateSG}, {TopicDeleteSG, s.handleDeleteSG}, {TopicUpdateSG, s.handleUpdateSG}, diff --git a/spinifex/network/subscribers/subscribers_test.go b/spinifex/network/subscribers/subscribers_test.go index 1d2f3c106..e528cb285 100644 --- a/spinifex/network/subscribers/subscribers_test.go +++ b/spinifex/network/subscribers/subscribers_test.go @@ -98,6 +98,7 @@ func TestSubscribe_RegistersAllTopics(t *testing.T) { TopicAddNATGateway, TopicDeleteNATGateway, TopicAddIGWRoute, TopicDeleteIGWRoute, TopicGateSubnetEgress, TopicUngateSubnetEgress, + TopicAddSystemEgress, TopicDeleteSystemEgress, TopicCreateSG, TopicDeleteSG, TopicUpdateSG, } if len(subs) != len(wantTopics) { @@ -173,6 +174,66 @@ func TestHandleAddNATGateway_InstallsPerSubnetEgress(t *testing.T) { }, 2*time.Second, 20*time.Millisecond, "NATGW egress policy must be removed on delete event") } +// TestHandleSystemEgress_InstallsAndRemoves drives the full event path: a +// vpc.add-system-egress event installs the /32 reroute + snat-only NAT, and +// vpc.delete-system-egress tears both down. +func TestHandleSystemEgress_InstallsAndRemoves(t *testing.T) { + ctx := context.Background() + _, nc := testutil.StartTestNATS(t) + sub, m := newTestSubscriber(t) + subs, err := sub.Subscribe(nc) + require.NoError(t, err) + defer func() { + for _, s := range subs { + _ = s.Unsubscribe() + } + }() + + require.NoError(t, m.CreateLogicalRouter(ctx, &nbdb.LogicalRouter{ + Name: topology.VPCRouter("vpc-1"), + ExternalIDs: map[string]string{"spinifex:vpc_id": "vpc-1"}, + })) + require.NoError(t, sub.igw.AttachIGW(ctx, external.IGWSpec{VPCID: "vpc-1", InternetGatewayID: "igw-1"})) + + evt := SystemEgressEvent{ + VpcId: "vpc-1", + SubnetId: "subnet-k3s", + InstanceIp: "10.0.4.10", + ExternalIp: "192.168.1.60", + } + data, err := json.Marshal(evt) + require.NoError(t, err) + require.NoError(t, nc.Publish(TopicAddSystemEgress, data)) + + require.Eventually(t, func() bool { + policies, perr := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + if perr != nil || len(policies) != 1 { + return false + } + return policies[0].Priority == policy.SystemInstanceEgressPriority + }, 2*time.Second, 20*time.Millisecond, "system egress reroute must be installed") + + policies, err := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + require.NoError(t, err) + require.Len(t, policies, 1) + assert.Contains(t, policies[0].Match, "ip4.src == 10.0.4.10/32") + + snat, err := m.FindNATByExternalIP(ctx, "snat", "192.168.1.60") + require.NoError(t, err) + require.NotNil(t, snat) + assert.Equal(t, "10.0.4.10/32", snat.LogicalIP) + + require.NoError(t, nc.Publish(TopicDeleteSystemEgress, data)) + require.Eventually(t, func() bool { + policies, perr := m.ListLogicalRouterPolicies(ctx, topology.VPCRouter("vpc-1")) + if perr != nil || len(policies) != 0 { + return false + } + got, gerr := m.FindNATByExternalIP(ctx, "snat", "192.168.1.60") + return gerr == nil && got == nil + }, 2*time.Second, 20*time.Millisecond, "system egress reroute + snat must be removed on delete") +} + // TestHandleAddNATGateway_InvalidCIDRSkipsPolicy: the SNAT install must still // happen, but a malformed DestinationCidr must not install any LR policy. func TestHandleAddNATGateway_InvalidCIDRSkipsPolicy(t *testing.T) { diff --git a/spinifex/network/subscribers/topology.go b/spinifex/network/subscribers/topology.go index 0db002c9d..18d44639e 100644 --- a/spinifex/network/subscribers/topology.go +++ b/spinifex/network/subscribers/topology.go @@ -31,7 +31,8 @@ func (s *Subscriber) handleVPCCreate(msg *nats.Msg) { } spec.CIDR = cidr } - if err := s.topology.EnsureVPC(context.Background(), spec); err != nil { + ctx := context.Background() + if err := s.topology.EnsureVPC(ctx, spec); err != nil { slog.Error("subscribers: EnsureVPC failed", "vpc_id", evt.VpcId, "err", err) respond(msg, err) return @@ -46,7 +47,8 @@ func (s *Subscriber) handleVPCDelete(msg *nats.Msg) { respond(msg, err) return } - if err := s.topology.DeleteVPC(context.Background(), evt.VpcId); err != nil { + ctx := context.Background() + if err := s.topology.DeleteVPC(ctx, evt.VpcId); err != nil { slog.Error("subscribers: DeleteVPC failed", "vpc_id", evt.VpcId, "err", err) respond(msg, err) return @@ -85,6 +87,14 @@ func (s *Subscriber) handleSubnetCreate(msg *nats.Msg) { respond(msg, err) return } + // The IMDS localport lives on every subnet switch (guests reach metadata over + // one L2 hop). Install is best-effort — the reconciler re-ensures it + // idempotently, so a transient OVN failure here must not fail CreateSubnet. + if s.imds != nil { + if _, err := s.imds.EnsureForSubnet(ctx, evt.SubnetId, evt.VpcId, cidr); err != nil { + slog.Warn("subscribers: IMDS EnsureForSubnet failed; reconciler will converge", "subnet_id", evt.SubnetId, "err", err) + } + } respond(msg, nil) } @@ -101,7 +111,17 @@ func (s *Subscriber) handleSubnetDelete(msg *nats.Msg) { spec.CIDR = cidr } } - if err := s.topology.DeleteSubnet(context.Background(), spec); err != nil { + ctx := context.Background() + // Remove the IMDS localport before the subnet switch goes away — the + // localport lives on subnet-{subnetID}. + if s.imds != nil { + if err := s.imds.RemoveForSubnet(ctx, evt.SubnetId); err != nil { + slog.Error("subscribers: IMDS RemoveForSubnet failed", "subnet_id", evt.SubnetId, "err", err) + respond(msg, err) + return + } + } + if err := s.topology.DeleteSubnet(ctx, spec); err != nil { slog.Error("subscribers: DeleteSubnet failed", "subnet_id", evt.SubnetId, "err", err) respond(msg, err) return @@ -249,7 +269,7 @@ func (s *Subscriber) handleDeleteNAT(msg *nats.Msg) { respond(msg, err) return } - if err := s.eip.DetachEIP(context.Background(), evt.VpcId, evt.LogicalIP); err != nil { + if err := s.eip.DetachEIP(context.Background(), evt.VpcId, evt.ExternalIP, evt.LogicalIP); err != nil { slog.Error("subscribers: DeleteEIP failed", "vpc_id", evt.VpcId, "logical_ip", evt.LogicalIP, "err", err) respond(msg, err) @@ -310,6 +330,41 @@ func (s *Subscriber) handleAddNATGateway(msg *nats.Msg) { "natgw_id", evt.NatGatewayId) } +func (s *Subscriber) handleAddSystemEgress(msg *nats.Msg) { + var evt SystemEgressEvent + if err := json.Unmarshal(msg.Data, &evt); err != nil { + slog.Error("subscribers: failed to unmarshal vpc.add-system-egress event", "err", err) + return + } + ctx := context.Background() + if err := s.igw.EnsureSystemInstanceEgress(ctx, evt.VpcId, evt.SubnetId, evt.InstanceIp, evt.ExternalIp); err != nil { + slog.Error("subscribers: EnsureSystemInstanceEgress failed", + "vpc_id", evt.VpcId, "subnet_id", evt.SubnetId, + "instance_ip", evt.InstanceIp, "external_ip", evt.ExternalIp, "err", err) + return + } + slog.Info("subscribers: system instance egress installed", + "vpc_id", evt.VpcId, "subnet_id", evt.SubnetId, + "instance_ip", evt.InstanceIp, "external_ip", evt.ExternalIp) +} + +func (s *Subscriber) handleDeleteSystemEgress(msg *nats.Msg) { + var evt SystemEgressEvent + if err := json.Unmarshal(msg.Data, &evt); err != nil { + slog.Error("subscribers: failed to unmarshal vpc.delete-system-egress event", "err", err) + return + } + ctx := context.Background() + if err := s.igw.RemoveSystemInstanceEgress(ctx, evt.VpcId, evt.SubnetId, evt.InstanceIp, evt.ExternalIp); err != nil { + slog.Error("subscribers: RemoveSystemInstanceEgress failed", + "vpc_id", evt.VpcId, "subnet_id", evt.SubnetId, + "instance_ip", evt.InstanceIp, "external_ip", evt.ExternalIp, "err", err) + return + } + slog.Info("subscribers: system instance egress removed", + "vpc_id", evt.VpcId, "subnet_id", evt.SubnetId, "instance_ip", evt.InstanceIp) +} + func (s *Subscriber) handleDeleteNATGateway(msg *nats.Msg) { var evt NATGatewayEvent if err := json.Unmarshal(msg.Data, &evt); err != nil { diff --git a/spinifex/network/topology/dhcp_options.go b/spinifex/network/topology/dhcp_options.go index 553c4f61e..b1c323539 100644 --- a/spinifex/network/topology/dhcp_options.go +++ b/spinifex/network/topology/dhcp_options.go @@ -4,6 +4,14 @@ package topology // subnet's DHCPOptions row. Both the live topology manager and the reconciler // call it so the two paths cannot drift — they previously hard-coded divergent // dns_server values (live used the configured server, the reconciler "8.8.8.8"). +// +// IMDS reachability (169.254.169.254) is not steered via DHCP option 121: the +// default gateway stays option 3 (router), and the localport on the subnet +// switch answers ARP for the metadata IP directly (one L2 hop, no router). The +// guest's on-link route to 169.254.169.254 is delivered out-of-band by the +// cloud-init network-config the instance handler renders (NoCloud seed) — see +// generateNetworkConfig in handlers/ec2/instance — so it reaches DHCP and static +// guests alike without folding the default route into option 121 (RFC 3442). func BuildSubnetDHCPOptions(gwIP, routerMAC, dnsServer string) map[string]string { return map[string]string{ "server_id": gwIP, diff --git a/spinifex/network/topology/dhcp_options_test.go b/spinifex/network/topology/dhcp_options_test.go new file mode 100644 index 000000000..2c72051f9 --- /dev/null +++ b/spinifex/network/topology/dhcp_options_test.go @@ -0,0 +1,24 @@ +package topology + +import "testing" + +// IMDS reachability is served by a localport on the subnet switch (answered over +// L2), not via DHCP option 121, so BuildSubnetDHCPOptions must no longer emit a +// classless_static_route key. The default gateway is still carried by option 3 +// (router), and dns_server/server_id remain present. +func TestBuildSubnetDHCPOptions_NoClasslessStaticRoute(t *testing.T) { + opts := BuildSubnetDHCPOptions("10.0.1.1", "02:00:00:00:00:01", "{8.8.8.8, 1.1.1.1}") + + if _, ok := opts["classless_static_route"]; ok { + t.Errorf("classless_static_route must be absent (IMDS is served by a subnet-switch localport over L2, not DHCP option 121); got %q", opts["classless_static_route"]) + } + if got := opts["router"]; got != "10.0.1.1" { + t.Errorf("router = %q, want %q", got, "10.0.1.1") + } + if got := opts["dns_server"]; got != "{8.8.8.8, 1.1.1.1}" { + t.Errorf("dns_server = %q, want %q", got, "{8.8.8.8, 1.1.1.1}") + } + if got := opts["server_id"]; got != "10.0.1.1" { + t.Errorf("server_id = %q, want %q", got, "10.0.1.1") + } +} diff --git a/spinifex/network/topology/names.go b/spinifex/network/topology/names.go index ce9e8aa84..62fa12488 100644 --- a/spinifex/network/topology/names.go +++ b/spinifex/network/topology/names.go @@ -36,6 +36,12 @@ func ExternalSwitch(vpcID string) string { return "ext-" + vpcID } // external switch onto the host uplink. func ExternalLocalnetPort(vpcID string) string { return "ext-port-" + vpcID } +// IMDSPort is the host-owned localport LSP that claims 169.254.169.254 directly +// on the subnet switch (SubnetSwitch). ovn-controller binds it on every chassis +// with a matching iface-id OVS port, so each chassis self-serves IMDS for its +// local VMs over a single L2 hop on the guest's own broadcast domain. +func IMDSPort(subnetID string) string { return "imds-port-" + subnetID } + // SecurityGroupPortGroup is the OVN port group name for an SG. // OVN names match [a-zA-Z_][a-zA-Z0-9_]*, so hyphens become underscores. func SecurityGroupPortGroup(sgID string) string { diff --git a/spinifex/services/awsgw/awsgw.go b/spinifex/services/awsgw/awsgw.go index ac7f49c1e..29daa81a1 100644 --- a/spinifex/services/awsgw/awsgw.go +++ b/spinifex/services/awsgw/awsgw.go @@ -146,6 +146,29 @@ func launchService(config *config.ClusterConfig) error { defer cancelJanitor() go stsService.RunJanitor(janitorCtx) + // IMDS serves 169.254.169.254 to guest VMs from vpcd, which holds the network + // capabilities the hardened awsgw sandbox can't grant. awsgw stays the home of + // STS + IAM, answering the IMDS handler's control-plane RPCs over NATS. + if _, err := stsService.SubscribeIMDSResponder(natsConn); err != nil { + return fmt.Errorf("subscribe IMDS STS responder: %w", err) + } + if _, err := iamService.SubscribeIMDSResponders(natsConn); err != nil { + return fmt.Errorf("subscribe IMDS IAM responders: %w", err) + } + + // Expose the in-process STS presigned-URL verify over NATS for the + // in-cluster eks-token-webhook (STS is gateway-local, not otherwise on the + // bus). Bound to the process lifetime via natsConn.Close on return. + tokenVerifySub, err := registerEKSTokenVerify(natsConn, stsService) + if err != nil { + return fmt.Errorf("subscribe EKS token verify: %w", err) + } + defer func() { + if err := tokenVerifySub.Unsubscribe(); err != nil { + slog.Warn("EKS token verify: unsubscribe failed", "err", err) + } + }() + // First boot: consume bootstrap.json → seed IAM users into NATS KV → delete file. // Check data directory first (production: /var/lib/spinifex/awsgw/), then // awsgw subdir (dev: ~/spinifex/awsgw/), then legacy config dir. diff --git a/spinifex/services/awsgw/eks_token_verify.go b/spinifex/services/awsgw/eks_token_verify.go new file mode 100644 index 000000000..730fc8e9c --- /dev/null +++ b/spinifex/services/awsgw/eks_token_verify.go @@ -0,0 +1,76 @@ +package awsgw + +import ( + "encoding/json" + "log/slog" + + "github.com/mulgadc/spinifex/spinifex/awserrors" + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/nats-io/nats.go" +) + +// eksTokenVerifyQueue is the NATS queue group for the EKS token-verify +// responders so the request is load-balanced across awsgw nodes. +const eksTokenVerifyQueue = "awsgw-eks-token-verify" //nolint:gosec // queue-group name, not a credential + +// presignedVerifier is the slice of the STS service the token-verify responder +// needs: resolve an `aws eks get-token` presigned URL into a caller principal, +// binding the signature to the expected cluster name (anti-replay, Q10). +type presignedVerifier interface { + VerifyPresignedGetCallerIdentity(presignedURL, expectedClusterName string) (*handlers_sts.PresignedCallerIdentity, error) +} + +var _ presignedVerifier = (*handlers_sts.STSServiceImpl)(nil) + +// registerEKSTokenVerify subscribes the awsgw service to TokenVerifySubject so +// the in-cluster eks-token-webhook can resolve get-token presigned URLs over +// NATS (STS is hosted in-process here and is not otherwise on the bus). +func registerEKSTokenVerify(nc *nats.Conn, verifier presignedVerifier) (*nats.Subscription, error) { + return nc.QueueSubscribe(handlers_eks.TokenVerifySubject, eksTokenVerifyQueue, handleEKSTokenVerify(verifier)) +} + +func handleEKSTokenVerify(verifier presignedVerifier) nats.MsgHandler { + return func(msg *nats.Msg) { + var req handlers_eks.TokenVerifyRequest + if err := json.Unmarshal(msg.Data, &req); err != nil { + respondTokenVerifyErr(msg, awserrors.ErrorInvalidParameterValue) + return + } + if req.PresignedURL == "" || req.ClusterName == "" { + respondTokenVerifyErr(msg, awserrors.ErrorInvalidParameterValue) + return + } + + ident, err := verifier.VerifyPresignedGetCallerIdentity(req.PresignedURL, req.ClusterName) + if err != nil { + // A verify failure is the common, expected case for a forged/replayed + // token — log at debug so a port-scan does not flood the error log. + slog.Debug("EKS token verify rejected", "cluster", req.ClusterName, "err", err) + respondTokenVerifyErr(msg, awserrors.ValidErrorCode(err.Error())) + return + } + + resp := handlers_eks.TokenVerifyResponse{ + AccountID: ident.AccountID, + ARN: ident.ARN, + UserID: ident.UserID, + PrincipalType: ident.PrincipalType, + } + data, err := json.Marshal(resp) + if err != nil { + respondTokenVerifyErr(msg, awserrors.ErrorServerInternal) + return + } + if err := msg.Respond(data); err != nil { + slog.Error("EKS token verify: failed to respond", "err", err) + } + } +} + +func respondTokenVerifyErr(msg *nats.Msg, code string) { + if err := msg.Respond(utils.GenerateErrorPayload(code)); err != nil { + slog.Error("EKS token verify: failed to respond with error", "err", err) + } +} diff --git a/spinifex/services/awsgw/eks_token_verify_test.go b/spinifex/services/awsgw/eks_token_verify_test.go new file mode 100644 index 000000000..069193fed --- /dev/null +++ b/spinifex/services/awsgw/eks_token_verify_test.go @@ -0,0 +1,79 @@ +package awsgw + +import ( + "errors" + "testing" + "time" + + handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" + handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" + "github.com/mulgadc/spinifex/spinifex/testutil" + "github.com/mulgadc/spinifex/spinifex/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type fakeVerifier struct { + ident *handlers_sts.PresignedCallerIdentity + err error + gotURL string + gotName string +} + +func (f *fakeVerifier) VerifyPresignedGetCallerIdentity(presignedURL, expectedClusterName string) (*handlers_sts.PresignedCallerIdentity, error) { + f.gotURL = presignedURL + f.gotName = expectedClusterName + return f.ident, f.err +} + +func TestEKSTokenVerify_ResolvesPrincipal(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + fake := &fakeVerifier{ident: &handlers_sts.PresignedCallerIdentity{ + AccountID: "111122223333", + ARN: "arn:aws:iam::111122223333:role/admin", + UserID: "AROAEXAMPLE:sess", + PrincipalType: "AssumedRole", + }} + sub, err := registerEKSTokenVerify(nc, fake) + require.NoError(t, err) + defer func() { _ = sub.Unsubscribe() }() + + req := handlers_eks.TokenVerifyRequest{PresignedURL: "https://sts/?x=1", ClusterName: "alpha"} + resp, err := utils.NATSRequest[handlers_eks.TokenVerifyResponse]( + nc, handlers_eks.TokenVerifySubject, req, 2*time.Second, "") + require.NoError(t, err) + + assert.Equal(t, "arn:aws:iam::111122223333:role/admin", resp.ARN) + assert.Equal(t, "AROAEXAMPLE:sess", resp.UserID) + assert.Equal(t, "AssumedRole", resp.PrincipalType) + // Cluster name is forwarded for the STS anti-replay pin. + assert.Equal(t, "alpha", fake.gotName) +} + +func TestEKSTokenVerify_RejectedTokenIsError(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + fake := &fakeVerifier{err: errors.New("signature mismatch (cluster name replay)")} + sub, err := registerEKSTokenVerify(nc, fake) + require.NoError(t, err) + defer func() { _ = sub.Unsubscribe() }() + + req := handlers_eks.TokenVerifyRequest{PresignedURL: "https://sts/?x=1", ClusterName: "alpha"} + _, err = utils.NATSRequest[handlers_eks.TokenVerifyResponse]( + nc, handlers_eks.TokenVerifySubject, req, 2*time.Second, "") + require.Error(t, err, "a verify failure must surface as a NATS error payload") +} + +func TestEKSTokenVerify_RejectsEmptyFields(t *testing.T) { + _, nc := testutil.StartTestNATS(t) + fake := &fakeVerifier{ident: &handlers_sts.PresignedCallerIdentity{ARN: "arn:..."}} + sub, err := registerEKSTokenVerify(nc, fake) + require.NoError(t, err) + defer func() { _ = sub.Unsubscribe() }() + + // Missing ClusterName — responder rejects before calling the verifier. + req := handlers_eks.TokenVerifyRequest{PresignedURL: "https://sts/?x=1"} + _, err = utils.NATSRequest[handlers_eks.TokenVerifyResponse]( + nc, handlers_eks.TokenVerifySubject, req, 2*time.Second, "") + require.Error(t, err) + assert.Empty(t, fake.gotURL, "verifier must not run on an invalid request") +} diff --git a/spinifex/services/awsgw/imds_absent_test.go b/spinifex/services/awsgw/imds_absent_test.go new file mode 100644 index 000000000..6640e703e --- /dev/null +++ b/spinifex/services/awsgw/imds_absent_test.go @@ -0,0 +1,23 @@ +package awsgw + +import ( + "os" + "strings" + "testing" +) + +// TestIMDSRuntimeAbsentFromAWSGW guards against re-introducing the privileged +// IMDS listener stack into awsgw, whose systemd unit is the most hardened in +// the fleet (NoNewPrivileges=yes, empty CapabilityBoundingSet). The veth + +// privileged-socket bind cannot succeed there; the runtime lives in vpcd, and +// awsgw only answers the IMDS control-plane RPCs (STS + IAM). +func TestIMDSRuntimeAbsentFromAWSGW(t *testing.T) { + src, err := os.ReadFile("awsgw.go") + if err != nil { + t.Fatalf("read awsgw.go: %v", err) + } + if strings.Contains(string(src), "NewIMDSServiceImpl") { + t.Fatal("awsgw must not construct the IMDS runtime (NewIMDSServiceImpl) — " + + "the privileged listener stack belongs in vpcd.") + } +} diff --git a/spinifex/services/spinifexui/frontend/dist/assets/SignatureV4.js b/spinifex/services/spinifexui/frontend/dist/assets/SignatureV4.js new file mode 100644 index 000000000..1125bf20a --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/SignatureV4.js @@ -0,0 +1,75 @@ +import{A as e,D as t,F as n,P as r,c as i,d as a,f as o,k as s,l as c}from"./index.browser.js";function l(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`)if(Array.isArray(e)){var i=e.length;for(t=0;t{let n=Array(e.length+t.length);for(let t=0;t({classGroupId:e,validator:t}),p=(e=new Map,t=null,n)=>({nextPart:e,validators:t,classGroupId:n}),m=`-`,h=[],g=`arbitrary..`,ee=e=>{let t=_(e),{conflictingClassGroups:n,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:e=>{if(e.startsWith(`[`)&&e.endsWith(`]`))return ne(e);let n=e.split(m);return te(n,+(n[0]===``&&n.length>1),t)},getConflictingClassGroupIds:(e,t)=>{if(t){let t=r[e],i=n[e];return t?i?d(i,t):t:i||h}return n[e]||h}}},te=(e,t,n)=>{if(e.length-t===0)return n.classGroupId;let r=e[t],i=n.nextPart.get(r);if(i){let n=te(e,t+1,i);if(n)return n}let a=n.validators;if(a===null)return;let o=t===0?e.join(m):e.slice(t).join(m),s=a.length;for(let e=0;ee.slice(1,-1).indexOf(`:`)===-1?void 0:(()=>{let t=e.slice(1,-1),n=t.indexOf(`:`),r=t.slice(0,n);return r?g+r:void 0})(),_=e=>{let{theme:t,classGroups:n}=e;return re(n,t)},re=(e,t)=>{let n=p();for(let r in e){let i=e[r];ie(i,n,r,t)}return n},ie=(e,t,n,r)=>{let i=e.length;for(let a=0;a{if(typeof e==`string`){v(e,t,n);return}if(typeof e==`function`){y(e,t,n,r);return}oe(e,t,n,r)},v=(e,t,n)=>{let r=e===``?t:se(t,e);r.classGroupId=n},y=(e,t,n,r)=>{if(ce(e)){ie(e(r),t,n,r);return}t.validators===null&&(t.validators=[]),t.validators.push(f(n,e))},oe=(e,t,n,r)=>{let i=Object.entries(e),a=i.length;for(let e=0;e{let n=e,r=t.split(m),i=r.length;for(let e=0;e`isThemeGetter`in e&&e.isThemeGetter===!0,le=e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let t=0,n=Object.create(null),r=Object.create(null),i=(i,a)=>{n[i]=a,t++,t>e&&(t=0,r=n,n=Object.create(null))};return{get(e){let t=n[e];if(t!==void 0)return t;if((t=r[e])!==void 0)return i(e,t),t},set(e,t){e in n?n[e]=t:i(e,t)}}},ue=`!`,b=`:`,x=[],S=(e,t,n,r,i)=>({modifiers:e,hasImportantModifier:t,baseClassName:n,maybePostfixModifierPosition:r,isExternal:i}),de=e=>{let{prefix:t,experimentalParseClassName:n}=e,r=e=>{let t=[],n=0,r=0,i=0,a,o=e.length;for(let s=0;si?a-i:void 0;return S(t,l,c,u)};if(t){let e=t+b,n=r;r=t=>t.startsWith(e)?n(t.slice(e.length)):S(x,!1,t,void 0,!0)}if(n){let e=r;r=t=>n({className:t,parseClassName:e})}return r},fe=e=>{let t=new Map;return e.orderSensitiveModifiers.forEach((e,n)=>{t.set(e,1e6+n)}),e=>{let n=[],r=[];for(let i=0;i0&&(r.sort(),n.push(...r),r=[]),n.push(a)):r.push(a)}return r.length>0&&(r.sort(),n.push(...r)),n}},C=e=>({cache:le(e.cacheSize),parseClassName:de(e),sortModifiers:fe(e),postfixLookupClassGroupIds:pe(e),...ee(e)}),pe=e=>{let t=Object.create(null),n=e.postfixLookupClassGroups;if(n)for(let e=0;e{let{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:i,sortModifiers:a,postfixLookupClassGroupIds:o}=t,s=[],c=e.trim().split(me),l=``;for(let e=c.length-1;e>=0;--e){let t=c[e],{isExternal:u,modifiers:d,hasImportantModifier:f,baseClassName:p,maybePostfixModifierPosition:m}=n(t);if(u){l=t+(l.length>0?` `+l:l);continue}let h=!!m,g;if(h){g=r(p.substring(0,m));let e=g&&o[g]?r(p):void 0;e&&e!==g&&(g=e,h=!1)}else g=r(p);if(!g){if(!h){l=t+(l.length>0?` `+l:l);continue}if(g=r(p),!g){l=t+(l.length>0?` `+l:l);continue}h=!1}let ee=d.length===0?``:d.length===1?d[0]:a(d).join(`:`),te=f?ee+ue:ee,ne=te+g;if(s.indexOf(ne)>-1)continue;s.push(ne);let _=i(g,h);for(let e=0;e<_.length;++e){let t=_[e];s.push(te+t)}l=t+(l.length>0?` `+l:l)}return l},ge=(...e)=>{let t=0,n,r,i=``;for(;t{if(typeof e==`string`)return e;let t,n=``;for(let r=0;r{let n,r,i,a,o=o=>(n=C(t.reduce((e,t)=>t(e),e())),r=n.cache.get,i=n.cache.set,a=s,s(o)),s=e=>{let t=r(e);if(t)return t;let a=he(e,n);return i(e,a),a};return a=o,(...e)=>a(ge(...e))},_e=[],E=e=>{let t=t=>t[e]||_e;return t.isThemeGetter=!0,t},ve=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,D=/^\((?:(\w[\w-]*):)?(.+)\)$/i,ye=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,be=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,xe=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,Se=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,Ce=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,we=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,O=e=>ye.test(e),k=e=>!!e&&!Number.isNaN(Number(e)),A=e=>!!e&&Number.isInteger(Number(e)),Te=e=>e.endsWith(`%`)&&k(e.slice(0,-1)),j=e=>be.test(e),Ee=()=>!0,De=e=>xe.test(e)&&!Se.test(e),Oe=()=>!1,ke=e=>Ce.test(e),Ae=e=>we.test(e),je=e=>!M(e)&&!P(e),Me=e=>e.startsWith(`@container`)&&(e[10]===`/`&&e[11]!==void 0||e[11]===`s`&&e[16]!==void 0&&e.startsWith(`-size/`,10)||e[11]===`n`&&e[18]!==void 0&&e.startsWith(`-normal/`,10)),Ne=e=>F(e,Ye,Oe),M=e=>ve.test(e),N=e=>F(e,Xe,De),Pe=e=>F(e,Ze,k),Fe=e=>F(e,$e,Ee),Ie=e=>F(e,Qe,Oe),Le=e=>F(e,qe,Oe),Re=e=>F(e,Je,Ae),ze=e=>F(e,et,ke),P=e=>D.test(e),Be=e=>I(e,Xe),Ve=e=>I(e,Qe),He=e=>I(e,qe),Ue=e=>I(e,Ye),We=e=>I(e,Je),Ge=e=>I(e,et,!0),Ke=e=>I(e,$e,!0),F=(e,t,n)=>{let r=ve.exec(e);return r?r[1]?t(r[1]):n(r[2]):!1},I=(e,t,n=!1)=>{let r=D.exec(e);return r?r[1]?t(r[1]):n:!1},qe=e=>e===`position`||e===`percentage`,Je=e=>e===`image`||e===`url`,Ye=e=>e===`length`||e===`size`||e===`bg-size`,Xe=e=>e===`length`,Ze=e=>e===`number`,Qe=e=>e===`family-name`,$e=e=>e===`number`||e===`weight`,et=e=>e===`shadow`,tt=T(()=>{let e=E(`color`),t=E(`font`),n=E(`text`),r=E(`font-weight`),i=E(`tracking`),a=E(`leading`),o=E(`breakpoint`),s=E(`container`),c=E(`spacing`),l=E(`radius`),u=E(`shadow`),d=E(`inset-shadow`),f=E(`text-shadow`),p=E(`drop-shadow`),m=E(`blur`),h=E(`perspective`),g=E(`aspect`),ee=E(`ease`),te=E(`animate`),ne=()=>[`auto`,`avoid`,`all`,`avoid-page`,`page`,`left`,`right`,`column`],_=()=>[`center`,`top`,`bottom`,`left`,`right`,`top-left`,`left-top`,`top-right`,`right-top`,`bottom-right`,`right-bottom`,`bottom-left`,`left-bottom`],re=()=>[..._(),P,M],ie=()=>[`auto`,`hidden`,`clip`,`visible`,`scroll`],ae=()=>[`auto`,`contain`,`none`],v=()=>[P,M,c],y=()=>[O,`full`,`auto`,...v()],oe=()=>[A,`none`,`subgrid`,P,M],se=()=>[`auto`,{span:[`full`,A,P,M]},A,P,M],ce=()=>[A,`auto`,P,M],le=()=>[`auto`,`min`,`max`,`fr`,P,M],ue=()=>[`start`,`end`,`center`,`between`,`around`,`evenly`,`stretch`,`baseline`,`center-safe`,`end-safe`],b=()=>[`start`,`end`,`center`,`stretch`,`center-safe`,`end-safe`],x=()=>[`auto`,...v()],S=()=>[O,`auto`,`full`,`dvw`,`dvh`,`lvw`,`lvh`,`svw`,`svh`,`min`,`max`,`fit`,...v()],de=()=>[O,`screen`,`full`,`dvw`,`lvw`,`svw`,`min`,`max`,`fit`,...v()],fe=()=>[O,`screen`,`full`,`lh`,`dvh`,`lvh`,`svh`,`min`,`max`,`fit`,...v()],C=()=>[e,P,M],pe=()=>[..._(),He,Le,{position:[P,M]}],me=()=>[`no-repeat`,{repeat:[``,`x`,`y`,`space`,`round`]}],he=()=>[`auto`,`cover`,`contain`,Ue,Ne,{size:[P,M]}],ge=()=>[Te,Be,N],w=()=>[``,`none`,`full`,l,P,M],T=()=>[``,k,Be,N],_e=()=>[`solid`,`dashed`,`dotted`,`double`],ve=()=>[`normal`,`multiply`,`screen`,`overlay`,`darken`,`lighten`,`color-dodge`,`color-burn`,`hard-light`,`soft-light`,`difference`,`exclusion`,`hue`,`saturation`,`color`,`luminosity`],D=()=>[k,Te,He,Le],ye=()=>[``,`none`,m,P,M],be=()=>[`none`,k,P,M],xe=()=>[`none`,k,P,M],Se=()=>[k,P,M],Ce=()=>[O,`full`,...v()];return{cacheSize:500,theme:{animate:[`spin`,`ping`,`pulse`,`bounce`],aspect:[`video`],blur:[j],breakpoint:[j],color:[Ee],container:[j],"drop-shadow":[j],ease:[`in`,`out`,`in-out`],font:[je],"font-weight":[`thin`,`extralight`,`light`,`normal`,`medium`,`semibold`,`bold`,`extrabold`,`black`],"inset-shadow":[j],leading:[`none`,`tight`,`snug`,`normal`,`relaxed`,`loose`],perspective:[`dramatic`,`near`,`normal`,`midrange`,`distant`,`none`],radius:[j],shadow:[j],spacing:[`px`,k],text:[j],"text-shadow":[j],tracking:[`tighter`,`tight`,`normal`,`wide`,`wider`,`widest`]},classGroups:{aspect:[{aspect:[`auto`,`square`,O,M,P,g]}],container:[`container`],"container-type":[{"@container":[``,`normal`,`size`,P,M]}],"container-named":[Me],columns:[{columns:[k,M,P,s]}],"break-after":[{"break-after":ne()}],"break-before":[{"break-before":ne()}],"break-inside":[{"break-inside":[`auto`,`avoid`,`avoid-page`,`avoid-column`]}],"box-decoration":[{"box-decoration":[`slice`,`clone`]}],box:[{box:[`border`,`content`]}],display:[`block`,`inline-block`,`inline`,`flex`,`inline-flex`,`table`,`inline-table`,`table-caption`,`table-cell`,`table-column`,`table-column-group`,`table-footer-group`,`table-header-group`,`table-row-group`,`table-row`,`flow-root`,`grid`,`inline-grid`,`contents`,`list-item`,`hidden`],sr:[`sr-only`,`not-sr-only`],float:[{float:[`right`,`left`,`none`,`start`,`end`]}],clear:[{clear:[`left`,`right`,`both`,`none`,`start`,`end`]}],isolation:[`isolate`,`isolation-auto`],"object-fit":[{object:[`contain`,`cover`,`fill`,`none`,`scale-down`]}],"object-position":[{object:re()}],overflow:[{overflow:ie()}],"overflow-x":[{"overflow-x":ie()}],"overflow-y":[{"overflow-y":ie()}],overscroll:[{overscroll:ae()}],"overscroll-x":[{"overscroll-x":ae()}],"overscroll-y":[{"overscroll-y":ae()}],position:[`static`,`fixed`,`absolute`,`relative`,`sticky`],inset:[{inset:y()}],"inset-x":[{"inset-x":y()}],"inset-y":[{"inset-y":y()}],start:[{"inset-s":y(),start:y()}],end:[{"inset-e":y(),end:y()}],"inset-bs":[{"inset-bs":y()}],"inset-be":[{"inset-be":y()}],top:[{top:y()}],right:[{right:y()}],bottom:[{bottom:y()}],left:[{left:y()}],visibility:[`visible`,`invisible`,`collapse`],z:[{z:[A,`auto`,P,M]}],basis:[{basis:[O,`full`,`auto`,s,...v()]}],"flex-direction":[{flex:[`row`,`row-reverse`,`col`,`col-reverse`]}],"flex-wrap":[{flex:[`nowrap`,`wrap`,`wrap-reverse`]}],flex:[{flex:[k,O,`auto`,`initial`,`none`,M]}],grow:[{grow:[``,k,P,M]}],shrink:[{shrink:[``,k,P,M]}],order:[{order:[A,`first`,`last`,`none`,P,M]}],"grid-cols":[{"grid-cols":oe()}],"col-start-end":[{col:se()}],"col-start":[{"col-start":ce()}],"col-end":[{"col-end":ce()}],"grid-rows":[{"grid-rows":oe()}],"row-start-end":[{row:se()}],"row-start":[{"row-start":ce()}],"row-end":[{"row-end":ce()}],"grid-flow":[{"grid-flow":[`row`,`col`,`dense`,`row-dense`,`col-dense`]}],"auto-cols":[{"auto-cols":le()}],"auto-rows":[{"auto-rows":le()}],gap:[{gap:v()}],"gap-x":[{"gap-x":v()}],"gap-y":[{"gap-y":v()}],"justify-content":[{justify:[...ue(),`normal`]}],"justify-items":[{"justify-items":[...b(),`normal`]}],"justify-self":[{"justify-self":[`auto`,...b()]}],"align-content":[{content:[`normal`,...ue()]}],"align-items":[{items:[...b(),{baseline:[``,`last`]}]}],"align-self":[{self:[`auto`,...b(),{baseline:[``,`last`]}]}],"place-content":[{"place-content":ue()}],"place-items":[{"place-items":[...b(),`baseline`]}],"place-self":[{"place-self":[`auto`,...b()]}],p:[{p:v()}],px:[{px:v()}],py:[{py:v()}],ps:[{ps:v()}],pe:[{pe:v()}],pbs:[{pbs:v()}],pbe:[{pbe:v()}],pt:[{pt:v()}],pr:[{pr:v()}],pb:[{pb:v()}],pl:[{pl:v()}],m:[{m:x()}],mx:[{mx:x()}],my:[{my:x()}],ms:[{ms:x()}],me:[{me:x()}],mbs:[{mbs:x()}],mbe:[{mbe:x()}],mt:[{mt:x()}],mr:[{mr:x()}],mb:[{mb:x()}],ml:[{ml:x()}],"space-x":[{"space-x":v()}],"space-x-reverse":[`space-x-reverse`],"space-y":[{"space-y":v()}],"space-y-reverse":[`space-y-reverse`],size:[{size:S()}],"inline-size":[{inline:[`auto`,...de()]}],"min-inline-size":[{"min-inline":[`auto`,...de()]}],"max-inline-size":[{"max-inline":[`none`,...de()]}],"block-size":[{block:[`auto`,...fe()]}],"min-block-size":[{"min-block":[`auto`,...fe()]}],"max-block-size":[{"max-block":[`none`,...fe()]}],w:[{w:[s,`screen`,...S()]}],"min-w":[{"min-w":[s,`screen`,`none`,...S()]}],"max-w":[{"max-w":[s,`screen`,`none`,`prose`,{screen:[o]},...S()]}],h:[{h:[`screen`,`lh`,...S()]}],"min-h":[{"min-h":[`screen`,`lh`,`none`,...S()]}],"max-h":[{"max-h":[`screen`,`lh`,...S()]}],"font-size":[{text:[`base`,n,Be,N]}],"font-smoothing":[`antialiased`,`subpixel-antialiased`],"font-style":[`italic`,`not-italic`],"font-weight":[{font:[r,Ke,Fe]}],"font-stretch":[{"font-stretch":[`ultra-condensed`,`extra-condensed`,`condensed`,`semi-condensed`,`normal`,`semi-expanded`,`expanded`,`extra-expanded`,`ultra-expanded`,Te,M]}],"font-family":[{font:[Ve,Ie,t]}],"font-features":[{"font-features":[M]}],"fvn-normal":[`normal-nums`],"fvn-ordinal":[`ordinal`],"fvn-slashed-zero":[`slashed-zero`],"fvn-figure":[`lining-nums`,`oldstyle-nums`],"fvn-spacing":[`proportional-nums`,`tabular-nums`],"fvn-fraction":[`diagonal-fractions`,`stacked-fractions`],tracking:[{tracking:[i,P,M]}],"line-clamp":[{"line-clamp":[k,`none`,P,Pe]}],leading:[{leading:[a,...v()]}],"list-image":[{"list-image":[`none`,P,M]}],"list-style-position":[{list:[`inside`,`outside`]}],"list-style-type":[{list:[`disc`,`decimal`,`none`,P,M]}],"text-alignment":[{text:[`left`,`center`,`right`,`justify`,`start`,`end`]}],"placeholder-color":[{placeholder:C()}],"text-color":[{text:C()}],"text-decoration":[`underline`,`overline`,`line-through`,`no-underline`],"text-decoration-style":[{decoration:[..._e(),`wavy`]}],"text-decoration-thickness":[{decoration:[k,`from-font`,`auto`,P,N]}],"text-decoration-color":[{decoration:C()}],"underline-offset":[{"underline-offset":[k,`auto`,P,M]}],"text-transform":[`uppercase`,`lowercase`,`capitalize`,`normal-case`],"text-overflow":[`truncate`,`text-ellipsis`,`text-clip`],"text-wrap":[{text:[`wrap`,`nowrap`,`balance`,`pretty`]}],indent:[{indent:v()}],"tab-size":[{tab:[A,P,M]}],"vertical-align":[{align:[`baseline`,`top`,`middle`,`bottom`,`text-top`,`text-bottom`,`sub`,`super`,P,M]}],whitespace:[{whitespace:[`normal`,`nowrap`,`pre`,`pre-line`,`pre-wrap`,`break-spaces`]}],break:[{break:[`normal`,`words`,`all`,`keep`]}],wrap:[{wrap:[`break-word`,`anywhere`,`normal`]}],hyphens:[{hyphens:[`none`,`manual`,`auto`]}],content:[{content:[`none`,P,M]}],"bg-attachment":[{bg:[`fixed`,`local`,`scroll`]}],"bg-clip":[{"bg-clip":[`border`,`padding`,`content`,`text`]}],"bg-origin":[{"bg-origin":[`border`,`padding`,`content`]}],"bg-position":[{bg:pe()}],"bg-repeat":[{bg:me()}],"bg-size":[{bg:he()}],"bg-image":[{bg:[`none`,{linear:[{to:[`t`,`tr`,`r`,`br`,`b`,`bl`,`l`,`tl`]},A,P,M],radial:[``,P,M],conic:[A,P,M]},We,Re]}],"bg-color":[{bg:C()}],"gradient-from-pos":[{from:ge()}],"gradient-via-pos":[{via:ge()}],"gradient-to-pos":[{to:ge()}],"gradient-from":[{from:C()}],"gradient-via":[{via:C()}],"gradient-to":[{to:C()}],rounded:[{rounded:w()}],"rounded-s":[{"rounded-s":w()}],"rounded-e":[{"rounded-e":w()}],"rounded-t":[{"rounded-t":w()}],"rounded-r":[{"rounded-r":w()}],"rounded-b":[{"rounded-b":w()}],"rounded-l":[{"rounded-l":w()}],"rounded-ss":[{"rounded-ss":w()}],"rounded-se":[{"rounded-se":w()}],"rounded-ee":[{"rounded-ee":w()}],"rounded-es":[{"rounded-es":w()}],"rounded-tl":[{"rounded-tl":w()}],"rounded-tr":[{"rounded-tr":w()}],"rounded-br":[{"rounded-br":w()}],"rounded-bl":[{"rounded-bl":w()}],"border-w":[{border:T()}],"border-w-x":[{"border-x":T()}],"border-w-y":[{"border-y":T()}],"border-w-s":[{"border-s":T()}],"border-w-e":[{"border-e":T()}],"border-w-bs":[{"border-bs":T()}],"border-w-be":[{"border-be":T()}],"border-w-t":[{"border-t":T()}],"border-w-r":[{"border-r":T()}],"border-w-b":[{"border-b":T()}],"border-w-l":[{"border-l":T()}],"divide-x":[{"divide-x":T()}],"divide-x-reverse":[`divide-x-reverse`],"divide-y":[{"divide-y":T()}],"divide-y-reverse":[`divide-y-reverse`],"border-style":[{border:[..._e(),`hidden`,`none`]}],"divide-style":[{divide:[..._e(),`hidden`,`none`]}],"border-color":[{border:C()}],"border-color-x":[{"border-x":C()}],"border-color-y":[{"border-y":C()}],"border-color-s":[{"border-s":C()}],"border-color-e":[{"border-e":C()}],"border-color-bs":[{"border-bs":C()}],"border-color-be":[{"border-be":C()}],"border-color-t":[{"border-t":C()}],"border-color-r":[{"border-r":C()}],"border-color-b":[{"border-b":C()}],"border-color-l":[{"border-l":C()}],"divide-color":[{divide:C()}],"outline-style":[{outline:[..._e(),`none`,`hidden`]}],"outline-offset":[{"outline-offset":[k,P,M]}],"outline-w":[{outline:[``,k,Be,N]}],"outline-color":[{outline:C()}],shadow:[{shadow:[``,`none`,u,Ge,ze]}],"shadow-color":[{shadow:C()}],"inset-shadow":[{"inset-shadow":[`none`,d,Ge,ze]}],"inset-shadow-color":[{"inset-shadow":C()}],"ring-w":[{ring:T()}],"ring-w-inset":[`ring-inset`],"ring-color":[{ring:C()}],"ring-offset-w":[{"ring-offset":[k,N]}],"ring-offset-color":[{"ring-offset":C()}],"inset-ring-w":[{"inset-ring":T()}],"inset-ring-color":[{"inset-ring":C()}],"text-shadow":[{"text-shadow":[`none`,f,Ge,ze]}],"text-shadow-color":[{"text-shadow":C()}],opacity:[{opacity:[k,P,M]}],"mix-blend":[{"mix-blend":[...ve(),`plus-darker`,`plus-lighter`]}],"bg-blend":[{"bg-blend":ve()}],"mask-clip":[{"mask-clip":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]},`mask-no-clip`],"mask-composite":[{mask:[`add`,`subtract`,`intersect`,`exclude`]}],"mask-image-linear-pos":[{"mask-linear":[k]}],"mask-image-linear-from-pos":[{"mask-linear-from":D()}],"mask-image-linear-to-pos":[{"mask-linear-to":D()}],"mask-image-linear-from-color":[{"mask-linear-from":C()}],"mask-image-linear-to-color":[{"mask-linear-to":C()}],"mask-image-t-from-pos":[{"mask-t-from":D()}],"mask-image-t-to-pos":[{"mask-t-to":D()}],"mask-image-t-from-color":[{"mask-t-from":C()}],"mask-image-t-to-color":[{"mask-t-to":C()}],"mask-image-r-from-pos":[{"mask-r-from":D()}],"mask-image-r-to-pos":[{"mask-r-to":D()}],"mask-image-r-from-color":[{"mask-r-from":C()}],"mask-image-r-to-color":[{"mask-r-to":C()}],"mask-image-b-from-pos":[{"mask-b-from":D()}],"mask-image-b-to-pos":[{"mask-b-to":D()}],"mask-image-b-from-color":[{"mask-b-from":C()}],"mask-image-b-to-color":[{"mask-b-to":C()}],"mask-image-l-from-pos":[{"mask-l-from":D()}],"mask-image-l-to-pos":[{"mask-l-to":D()}],"mask-image-l-from-color":[{"mask-l-from":C()}],"mask-image-l-to-color":[{"mask-l-to":C()}],"mask-image-x-from-pos":[{"mask-x-from":D()}],"mask-image-x-to-pos":[{"mask-x-to":D()}],"mask-image-x-from-color":[{"mask-x-from":C()}],"mask-image-x-to-color":[{"mask-x-to":C()}],"mask-image-y-from-pos":[{"mask-y-from":D()}],"mask-image-y-to-pos":[{"mask-y-to":D()}],"mask-image-y-from-color":[{"mask-y-from":C()}],"mask-image-y-to-color":[{"mask-y-to":C()}],"mask-image-radial":[{"mask-radial":[P,M]}],"mask-image-radial-from-pos":[{"mask-radial-from":D()}],"mask-image-radial-to-pos":[{"mask-radial-to":D()}],"mask-image-radial-from-color":[{"mask-radial-from":C()}],"mask-image-radial-to-color":[{"mask-radial-to":C()}],"mask-image-radial-shape":[{"mask-radial":[`circle`,`ellipse`]}],"mask-image-radial-size":[{"mask-radial":[{closest:[`side`,`corner`],farthest:[`side`,`corner`]}]}],"mask-image-radial-pos":[{"mask-radial-at":_()}],"mask-image-conic-pos":[{"mask-conic":[k]}],"mask-image-conic-from-pos":[{"mask-conic-from":D()}],"mask-image-conic-to-pos":[{"mask-conic-to":D()}],"mask-image-conic-from-color":[{"mask-conic-from":C()}],"mask-image-conic-to-color":[{"mask-conic-to":C()}],"mask-mode":[{mask:[`alpha`,`luminance`,`match`]}],"mask-origin":[{"mask-origin":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]}],"mask-position":[{mask:pe()}],"mask-repeat":[{mask:me()}],"mask-size":[{mask:he()}],"mask-type":[{"mask-type":[`alpha`,`luminance`]}],"mask-image":[{mask:[`none`,P,M]}],filter:[{filter:[``,`none`,P,M]}],blur:[{blur:ye()}],brightness:[{brightness:[k,P,M]}],contrast:[{contrast:[k,P,M]}],"drop-shadow":[{"drop-shadow":[``,`none`,p,Ge,ze]}],"drop-shadow-color":[{"drop-shadow":C()}],grayscale:[{grayscale:[``,k,P,M]}],"hue-rotate":[{"hue-rotate":[k,P,M]}],invert:[{invert:[``,k,P,M]}],saturate:[{saturate:[k,P,M]}],sepia:[{sepia:[``,k,P,M]}],"backdrop-filter":[{"backdrop-filter":[``,`none`,P,M]}],"backdrop-blur":[{"backdrop-blur":ye()}],"backdrop-brightness":[{"backdrop-brightness":[k,P,M]}],"backdrop-contrast":[{"backdrop-contrast":[k,P,M]}],"backdrop-grayscale":[{"backdrop-grayscale":[``,k,P,M]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[k,P,M]}],"backdrop-invert":[{"backdrop-invert":[``,k,P,M]}],"backdrop-opacity":[{"backdrop-opacity":[k,P,M]}],"backdrop-saturate":[{"backdrop-saturate":[k,P,M]}],"backdrop-sepia":[{"backdrop-sepia":[``,k,P,M]}],"border-collapse":[{border:[`collapse`,`separate`]}],"border-spacing":[{"border-spacing":v()}],"border-spacing-x":[{"border-spacing-x":v()}],"border-spacing-y":[{"border-spacing-y":v()}],"table-layout":[{table:[`auto`,`fixed`]}],caption:[{caption:[`top`,`bottom`]}],transition:[{transition:[``,`all`,`colors`,`opacity`,`shadow`,`transform`,`none`,P,M]}],"transition-behavior":[{transition:[`normal`,`discrete`]}],duration:[{duration:[k,`initial`,P,M]}],ease:[{ease:[`linear`,`initial`,ee,P,M]}],delay:[{delay:[k,P,M]}],animate:[{animate:[`none`,te,P,M]}],backface:[{backface:[`hidden`,`visible`]}],perspective:[{perspective:[h,P,M]}],"perspective-origin":[{"perspective-origin":re()}],rotate:[{rotate:be()}],"rotate-x":[{"rotate-x":be()}],"rotate-y":[{"rotate-y":be()}],"rotate-z":[{"rotate-z":be()}],scale:[{scale:xe()}],"scale-x":[{"scale-x":xe()}],"scale-y":[{"scale-y":xe()}],"scale-z":[{"scale-z":xe()}],"scale-3d":[`scale-3d`],skew:[{skew:Se()}],"skew-x":[{"skew-x":Se()}],"skew-y":[{"skew-y":Se()}],transform:[{transform:[P,M,``,`none`,`gpu`,`cpu`]}],"transform-origin":[{origin:re()}],"transform-style":[{transform:[`3d`,`flat`]}],translate:[{translate:Ce()}],"translate-x":[{"translate-x":Ce()}],"translate-y":[{"translate-y":Ce()}],"translate-z":[{"translate-z":Ce()}],"translate-none":[`translate-none`],zoom:[{zoom:[A,P,M]}],accent:[{accent:C()}],appearance:[{appearance:[`none`,`auto`]}],"caret-color":[{caret:C()}],"color-scheme":[{scheme:[`normal`,`dark`,`light`,`light-dark`,`only-dark`,`only-light`]}],cursor:[{cursor:[`auto`,`default`,`pointer`,`wait`,`text`,`move`,`help`,`not-allowed`,`none`,`context-menu`,`progress`,`cell`,`crosshair`,`vertical-text`,`alias`,`copy`,`no-drop`,`grab`,`grabbing`,`all-scroll`,`col-resize`,`row-resize`,`n-resize`,`e-resize`,`s-resize`,`w-resize`,`ne-resize`,`nw-resize`,`se-resize`,`sw-resize`,`ew-resize`,`ns-resize`,`nesw-resize`,`nwse-resize`,`zoom-in`,`zoom-out`,P,M]}],"field-sizing":[{"field-sizing":[`fixed`,`content`]}],"pointer-events":[{"pointer-events":[`auto`,`none`]}],resize:[{resize:[`none`,``,`y`,`x`]}],"scroll-behavior":[{scroll:[`auto`,`smooth`]}],"scrollbar-thumb-color":[{"scrollbar-thumb":C()}],"scrollbar-track-color":[{"scrollbar-track":C()}],"scrollbar-gutter":[{"scrollbar-gutter":[`auto`,`stable`,`both`]}],"scrollbar-w":[{scrollbar:[`auto`,`thin`,`none`]}],"scroll-m":[{"scroll-m":v()}],"scroll-mx":[{"scroll-mx":v()}],"scroll-my":[{"scroll-my":v()}],"scroll-ms":[{"scroll-ms":v()}],"scroll-me":[{"scroll-me":v()}],"scroll-mbs":[{"scroll-mbs":v()}],"scroll-mbe":[{"scroll-mbe":v()}],"scroll-mt":[{"scroll-mt":v()}],"scroll-mr":[{"scroll-mr":v()}],"scroll-mb":[{"scroll-mb":v()}],"scroll-ml":[{"scroll-ml":v()}],"scroll-p":[{"scroll-p":v()}],"scroll-px":[{"scroll-px":v()}],"scroll-py":[{"scroll-py":v()}],"scroll-ps":[{"scroll-ps":v()}],"scroll-pe":[{"scroll-pe":v()}],"scroll-pbs":[{"scroll-pbs":v()}],"scroll-pbe":[{"scroll-pbe":v()}],"scroll-pt":[{"scroll-pt":v()}],"scroll-pr":[{"scroll-pr":v()}],"scroll-pb":[{"scroll-pb":v()}],"scroll-pl":[{"scroll-pl":v()}],"snap-align":[{snap:[`start`,`end`,`center`,`align-none`]}],"snap-stop":[{snap:[`normal`,`always`]}],"snap-type":[{snap:[`none`,`x`,`y`,`both`]}],"snap-strictness":[{snap:[`mandatory`,`proximity`]}],touch:[{touch:[`auto`,`none`,`manipulation`]}],"touch-x":[{"touch-pan":[`x`,`left`,`right`]}],"touch-y":[{"touch-pan":[`y`,`up`,`down`]}],"touch-pz":[`touch-pinch-zoom`],select:[{select:[`none`,`text`,`all`,`auto`]}],"will-change":[{"will-change":[`auto`,`scroll`,`contents`,`transform`,P,M]}],fill:[{fill:[`none`,...C()]}],"stroke-w":[{stroke:[k,Be,N,Pe]}],stroke:[{stroke:[`none`,...C()]}],"forced-color-adjust":[{"forced-color-adjust":[`auto`,`none`]}]},conflictingClassGroups:{"container-named":[`container-type`],overflow:[`overflow-x`,`overflow-y`],overscroll:[`overscroll-x`,`overscroll-y`],inset:[`inset-x`,`inset-y`,`inset-bs`,`inset-be`,`start`,`end`,`top`,`right`,`bottom`,`left`],"inset-x":[`right`,`left`],"inset-y":[`top`,`bottom`],flex:[`basis`,`grow`,`shrink`],gap:[`gap-x`,`gap-y`],p:[`px`,`py`,`ps`,`pe`,`pbs`,`pbe`,`pt`,`pr`,`pb`,`pl`],px:[`pr`,`pl`],py:[`pt`,`pb`],m:[`mx`,`my`,`ms`,`me`,`mbs`,`mbe`,`mt`,`mr`,`mb`,`ml`],mx:[`mr`,`ml`],my:[`mt`,`mb`],size:[`w`,`h`],"font-size":[`leading`],"fvn-normal":[`fvn-ordinal`,`fvn-slashed-zero`,`fvn-figure`,`fvn-spacing`,`fvn-fraction`],"fvn-ordinal":[`fvn-normal`],"fvn-slashed-zero":[`fvn-normal`],"fvn-figure":[`fvn-normal`],"fvn-spacing":[`fvn-normal`],"fvn-fraction":[`fvn-normal`],"line-clamp":[`display`,`overflow`],rounded:[`rounded-s`,`rounded-e`,`rounded-t`,`rounded-r`,`rounded-b`,`rounded-l`,`rounded-ss`,`rounded-se`,`rounded-ee`,`rounded-es`,`rounded-tl`,`rounded-tr`,`rounded-br`,`rounded-bl`],"rounded-s":[`rounded-ss`,`rounded-es`],"rounded-e":[`rounded-se`,`rounded-ee`],"rounded-t":[`rounded-tl`,`rounded-tr`],"rounded-r":[`rounded-tr`,`rounded-br`],"rounded-b":[`rounded-br`,`rounded-bl`],"rounded-l":[`rounded-tl`,`rounded-bl`],"border-spacing":[`border-spacing-x`,`border-spacing-y`],"border-w":[`border-w-x`,`border-w-y`,`border-w-s`,`border-w-e`,`border-w-bs`,`border-w-be`,`border-w-t`,`border-w-r`,`border-w-b`,`border-w-l`],"border-w-x":[`border-w-r`,`border-w-l`],"border-w-y":[`border-w-t`,`border-w-b`],"border-color":[`border-color-x`,`border-color-y`,`border-color-s`,`border-color-e`,`border-color-bs`,`border-color-be`,`border-color-t`,`border-color-r`,`border-color-b`,`border-color-l`],"border-color-x":[`border-color-r`,`border-color-l`],"border-color-y":[`border-color-t`,`border-color-b`],translate:[`translate-x`,`translate-y`,`translate-none`],"translate-none":[`translate`,`translate-x`,`translate-y`,`translate-z`],"scroll-m":[`scroll-mx`,`scroll-my`,`scroll-ms`,`scroll-me`,`scroll-mbs`,`scroll-mbe`,`scroll-mt`,`scroll-mr`,`scroll-mb`,`scroll-ml`],"scroll-mx":[`scroll-mr`,`scroll-ml`],"scroll-my":[`scroll-mt`,`scroll-mb`],"scroll-p":[`scroll-px`,`scroll-py`,`scroll-ps`,`scroll-pe`,`scroll-pbs`,`scroll-pbe`,`scroll-pt`,`scroll-pr`,`scroll-pb`,`scroll-pl`],"scroll-px":[`scroll-pr`,`scroll-pl`],"scroll-py":[`scroll-pt`,`scroll-pb`],touch:[`touch-x`,`touch-y`,`touch-pz`],"touch-x":[`touch`],"touch-y":[`touch`],"touch-pz":[`touch`]},conflictingClassGroupModifiers:{"font-size":[`leading`]},postfixLookupClassGroups:[`container-type`],orderSensitiveModifiers:[`*`,`**`,`after`,`backdrop`,`before`,`details-content`,`file`,`first-letter`,`first-line`,`marker`,`placeholder`,`selection`]}});function nt(...e){return tt(u(e))}var rt=new Intl.DateTimeFormat(void 0,{year:`numeric`,month:`long`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`});function it(e){return e?rt.format(new Date(e)):`Unknown`}function at(e){return`${Math.round(e/1024)} GiB`}var ot=[`B`,`KB`,`MB`,`GB`,`TB`];function st(e){if(e===0)return`0 B`;let t=Math.floor(Math.log(e)/Math.log(1024));return`${(e/1024**t).toFixed(2)} ${ot[t]}`}function ct(e){return e?.find(e=>e.Key===`Name`)?.Value}var lt=/\/$/;function ut(e){return e.replace(lt,``)}function dt(e){return e&&(e.endsWith(`/`)?e:`${e}/`)}function ft(e,t){return e.startsWith(t)?e:`${t}${e}`}function pt(e,t){return e.startsWith(t)?e.slice(t.length):e}var mt;function L(e,t,n){function r(n,r){if(n._zod||Object.defineProperty(n,"_zod",{value:{def:r,constr:o,traits:new Set},enumerable:!1}),n._zod.traits.has(e))return;n._zod.traits.add(e),t(n,r);let i=o.prototype,a=Object.keys(i);for(let e=0;en?.Parent&&t instanceof n.Parent?!0:t?._zod?.traits?.has(e)}),Object.defineProperty(o,"name",{value:e}),o}var ht=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}},gt=class extends Error{constructor(e){super(`Encountered unidirectional transform during encode: ${e}`),this.name=`ZodEncodeError`}};(mt=globalThis).__zod_globalConfig??(mt.__zod_globalConfig={});var _t=globalThis.__zod_globalConfig;function R(e){return e&&Object.assign(_t,e),_t}function vt(e){let t=Object.values(e).filter(e=>typeof e==`number`);return Object.entries(e).filter(([e,n])=>t.indexOf(+e)===-1).map(([e,t])=>t)}function yt(e,t){return typeof t==`bigint`?t.toString():t}function bt(e){return{get value(){{let t=e();return Object.defineProperty(this,"value",{value:t}),t}throw Error(`cached value already set`)}}}function xt(e){return e==null}function St(e){let t=+!!e.startsWith(`^`),n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}function Ct(e,t){let n=e/t,r=Math.round(n),i=2**-52*Math.max(Math.abs(n),1);return Math.abs(n-r){};function Ot(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}var kt=bt(()=>{if(_t.jitless||typeof navigator<`u`&&navigator?.userAgent?.includes(`Cloudflare`))return!1;try{return Function(``),!0}catch{return!1}});function At(e){if(Ot(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(Ot(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}function jt(e){return At(e)?{...e}:Array.isArray(e)?[...e]:e instanceof Map?new Map(e):e instanceof Set?new Set(e):e}var Mt=new Set([`string`,`number`,`symbol`]);function Nt(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function H(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function U(e){let t=e;if(!t)return{};if(typeof t==`string`)return{error:()=>t};if(t?.message!==void 0){if(t?.error!==void 0)throw Error("Cannot specify both `message` and `error` params");t.error=t.message}return delete t.message,typeof t.error==`string`?{...t,error:()=>t.error}:t}function Pt(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}var Ft={safeint:[-(2**53-1),2**53-1],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};function It(e,t){let n=e._zod.def,r=n.checks;if(r&&r.length>0)throw Error(`.pick() cannot be used on object schemas containing refinements`);return H(e,V(e._zod.def,{get shape(){let e={};for(let r in t){if(!(r in n.shape))throw Error(`Unrecognized key: "${r}"`);t[r]&&(e[r]=n.shape[r])}return B(this,`shape`,e),e},checks:[]}))}function Lt(e,t){let n=e._zod.def,r=n.checks;if(r&&r.length>0)throw Error(`.omit() cannot be used on object schemas containing refinements`);return H(e,V(e._zod.def,{get shape(){let r={...e._zod.def.shape};for(let e in t){if(!(e in n.shape))throw Error(`Unrecognized key: "${e}"`);t[e]&&delete r[e]}return B(this,`shape`,r),r},checks:[]}))}function Rt(e,t){if(!At(t))throw Error(`Invalid input to extend: expected a plain object`);let n=e._zod.def.checks;if(n&&n.length>0){let n=e._zod.def.shape;for(let e in t)if(Object.getOwnPropertyDescriptor(n,e)!==void 0)throw Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}return H(e,V(e._zod.def,{get shape(){let n={...e._zod.def.shape,...t};return B(this,`shape`,n),n}}))}function zt(e,t){if(!At(t))throw Error(`Invalid input to safeExtend: expected a plain object`);return H(e,V(e._zod.def,{get shape(){let n={...e._zod.def.shape,...t};return B(this,`shape`,n),n}}))}function Bt(e,t){if(e._zod.def.checks?.length)throw Error(`.merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.`);return H(e,V(e._zod.def,{get shape(){let n={...e._zod.def.shape,...t._zod.def.shape};return B(this,`shape`,n),n},get catchall(){return t._zod.def.catchall},checks:t._zod.def.checks??[]}))}function Vt(e,t,n){let r=t._zod.def.checks;if(r&&r.length>0)throw Error(`.partial() cannot be used on object schemas containing refinements`);return H(t,V(t._zod.def,{get shape(){let r=t._zod.def.shape,i={...r};if(n)for(let t in n){if(!(t in r))throw Error(`Unrecognized key: "${t}"`);n[t]&&(i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t])}else for(let t in r)i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t];return B(this,`shape`,i),i},checks:[]}))}function Ht(e,t,n){return H(t,V(t._zod.def,{get shape(){let r=t._zod.def.shape,i={...r};if(n)for(let t in n){if(!(t in i))throw Error(`Unrecognized key: "${t}"`);n[t]&&(i[t]=new e({type:`nonoptional`,innerType:r[t]}))}else for(let t in r)i[t]=new e({type:`nonoptional`,innerType:r[t]});return B(this,`shape`,i),i}}))}function Ut(e,t=0){if(e.aborted===!0)return!0;for(let n=t;n{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function Kt(e){return typeof e==`string`?e:e?.message}function W(e,t,n){let r=e.message?e.message:Kt(e.inst?._zod.def?.error?.(e))??Kt(t?.error?.(e))??Kt(n.customError?.(e))??Kt(n.localeError?.(e))??`Invalid input`,{inst:i,continue:a,input:o,...s}=e;return s.path??=[],s.message=r,t?.reportInput&&(s.input=o),s}function qt(e){return Array.isArray(e)?`array`:typeof e==`string`?`string`:`unknown`}function Jt(...e){let[t,n,r]=e;return typeof t==`string`?{message:t,code:`custom`,input:n,inst:r}:{...t}}var Yt=(e,t)=>{e.name=`$ZodError`,Object.defineProperty(e,"_zod",{value:e._zod,enumerable:!1}),Object.defineProperty(e,"issues",{value:t,enumerable:!1}),e.message=JSON.stringify(t,yt,2),Object.defineProperty(e,"toString",{value:()=>e.message,enumerable:!1})},Xt=L(`$ZodError`,Yt),Zt=L(`$ZodError`,Yt,{Parent:Error});function Qt(e,t=e=>e.message){let n={},r=[];for(let i of e.issues)i.path.length>0?(n[i.path[0]]=n[i.path[0]]||[],n[i.path[0]].push(t(i))):r.push(t(i));return{formErrors:r,fieldErrors:n}}function $t(e,t=e=>e.message){let n={_errors:[]},r=(e,i=[])=>{for(let a of e.issues)if(a.code===`invalid_union`&&a.errors.length)a.errors.map(e=>r({issues:e},[...i,...a.path]));else if(a.code===`invalid_key`)r({issues:a.issues},[...i,...a.path]);else if(a.code===`invalid_element`)r({issues:a.issues},[...i,...a.path]);else{let e=[...i,...a.path];if(e.length===0)n._errors.push(t(a));else{let r=n,i=0;for(;i(t,n,r,i)=>{let a=r?{...r,async:!1}:{async:!1},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise)throw new ht;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>W(e,a,R())));throw Dt(t,i?.callee),t}return o.value},tn=en(Zt),nn=e=>async(t,n,r,i)=>{let a=r?{...r,async:!0}:{async:!0},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise&&(o=await o),o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>W(e,a,R())));throw Dt(t,i?.callee),t}return o.value},rn=nn(Zt),an=e=>(t,n,r)=>{let i=r?{...r,async:!1}:{async:!1},a=t._zod.run({value:n,issues:[]},i);if(a instanceof Promise)throw new ht;return a.issues.length?{success:!1,error:new(e??Xt)(a.issues.map(e=>W(e,i,R())))}:{success:!0,data:a.value}},on=an(Zt),sn=e=>async(t,n,r)=>{let i=r?{...r,async:!0}:{async:!0},a=t._zod.run({value:n,issues:[]},i);return a instanceof Promise&&(a=await a),a.issues.length?{success:!1,error:new e(a.issues.map(e=>W(e,i,R())))}:{success:!0,data:a.value}},cn=sn(Zt),ln=e=>(t,n,r)=>{let i=r?{...r,direction:`backward`}:{direction:`backward`};return en(e)(t,n,i)},un=e=>(t,n,r)=>en(e)(t,n,r),dn=e=>async(t,n,r)=>{let i=r?{...r,direction:`backward`}:{direction:`backward`};return nn(e)(t,n,i)},fn=e=>async(t,n,r)=>nn(e)(t,n,r),pn=e=>(t,n,r)=>{let i=r?{...r,direction:`backward`}:{direction:`backward`};return an(e)(t,n,i)},mn=e=>(t,n,r)=>an(e)(t,n,r),hn=e=>async(t,n,r)=>{let i=r?{...r,direction:`backward`}:{direction:`backward`};return sn(e)(t,n,i)},gn=e=>async(t,n,r)=>sn(e)(t,n,r),_n=/^[cC][0-9a-z]{6,}$/,vn=/^[0-9a-z]+$/,yn=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,bn=/^[0-9a-vA-V]{20}$/,xn=/^[A-Za-z0-9]{27}$/,Sn=/^[a-zA-Z0-9_-]{21}$/,Cn=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,wn=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,Tn=e=>e?RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${e}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`):/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,En=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/,Dn=`^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;function On(){return new RegExp(Dn,`u`)}var kn=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,An=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,jn=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,Mn=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,Nn=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,Pn=/^[A-Za-z0-9_-]*$/,Fn=/^https?$/,In=/^\+[1-9]\d{6,14}$/,Ln=`(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`,Rn=RegExp(`^${Ln}$`);function zn(e){let t=`(?:[01]\\d|2[0-3]):[0-5]\\d`;return typeof e.precision==`number`?e.precision===-1?`${t}`:e.precision===0?`${t}:[0-5]\\d`:`${t}:[0-5]\\d\\.\\d{${e.precision}}`:`${t}(?::[0-5]\\d(?:\\.\\d+)?)?`}function Bn(e){return RegExp(`^${zn(e)}$`)}function Vn(e){let t=zn({precision:e.precision}),n=[`Z`];e.local&&n.push(``),e.offset&&n.push(`([+-](?:[01]\\d|2[0-3]):[0-5]\\d)`);let r=`${t}(?:${n.join(`|`)})`;return RegExp(`^${Ln}T(?:${r})$`)}var Hn=e=>{let t=e?`[\\s\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\s\\S]*`;return RegExp(`^${t}$`)},Un=/^-?\d+$/,Wn=/^-?\d+(?:\.\d+)?$/,Gn=/^(?:true|false)$/i,Kn=/^[^A-Z]*$/,qn=/^[^a-z]*$/,G=L(`$ZodCheck`,(e,t)=>{var n;e._zod??={},e._zod.def=t,(n=e._zod).onattach??(n.onattach=[])}),Jn={number:`number`,bigint:`bigint`,object:`date`},Yn=L(`$ZodCheckLessThan`,(e,t)=>{G.init(e,t);let n=Jn[typeof t.value];e._zod.onattach.push(e=>{let n=e._zod.bag,r=(t.inclusive?n.maximum:n.exclusiveMaximum)??1/0;t.value{(t.inclusive?r.value<=t.value:r.value{G.init(e,t);let n=Jn[typeof t.value];e._zod.onattach.push(e=>{let n=e._zod.bag,r=(t.inclusive?n.minimum:n.exclusiveMinimum)??-1/0;t.value>r&&(t.inclusive?n.minimum=t.value:n.exclusiveMinimum=t.value)}),e._zod.check=r=>{(t.inclusive?r.value>=t.value:r.value>t.value)||r.issues.push({origin:n,code:`too_small`,minimum:typeof t.value==`object`?t.value.getTime():t.value,input:r.value,inclusive:t.inclusive,inst:e,continue:!t.abort})}}),Zn=L(`$ZodCheckMultipleOf`,(e,t)=>{G.init(e,t),e._zod.onattach.push(e=>{var n;(n=e._zod.bag).multipleOf??(n.multipleOf=t.value)}),e._zod.check=n=>{if(typeof n.value!=typeof t.value)throw Error(`Cannot mix number and bigint in multiple_of check.`);(typeof n.value==`bigint`?n.value%t.value===BigInt(0):Ct(n.value,t.value)===0)||n.issues.push({origin:typeof n.value,code:`not_multiple_of`,divisor:t.value,input:n.value,inst:e,continue:!t.abort})}}),Qn=L(`$ZodCheckNumberFormat`,(e,t)=>{G.init(e,t),t.format=t.format||`float64`;let n=t.format?.includes(`int`),r=n?`int`:`number`,[i,a]=Ft[t.format];e._zod.onattach.push(e=>{let r=e._zod.bag;r.format=t.format,r.minimum=i,r.maximum=a,n&&(r.pattern=Un)}),e._zod.check=o=>{let s=o.value;if(n){if(!Number.isInteger(s)){o.issues.push({expected:r,format:t.format,code:`invalid_type`,continue:!1,input:s,inst:e});return}if(!Number.isSafeInteger(s)){s>0?o.issues.push({input:s,code:`too_big`,maximum:2**53-1,note:`Integers must be within the safe integer range.`,inst:e,origin:r,inclusive:!0,continue:!t.abort}):o.issues.push({input:s,code:`too_small`,minimum:-(2**53-1),note:`Integers must be within the safe integer range.`,inst:e,origin:r,inclusive:!0,continue:!t.abort});return}}sa&&o.issues.push({origin:`number`,input:s,code:`too_big`,maximum:a,inclusive:!0,inst:e,continue:!t.abort})}}),$n=L(`$ZodCheckMaxLength`,(e,t)=>{var n;G.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!xt(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag.maximum??1/0;t.maximum{let r=n.value;if(r.length<=t.maximum)return;let i=qt(r);n.issues.push({origin:i,code:`too_big`,maximum:t.maximum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),er=L(`$ZodCheckMinLength`,(e,t)=>{var n;G.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!xt(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag.minimum??-1/0;t.minimum>n&&(e._zod.bag.minimum=t.minimum)}),e._zod.check=n=>{let r=n.value;if(r.length>=t.minimum)return;let i=qt(r);n.issues.push({origin:i,code:`too_small`,minimum:t.minimum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),tr=L(`$ZodCheckLengthEquals`,(e,t)=>{var n;G.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!xt(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag;n.minimum=t.length,n.maximum=t.length,n.length=t.length}),e._zod.check=n=>{let r=n.value,i=r.length;if(i===t.length)return;let a=qt(r),o=i>t.length;n.issues.push({origin:a,...o?{code:`too_big`,maximum:t.length}:{code:`too_small`,minimum:t.length},inclusive:!0,exact:!0,input:n.value,inst:e,continue:!t.abort})}}),nr=L(`$ZodCheckStringFormat`,(e,t)=>{var n,r;G.init(e,t),e._zod.onattach.push(e=>{let n=e._zod.bag;n.format=t.format,t.pattern&&(n.patterns??=new Set,n.patterns.add(t.pattern))}),t.pattern?(n=e._zod).check??(n.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:t.format,input:n.value,...t.pattern?{pattern:t.pattern.toString()}:{},inst:e,continue:!t.abort})}):(r=e._zod).check??(r.check=()=>{})}),rr=L(`$ZodCheckRegex`,(e,t)=>{nr.init(e,t),e._zod.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:`regex`,input:n.value,pattern:t.pattern.toString(),inst:e,continue:!t.abort})}}),ir=L(`$ZodCheckLowerCase`,(e,t)=>{t.pattern??=Kn,nr.init(e,t)}),ar=L(`$ZodCheckUpperCase`,(e,t)=>{t.pattern??=qn,nr.init(e,t)}),or=L(`$ZodCheckIncludes`,(e,t)=>{G.init(e,t);let n=Nt(t.includes),r=new RegExp(typeof t.position==`number`?`^.{${t.position}}${n}`:n);t.pattern=r,e._zod.onattach.push(e=>{let t=e._zod.bag;t.patterns??=new Set,t.patterns.add(r)}),e._zod.check=n=>{n.value.includes(t.includes,t.position)||n.issues.push({origin:`string`,code:`invalid_format`,format:`includes`,includes:t.includes,input:n.value,inst:e,continue:!t.abort})}}),sr=L(`$ZodCheckStartsWith`,(e,t)=>{G.init(e,t);let n=RegExp(`^${Nt(t.prefix)}.*`);t.pattern??=n,e._zod.onattach.push(e=>{let t=e._zod.bag;t.patterns??=new Set,t.patterns.add(n)}),e._zod.check=n=>{n.value.startsWith(t.prefix)||n.issues.push({origin:`string`,code:`invalid_format`,format:`starts_with`,prefix:t.prefix,input:n.value,inst:e,continue:!t.abort})}}),cr=L(`$ZodCheckEndsWith`,(e,t)=>{G.init(e,t);let n=RegExp(`.*${Nt(t.suffix)}$`);t.pattern??=n,e._zod.onattach.push(e=>{let t=e._zod.bag;t.patterns??=new Set,t.patterns.add(n)}),e._zod.check=n=>{n.value.endsWith(t.suffix)||n.issues.push({origin:`string`,code:`invalid_format`,format:`ends_with`,suffix:t.suffix,input:n.value,inst:e,continue:!t.abort})}}),lr=L(`$ZodCheckOverwrite`,(e,t)=>{G.init(e,t),e._zod.check=e=>{e.value=t.tx(e.value)}}),ur=class{constructor(e=[]){this.content=[],this.indent=0,this&&(this.args=e)}indented(e){this.indent+=1,e(this),--this.indent}write(e){if(typeof e==`function`){e(this,{execution:`sync`}),e(this,{execution:`async`});return}let t=e.split(` +`).filter(e=>e),n=Math.min(...t.map(e=>e.length-e.trimStart().length)),r=t.map(e=>e.slice(n)).map(e=>` `.repeat(this.indent*2)+e);for(let e of r)this.content.push(e)}compile(){let e=Function,t=this?.args,n=[...(this?.content??[``]).map(e=>` ${e}`)];return new e(...t,n.join(` +`))}},dr={major:4,minor:4,patch:3},K=L(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=dr;let r=[...e._zod.def.checks??[]];e._zod.traits.has(`$ZodCheck`)&&r.unshift(e);for(let t of r)for(let n of t._zod.onattach)n(e);if(r.length===0)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let t=(e,t,n)=>{let r=Ut(e),i;for(let a of t){if(a._zod.def.when){if(Wt(e)||!a._zod.def.when(e))continue}else if(r)continue;let t=e.issues.length,o=a._zod.check(e);if(o instanceof Promise&&n?.async===!1)throw new ht;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=Ut(e,t))});else{if(e.issues.length===t)continue;r||=Ut(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(Ut(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new ht;return o.then(t=>e._zod.parse(t,a))}return e._zod.parse(o,a)};e._zod.run=(i,a)=>{if(a.skipChecks)return e._zod.parse(i,a);if(a.direction===`backward`){let t=e._zod.parse({value:i.value,issues:[]},{...a,skipChecks:!0});return t instanceof Promise?t.then(e=>n(e,i,a)):n(t,i,a)}let o=e._zod.parse(i,a);if(o instanceof Promise){if(a.async===!1)throw new ht;return o.then(e=>t(e,r,a))}return t(o,r,a)}}z(e,`~standard`,()=>({validate:t=>{try{let n=on(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return cn(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),fr=L(`$ZodString`,(e,t)=>{K.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??Hn(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch{}return typeof n.value==`string`||n.issues.push({expected:`string`,code:`invalid_type`,input:n.value,inst:e}),n}}),q=L(`$ZodStringFormat`,(e,t)=>{nr.init(e,t),fr.init(e,t)}),pr=L(`$ZodGUID`,(e,t)=>{t.pattern??=wn,q.init(e,t)}),mr=L(`$ZodUUID`,(e,t)=>{if(t.version){let e={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8}[t.version];if(e===void 0)throw Error(`Invalid UUID version: "${t.version}"`);t.pattern??=Tn(e)}else t.pattern??=Tn();q.init(e,t)}),hr=L(`$ZodEmail`,(e,t)=>{t.pattern??=En,q.init(e,t)}),gr=L(`$ZodURL`,(e,t)=>{q.init(e,t),e._zod.check=n=>{try{let r=n.value.trim();if(!t.normalize&&t.protocol?.source===Fn.source&&!/^https?:\/\//i.test(r)){n.issues.push({code:`invalid_format`,format:`url`,note:`Invalid URL format`,input:n.value,inst:e,continue:!t.abort});return}let i=new URL(r);t.hostname&&(t.hostname.lastIndex=0,t.hostname.test(i.hostname)||n.issues.push({code:`invalid_format`,format:`url`,note:`Invalid hostname`,pattern:t.hostname.source,input:n.value,inst:e,continue:!t.abort})),t.protocol&&(t.protocol.lastIndex=0,t.protocol.test(i.protocol.endsWith(`:`)?i.protocol.slice(0,-1):i.protocol)||n.issues.push({code:`invalid_format`,format:`url`,note:`Invalid protocol`,pattern:t.protocol.source,input:n.value,inst:e,continue:!t.abort})),t.normalize?n.value=i.href:n.value=r;return}catch{n.issues.push({code:`invalid_format`,format:`url`,input:n.value,inst:e,continue:!t.abort})}}}),_r=L(`$ZodEmoji`,(e,t)=>{t.pattern??=On(),q.init(e,t)}),vr=L(`$ZodNanoID`,(e,t)=>{t.pattern??=Sn,q.init(e,t)}),yr=L(`$ZodCUID`,(e,t)=>{t.pattern??=_n,q.init(e,t)}),br=L(`$ZodCUID2`,(e,t)=>{t.pattern??=vn,q.init(e,t)}),xr=L(`$ZodULID`,(e,t)=>{t.pattern??=yn,q.init(e,t)}),Sr=L(`$ZodXID`,(e,t)=>{t.pattern??=bn,q.init(e,t)}),Cr=L(`$ZodKSUID`,(e,t)=>{t.pattern??=xn,q.init(e,t)}),wr=L(`$ZodISODateTime`,(e,t)=>{t.pattern??=Vn(t),q.init(e,t)}),Tr=L(`$ZodISODate`,(e,t)=>{t.pattern??=Rn,q.init(e,t)}),Er=L(`$ZodISOTime`,(e,t)=>{t.pattern??=Bn(t),q.init(e,t)}),Dr=L(`$ZodISODuration`,(e,t)=>{t.pattern??=Cn,q.init(e,t)}),Or=L(`$ZodIPv4`,(e,t)=>{t.pattern??=kn,q.init(e,t),e._zod.bag.format=`ipv4`}),kr=L(`$ZodIPv6`,(e,t)=>{t.pattern??=An,q.init(e,t),e._zod.bag.format=`ipv6`,e._zod.check=n=>{try{new URL(`http://[${n.value}]`)}catch{n.issues.push({code:`invalid_format`,format:`ipv6`,input:n.value,inst:e,continue:!t.abort})}}}),Ar=L(`$ZodCIDRv4`,(e,t)=>{t.pattern??=jn,q.init(e,t)}),jr=L(`$ZodCIDRv6`,(e,t)=>{t.pattern??=Mn,q.init(e,t),e._zod.check=n=>{let r=n.value.split(`/`);try{if(r.length!==2)throw Error();let[e,t]=r;if(!t)throw Error();let n=Number(t);if(`${n}`!==t||n<0||n>128)throw Error();new URL(`http://[${e}]`)}catch{n.issues.push({code:`invalid_format`,format:`cidrv6`,input:n.value,inst:e,continue:!t.abort})}}});function Mr(e){if(e===``)return!0;if(/\s/.test(e)||e.length%4!=0)return!1;try{return atob(e),!0}catch{return!1}}var Nr=L(`$ZodBase64`,(e,t)=>{t.pattern??=Nn,q.init(e,t),e._zod.bag.contentEncoding=`base64`,e._zod.check=n=>{Mr(n.value)||n.issues.push({code:`invalid_format`,format:`base64`,input:n.value,inst:e,continue:!t.abort})}});function Pr(e){if(!Pn.test(e))return!1;let t=e.replace(/[-_]/g,e=>e===`-`?`+`:`/`);return Mr(t.padEnd(Math.ceil(t.length/4)*4,`=`))}var Fr=L(`$ZodBase64URL`,(e,t)=>{t.pattern??=Pn,q.init(e,t),e._zod.bag.contentEncoding=`base64url`,e._zod.check=n=>{Pr(n.value)||n.issues.push({code:`invalid_format`,format:`base64url`,input:n.value,inst:e,continue:!t.abort})}}),Ir=L(`$ZodE164`,(e,t)=>{t.pattern??=In,q.init(e,t)});function Lr(e,t=null){try{let n=e.split(`.`);if(n.length!==3)return!1;let[r]=n;if(!r)return!1;let i=JSON.parse(atob(r));return!(`typ`in i&&i?.typ!==`JWT`||!i.alg||t&&(!(`alg`in i)||i.alg!==t))}catch{return!1}}var Rr=L(`$ZodJWT`,(e,t)=>{q.init(e,t),e._zod.check=n=>{Lr(n.value,t.alg)||n.issues.push({code:`invalid_format`,format:`jwt`,input:n.value,inst:e,continue:!t.abort})}}),zr=L(`$ZodNumber`,(e,t)=>{K.init(e,t),e._zod.pattern=e._zod.bag.pattern??Wn,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=Number(n.value)}catch{}let i=n.value;if(typeof i==`number`&&!Number.isNaN(i)&&Number.isFinite(i))return n;let a=typeof i==`number`?Number.isNaN(i)?`NaN`:Number.isFinite(i)?void 0:`Infinity`:void 0;return n.issues.push({expected:`number`,code:`invalid_type`,input:i,inst:e,...a?{received:a}:{}}),n}}),Br=L(`$ZodNumberFormat`,(e,t)=>{Qn.init(e,t),zr.init(e,t)}),Vr=L(`$ZodBoolean`,(e,t)=>{K.init(e,t),e._zod.pattern=Gn,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=!!n.value}catch{}let i=n.value;return typeof i==`boolean`||n.issues.push({expected:`boolean`,code:`invalid_type`,input:i,inst:e}),n}}),Hr=L(`$ZodUnknown`,(e,t)=>{K.init(e,t),e._zod.parse=e=>e}),Ur=L(`$ZodNever`,(e,t)=>{K.init(e,t),e._zod.parse=(t,n)=>(t.issues.push({expected:`never`,code:`invalid_type`,input:t.value,inst:e}),t)});function Wr(e,t,n){e.issues.length&&t.issues.push(...Gt(n,e.issues)),t.value[n]=e.value}var Gr=L(`$ZodArray`,(e,t)=>{K.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({expected:`array`,code:`invalid_type`,input:i,inst:e}),n;n.value=Array(i.length);let a=[];for(let e=0;eWr(t,n,e))):Wr(s,n,e)}return a.length?Promise.all(a).then(()=>n):n}});function Kr(e,t,n,r,i,a){let o=n in r;if(e.issues.length){if(i&&a&&!o)return;t.issues.push(...Gt(n,e.issues))}if(!o&&!i){e.issues.length||t.issues.push({code:`invalid_type`,expected:`nonoptional`,input:void 0,path:[n]});return}e.value===void 0?o&&(t.value[n]=void 0):t.value[n]=e.value}function qr(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(`$ZodType`))throw Error(`Invalid element at key "${n}": expected a Zod schema`);let n=Pt(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function Jr(e,t,n,r,i,a){let o=[],s=i.keySet,c=i.catchall._zod,l=c.def.type,u=c.optin===`optional`,d=c.optout===`optional`;for(let i in t){if(i===`__proto__`||s.has(i))continue;if(l===`never`){o.push(i);continue}let a=c.run({value:t[i],issues:[]},r);a instanceof Promise?e.push(a.then(e=>Kr(e,n,i,t,u,d))):Kr(a,n,i,t,u,d)}return o.length&&n.issues.push({code:`unrecognized_keys`,keys:o,input:t,inst:a}),e.length?Promise.all(e).then(()=>n):n}var Yr=L(`$ZodObject`,(e,t)=>{if(K.init(e,t),!Object.getOwnPropertyDescriptor(t,`shape`)?.get){let e=t.shape;Object.defineProperty(t,"shape",{get:()=>{let n={...e};return Object.defineProperty(t,"shape",{value:n}),n}})}let n=bt(()=>qr(t));z(e._zod,`propValues`,()=>{let e=t.shape,n={};for(let t in e){let r=e[t]._zod;if(r.values){n[t]??(n[t]=new Set);for(let e of r.values)n[t].add(e)}}return n});let r=Ot,i=t.catchall,a;e._zod.parse=(t,o)=>{a??=n.value;let s=t.value;if(!r(s))return t.issues.push({expected:`object`,code:`invalid_type`,input:s,inst:e}),t;t.value={};let c=[],l=a.shape;for(let e of a.keys){let n=l[e],r=n._zod.optin===`optional`,i=n._zod.optout===`optional`,a=n._zod.run({value:s[e],issues:[]},o);a instanceof Promise?c.push(a.then(n=>Kr(n,t,e,s,r,i))):Kr(a,t,e,s,r,i)}return i?Jr(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}}),Xr=L(`$ZodObjectJIT`,(e,t)=>{Yr.init(e,t);let n=e._zod.parse,r=bt(()=>qr(t)),i=e=>{let t=new ur([`shape`,`payload`,`ctx`]),n=r.value,i=e=>{let t=Tt(e);return`shape[${t}]._zod.run({ value: input[${t}], issues: [] }, ctx)`};t.write(`const input = payload.value;`);let a=Object.create(null),o=0;for(let e of n.keys)a[e]=`key_${o++}`;t.write(`const newResult = {};`);for(let r of n.keys){let n=a[r],o=Tt(r),s=e[r],c=s?._zod?.optin===`optional`,l=s?._zod?.optout===`optional`;t.write(`const ${n} = ${i(r)};`),c&&l?t.write(` + if (${n}.issues.length) { + if (${o} in input) { + payload.issues = payload.issues.concat(${n}.issues.map(iss => ({ + ...iss, + path: iss.path ? [${o}, ...iss.path] : [${o}] + }))); + } + } + + if (${n}.value === undefined) { + if (${o} in input) { + newResult[${o}] = undefined; + } + } else { + newResult[${o}] = ${n}.value; + } + + `):c?t.write(` + if (${n}.issues.length) { + payload.issues = payload.issues.concat(${n}.issues.map(iss => ({ + ...iss, + path: iss.path ? [${o}, ...iss.path] : [${o}] + }))); + } + + if (${n}.value === undefined) { + if (${o} in input) { + newResult[${o}] = undefined; + } + } else { + newResult[${o}] = ${n}.value; + } + + `):t.write(` + const ${n}_present = ${o} in input; + if (${n}.issues.length) { + payload.issues = payload.issues.concat(${n}.issues.map(iss => ({ + ...iss, + path: iss.path ? [${o}, ...iss.path] : [${o}] + }))); + } + if (!${n}_present && !${n}.issues.length) { + payload.issues.push({ + code: "invalid_type", + expected: "nonoptional", + input: undefined, + path: [${o}] + }); + } + + if (${n}_present) { + if (${n}.value === undefined) { + newResult[${o}] = undefined; + } else { + newResult[${o}] = ${n}.value; + } + } + + `)}t.write(`payload.value = newResult;`),t.write(`return payload;`);let s=t.compile();return(t,n)=>s(e,t,n)},a,o=Ot,s=!_t.jitless,c=s&&kt.value,l=t.catchall,u;e._zod.parse=(d,f)=>{u??=r.value;let p=d.value;return o(p)?s&&c&&f?.async===!1&&f.jitless!==!0?(a||=i(t.shape),d=a(d,f),l?Jr([],p,d,f,u,e):d):n(d,f):(d.issues.push({expected:`object`,code:`invalid_type`,input:p,inst:e}),d)}});function Zr(e,t,n,r){for(let n of e)if(n.issues.length===0)return t.value=n.value,t;let i=e.filter(e=>!Ut(e));return i.length===1?(t.value=i[0].value,i[0]):(t.issues.push({code:`invalid_union`,input:t.value,inst:n,errors:e.map(e=>e.issues.map(e=>W(e,r,R())))}),t)}var Qr=L(`$ZodUnion`,(e,t)=>{K.init(e,t),z(e._zod,`optin`,()=>t.options.some(e=>e._zod.optin===`optional`)?`optional`:void 0),z(e._zod,`optout`,()=>t.options.some(e=>e._zod.optout===`optional`)?`optional`:void 0),z(e._zod,`values`,()=>{if(t.options.every(e=>e._zod.values))return new Set(t.options.flatMap(e=>Array.from(e._zod.values)))}),z(e._zod,`pattern`,()=>{if(t.options.every(e=>e._zod.pattern)){let e=t.options.map(e=>e._zod.pattern);return RegExp(`^(${e.map(e=>St(e.source)).join(`|`)})$`)}});let n=t.options.length===1?t.options[0]._zod.run:null;e._zod.parse=(r,i)=>{if(n)return n(r,i);let a=!1,o=[];for(let e of t.options){let t=e._zod.run({value:r.value,issues:[]},i);if(t instanceof Promise)o.push(t),a=!0;else{if(t.issues.length===0)return t;o.push(t)}}return a?Promise.all(o).then(t=>Zr(t,r,e,i)):Zr(o,r,e,i)}}),$r=L(`$ZodIntersection`,(e,t)=>{K.init(e,t),e._zod.parse=(e,n)=>{let r=e.value,i=t.left._zod.run({value:r,issues:[]},n),a=t.right._zod.run({value:r,issues:[]},n);return i instanceof Promise||a instanceof Promise?Promise.all([i,a]).then(([t,n])=>ti(e,t,n)):ti(e,i,a)}});function ei(e,t){if(e===t||e instanceof Date&&t instanceof Date&&+e==+t)return{valid:!0,data:e};if(At(e)&&At(t)){let n=Object.keys(t),r=Object.keys(e).filter(e=>n.indexOf(e)!==-1),i={...e,...t};for(let n of r){let r=ei(e[n],t[n]);if(!r.valid)return{valid:!1,mergeErrorPath:[n,...r.mergeErrorPath]};i[n]=r.data}return{valid:!0,data:i}}if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return{valid:!1,mergeErrorPath:[]};let n=[];for(let r=0;re.l&&e.r).map(([e])=>e);if(a.length&&i&&e.issues.push({...i,keys:a}),Ut(e))return e;let o=ei(t.value,n.value);if(!o.valid)throw Error(`Unmergable intersection. Error path: ${JSON.stringify(o.mergeErrorPath)}`);return e.value=o.data,e}var ni=L(`$ZodRecord`,(e,t)=>{K.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!At(i))return n.issues.push({expected:`record`,code:`invalid_type`,input:i,inst:e}),n;let a=[],o=t.keyType._zod.values;if(o){n.value={};let s=new Set;for(let c of o)if(typeof c==`string`||typeof c==`number`||typeof c==`symbol`){s.add(typeof c==`number`?c.toString():c);let o=t.keyType._zod.run({value:c,issues:[]},r);if(o instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(o.issues.length){n.issues.push({code:`invalid_key`,origin:`record`,issues:o.issues.map(e=>W(e,r,R())),input:c,path:[c],inst:e});continue}let l=o.value,u=t.valueType._zod.run({value:i[c],issues:[]},r);u instanceof Promise?a.push(u.then(e=>{e.issues.length&&n.issues.push(...Gt(c,e.issues)),n.value[l]=e.value})):(u.issues.length&&n.issues.push(...Gt(c,u.issues)),n.value[l]=u.value)}let c;for(let e in i)s.has(e)||(c??=[],c.push(e));c&&c.length>0&&n.issues.push({code:`unrecognized_keys`,input:i,inst:e,keys:c})}else{n.value={};for(let o of Reflect.ownKeys(i)){if(o===`__proto__`||!Object.prototype.propertyIsEnumerable.call(i,o))continue;let s=t.keyType._zod.run({value:o,issues:[]},r);if(s instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(typeof o==`string`&&Wn.test(o)&&s.issues.length){let e=t.keyType._zod.run({value:Number(o),issues:[]},r);if(e instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);e.issues.length===0&&(s=e)}if(s.issues.length){t.mode===`loose`?n.value[o]=i[o]:n.issues.push({code:`invalid_key`,origin:`record`,issues:s.issues.map(e=>W(e,r,R())),input:o,path:[o],inst:e});continue}let c=t.valueType._zod.run({value:i[o],issues:[]},r);c instanceof Promise?a.push(c.then(e=>{e.issues.length&&n.issues.push(...Gt(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...Gt(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}}),ri=L(`$ZodEnum`,(e,t)=>{K.init(e,t);let n=vt(t.entries),r=new Set(n);e._zod.values=r,e._zod.pattern=RegExp(`^(${n.filter(e=>Mt.has(typeof e)).map(e=>typeof e==`string`?Nt(e):e.toString()).join(`|`)})$`),e._zod.parse=(t,i)=>{let a=t.value;return r.has(a)||t.issues.push({code:`invalid_value`,values:n,input:a,inst:e}),t}}),ii=L(`$ZodTransform`,(e,t)=>{K.init(e,t),e._zod.optin=`optional`,e._zod.parse=(n,r)=>{if(r.direction===`backward`)throw new gt(e.constructor.name);let i=t.transform(n.value,n);if(r.async)return(i instanceof Promise?i:Promise.resolve(i)).then(e=>(n.value=e,n.fallback=!0,n));if(i instanceof Promise)throw new ht;return n.value=i,n.fallback=!0,n}});function ai(e,t){return t===void 0&&(e.issues.length||e.fallback)?{issues:[],value:void 0}:e}var oi=L(`$ZodOptional`,(e,t)=>{K.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,z(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),z(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${St(e.source)})?$`):void 0}),e._zod.parse=(e,n)=>{if(t.innerType._zod.optin===`optional`){let r=e.value,i=t.innerType._zod.run(e,n);return i instanceof Promise?i.then(e=>ai(e,r)):ai(i,r)}return e.value===void 0?e:t.innerType._zod.run(e,n)}}),si=L(`$ZodExactOptional`,(e,t)=>{oi.init(e,t),z(e._zod,`values`,()=>t.innerType._zod.values),z(e._zod,`pattern`,()=>t.innerType._zod.pattern),e._zod.parse=(e,n)=>t.innerType._zod.run(e,n)}),ci=L(`$ZodNullable`,(e,t)=>{K.init(e,t),z(e._zod,`optin`,()=>t.innerType._zod.optin),z(e._zod,`optout`,()=>t.innerType._zod.optout),z(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${St(e.source)}|null)$`):void 0}),z(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,null]):void 0),e._zod.parse=(e,n)=>e.value===null?e:t.innerType._zod.run(e,n)}),li=L(`$ZodDefault`,(e,t)=>{K.init(e,t),e._zod.optin=`optional`,z(e._zod,`values`,()=>t.innerType._zod.values),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);if(e.value===void 0)return e.value=t.defaultValue,e;let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(e=>ui(e,t)):ui(r,t)}});function ui(e,t){return e.value===void 0&&(e.value=t.defaultValue),e}var di=L(`$ZodPrefault`,(e,t)=>{K.init(e,t),e._zod.optin=`optional`,z(e._zod,`values`,()=>t.innerType._zod.values),e._zod.parse=(e,n)=>(n.direction===`backward`||e.value===void 0&&(e.value=t.defaultValue),t.innerType._zod.run(e,n))}),fi=L(`$ZodNonOptional`,(e,t)=>{K.init(e,t),z(e._zod,`values`,()=>{let e=t.innerType._zod.values;return e?new Set([...e].filter(e=>e!==void 0)):void 0}),e._zod.parse=(n,r)=>{let i=t.innerType._zod.run(n,r);return i instanceof Promise?i.then(t=>pi(t,e)):pi(i,e)}});function pi(e,t){return!e.issues.length&&e.value===void 0&&e.issues.push({code:`invalid_type`,expected:`nonoptional`,input:e.value,inst:t}),e}var mi=L(`$ZodCatch`,(e,t)=>{K.init(e,t),e._zod.optin=`optional`,z(e._zod,`optout`,()=>t.innerType._zod.optout),z(e._zod,`values`,()=>t.innerType._zod.values),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(r=>(e.value=r.value,r.issues.length&&(e.value=t.catchValue({...e,error:{issues:r.issues.map(e=>W(e,n,R()))},input:e.value}),e.issues=[],e.fallback=!0),e)):(e.value=r.value,r.issues.length&&(e.value=t.catchValue({...e,error:{issues:r.issues.map(e=>W(e,n,R()))},input:e.value}),e.issues=[],e.fallback=!0),e)}}),hi=L(`$ZodPipe`,(e,t)=>{K.init(e,t),z(e._zod,`values`,()=>t.in._zod.values),z(e._zod,`optin`,()=>t.in._zod.optin),z(e._zod,`optout`,()=>t.out._zod.optout),z(e._zod,`propValues`,()=>t.in._zod.propValues),e._zod.parse=(e,n)=>{if(n.direction===`backward`){let r=t.out._zod.run(e,n);return r instanceof Promise?r.then(e=>gi(e,t.in,n)):gi(r,t.in,n)}let r=t.in._zod.run(e,n);return r instanceof Promise?r.then(e=>gi(e,t.out,n)):gi(r,t.out,n)}});function gi(e,t,n){return e.issues.length?(e.aborted=!0,e):t._zod.run({value:e.value,issues:e.issues,fallback:e.fallback},n)}var _i=L(`$ZodReadonly`,(e,t)=>{K.init(e,t),z(e._zod,`propValues`,()=>t.innerType._zod.propValues),z(e._zod,`values`,()=>t.innerType._zod.values),z(e._zod,`optin`,()=>t.innerType?._zod?.optin),z(e._zod,`optout`,()=>t.innerType?._zod?.optout),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(vi):vi(r)}});function vi(e){return e.value=Object.freeze(e.value),e}var yi=L(`$ZodCustom`,(e,t)=>{G.init(e,t),K.init(e,t),e._zod.parse=(e,t)=>e,e._zod.check=n=>{let r=n.value,i=t.fn(r);if(i instanceof Promise)return i.then(t=>bi(t,n,r,e));bi(i,n,r,e)}});function bi(e,t,n,r){if(!e){let e={code:`custom`,input:n,inst:r,path:[...r._zod.def.path??[]],continue:!r._zod.def.abort};r._zod.def.params&&(e.params=r._zod.def.params),t.issues.push(Jt(e))}}var xi,Si=class{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){let n=t[0];return this._map.set(e,n),n&&typeof n==`object`&&`id`in n&&this._idmap.set(n.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){let t=this._map.get(e);return t&&typeof t==`object`&&`id`in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){let t=e._zod.parent;if(t){let n={...this.get(t)??{}};delete n.id;let r={...n,...this._map.get(e)};return Object.keys(r).length?r:void 0}return this._map.get(e)}has(e){return this._map.has(e)}};function Ci(){return new Si}(xi=globalThis).__zod_globalRegistry??(xi.__zod_globalRegistry=Ci());var wi=globalThis.__zod_globalRegistry;function Ti(e,t){return new e({type:`string`,...U(t)})}function Ei(e,t){return new e({type:`string`,format:`email`,check:`string_format`,abort:!1,...U(t)})}function Di(e,t){return new e({type:`string`,format:`guid`,check:`string_format`,abort:!1,...U(t)})}function Oi(e,t){return new e({type:`string`,format:`uuid`,check:`string_format`,abort:!1,...U(t)})}function ki(e,t){return new e({type:`string`,format:`uuid`,check:`string_format`,abort:!1,version:`v4`,...U(t)})}function Ai(e,t){return new e({type:`string`,format:`uuid`,check:`string_format`,abort:!1,version:`v6`,...U(t)})}function ji(e,t){return new e({type:`string`,format:`uuid`,check:`string_format`,abort:!1,version:`v7`,...U(t)})}function Mi(e,t){return new e({type:`string`,format:`url`,check:`string_format`,abort:!1,...U(t)})}function Ni(e,t){return new e({type:`string`,format:`emoji`,check:`string_format`,abort:!1,...U(t)})}function Pi(e,t){return new e({type:`string`,format:`nanoid`,check:`string_format`,abort:!1,...U(t)})}function Fi(e,t){return new e({type:`string`,format:`cuid`,check:`string_format`,abort:!1,...U(t)})}function Ii(e,t){return new e({type:`string`,format:`cuid2`,check:`string_format`,abort:!1,...U(t)})}function Li(e,t){return new e({type:`string`,format:`ulid`,check:`string_format`,abort:!1,...U(t)})}function Ri(e,t){return new e({type:`string`,format:`xid`,check:`string_format`,abort:!1,...U(t)})}function zi(e,t){return new e({type:`string`,format:`ksuid`,check:`string_format`,abort:!1,...U(t)})}function Bi(e,t){return new e({type:`string`,format:`ipv4`,check:`string_format`,abort:!1,...U(t)})}function Vi(e,t){return new e({type:`string`,format:`ipv6`,check:`string_format`,abort:!1,...U(t)})}function Hi(e,t){return new e({type:`string`,format:`cidrv4`,check:`string_format`,abort:!1,...U(t)})}function Ui(e,t){return new e({type:`string`,format:`cidrv6`,check:`string_format`,abort:!1,...U(t)})}function Wi(e,t){return new e({type:`string`,format:`base64`,check:`string_format`,abort:!1,...U(t)})}function Gi(e,t){return new e({type:`string`,format:`base64url`,check:`string_format`,abort:!1,...U(t)})}function Ki(e,t){return new e({type:`string`,format:`e164`,check:`string_format`,abort:!1,...U(t)})}function qi(e,t){return new e({type:`string`,format:`jwt`,check:`string_format`,abort:!1,...U(t)})}function Ji(e,t){return new e({type:`string`,format:`datetime`,check:`string_format`,offset:!1,local:!1,precision:null,...U(t)})}function Yi(e,t){return new e({type:`string`,format:`date`,check:`string_format`,...U(t)})}function Xi(e,t){return new e({type:`string`,format:`time`,check:`string_format`,precision:null,...U(t)})}function Zi(e,t){return new e({type:`string`,format:`duration`,check:`string_format`,...U(t)})}function Qi(e,t){return new e({type:`number`,checks:[],...U(t)})}function $i(e,t){return new e({type:`number`,check:`number_format`,abort:!1,format:`safeint`,...U(t)})}function ea(e,t){return new e({type:`boolean`,...U(t)})}function ta(e){return new e({type:`unknown`})}function na(e,t){return new e({type:`never`,...U(t)})}function ra(e,t){return new Yn({check:`less_than`,...U(t),value:e,inclusive:!1})}function ia(e,t){return new Yn({check:`less_than`,...U(t),value:e,inclusive:!0})}function aa(e,t){return new Xn({check:`greater_than`,...U(t),value:e,inclusive:!1})}function oa(e,t){return new Xn({check:`greater_than`,...U(t),value:e,inclusive:!0})}function sa(e,t){return new Zn({check:`multiple_of`,...U(t),value:e})}function ca(e,t){return new $n({check:`max_length`,...U(t),maximum:e})}function la(e,t){return new er({check:`min_length`,...U(t),minimum:e})}function ua(e,t){return new tr({check:`length_equals`,...U(t),length:e})}function da(e,t){return new rr({check:`string_format`,format:`regex`,...U(t),pattern:e})}function fa(e){return new ir({check:`string_format`,format:`lowercase`,...U(e)})}function pa(e){return new ar({check:`string_format`,format:`uppercase`,...U(e)})}function ma(e,t){return new or({check:`string_format`,format:`includes`,...U(t),includes:e})}function ha(e,t){return new sr({check:`string_format`,format:`starts_with`,...U(t),prefix:e})}function ga(e,t){return new cr({check:`string_format`,format:`ends_with`,...U(t),suffix:e})}function _a(e){return new lr({check:`overwrite`,tx:e})}function va(e){return _a(t=>t.normalize(e))}function ya(){return _a(e=>e.trim())}function ba(){return _a(e=>e.toLowerCase())}function xa(){return _a(e=>e.toUpperCase())}function Sa(){return _a(e=>Et(e))}function Ca(e,t,n){return new e({type:`array`,element:t,...U(n)})}function wa(e,t,n){return new e({type:`custom`,check:`custom`,fn:t,...U(n)})}function Ta(e,t){let n=Ea(t=>(t.addIssue=e=>{if(typeof e==`string`)t.issues.push(Jt(e,t.value,n._zod.def));else{let r=e;r.fatal&&(r.continue=!1),r.code??=`custom`,r.input??=t.value,r.inst??=n,r.continue??=!n._zod.def.abort,t.issues.push(Jt(r))}},e(t.value,t)),t);return n}function Ea(e,t){let n=new G({check:`custom`,...U(t)});return n._zod.check=e,n}function Da(e){let t=e?.target??`draft-2020-12`;return t===`draft-4`&&(t=`draft-04`),t===`draft-7`&&(t=`draft-07`),{processors:e.processors??{},metadataRegistry:e?.metadata??wi,target:t,unrepresentable:e?.unrepresentable??`throw`,override:e?.override??(()=>{}),io:e?.io??`output`,counter:0,seen:new Map,cycles:e?.cycles??`ref`,reused:e?.reused??`inline`,external:e?.external??void 0}}function J(e,t,n={path:[],schemaPath:[]}){var r;let i=e._zod.def,a=t.seen.get(e);if(a)return a.count++,n.schemaPath.includes(e)&&(a.cycle=n.path),a.schema;let o={schema:{},count:1,cycle:void 0,path:n.path};t.seen.set(e,o);let s=e._zod.toJSONSchema?.();if(s)o.schema=s;else{let r={...n,schemaPath:[...n.schemaPath,e],path:n.path};if(e._zod.processJSONSchema)e._zod.processJSONSchema(t,o.schema,r);else{let n=o.schema,a=t.processors[i.type];if(!a)throw Error(`[toJSONSchema]: Non-representable type encountered: ${i.type}`);a(e,t,n,r)}let a=e._zod.parent;a&&(o.ref||=a,J(a,t,r),t.seen.get(a).isParent=!0)}let c=t.metadataRegistry.get(e);return c&&Object.assign(o.schema,c),t.io===`input`&&Y(e)&&(delete o.schema.examples,delete o.schema.default),t.io===`input`&&`_prefault`in o.schema&&((r=o.schema).default??(r.default=o.schema._prefault)),delete o.schema._prefault,t.seen.get(e).schema}function Oa(e,t){let n=e.seen.get(t);if(!n)throw Error(`Unprocessed schema. This is a bug in Zod.`);let r=new Map;for(let t of e.seen.entries()){let n=e.metadataRegistry.get(t[0])?.id;if(n){let e=r.get(n);if(e&&e!==t[0])throw Error(`Duplicate schema id "${n}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);r.set(n,t[0])}}let i=t=>{let r=e.target===`draft-2020-12`?`$defs`:`definitions`;if(e.external){let n=e.external.registry.get(t[0])?.id,i=e.external.uri??(e=>e);if(n)return{ref:i(n)};let a=t[1].defId??t[1].schema.id??`schema${e.counter++}`;return t[1].defId=a,{defId:a,ref:`${i(`__shared`)}#/${r}/${a}`}}if(t[1]===n)return{ref:`#`};let i=`#/${r}/`,a=t[1].schema.id??`__schema${e.counter++}`;return{defId:a,ref:i+a}},a=e=>{if(e[1].schema.$ref)return;let t=e[1],{ref:n,defId:r}=i(e);t.def={...t.schema},r&&(t.defId=r);let a=t.schema;for(let e in a)delete a[e];a.$ref=n};if(e.cycles===`throw`)for(let t of e.seen.entries()){let e=t[1];if(e.cycle)throw Error(`Cycle detected: #/${e.cycle?.join(`/`)}/ + +Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`)}for(let n of e.seen.entries()){let r=n[1];if(t===n[0]){a(n);continue}if(e.external){let r=e.external.registry.get(n[0])?.id;if(t!==n[0]&&r){a(n);continue}}if(e.metadataRegistry.get(n[0])?.id){a(n);continue}if(r.cycle){a(n);continue}if(r.count>1&&e.reused===`ref`){a(n);continue}}}function ka(e,t){let n=e.seen.get(t);if(!n)throw Error(`Unprocessed schema. This is a bug in Zod.`);let r=t=>{let n=e.seen.get(t);if(n.ref===null)return;let i=n.def??n.schema,a={...i},o=n.ref;if(n.ref=null,o){r(o);let n=e.seen.get(o),s=n.schema;if(s.$ref&&(e.target===`draft-07`||e.target===`draft-04`||e.target===`openapi-3.0`)?(i.allOf=i.allOf??[],i.allOf.push(s)):Object.assign(i,s),Object.assign(i,a),t._zod.parent===o)for(let e in i)e===`$ref`||e===`allOf`||e in a||delete i[e];if(s.$ref&&n.def)for(let e in i)e===`$ref`||e===`allOf`||e in n.def&&JSON.stringify(i[e])===JSON.stringify(n.def[e])&&delete i[e]}let s=t._zod.parent;if(s&&s!==o){r(s);let t=e.seen.get(s);if(t?.schema.$ref&&(i.$ref=t.schema.$ref,t.def))for(let e in i)e===`$ref`||e===`allOf`||e in t.def&&JSON.stringify(i[e])===JSON.stringify(t.def[e])&&delete i[e]}e.override({zodSchema:t,jsonSchema:i,path:n.path??[]})};for(let t of[...e.seen.entries()].reverse())r(t[0]);let i={};if(e.target===`draft-2020-12`?i.$schema=`https://json-schema.org/draft/2020-12/schema`:e.target===`draft-07`?i.$schema=`http://json-schema.org/draft-07/schema#`:e.target===`draft-04`?i.$schema=`http://json-schema.org/draft-04/schema#`:e.target,e.external?.uri){let n=e.external.registry.get(t)?.id;if(!n)throw Error("Schema is missing an `id` property");i.$id=e.external.uri(n)}Object.assign(i,n.def??n.schema);let a=e.metadataRegistry.get(t)?.id;a!==void 0&&i.id===a&&delete i.id;let o=e.external?.defs??{};for(let t of e.seen.entries()){let e=t[1];e.def&&e.defId&&(e.def.id===e.defId&&delete e.def.id,o[e.defId]=e.def)}e.external||Object.keys(o).length>0&&(e.target===`draft-2020-12`?i.$defs=o:i.definitions=o);try{let n=JSON.parse(JSON.stringify(i));return Object.defineProperty(n,"~standard",{value:{...t[`~standard`],jsonSchema:{input:ja(t,`input`,e.processors),output:ja(t,`output`,e.processors)}},enumerable:!1,writable:!1}),n}catch{throw Error(`Error converting schema to JSON.`)}}function Y(e,t){let n=t??{seen:new Set};if(n.seen.has(e))return!1;n.seen.add(e);let r=e._zod.def;if(r.type===`transform`)return!0;if(r.type===`array`)return Y(r.element,n);if(r.type===`set`)return Y(r.valueType,n);if(r.type===`lazy`)return Y(r.getter(),n);if(r.type===`promise`||r.type===`optional`||r.type===`nonoptional`||r.type===`nullable`||r.type===`readonly`||r.type==="default"||r.type===`prefault`)return Y(r.innerType,n);if(r.type===`intersection`)return Y(r.left,n)||Y(r.right,n);if(r.type===`record`||r.type===`map`)return Y(r.keyType,n)||Y(r.valueType,n);if(r.type===`pipe`)return e._zod.traits.has(`$ZodCodec`)?!0:Y(r.in,n)||Y(r.out,n);if(r.type===`object`){for(let e in r.shape)if(Y(r.shape[e],n))return!0;return!1}if(r.type===`union`){for(let e of r.options)if(Y(e,n))return!0;return!1}if(r.type===`tuple`){for(let e of r.items)if(Y(e,n))return!0;return!!(r.rest&&Y(r.rest,n))}return!1}var Aa=(e,t={})=>n=>{let r=Da({...n,processors:t});return J(e,r),Oa(r,e),ka(r,e)},ja=(e,t,n={})=>r=>{let{libraryOptions:i,target:a}=r??{},o=Da({...i??{},target:a,io:t,processors:n});return J(e,o),Oa(o,e),ka(o,e)},Ma={guid:`uuid`,url:`uri`,datetime:`date-time`,json_string:`json-string`,regex:``},Na=(e,t,n,r)=>{let i=n;i.type=`string`;let{minimum:a,maximum:o,format:s,patterns:c,contentEncoding:l}=e._zod.bag;if(typeof a==`number`&&(i.minLength=a),typeof o==`number`&&(i.maxLength=o),s&&(i.format=Ma[s]??s,i.format===``&&delete i.format,s===`time`&&delete i.format),l&&(i.contentEncoding=l),c&&c.size>0){let e=[...c];e.length===1?i.pattern=e[0].source:e.length>1&&(i.allOf=[...e.map(e=>({...t.target===`draft-07`||t.target===`draft-04`||t.target===`openapi-3.0`?{type:`string`}:{},pattern:e.source}))])}},Pa=(e,t,n,r)=>{let i=n,{minimum:a,maximum:o,format:s,multipleOf:c,exclusiveMaximum:l,exclusiveMinimum:u}=e._zod.bag;typeof s==`string`&&s.includes(`int`)?i.type=`integer`:i.type=`number`;let d=typeof u==`number`&&u>=(a??-1/0),f=typeof l==`number`&&l<=(o??1/0),p=t.target===`draft-04`||t.target===`openapi-3.0`;d?p?(i.minimum=u,i.exclusiveMinimum=!0):i.exclusiveMinimum=u:typeof a==`number`&&(i.minimum=a),f?p?(i.maximum=l,i.exclusiveMaximum=!0):i.exclusiveMaximum=l:typeof o==`number`&&(i.maximum=o),typeof c==`number`&&(i.multipleOf=c)},Fa=(e,t,n,r)=>{n.type=`boolean`},Ia=(e,t,n,r)=>{n.not={}},La=(e,t,n,r)=>{let i=e._zod.def,a=vt(i.entries);a.every(e=>typeof e==`number`)&&(n.type=`number`),a.every(e=>typeof e==`string`)&&(n.type=`string`),n.enum=a},Ra=(e,t,n,r)=>{if(t.unrepresentable===`throw`)throw Error(`Custom types cannot be represented in JSON Schema`)},za=(e,t,n,r)=>{if(t.unrepresentable===`throw`)throw Error(`Transforms cannot be represented in JSON Schema`)},Ba=(e,t,n,r)=>{let i=n,a=e._zod.def,{minimum:o,maximum:s}=e._zod.bag;typeof o==`number`&&(i.minItems=o),typeof s==`number`&&(i.maxItems=s),i.type=`array`,i.items=J(a.element,t,{...r,path:[...r.path,`items`]})},Va=(e,t,n,r)=>{let i=n,a=e._zod.def;i.type=`object`,i.properties={};let o=a.shape;for(let e in o)i.properties[e]=J(o[e],t,{...r,path:[...r.path,`properties`,e]});let s=new Set(Object.keys(o)),c=new Set([...s].filter(e=>{let n=a.shape[e]._zod;return t.io===`input`?n.optin===void 0:n.optout===void 0}));c.size>0&&(i.required=Array.from(c)),a.catchall?._zod.def.type===`never`?i.additionalProperties=!1:a.catchall?a.catchall&&(i.additionalProperties=J(a.catchall,t,{...r,path:[...r.path,`additionalProperties`]})):t.io===`output`&&(i.additionalProperties=!1)},Ha=(e,t,n,r)=>{let i=e._zod.def,a=i.inclusive===!1,o=i.options.map((e,n)=>J(e,t,{...r,path:[...r.path,a?`oneOf`:`anyOf`,n]}));a?n.oneOf=o:n.anyOf=o},Ua=(e,t,n,r)=>{let i=e._zod.def,a=J(i.left,t,{...r,path:[...r.path,`allOf`,0]}),o=J(i.right,t,{...r,path:[...r.path,`allOf`,1]}),s=e=>`allOf`in e&&Object.keys(e).length===1;n.allOf=[...s(a)?a.allOf:[a],...s(o)?o.allOf:[o]]},Wa=(e,t,n,r)=>{let i=n,a=e._zod.def;i.type=`object`;let o=a.keyType,s=o._zod.bag?.patterns;if(a.mode===`loose`&&s&&s.size>0){let e=J(a.valueType,t,{...r,path:[...r.path,`patternProperties`,`*`]});i.patternProperties={};for(let t of s)i.patternProperties[t.source]=e}else (t.target===`draft-07`||t.target===`draft-2020-12`)&&(i.propertyNames=J(a.keyType,t,{...r,path:[...r.path,`propertyNames`]})),i.additionalProperties=J(a.valueType,t,{...r,path:[...r.path,`additionalProperties`]});let c=o._zod.values;if(c){let e=[...c].filter(e=>typeof e==`string`||typeof e==`number`);e.length>0&&(i.required=e)}},Ga=(e,t,n,r)=>{let i=e._zod.def,a=J(i.innerType,t,r),o=t.seen.get(e);t.target===`openapi-3.0`?(o.ref=i.innerType,n.nullable=!0):n.anyOf=[a,{type:`null`}]},Ka=(e,t,n,r)=>{let i=e._zod.def;J(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType},qa=(e,t,n,r)=>{let i=e._zod.def;J(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType,n.default=JSON.parse(JSON.stringify(i.defaultValue))},Ja=(e,t,n,r)=>{let i=e._zod.def;J(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType,t.io===`input`&&(n._prefault=JSON.parse(JSON.stringify(i.defaultValue)))},Ya=(e,t,n,r)=>{let i=e._zod.def;J(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType;let o;try{o=i.catchValue(void 0)}catch{throw Error(`Dynamic catch values are not supported in JSON Schema`)}n.default=o},Xa=(e,t,n,r)=>{let i=e._zod.def,a=i.in._zod.traits.has(`$ZodTransform`),o=t.io===`input`?a?i.out:i.in:i.out;J(o,t,r);let s=t.seen.get(e);s.ref=o},Za=(e,t,n,r)=>{let i=e._zod.def;J(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType,n.readOnly=!0},Qa=(e,t,n,r)=>{let i=e._zod.def;J(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType},$a=L(`ZodISODateTime`,(e,t)=>{wr.init(e,t),Q.init(e,t)});function eo(e){return Ji($a,e)}var to=L(`ZodISODate`,(e,t)=>{Tr.init(e,t),Q.init(e,t)});function no(e){return Yi(to,e)}var ro=L(`ZodISOTime`,(e,t)=>{Er.init(e,t),Q.init(e,t)});function io(e){return Xi(ro,e)}var ao=L(`ZodISODuration`,(e,t)=>{Dr.init(e,t),Q.init(e,t)});function oo(e){return Zi(ao,e)}var X=L(`ZodError`,(e,t)=>{Xt.init(e,t),e.name=`ZodError`,Object.defineProperties(e,{format:{value:t=>$t(e,t)},flatten:{value:t=>Qt(e,t)},addIssue:{value:t=>{e.issues.push(t),e.message=JSON.stringify(e.issues,yt,2)}},addIssues:{value:t=>{e.issues.push(...t),e.message=JSON.stringify(e.issues,yt,2)}},isEmpty:{get(){return e.issues.length===0}}})},{Parent:Error}),so=en(X),co=nn(X),lo=an(X),uo=sn(X),fo=ln(X),po=un(X),mo=dn(X),ho=fn(X),go=pn(X),_o=mn(X),vo=hn(X),yo=gn(X),bo=new WeakMap;function xo(e,t,n){let r=Object.getPrototypeOf(e),i=bo.get(r);if(i||(i=new Set,bo.set(r,i)),!i.has(t)){i.add(t);for(let e in n){let t=n[e];Object.defineProperty(r,e,{configurable:!0,enumerable:!1,get(){let n=t.bind(this);return Object.defineProperty(this,e,{configurable:!0,writable:!0,enumerable:!0,value:n}),n},set(t){Object.defineProperty(this,e,{configurable:!0,writable:!0,enumerable:!0,value:t})}})}}}var Z=L(`ZodType`,(e,t)=>(K.init(e,t),Object.assign(e[`~standard`],{jsonSchema:{input:ja(e,`input`),output:ja(e,`output`)}}),e.toJSONSchema=Aa(e,{}),e.def=t,e.type=t.type,Object.defineProperty(e,"_def",{value:t}),e.parse=(t,n)=>so(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>lo(e,t,n),e.parseAsync=async(t,n)=>co(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>uo(e,t,n),e.spa=e.safeParseAsync,e.encode=(t,n)=>fo(e,t,n),e.decode=(t,n)=>po(e,t,n),e.encodeAsync=async(t,n)=>mo(e,t,n),e.decodeAsync=async(t,n)=>ho(e,t,n),e.safeEncode=(t,n)=>go(e,t,n),e.safeDecode=(t,n)=>_o(e,t,n),e.safeEncodeAsync=async(t,n)=>vo(e,t,n),e.safeDecodeAsync=async(t,n)=>yo(e,t,n),xo(e,`ZodType`,{check(...e){let t=this.def;return this.clone(V(t,{checks:[...t.checks??[],...e.map(e=>typeof e==`function`?{_zod:{check:e,def:{check:`custom`},onattach:[]}}:e)]}),{parent:!0})},with(...e){return this.check(...e)},clone(e,t){return H(this,e,t)},brand(){return this},register(e,t){return e.add(this,t),this},refine(e,t){return this.check(Ns(e,t))},superRefine(e,t){return this.check(Ps(e,t))},overwrite(e){return this.check(_a(e))},optional(){return hs(this)},exactOptional(){return _s(this)},nullable(){return ys(this)},nullish(){return hs(ys(this))},nonoptional(e){return Ts(this,e)},array(){return ts(this)},or(e){return as([this,e])},and(e){return ss(this,e)},transform(e){return ks(this,ps(e))},default(e){return xs(this,e)},prefault(e){return Cs(this,e)},catch(e){return Ds(this,e)},pipe(e){return ks(this,e)},readonly(){return js(this)},describe(e){let t=this.clone();return wi.add(t,{description:e}),t},meta(...e){if(e.length===0)return wi.get(this);let t=this.clone();return wi.add(t,e[0]),t},isOptional(){return this.safeParse(void 0).success},isNullable(){return this.safeParse(null).success},apply(e){return e(this)}}),Object.defineProperty(e,"description",{get(){return wi.get(e)?.description},configurable:!0}),e)),So=L(`_ZodString`,(e,t)=>{fr.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Na(e,t,n,r);let n=e._zod.bag;e.format=n.format??null,e.minLength=n.minimum??null,e.maxLength=n.maximum??null,xo(e,`_ZodString`,{regex(...e){return this.check(da(...e))},includes(...e){return this.check(ma(...e))},startsWith(...e){return this.check(ha(...e))},endsWith(...e){return this.check(ga(...e))},min(...e){return this.check(la(...e))},max(...e){return this.check(ca(...e))},length(...e){return this.check(ua(...e))},nonempty(...e){return this.check(la(1,...e))},lowercase(e){return this.check(fa(e))},uppercase(e){return this.check(pa(e))},trim(){return this.check(ya())},normalize(...e){return this.check(va(...e))},toLowerCase(){return this.check(ba())},toUpperCase(){return this.check(xa())},slugify(){return this.check(Sa())}})}),Co=L(`ZodString`,(e,t)=>{fr.init(e,t),So.init(e,t),e.email=t=>e.check(Ei(To,t)),e.url=t=>e.check(Mi(Oo,t)),e.jwt=t=>e.check(qi(Uo,t)),e.emoji=t=>e.check(Ni(ko,t)),e.guid=t=>e.check(Di(Eo,t)),e.uuid=t=>e.check(Oi(Do,t)),e.uuidv4=t=>e.check(ki(Do,t)),e.uuidv6=t=>e.check(Ai(Do,t)),e.uuidv7=t=>e.check(ji(Do,t)),e.nanoid=t=>e.check(Pi(Ao,t)),e.guid=t=>e.check(Di(Eo,t)),e.cuid=t=>e.check(Fi(jo,t)),e.cuid2=t=>e.check(Ii(Mo,t)),e.ulid=t=>e.check(Li(No,t)),e.base64=t=>e.check(Wi(Bo,t)),e.base64url=t=>e.check(Gi(Vo,t)),e.xid=t=>e.check(Ri(Po,t)),e.ksuid=t=>e.check(zi(Fo,t)),e.ipv4=t=>e.check(Bi(Io,t)),e.ipv6=t=>e.check(Vi(Lo,t)),e.cidrv4=t=>e.check(Hi(Ro,t)),e.cidrv6=t=>e.check(Ui(zo,t)),e.e164=t=>e.check(Ki(Ho,t)),e.datetime=t=>e.check(eo(t)),e.date=t=>e.check(no(t)),e.time=t=>e.check(io(t)),e.duration=t=>e.check(oo(t))});function wo(e){return Ti(Co,e)}var Q=L(`ZodStringFormat`,(e,t)=>{q.init(e,t),So.init(e,t)}),To=L(`ZodEmail`,(e,t)=>{hr.init(e,t),Q.init(e,t)}),Eo=L(`ZodGUID`,(e,t)=>{pr.init(e,t),Q.init(e,t)}),Do=L(`ZodUUID`,(e,t)=>{mr.init(e,t),Q.init(e,t)}),Oo=L(`ZodURL`,(e,t)=>{gr.init(e,t),Q.init(e,t)}),ko=L(`ZodEmoji`,(e,t)=>{_r.init(e,t),Q.init(e,t)}),Ao=L(`ZodNanoID`,(e,t)=>{vr.init(e,t),Q.init(e,t)}),jo=L(`ZodCUID`,(e,t)=>{yr.init(e,t),Q.init(e,t)}),Mo=L(`ZodCUID2`,(e,t)=>{br.init(e,t),Q.init(e,t)}),No=L(`ZodULID`,(e,t)=>{xr.init(e,t),Q.init(e,t)}),Po=L(`ZodXID`,(e,t)=>{Sr.init(e,t),Q.init(e,t)}),Fo=L(`ZodKSUID`,(e,t)=>{Cr.init(e,t),Q.init(e,t)}),Io=L(`ZodIPv4`,(e,t)=>{Or.init(e,t),Q.init(e,t)}),Lo=L(`ZodIPv6`,(e,t)=>{kr.init(e,t),Q.init(e,t)}),Ro=L(`ZodCIDRv4`,(e,t)=>{Ar.init(e,t),Q.init(e,t)}),zo=L(`ZodCIDRv6`,(e,t)=>{jr.init(e,t),Q.init(e,t)}),Bo=L(`ZodBase64`,(e,t)=>{Nr.init(e,t),Q.init(e,t)}),Vo=L(`ZodBase64URL`,(e,t)=>{Fr.init(e,t),Q.init(e,t)}),Ho=L(`ZodE164`,(e,t)=>{Ir.init(e,t),Q.init(e,t)}),Uo=L(`ZodJWT`,(e,t)=>{Rr.init(e,t),Q.init(e,t)}),Wo=L(`ZodNumber`,(e,t)=>{zr.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Pa(e,t,n,r),xo(e,`ZodNumber`,{gt(e,t){return this.check(aa(e,t))},gte(e,t){return this.check(oa(e,t))},min(e,t){return this.check(oa(e,t))},lt(e,t){return this.check(ra(e,t))},lte(e,t){return this.check(ia(e,t))},max(e,t){return this.check(ia(e,t))},int(e){return this.check(qo(e))},safe(e){return this.check(qo(e))},positive(e){return this.check(aa(0,e))},nonnegative(e){return this.check(oa(0,e))},negative(e){return this.check(ra(0,e))},nonpositive(e){return this.check(ia(0,e))},multipleOf(e,t){return this.check(sa(e,t))},step(e,t){return this.check(sa(e,t))},finite(){return this}});let n=e._zod.bag;e.minValue=Math.max(n.minimum??-1/0,n.exclusiveMinimum??-1/0)??null,e.maxValue=Math.min(n.maximum??1/0,n.exclusiveMaximum??1/0)??null,e.isInt=(n.format??``).includes(`int`)||Number.isSafeInteger(n.multipleOf??.5),e.isFinite=!0,e.format=n.format??null});function Go(e){return Qi(Wo,e)}var Ko=L(`ZodNumberFormat`,(e,t)=>{Br.init(e,t),Wo.init(e,t)});function qo(e){return $i(Ko,e)}var Jo=L(`ZodBoolean`,(e,t)=>{Vr.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Fa(e,t,n,r)});function Yo(e){return ea(Jo,e)}var Xo=L(`ZodUnknown`,(e,t)=>{Hr.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(e,t,n)=>void 0});function Zo(){return ta(Xo)}var Qo=L(`ZodNever`,(e,t)=>{Ur.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ia(e,t,n,r)});function $o(e){return na(Qo,e)}var es=L(`ZodArray`,(e,t)=>{Gr.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ba(e,t,n,r),e.element=t.element,xo(e,`ZodArray`,{min(e,t){return this.check(la(e,t))},nonempty(e){return this.check(la(1,e))},max(e,t){return this.check(ca(e,t))},length(e,t){return this.check(ua(e,t))},unwrap(){return this.element}})});function ts(e,t){return Ca(es,e,t)}var ns=L(`ZodObject`,(e,t)=>{Xr.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Va(e,t,n,r),z(e,`shape`,()=>t.shape),xo(e,`ZodObject`,{keyof(){return ds(Object.keys(this._zod.def.shape))},catchall(e){return this.clone({...this._zod.def,catchall:e})},passthrough(){return this.clone({...this._zod.def,catchall:Zo()})},loose(){return this.clone({...this._zod.def,catchall:Zo()})},strict(){return this.clone({...this._zod.def,catchall:$o()})},strip(){return this.clone({...this._zod.def,catchall:void 0})},extend(e){return Rt(this,e)},safeExtend(e){return zt(this,e)},merge(e){return Bt(this,e)},pick(e){return It(this,e)},omit(e){return Lt(this,e)},partial(...e){return Vt(ms,this,e[0])},required(...e){return Ht(ws,this,e[0])}})});function rs(e,t){return new ns({type:`object`,shape:e??{},...U(t)})}var is=L(`ZodUnion`,(e,t)=>{Qr.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ha(e,t,n,r),e.options=t.options});function as(e,t){return new is({type:`union`,options:e,...U(t)})}var os=L(`ZodIntersection`,(e,t)=>{$r.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ua(e,t,n,r)});function ss(e,t){return new os({type:`intersection`,left:e,right:t})}var cs=L(`ZodRecord`,(e,t)=>{ni.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Wa(e,t,n,r),e.keyType=t.keyType,e.valueType=t.valueType});function ls(e,t,n){return!t||!t._zod?new cs({type:`record`,keyType:wo(),valueType:e,...U(t)}):new cs({type:`record`,keyType:e,valueType:t,...U(n)})}var us=L(`ZodEnum`,(e,t)=>{ri.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>La(e,t,n,r),e.enum=t.entries,e.options=Object.values(t.entries);let n=new Set(Object.keys(t.entries));e.extract=(e,r)=>{let i={};for(let r of e)if(n.has(r))i[r]=t.entries[r];else throw Error(`Key ${r} not found in enum`);return new us({...t,checks:[],...U(r),entries:i})},e.exclude=(e,r)=>{let i={...t.entries};for(let t of e)if(n.has(t))delete i[t];else throw Error(`Key ${t} not found in enum`);return new us({...t,checks:[],...U(r),entries:i})}});function ds(e,t){return new us({type:`enum`,entries:Array.isArray(e)?Object.fromEntries(e.map(e=>[e,e])):e,...U(t)})}var fs=L(`ZodTransform`,(e,t)=>{ii.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>za(e,t,n,r),e._zod.parse=(n,r)=>{if(r.direction===`backward`)throw new gt(e.constructor.name);n.addIssue=r=>{if(typeof r==`string`)n.issues.push(Jt(r,n.value,t));else{let t=r;t.fatal&&(t.continue=!1),t.code??=`custom`,t.input??=n.value,t.inst??=e,n.issues.push(Jt(t))}};let i=t.transform(n.value,n);return i instanceof Promise?i.then(e=>(n.value=e,n.fallback=!0,n)):(n.value=i,n.fallback=!0,n)}});function ps(e){return new fs({type:`transform`,transform:e})}var ms=L(`ZodOptional`,(e,t)=>{oi.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Qa(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function hs(e){return new ms({type:`optional`,innerType:e})}var gs=L(`ZodExactOptional`,(e,t)=>{si.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Qa(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function _s(e){return new gs({type:`optional`,innerType:e})}var vs=L(`ZodNullable`,(e,t)=>{ci.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ga(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function ys(e){return new vs({type:`nullable`,innerType:e})}var bs=L(`ZodDefault`,(e,t)=>{li.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>qa(e,t,n,r),e.unwrap=()=>e._zod.def.innerType,e.removeDefault=e.unwrap});function xs(e,t){return new bs({type:`default`,innerType:e,get defaultValue(){return typeof t==`function`?t():jt(t)}})}var Ss=L(`ZodPrefault`,(e,t)=>{di.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ja(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function Cs(e,t){return new Ss({type:`prefault`,innerType:e,get defaultValue(){return typeof t==`function`?t():jt(t)}})}var ws=L(`ZodNonOptional`,(e,t)=>{fi.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ka(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function Ts(e,t){return new ws({type:`nonoptional`,innerType:e,...U(t)})}var Es=L(`ZodCatch`,(e,t)=>{mi.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ya(e,t,n,r),e.unwrap=()=>e._zod.def.innerType,e.removeCatch=e.unwrap});function Ds(e,t){return new Es({type:`catch`,innerType:e,catchValue:typeof t==`function`?t:()=>t})}var Os=L(`ZodPipe`,(e,t)=>{hi.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Xa(e,t,n,r),e.in=t.in,e.out=t.out});function ks(e,t){return new Os({type:`pipe`,in:e,out:t})}var As=L(`ZodReadonly`,(e,t)=>{_i.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Za(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function js(e){return new As({type:`readonly`,innerType:e})}var Ms=L(`ZodCustom`,(e,t)=>{yi.init(e,t),Z.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ra(e,t,n,r)});function Ns(e,t={}){return wa(Ms,e,t)}function Ps(e,t){return Ta(e,t)}var Fs=`spinifex:v1:aws-credentials`,Is=rs({accessKeyId:wo().min(16,`Access Key ID must be at least 16 characters`),secretAccessKey:wo().min(1,`Secret Access Key is required`)}),$;function Ls(){if($!==void 0)return $;try{let e=localStorage.getItem(Fs);if(!e)return $=null,null;let t=JSON.parse(e),n=Is.safeParse(t);return $=n.success?n.data:null,$}catch{return $=null,null}}function Rs(e){try{localStorage.setItem(Fs,JSON.stringify(e)),$=e}catch{$=e}}function zs(){try{localStorage.removeItem(Fs)}catch{}$=null}var Bs={name:`SHA-256`},Vs={name:`HMAC`,hash:Bs},Hs=new Uint8Array([227,176,196,66,152,252,28,20,154,251,244,200,153,111,185,36,39,174,65,228,100,155,147,76,164,149,153,27,120,82,184,85]),Us={};function Ws(){return typeof window<`u`?window:typeof self<`u`?self:Us}var Gs=function(){function e(e){this.toHash=new Uint8Array,this.secret=e,this.reset()}return e.prototype.update=function(e){if(!r(e)){var t=n(e),i=new Uint8Array(this.toHash.byteLength+t.byteLength);i.set(this.toHash,0),i.set(t,this.toHash.byteLength),this.toHash=i}},e.prototype.digest=function(){var e=this;return this.key?this.key.then(function(t){return Ws().crypto.subtle.sign(Vs,t,e.toHash).then(function(e){return new Uint8Array(e)})}):r(this.toHash)?Promise.resolve(Hs):Promise.resolve().then(function(){return Ws().crypto.subtle.digest(Bs,e.toHash)}).then(function(e){return Promise.resolve(new Uint8Array(e))})},e.prototype.reset=function(){var e=this;this.toHash=new Uint8Array,this.secret&&this.secret!==void 0&&(this.key=new Promise(function(t,r){Ws().crypto.subtle.importKey(`raw`,n(e.secret),Vs,!1,[`sign`]).then(t,r)}),this.key.catch(function(){}))},e}(),Ks=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),qs=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],Js=2**53-1,Ys=function(){function e(){this.state=Int32Array.from(qs),this.temp=new Int32Array(64),this.buffer=new Uint8Array(64),this.bufferLength=0,this.bytesHashed=0,this.finished=!1}return e.prototype.update=function(e){if(this.finished)throw Error(`Attempted to update an already finished hash.`);var t=0,n=e.byteLength;if(this.bytesHashed+=n,this.bytesHashed*8>Js)throw Error(`Cannot hash more than 2^53 - 1 bits`);for(;n>0;)this.buffer[this.bufferLength++]=e[t++],n--,this.bufferLength===64&&(this.hashBuffer(),this.bufferLength=0)},e.prototype.digest=function(){if(!this.finished){var e=this.bytesHashed*8,t=new DataView(this.buffer.buffer,this.buffer.byteOffset,this.buffer.byteLength),n=this.bufferLength;if(t.setUint8(this.bufferLength++,128),n%64>=56){for(var r=this.bufferLength;r<64;r++)t.setUint8(r,0);this.hashBuffer(),this.bufferLength=0}for(var r=this.bufferLength;r<56;r++)t.setUint8(r,0);t.setUint32(56,Math.floor(e/4294967296),!0),t.setUint32(60,e),this.hashBuffer(),this.finished=!0}for(var i=new Uint8Array(32),r=0;r<8;r++)i[r*4]=this.state[r]>>>24&255,i[r*4+1]=this.state[r]>>>16&255,i[r*4+2]=this.state[r]>>>8&255,i[r*4+3]=this.state[r]>>>0&255;return i},e.prototype.hashBuffer=function(){for(var e=this,t=e.buffer,n=e.state,r=n[0],i=n[1],a=n[2],o=n[3],s=n[4],c=n[5],l=n[6],u=n[7],d=0;d<64;d++){if(d<16)this.temp[d]=(t[d*4]&255)<<24|(t[d*4+1]&255)<<16|(t[d*4+2]&255)<<8|t[d*4+3]&255;else{var f=this.temp[d-2],p=(f>>>17|f<<15)^(f>>>19|f<<13)^f>>>10;f=this.temp[d-15];var m=(f>>>7|f<<25)^(f>>>18|f<<14)^f>>>3;this.temp[d]=(p+this.temp[d-7]|0)+(m+this.temp[d-16]|0)}var h=(((s>>>6|s<<26)^(s>>>11|s<<21)^(s>>>25|s<<7))+(s&c^~s&l)|0)+(u+(Ks[d]+this.temp[d]|0)|0)|0,g=((r>>>2|r<<30)^(r>>>13|r<<19)^(r>>>22|r<<10))+(r&i^r&a^i&a)|0;u=l,l=c,c=s,s=o+h|0,o=a,a=i,i=r,r=h+g|0}n[0]+=r,n[1]+=i,n[2]+=a,n[3]+=o,n[4]+=s,n[5]+=c,n[6]+=l,n[7]+=u},e}(),Xs=function(){function t(e){this.secret=e,this.hash=new Ys,this.reset()}return t.prototype.update=function(e){if(!(r(e)||this.error))try{this.hash.update(n(e))}catch(e){this.error=e}},t.prototype.digestSync=function(){if(this.error)throw this.error;return this.outer?(this.outer.finished||this.outer.update(this.hash.digest()),this.outer.digest()):this.hash.digest()},t.prototype.digest=function(){return s(this,void 0,void 0,function(){return e(this,function(e){return[2,this.digestSync()]})})},t.prototype.reset=function(){if(this.hash=new Ys,this.secret){this.outer=new Ys;var e=Zs(this.secret),t=new Uint8Array(64);t.set(e);for(var n=0;n<64;n++)e[n]^=54,t[n]^=92;this.hash.update(e),this.outer.update(t);for(var n=0;n64){var r=new Ys;r.update(t),t=r.digest()}var i=new Uint8Array(64);return i.set(t),i}var Qs=[`decrypt`,`digest`,`encrypt`,`exportKey`,`generateKey`,`importKey`,`sign`,`verify`];function $s(e){if(ec(e)&&typeof e.crypto.subtle==`object`){var t=e.crypto.subtle;return tc(t)}return!1}function ec(e){return typeof e==`object`&&typeof e.crypto==`object`?typeof e.crypto.getRandomValues==`function`:!1}function tc(e){return e&&Qs.every(function(t){return typeof e[t]==`function`})}var nc=function(){function e(e){$s(Ws())?this.hash=new Gs(e):this.hash=new Xs(e)}return e.prototype.update=function(e,t){this.hash.update(n(e))},e.prototype.digest=function(){return this.hash.digest()},e.prototype.reset=function(){this.hash.reset()},e}(),rc=class e{method;protocol;hostname;port;path;query;headers;username;password;fragment;body;constructor(e){this.method=e.method||`GET`,this.hostname=e.hostname||`localhost`,this.port=e.port,this.query=e.query||{},this.headers=e.headers||{},this.body=e.body,this.protocol=e.protocol?e.protocol.slice(-1)===`:`?e.protocol:`${e.protocol}:`:`https:`,this.path=e.path?e.path.charAt(0)===`/`?e.path:`/${e.path}`:`/`,this.username=e.username,this.password=e.password,this.fragment=e.fragment}static clone(t){let n=new e({...t,headers:{...t.headers}});return n.query&&=ic(n.query),n}static isInstance(e){if(!e)return!1;let t=e;return`method`in t&&`protocol`in t&&`hostname`in t&&`path`in t&&typeof t.query==`object`&&typeof t.headers==`object`}clone(){return e.clone(this)}};function ic(e){return Object.keys(e).reduce((t,n)=>{let r=e[n];return{...t,[n]:Array.isArray(r)?[...r]:r}},{})}var ac=e=>{if(typeof e==`function`)return e;let t=Promise.resolve(e);return()=>t},oc=e=>encodeURIComponent(e).replace(/[!'()*]/g,sc),sc=e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`,cc=class{format(e){let n=[];for(let r of Object.keys(e)){let i=t(r);n.push(Uint8Array.from([i.byteLength]),i,this.formatHeaderValue(e[r]))}let r=new Uint8Array(n.reduce((e,t)=>e+t.byteLength,0)),i=0;for(let e of n)r.set(e,i),i+=e.byteLength;return r}formatHeaderValue(e){switch(e.type){case`boolean`:return Uint8Array.from([+!e.value]);case`byte`:return Uint8Array.from([2,e.value]);case`short`:let n=new DataView(new ArrayBuffer(3));return n.setUint8(0,3),n.setInt16(1,e.value,!1),new Uint8Array(n.buffer);case`integer`:let r=new DataView(new ArrayBuffer(5));return r.setUint8(0,4),r.setInt32(1,e.value,!1),new Uint8Array(r.buffer);case`long`:let i=new Uint8Array(9);return i[0]=5,i.set(e.value.bytes,1),i;case`binary`:let o=new DataView(new ArrayBuffer(3+e.value.byteLength));o.setUint8(0,6),o.setUint16(1,e.value.byteLength,!1);let s=new Uint8Array(o.buffer);return s.set(e.value,3),s;case`string`:let c=t(e.value),l=new DataView(new ArrayBuffer(3+c.byteLength));l.setUint8(0,7),l.setUint16(1,c.byteLength,!1);let u=new Uint8Array(l.buffer);return u.set(c,3),u;case`timestamp`:let d=new Uint8Array(9);return d[0]=8,d.set(dc.fromNumber(e.value.valueOf()).bytes,1),d;case`uuid`:if(!uc.test(e.value))throw Error(`Invalid UUID received: ${e.value}`);let f=new Uint8Array(17);return f[0]=9,f.set(a(e.value.replace(/\-/g,``)),1),f}}},lc;(function(e){e[e.boolTrue=0]=`boolTrue`,e[e.boolFalse=1]=`boolFalse`,e[e.byte=2]=`byte`,e[e.short=3]=`short`,e[e.integer=4]=`integer`,e[e.long=5]=`long`,e[e.byteArray=6]=`byteArray`,e[e.string=7]=`string`,e[e.timestamp=8]=`timestamp`,e[e.uuid=9]=`uuid`})(lc||={});var uc=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,dc=class e{bytes;constructor(e){if(this.bytes=e,e.byteLength!==8)throw Error(`Int64 buffers must be exactly 8 bytes`)}static fromNumber(t){if(t>0x8000000000000000||t<-0x8000000000000000)throw Error(`${t} is too large (or, if negative, too small) to represent as an Int64`);let n=new Uint8Array(8);for(let e=7,r=Math.abs(Math.round(t));e>-1&&r>0;e--,r/=256)n[e]=r;return t<0&&fc(n),new e(n)}valueOf(){let e=this.bytes.slice(0),t=e[0]&128;return t&&fc(e),parseInt(o(e),16)*(t?-1:1)}toString(){return String(this.valueOf())}};function fc(e){for(let t=0;t<8;t++)e[t]^=255;for(let t=7;t>-1&&(e[t]++,e[t]===0);t--);}var pc=`X-Amz-Algorithm`,mc=`X-Amz-Credential`,hc=`X-Amz-Date`,gc=`X-Amz-SignedHeaders`,_c=`X-Amz-Expires`,vc=`X-Amz-Signature`,yc=`X-Amz-Security-Token`,bc=`authorization`,xc=hc.toLowerCase(),Sc=[bc,xc,`date`];vc.toLowerCase();var Cc=`x-amz-content-sha256`,wc=yc.toLowerCase(),Tc={authorization:!0,"cache-control":!0,connection:!0,expect:!0,from:!0,"keep-alive":!0,"max-forwards":!0,pragma:!0,referer:!0,te:!0,trailer:!0,"transfer-encoding":!0,upgrade:!0,"user-agent":!0,"x-amzn-trace-id":!0},Ec=/^proxy-/,Dc=/^sec-/,Oc=`AWS4-HMAC-SHA256`,kc=`AWS4-HMAC-SHA256-PAYLOAD`,Ac=`UNSIGNED-PAYLOAD`,jc=`aws4_request`,Mc=({query:e={}})=>{let t=[],n={};for(let r of Object.keys(e)){if(r.toLowerCase()===`x-amz-signature`)continue;let i=oc(r);t.push(i);let a=e[r];typeof a==`string`?n[i]=`${i}=${oc(a)}`:Array.isArray(a)&&(n[i]=a.slice(0).reduce((e,t)=>e.concat([`${i}=${oc(t)}`]),[]).sort().join(`&`))}return t.sort().map(e=>n[e]).filter(e=>e).join(`&`)},Nc=e=>Pc(e).toISOString().replace(/\.\d{3}Z$/,`Z`),Pc=e=>typeof e==`number`?new Date(e*1e3):typeof e==`string`?Number(e)?new Date(Number(e)*1e3):new Date(e):e,Fc=class{service;regionProvider;credentialProvider;sha256;uriEscapePath;applyChecksum;constructor({applyChecksum:e,credentials:t,region:n,service:r,sha256:i,uriEscapePath:a=!0}){this.service=r,this.sha256=i,this.uriEscapePath=a,this.applyChecksum=typeof e==`boolean`?e:!0,this.regionProvider=ac(n),this.credentialProvider=ac(t)}createCanonicalRequest(e,t,n){let r=Object.keys(t).sort();return`${e.method} +${this.getCanonicalPath(e)} +${Mc(e)} +${r.map(e=>`${e}:${t[e]}`).join(` +`)} + +${r.join(`;`)} +${n}`}async createStringToSign(e,t,n,r){let i=new this.sha256;return i.update(c(n)),`${r} +${e} +${t} +${o(await i.digest())}`}getCanonicalPath({path:e}){if(this.uriEscapePath){let t=[];for(let n of e.split(`/`))n?.length!==0&&n!==`.`&&(n===`..`?t.pop():t.push(n));return oc(`${e?.startsWith(`/`)?`/`:``}${t.join(`/`)}${t.length>0&&e?.endsWith(`/`)?`/`:``}`).replace(/%2F/g,`/`)}return e}validateResolvedCredentials(e){if(typeof e!=`object`||typeof e.accessKeyId!=`string`||typeof e.secretAccessKey!=`string`)throw Error(`Resolved credential object is not valid`)}formatDate(e){let t=Nc(e).replace(/[\-:]/g,``);return{longDate:t,shortDate:t.slice(0,8)}}getCanonicalHeaderList(e){return Object.keys(e).sort().join(`;`)}},Ic={},Lc=[],Rc=(e,t,n)=>`${e}/${t}/${n}/${jc}`,zc=async(e,t,n,r,i)=>{let a=`${n}:${r}:${i}:${o(await Bc(e,t.secretAccessKey,t.accessKeyId))}:${t.sessionToken}`;if(a in Ic)return Ic[a];for(Lc.push(a);Lc.length>50;)delete Ic[Lc.shift()];let s=`AWS4${t.secretAccessKey}`;for(let t of[n,r,i,jc])s=await Bc(e,s,t);return Ic[a]=s},Bc=(e,t,n)=>{let r=new e(t);return r.update(c(n)),r.digest()},Vc=({headers:e},t,n)=>{let r={};for(let i of Object.keys(e).sort()){if(e[i]==null)continue;let a=i.toLowerCase();(a in Tc||t?.has(a)||Ec.test(a)||Dc.test(a))&&(!n||n&&!n.has(a))||(r[a]=e[i].trim().replace(/\s+/g,` `))}return r},Hc=async({headers:e,body:t},n)=>{for(let t of Object.keys(e))if(t.toLowerCase()===`x-amz-content-sha256`)return e[t];if(t==null)return`e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`;if(typeof t==`string`||ArrayBuffer.isView(t)||i(t)){let e=new n;return e.update(c(t)),o(await e.digest())}return Ac},Uc=(e,t)=>{e=e.toLowerCase();for(let n of Object.keys(t))if(e===n.toLowerCase())return!0;return!1},Wc=(e,t={})=>{let{headers:n,query:r={}}=rc.clone(e);for(let e of Object.keys(n)){let i=e.toLowerCase();(i.slice(0,6)===`x-amz-`&&!t.unhoistableHeaders?.has(i)||t.hoistableHeaders?.has(i))&&(r[e]=n[e],delete n[e])}return{...e,headers:n,query:r}},Gc=e=>{e=rc.clone(e);for(let t of Object.keys(e.headers))Sc.indexOf(t.toLowerCase())>-1&&delete e.headers[t];return e},Kc=class extends Fc{headerFormatter=new cc;constructor({applyChecksum:e,credentials:t,region:n,service:r,sha256:i,uriEscapePath:a=!0}){super({applyChecksum:e,credentials:t,region:n,service:r,sha256:i,uriEscapePath:a})}async presign(e,t={}){let{signingDate:n=new Date,expiresIn:r=3600,unsignableHeaders:i,unhoistableHeaders:a,signableHeaders:o,hoistableHeaders:s,signingRegion:c,signingService:l}=t,u=await this.credentialProvider();this.validateResolvedCredentials(u);let d=c??await this.regionProvider(),{longDate:f,shortDate:p}=this.formatDate(n);if(r>604800)return Promise.reject(`Signature version 4 presigned URLs must have an expiration date less than one week in the future`);let m=Rc(p,d,l??this.service),h=Wc(Gc(e),{unhoistableHeaders:a,hoistableHeaders:s});u.sessionToken&&(h.query[yc]=u.sessionToken),h.query[pc]=Oc,h.query[mc]=`${u.accessKeyId}/${m}`,h.query[hc]=f,h.query[_c]=r.toString(10);let g=Vc(h,i,o);return h.query[gc]=this.getCanonicalHeaderList(g),h.query[vc]=await this.getSignature(f,m,this.getSigningKey(u,d,p,l),this.createCanonicalRequest(h,g,await Hc(e,this.sha256))),h}async sign(e,t){return typeof e==`string`?this.signString(e,t):e.headers&&e.payload?this.signEvent(e,t):e.message?this.signMessage(e,t):this.signRequest(e,t)}async signEvent({headers:e,payload:t},{signingDate:n=new Date,priorSignature:r,signingRegion:i,signingService:a,eventStreamCredentials:s}){let c=i??await this.regionProvider(),{shortDate:l,longDate:u}=this.formatDate(n),d=Rc(l,c,a??this.service),f=await Hc({headers:{},body:t},this.sha256),p=new this.sha256;p.update(e);let m=[kc,u,d,r,o(await p.digest()),f].join(` +`);return this.signString(m,{signingDate:n,signingRegion:c,signingService:a,eventStreamCredentials:s})}async signMessage(e,{signingDate:t=new Date,signingRegion:n,signingService:r,eventStreamCredentials:i}){return this.signEvent({headers:this.headerFormatter.format(e.message.headers),payload:e.message.body},{signingDate:t,signingRegion:n,signingService:r,priorSignature:e.priorSignature,eventStreamCredentials:i}).then(t=>({message:e.message,signature:t}))}async signString(e,{signingDate:t=new Date,signingRegion:n,signingService:r,eventStreamCredentials:i}={}){let a=i??await this.credentialProvider();this.validateResolvedCredentials(a);let s=n??await this.regionProvider(),{shortDate:l}=this.formatDate(t),u=new this.sha256(await this.getSigningKey(a,s,l,r));return u.update(c(e)),o(await u.digest())}async signRequest(e,{signingDate:t=new Date,signableHeaders:n,unsignableHeaders:r,signingRegion:i,signingService:a}={}){let o=await this.credentialProvider();this.validateResolvedCredentials(o);let s=i??await this.regionProvider(),c=Gc(e),{longDate:l,shortDate:u}=this.formatDate(t),d=Rc(u,s,a??this.service);c.headers[xc]=l,o.sessionToken&&(c.headers[wc]=o.sessionToken);let f=await Hc(c,this.sha256);!Uc(`x-amz-content-sha256`,c.headers)&&this.applyChecksum&&(c.headers[Cc]=f);let p=Vc(c,r,n),m=await this.getSignature(l,d,this.getSigningKey(o,s,u,a),this.createCanonicalRequest(c,p,f));return c.headers[bc]=`${Oc} Credential=${o.accessKeyId}/${d}, SignedHeaders=${this.getCanonicalHeaderList(p)}, Signature=${m}`,c}async getSignature(e,t,n,r){let i=await this.createStringToSign(e,t,r,Oc),a=new this.sha256(await n);return a.update(c(i)),o(await a.digest())}getSigningKey(e,t,n,r){return zc(this.sha256,e,n,t,r||this.service)}};export{ct as A,ft as C,it as D,pt as E,u as M,st as O,Xt as S,dt as T,rs as _,nc as a,tn as b,Is as c,Rs as d,ds as f,Go as g,qo as h,rc as i,ut as j,at as k,zs as l,Yo as m,oc as n,$s as o,ts as p,ac as r,Ws as s,Kc as t,Ls as u,ls as v,nt as w,rn as x,wo as y}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_.js b/spinifex/services/spinifexui/frontend/dist/assets/_.js index 7594c96ee..5aced5cb1 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{n as r,r as i,t as a}from"./upload-button.js";import{r as o}from"./s3.js";import{Fo as s,Ho as c,Lo as l,Qa as u,eo as d,oo as f,to as p,y as m,z as h}from"./index.js";var g=s(),_=c();function v(){let s=(0,g.c)(24),{bucket:c,_splat:v}=m.useParams(),y=d(v??``),{data:b}=t(h(c,y)),x=o(),S;s[0]===b.Contents?S=s[1]:(S=b.Contents??[],s[0]=b.Contents,s[1]=S);let C=S,w;s[2]===b.CommonPrefixes?w=s[3]:(w=b.CommonPrefixes??[],s[2]=b.CommonPrefixes,s[3]=w);let T=w,E=y.split(`/`).filter(Boolean),D=[{name:c,path:`/s3/ls/${c}`},...E.map((e,t)=>({name:e,path:`/s3/ls/${c}/${`${E.slice(0,t+1).join(`/`)}/`}`}))],O=E.length>1?`/s3/ls/${c}/${E.slice(0,-1).join(`/`)}/`:`/s3/ls/${c}`,k;s[4]===O?k=s[5]:(k=(0,_.jsx)(n,{to:O,children:`Back`}),s[4]=O,s[5]=k);let A;s[6]!==c||s[7]!==y||s[8]!==x.isPending||s[9]!==x.mutateAsync?(A=(0,_.jsx)(a,{bucket:c,isPending:x.isPending,onUpload:x.mutateAsync,prefix:y}),s[6]=c,s[7]=y,s[8]=x.isPending,s[9]=x.mutateAsync,s[10]=A):A=s[10];let j;s[11]===x.error?j=s[12]:(j=x.error&&(0,_.jsx)(e,{error:x.error,msg:`Failed to upload object`}),s[11]=x.error,s[12]=j);let M;s[13]!==c||s[14]!==T||s[15]!==y?(M=T.length>0&&(0,_.jsxs)(`div`,{className:`mb-6`,children:[(0,_.jsx)(`h2`,{className:`mb-3 text-lg font-semibold`,children:`Folders`}),(0,_.jsx)(`div`,{className:`space-y-2`,children:T.map(e=>e.Prefix?(0,_.jsx)(i,{bucketName:c,folderName:f(p(e.Prefix,y)),fullPrefix:u(e.Prefix,y)},e.Prefix):null)})]}),s[13]=c,s[14]=T,s[15]=y,s[16]=M):M=s[16];let N;s[17]!==c||s[18]!==C||s[19]!==y?(N=C.length>0&&(0,_.jsxs)(`div`,{className:`space-y-2`,children:[(0,_.jsx)(`h2`,{className:`mb-3 text-lg font-semibold`,children:`Objects`}),C.map(e=>e.Key?(0,_.jsx)(r,{bucketName:c,displayName:p(e.Key,y),fullKey:u(e.Key,y),object:e},e.Key):null)]}),s[17]=c,s[18]=C,s[19]=y,s[20]=N):N=s[20];let P;return s[21]!==T.length||s[22]!==C.length?(P=C.length===0&&T.length===0&&(0,_.jsx)(`p`,{className:`text-muted-foreground`,children:`No objects found in this folder.`}),s[21]=T.length,s[22]=C.length,s[23]=P):P=s[23],(0,_.jsxs)(_.Fragment,{children:[k,(0,_.jsx)(`div`,{className:`mb-6`,children:(0,_.jsxs)(`div`,{className:`mb-2 flex items-center justify-between`,children:[(0,_.jsx)(`div`,{className:`flex items-center gap-2 text-sm text-muted-foreground`,children:D.map((e,t)=>(0,_.jsxs)(`div`,{className:`flex items-center gap-2`,children:[t>0&&(0,_.jsx)(`span`,{children:`/`}),t===D.length-1?(0,_.jsx)(`span`,{className:`text-foreground`,children:e.name}):(0,_.jsx)(l,{className:`hover:text-foreground`,to:e.path,children:e.name})]},e.path))}),A]})}),j,M,N,P]})}export{v as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{Or as i}from"./awsClient.js";import{C as a,E as o,T as s,j as c}from"./SignatureV4.js";import{t as l}from"./back-link.js";import{n as u,r as d,t as f}from"./upload-button.js";import{r as p}from"./s3.js";import{S as m,ot as h}from"./index.js";var g=t(),_=e();function v(){let e=(0,g.c)(24),{bucket:t,_splat:v}=m.useParams(),y=s(v??``),{data:b}=r(h(t,y)),x=p(),S;e[0]===b.Contents?S=e[1]:(S=b.Contents??[],e[0]=b.Contents,e[1]=S);let C=S,w;e[2]===b.CommonPrefixes?w=e[3]:(w=b.CommonPrefixes??[],e[2]=b.CommonPrefixes,e[3]=w);let T=w,E=y.split(`/`).filter(Boolean),D=[{name:t,path:`/s3/ls/${t}`},...E.map((e,n)=>({name:e,path:`/s3/ls/${t}/${`${E.slice(0,n+1).join(`/`)}/`}`}))],O=E.length>1?`/s3/ls/${t}/${E.slice(0,-1).join(`/`)}/`:`/s3/ls/${t}`,k;e[4]===O?k=e[5]:(k=(0,_.jsx)(l,{to:O,children:`Back`}),e[4]=O,e[5]=k);let A;e[6]!==t||e[7]!==y||e[8]!==x.isPending||e[9]!==x.mutateAsync?(A=(0,_.jsx)(f,{bucket:t,isPending:x.isPending,onUpload:x.mutateAsync,prefix:y}),e[6]=t,e[7]=y,e[8]=x.isPending,e[9]=x.mutateAsync,e[10]=A):A=e[10];let j;e[11]===x.error?j=e[12]:(j=x.error&&(0,_.jsx)(n,{error:x.error,msg:`Failed to upload object`}),e[11]=x.error,e[12]=j);let M;e[13]!==t||e[14]!==T||e[15]!==y?(M=T.length>0&&(0,_.jsxs)(`div`,{className:`mb-6`,children:[(0,_.jsx)(`h2`,{className:`mb-3 text-lg font-semibold`,children:`Folders`}),(0,_.jsx)(`div`,{className:`space-y-2`,children:T.map(e=>e.Prefix?(0,_.jsx)(d,{bucketName:t,folderName:c(o(e.Prefix,y)),fullPrefix:a(e.Prefix,y)},e.Prefix):null)})]}),e[13]=t,e[14]=T,e[15]=y,e[16]=M):M=e[16];let N;e[17]!==t||e[18]!==C||e[19]!==y?(N=C.length>0&&(0,_.jsxs)(`div`,{className:`space-y-2`,children:[(0,_.jsx)(`h2`,{className:`mb-3 text-lg font-semibold`,children:`Objects`}),C.map(e=>e.Key?(0,_.jsx)(u,{bucketName:t,displayName:o(e.Key,y),fullKey:a(e.Key,y),object:e},e.Key):null)]}),e[17]=t,e[18]=C,e[19]=y,e[20]=N):N=e[20];let P;return e[21]!==T.length||e[22]!==C.length?(P=C.length===0&&T.length===0&&(0,_.jsx)(`p`,{className:`text-muted-foreground`,children:`No objects found in this folder.`}),e[21]=T.length,e[22]=C.length,e[23]=P):P=e[23],(0,_.jsxs)(_.Fragment,{children:[k,(0,_.jsx)(`div`,{className:`mb-6`,children:(0,_.jsxs)(`div`,{className:`mb-2 flex items-center justify-between`,children:[(0,_.jsx)(`div`,{className:`flex items-center gap-2 text-sm text-muted-foreground`,children:D.map((e,t)=>(0,_.jsxs)(`div`,{className:`flex items-center gap-2`,children:[t>0&&(0,_.jsx)(`span`,{children:`/`}),t===D.length-1?(0,_.jsx)(`span`,{className:`text-foreground`,children:e.name}):(0,_.jsx)(i,{className:`hover:text-foreground`,to:e.path,children:e.name})]},e.path))}),A]})}),j,M,N,P]})}export{v as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_auth.js b/spinifex/services/spinifexui/frontend/dist/assets/_auth.js index 8ba71f311..b2eafb3e5 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_auth.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_auth.js @@ -1 +1 @@ -import{t as e}from"./useQuery.js";import{t}from"./chevron-down.js";import{t as n}from"./chevron-right.js";import{B as r,Fo as i,H as a,Ho as o,J as s,Lo as c,Q as l,So as u,Uo as d,X as f,_o as p,ao as m,bo as h,cs as g,et as _,go as v,gr as y,ho as b,ht as x,pt as S,st as C,vo as w,wo as T,xo as E,yo as D}from"./index.js";import{t as O}from"./page-heading.js";import{t as k}from"./state-badge.js";import{r as ee,t as te}from"./admin.js";import{t as ne}from"./badge.js";import{a as A,i as j,n as M,o as N,r as P,t as F}from"./card.js";var I=T(`box`,[[`path`,{d:`M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z`,key:`hh9hay`}],[`path`,{d:`m3.3 7 8.7 5 8.7-5`,key:`g66t2b`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}]]),L=T(`folder-open`,[[`path`,{d:`m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2`,key:`usdka0`}]]),R=T(`lock`,[[`rect`,{width:`18`,height:`11`,x:`3`,y:`11`,rx:`2`,ry:`2`,key:`1w4ew1`}],[`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`,key:`fwvmzm`}]]),re=T(`monitor`,[[`rect`,{width:`20`,height:`14`,x:`2`,y:`3`,rx:`2`,key:`48i651`}],[`line`,{x1:`8`,x2:`16`,y1:`21`,y2:`21`,key:`1svkeh`}],[`line`,{x1:`12`,x2:`12`,y1:`17`,y2:`21`,key:`vw1qmm`}]]),z=i(),B=g(d(),1),V=o(),H=[{name:`Air-gap Support`,key:`airgap`},{name:`NVIDIA Bluefield DPU`,key:`bluefield`},{name:`NVIDIA MIG Support`,key:`mig`}];function U(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600);if(t>0)return`${t}d ${n}h`;let r=Math.floor(e%3600/60);return n>0?`${n}h ${r}m`:`${r}m`}function W(){let r=(0,z.c)(110),{isAdmin:i,version:a,license:o}=y(),[s,c]=(0,B.useState)(!1),l;r[0]===i?l=r[1]:(l={...te,enabled:i},r[0]=i,r[1]=l);let{data:u}=e(l),d;r[2]===i?d=r[3]:(d={...ee,enabled:i},r[2]=i,r[3]=d);let{data:f}=e(d);if(!i||!a)return null;let m,h,g,_,v,b,x,S,C,w,T,E,D,O,k,j,M,I,L;if(r[4]!==o||r[5]!==u?.cluster_mode||r[6]!==u?.nodes||r[7]!==a.arch||r[8]!==a.commit||r[9]!==a.os||r[10]!==a.version||r[11]!==f?.vms){_=u?.nodes??[];let e;r[31]===f?.vms?e=r[32]:(e=f?.vms??[],r[31]=f?.vms,r[32]=e);let t=e,n=u?.cluster_mode??`single-node`;g=o===`open-source`;let i=_[0]?.region??null,s=_.reduce(se,0),c=_.reduce(oe,0),l=_.reduce(ae,0),d=_.reduce(ie,0);T=`mb-6 space-y-4`,S=`grid grid-cols-1 gap-4 md:grid-cols-3`;let y;r[33]===Symbol.for(`react.memo_cache_sentinel`)?(y=(0,V.jsx)(A,{className:`pb-2`,children:(0,V.jsxs)(N,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(re,{className:`size-3.5`}),`Spinifex Info`]})}),r[33]=y):y=r[33];let ee;r[34]===Symbol.for(`react.memo_cache_sentinel`)?(ee=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Version`}),r[34]=ee):ee=r[34];let te;r[35]===a.version?te=r[36]:(te=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[ee,(0,V.jsx)(`span`,{className:`font-mono`,children:a.version})]}),r[35]=a.version,r[36]=te);let R;r[37]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Commit`}),r[37]=R):R=r[37];let z;r[38]===a.commit?z=r[39]:(z=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[R,(0,V.jsx)(`span`,{className:`font-mono`,children:a.commit})]}),r[38]=a.commit,r[39]=z);let B;r[40]===Symbol.for(`react.memo_cache_sentinel`)?(B=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Platform`}),r[40]=B):B=r[40];let H;r[41]!==a.arch||r[42]!==a.os?(H=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[B,(0,V.jsxs)(`span`,{children:[a.os,`/`,a.arch]})]}),r[41]=a.arch,r[42]=a.os,r[43]=H):H=r[43],C=(0,V.jsxs)(F,{children:[y,(0,V.jsxs)(P,{className:`space-y-1 text-xs`,children:[te,z,H,i&&(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Region`}),(0,V.jsx)(`span`,{children:i})]})]})]});let U;r[44]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,V.jsx)(A,{className:`pb-2`,children:(0,V.jsx)(N,{className:`text-sm`,children:`License & Support`})}),r[44]=U):U=r[44];let W;r[45]===Symbol.for(`react.memo_cache_sentinel`)?(W=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`License`}),r[45]=W):W=r[45];let ce=g?`secondary`:`default`,G=g?`Open Source (AGPLv3)`:`Commercial`,K;r[46]!==ce||r[47]!==G?(K=(0,V.jsxs)(`div`,{className:`flex items-center justify-between`,children:[W,(0,V.jsx)(ne,{variant:ce,children:G})]}),r[46]=ce,r[47]=G,r[48]=K):K=r[48];let q;r[49]===Symbol.for(`react.memo_cache_sentinel`)?(q=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Support`}),r[49]=q):q=r[49];let J=g?`Community`:`Standard`,Y;r[50]===J?Y=r[51]:(Y=(0,V.jsxs)(`div`,{className:`flex items-center justify-between`,children:[q,(0,V.jsx)(`span`,{children:J})]}),r[50]=J,r[51]=Y);let X;r[52]===g?X=r[53]:(X=g&&(0,V.jsx)(`a`,{href:`https://mulgadc.com/purchase`,target:`_blank`,rel:`noopener noreferrer`,className:`mt-2 block text-xs font-medium text-primary hover:underline`,children:`Upgrade to Commercial →`}),r[52]=g,r[53]=X),r[54]!==K||r[55]!==Y||r[56]!==X?(w=(0,V.jsxs)(F,{children:[U,(0,V.jsxs)(P,{className:`space-y-2 text-xs`,children:[K,Y,X]})]}),r[54]=K,r[55]=Y,r[56]=X,r[57]=w):w=r[57],h=F,r[58]===Symbol.for(`react.memo_cache_sentinel`)?(x=(0,V.jsx)(A,{className:`pb-2`,children:(0,V.jsxs)(N,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(p,{className:`size-3.5`}),`Cluster Status`]})}),r[58]=x):x=r[58],m=P,M=`space-y-2 text-xs`,r[59]===n?I=r[60]:(I=(0,V.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,V.jsx)(ne,{variant:`outline`,children:n})}),r[59]=n,r[60]=I);let Z;r[61]===Symbol.for(`react.memo_cache_sentinel`)?(Z=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Nodes`}),r[61]=Z):Z=r[61],L=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[Z,(0,V.jsx)(`span`,{children:_.length})]});let Q;r[62]===Symbol.for(`react.memo_cache_sentinel`)?(Q=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Instances`}),r[62]=Q):Q=r[62],r[63]===t.length?v=r[64]:(v=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[Q,(0,V.jsx)(`span`,{children:t.length})]}),r[63]=t.length,r[64]=v);let $;r[65]===Symbol.for(`react.memo_cache_sentinel`)?($=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`vCPU`}),r[65]=$):$=r[65],b=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[$,(0,V.jsxs)(`span`,{children:[c,` / `,s]})]}),k=`flex justify-between`,r[66]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Memory`}),r[66]=j):j=r[66],E=d.toFixed(1),D=` / `,O=l.toFixed(1),r[4]=o,r[5]=u?.cluster_mode,r[6]=u?.nodes,r[7]=a.arch,r[8]=a.commit,r[9]=a.os,r[10]=a.version,r[11]=f?.vms,r[12]=m,r[13]=h,r[14]=g,r[15]=_,r[16]=v,r[17]=b,r[18]=x,r[19]=S,r[20]=C,r[21]=w,r[22]=T,r[23]=E,r[24]=D,r[25]=O,r[26]=k,r[27]=j,r[28]=M,r[29]=I,r[30]=L}else m=r[12],h=r[13],g=r[14],_=r[15],v=r[16],b=r[17],x=r[18],S=r[19],C=r[20],w=r[21],T=r[22],E=r[23],D=r[24],O=r[25],k=r[26],j=r[27],M=r[28],I=r[29],L=r[30];let U;r[67]!==E||r[68]!==D||r[69]!==O?(U=(0,V.jsxs)(`span`,{children:[E,D,O,` GB`]}),r[67]=E,r[68]=D,r[69]=O,r[70]=U):U=r[70];let W;r[71]!==U||r[72]!==k||r[73]!==j?(W=(0,V.jsxs)(`div`,{className:k,children:[j,U]}),r[71]=U,r[72]=k,r[73]=j,r[74]=W):W=r[74];let G;r[75]!==m||r[76]!==v||r[77]!==b||r[78]!==W||r[79]!==M||r[80]!==I||r[81]!==L?(G=(0,V.jsxs)(m,{className:M,children:[I,L,v,b,W]}),r[75]=m,r[76]=v,r[77]=b,r[78]=W,r[79]=M,r[80]=I,r[81]=L,r[82]=G):G=r[82];let K;r[83]!==h||r[84]!==x||r[85]!==G?(K=(0,V.jsxs)(h,{children:[x,G]}),r[83]=h,r[84]=x,r[85]=G,r[86]=K):K=r[86];let q;r[87]!==S||r[88]!==C||r[89]!==w||r[90]!==K?(q=(0,V.jsxs)(`div`,{className:S,children:[C,w,K]}),r[87]=S,r[88]=C,r[89]=w,r[90]=K,r[91]=q):q=r[91];let J;r[92]!==_||r[93]!==s?(J=_.length>0&&(0,V.jsxs)(F,{children:[(0,V.jsx)(A,{className:`pb-0`,children:(0,V.jsxs)(`button`,{type:`button`,className:`flex items-center gap-2 text-sm font-medium`,onClick:()=>c(!s),children:[s?(0,V.jsx)(t,{className:`size-3.5`}):(0,V.jsx)(n,{className:`size-3.5`}),`Nodes (`,_.length,`)`]})}),s&&(0,V.jsx)(P,{className:`pt-2`,children:(0,V.jsx)(ce,{nodes:_})})]}),r[92]=_,r[93]=s,r[94]=J):J=r[94];let Y;r[95]===Symbol.for(`react.memo_cache_sentinel`)?(Y=(0,V.jsx)(A,{className:`pb-2`,children:(0,V.jsx)(N,{className:`text-sm`,children:`Commercial Add-ons`})}),r[95]=Y):Y=r[95];let X;r[96]===g?X=r[97]:(X=H.map(e=>(0,V.jsxs)(`div`,{className:`flex items-center justify-between text-xs`,children:[(0,V.jsx)(`span`,{className:g?`text-muted-foreground`:void 0,children:e.name}),g?(0,V.jsx)(R,{className:`size-3 text-muted-foreground`}):(0,V.jsx)(ne,{variant:`outline`,className:`text-[0.625rem]`,children:`Active`})]},e.key)),r[96]=g,r[97]=X);let Z;r[98]===X?Z=r[99]:(Z=(0,V.jsx)(`div`,{className:`space-y-2`,children:X}),r[98]=X,r[99]=Z);let Q;r[100]===g?Q=r[101]:(Q=g&&(0,V.jsx)(`a`,{href:`https://mulgadc.com/purchase`,target:`_blank`,rel:`noopener noreferrer`,className:`mt-3 block text-xs font-medium text-primary hover:underline`,children:`Learn more about commercial features →`}),r[100]=g,r[101]=Q);let $;r[102]!==Z||r[103]!==Q?($=(0,V.jsxs)(F,{children:[Y,(0,V.jsxs)(P,{children:[Z,Q]})]}),r[102]=Z,r[103]=Q,r[104]=$):$=r[104];let le;return r[105]!==T||r[106]!==q||r[107]!==J||r[108]!==$?(le=(0,V.jsxs)(`div`,{className:T,children:[q,J,$]}),r[105]=T,r[106]=q,r[107]=J,r[108]=$,r[109]=le):le=r[109],le}function ie(e,t){return e+t.alloc_mem_gb}function ae(e,t){return e+t.total_mem_gb}function oe(e,t){return e+t.alloc_vcpu}function se(e,t){return e+t.total_vcpu}function ce(e){let t=(0,z.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,V.jsx)(`thead`,{children:(0,V.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Name`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Status`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Roles`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`IP`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Region`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`AZ`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Uptime`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`EC2`}),(0,V.jsx)(`th`,{className:`pb-1 font-medium`,children:`Services`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(G),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,V.jsx)(`div`,{className:`overflow-x-auto`,children:(0,V.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,V.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function G(e){return(0,V.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,V.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.node}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,V.jsx)(ne,{variant:e.status===`Ready`?`default`:`secondary`,className:`text-[0.625rem]`,children:e.status})}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,V.jsxs)(`div`,{className:`flex gap-1`,children:[e.nats_role&&(0,V.jsxs)(`span`,{className:`text-muted-foreground`,children:[`nats:`,e.nats_role]}),e.predastore_role&&(0,V.jsxs)(`span`,{className:`text-muted-foreground`,children:[`ps:`,e.predastore_role]})]})}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.host}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.region}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.az}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:U(e.uptime)}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.vm_count}),(0,V.jsx)(`td`,{className:`py-1.5`,children:(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.services.length})})]},e.node)}var K=5;function q(){let e=(0,z.c)(11),t,n;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,V.jsx)(O,{title:`Dashboard`}),n=(0,V.jsx)(W,{}),e[0]=t,e[1]=n):(t=e[0],n=e[1]);let r;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(Q,{})}),e[2]=r):r=e[2];let i;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(i=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(me,{})}),e[3]=i):i=e[3];let a;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(ye,{})}),e[4]=a):a=e[4];let o;e[5]===Symbol.for(`react.memo_cache_sentinel`)?(o=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(xe,{})}),e[5]=o):o=e[5];let s;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(Ce,{})}),e[6]=s):s=e[6];let c;e[7]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(Te,{})}),e[7]=c):c=e[7];let l;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(De,{})}),e[8]=l):l=e[8];let u;e[9]===Symbol.for(`react.memo_cache_sentinel`)?(u=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(je,{})}),e[9]=u):u=e[9];let d;return e[10]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,V.jsxs)(V.Fragment,{children:[t,n,(0,V.jsxs)(`div`,{className:`grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3`,children:[r,i,a,o,s,c,l,u,(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(ke,{})})]})]}),e[10]=d):d=e[10],d}function J(){let e=(0,z.c)(2),t;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,V.jsx)(A,{children:(0,V.jsx)(`div`,{className:`h-4 w-32 animate-pulse rounded bg-muted`})}),e[0]=t):t=e[0];let n;return e[1]===Symbol.for(`react.memo_cache_sentinel`)?(n=(0,V.jsxs)(F,{children:[t,(0,V.jsx)(P,{children:(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsx)(`div`,{className:`h-3 w-48 animate-pulse rounded bg-muted`}),(0,V.jsx)(`div`,{className:`h-3 w-36 animate-pulse rounded bg-muted`})]})})]}),e[1]=n):n=e[1],n}function Y(e){let t=(0,z.c)(16),{title:n,icon:r,count:i,to:a,children:o}=e,s;t[0]!==r||t[1]!==n?(s=(0,V.jsxs)(N,{className:`flex items-center gap-2`,children:[r,n]}),t[0]=r,t[1]=n,t[2]=s):s=t[2];let l;t[3]===i?l=t[4]:(l=(0,V.jsx)(M,{children:(0,V.jsx)(`span`,{className:`rounded-md bg-muted px-2 py-0.5 text-xs font-semibold tabular-nums`,children:i})}),t[3]=i,t[4]=l);let u;t[5]!==s||t[6]!==l?(u=(0,V.jsxs)(A,{children:[s,l]}),t[5]=s,t[6]=l,t[7]=u):u=t[7];let d;t[8]===o?d=t[9]:(d=(0,V.jsx)(P,{children:o}),t[8]=o,t[9]=d);let f;t[10]===a?f=t[11]:(f=(0,V.jsx)(j,{children:(0,V.jsx)(c,{to:a,className:`text-xs font-medium text-primary hover:underline`,children:`View all →`})}),t[10]=a,t[11]=f);let p;return t[12]!==u||t[13]!==d||t[14]!==f?(p=(0,V.jsxs)(F,{children:[u,d,f]}),t[12]=u,t[13]=d,t[14]=f,t[15]=p):p=t[15],p}function X(e){let t=(0,z.c)(2),{total:n}=e;if(n<=K)return null;let r=n-K,i;return t[0]===r?i=t[1]:(i=(0,V.jsxs)(`p`,{className:`mt-1 text-[0.625rem] text-muted-foreground`,children:[`+`,r,` more`]}),t[0]=r,t[1]=i),i}function Z(e){let t=(0,z.c)(2),{children:n}=e,r;return t[0]===n?r=t[1]:(r=(0,V.jsx)(`div`,{className:`flex items-center justify-between py-0.5 text-xs`,children:n}),t[0]=n,t[1]=r),r}function Q(){let t=(0,z.c)(16),{data:n}=e(s),r,i,a,o,c;if(t[0]!==n?.Reservations){let e=n?.Reservations?.flatMap(pe)??[];r=e.filter(fe),a=e.filter(de),o=e.filter(ue),i=e.filter(le),c=[...a,...i].slice(0,K),t[0]=n?.Reservations,t[1]=r,t[2]=i,t[3]=a,t[4]=o,t[5]=c}else r=t[1],i=t[2],a=t[3],o=t[4],c=t[5];let l=c,u;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(u=(0,V.jsx)(E,{className:`size-3.5`}),t[6]=u):u=t[6];let d;t[7]!==r.length||t[8]!==l||t[9]!==i||t[10]!==a||t[11]!==o?(d=r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No instances`}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsxs)(`div`,{className:`mb-2 flex gap-3 text-[0.625rem] text-muted-foreground`,children:[(0,V.jsxs)(`span`,{children:[a.length,` running`]}),(0,V.jsxs)(`span`,{children:[o.length,` stopped`]})]}),(0,V.jsx)(`div`,{className:`space-y-0.5`,children:l.map($)}),(0,V.jsx)(X,{total:a.length+i.length})]}),t[7]=r.length,t[8]=l,t[9]=i,t[10]=a,t[11]=o,t[12]=d):d=t[12];let f;return t[13]!==r.length||t[14]!==d?(f=(0,V.jsx)(Y,{title:`Instances`,icon:u,count:r.length,to:`/ec2/describe-instances`,children:d}),t[13]=r.length,t[14]=d,t[15]=f):f=t[15],f}function $(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.InstanceId}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.InstanceType})]},e.InstanceId)}function le(e){return e.State?.Name===`pending`}function ue(e){return e.State?.Name===`stopped`}function de(e){return e.State?.Name===`running`}function fe(e){return e.State?.Name!==`terminated`}function pe(e){return e.Instances??[]}function me(){let t=(0,z.c)(3),{data:n}=e(S),r;if(t[0]!==n?.Volumes){let e=n?.Volumes??[],i=e.reduce(ve,0),a=e.filter(_e),o=e.filter(ge),s=e.slice(0,K),c;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,V.jsx)(I,{className:`size-3.5`}),t[2]=c):c=t[2],r=(0,V.jsx)(Y,{title:`Volumes`,icon:c,count:e.length,to:`/ec2/describe-volumes`,children:e.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No volumes`}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsxs)(`div`,{className:`mb-2 flex gap-3 text-[0.625rem] text-muted-foreground`,children:[(0,V.jsxs)(`span`,{children:[i,` GiB total`]}),(0,V.jsxs)(`span`,{children:[a.length,` in-use`]}),(0,V.jsxs)(`span`,{children:[o.length,` available`]})]}),(0,V.jsx)(`div`,{className:`space-y-0.5`,children:s.map(he)}),(0,V.jsx)(X,{total:e.length})]})}),t[0]=n?.Volumes,t[1]=r}else r=t[1];return r}function he(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.VolumeId}),(0,V.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,V.jsxs)(`span`,{className:`text-muted-foreground`,children:[e.Size,` GiB`]}),(0,V.jsx)(k,{state:e.State})]})]},e.VolumeId)}function ge(e){return e.State===`available`}function _e(e){return e.State===`in-use`}function ve(e,t){return e+(t.Size??0)}function ye(){let t=(0,z.c)(3),{data:n}=e(x),r;if(t[0]!==n?.Vpcs){let e=n?.Vpcs??[],i=e.slice(0,K),a;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(D,{className:`size-3.5`}),t[2]=a):a=t[2],r=(0,V.jsx)(Y,{title:`VPCs`,icon:a,count:e.length,to:`/ec2/describe-vpcs`,children:e.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No VPCs`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(be),(0,V.jsx)(X,{total:e.length})]})}),t[0]=n?.Vpcs,t[1]=r}else r=t[1];return r}function be(e){let t=m(e.Tags);return(0,V.jsxs)(Z,{children:[(0,V.jsxs)(`span`,{className:`font-mono`,children:[e.VpcId,t&&(0,V.jsxs)(`span`,{className:`ml-1 font-sans text-muted-foreground`,children:[`(`,t,`)`]})]}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.CidrBlock})]},e.VpcId)}function xe(){let t=(0,z.c)(3),{data:n}=e(l),r;if(t[0]!==n?.KeyPairs){let e=n?.KeyPairs??[],i=e.slice(0,K),a;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(h,{className:`size-3.5`}),t[2]=a):a=t[2],r=(0,V.jsx)(Y,{title:`Key Pairs`,icon:a,count:e.length,to:`/ec2/describe-key-pairs`,children:e.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No key pairs`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Se),(0,V.jsx)(X,{total:e.length})]})}),t[0]=n?.KeyPairs,t[1]=r}else r=t[1];return r}function Se(e){return(0,V.jsx)(Z,{children:(0,V.jsx)(`span`,{className:`font-mono`,children:e.KeyName})},e.KeyPairId)}function Ce(){let t=(0,z.c)(3),{data:n}=e(r),i;if(t[0]!==n?.Buckets){let e=n?.Buckets??[],r=e.slice(0,K),a;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(L,{className:`size-3.5`}),t[2]=a):a=t[2],i=(0,V.jsx)(Y,{title:`S3 Buckets`,icon:a,count:e.length,to:`/s3/ls`,children:e.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No buckets`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[r.map(we),(0,V.jsx)(X,{total:e.length})]})}),t[0]=n?.Buckets,t[1]=i}else i=t[1];return i}function we(e){return(0,V.jsx)(Z,{children:(0,V.jsx)(`span`,{className:`font-mono`,children:e.Name})},e.Name)}function Te(){let t=(0,z.c)(3),{data:n}=e(C),r;if(t[0]!==n?.SecurityGroups){let e=n?.SecurityGroups??[],i=e.slice(0,K),a;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(v,{className:`size-3.5`}),t[2]=a):a=t[2],r=(0,V.jsx)(Y,{title:`Security Groups`,icon:a,count:e.length,to:`/ec2/describe-security-groups`,children:e.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No security groups`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Ee),(0,V.jsx)(X,{total:e.length})]})}),t[0]=n?.SecurityGroups,t[1]=r}else r=t[1];return r}function Ee(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.GroupName}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.VpcId})]},e.GroupId)}function De(){let t=(0,z.c)(3),{data:n}=e(_),r;if(t[0]!==n?.NatGateways){let e=n?.NatGateways??[],i=e.slice(0,K),a;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(b,{className:`size-3.5`}),t[2]=a):a=t[2],r=(0,V.jsx)(Y,{title:`NAT Gateways`,icon:a,count:e.length,to:`/ec2/describe-nat-gateways`,children:e.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No NAT gateways`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Oe),(0,V.jsx)(X,{total:e.length})]})}),t[0]=n?.NatGateways,t[1]=r}else r=t[1];return r}function Oe(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.NatGatewayId}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.State})]},e.NatGatewayId)}function ke(){let t=(0,z.c)(3),{data:n}=e(f),r;if(t[0]!==n?.InternetGateways){let e=n?.InternetGateways??[],i=e.slice(0,K),a;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(w,{className:`size-3.5`}),t[2]=a):a=t[2],r=(0,V.jsx)(Y,{title:`Internet Gateways`,icon:a,count:e.length,to:`/ec2/describe-internet-gateways`,children:e.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No internet gateways`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Ae),(0,V.jsx)(X,{total:e.length})]})}),t[0]=n?.InternetGateways,t[1]=r}else r=t[1];return r}function Ae(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.InternetGatewayId}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.Attachments?.[0]?.VpcId?`attached`:`detached`})]},e.InternetGatewayId)}function je(){let t=(0,z.c)(3),{data:n}=e(a),r;if(t[0]!==n?.Addresses){let e=n?.Addresses??[],i=e.slice(0,K),a;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(u,{className:`size-3.5`}),t[2]=a):a=t[2],r=(0,V.jsx)(Y,{title:`Elastic IPs`,icon:a,count:e.length,to:`/ec2/describe-addresses`,children:e.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No Elastic IPs`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Me),(0,V.jsx)(X,{total:e.length})]})}),t[0]=n?.Addresses,t[1]=r}else r=t[1];return r}function Me(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.PublicIp}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.AssociationId?`associated`:`available`})]},e.AllocationId)}export{q as component}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./useQuery.js";import{Or as a}from"./awsClient.js";import{A as o}from"./SignatureV4.js";import{t as s}from"./chevron-down.js";import{t as c}from"./chevron-right.js";import{Ar as l,Bt as u,Dr as d,Er as f,Ft as p,Lt as m,Nr as h,Or as g,St as _,Tr as v,bt as y,gt as b,jr as x,kr as S,kt as C,lt as w,st as T,vt as E,wr as D}from"./index.js";import{t as O}from"./page-heading.js";import{t as k}from"./state-badge.js";import{r as A,t as ee}from"./admin.js";import{t as te}from"./badge.js";import{a as j,i as M,n as N,o as ne,r as P,t as F}from"./card.js";var I=h(`box`,[[`path`,{d:`M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z`,key:`hh9hay`}],[`path`,{d:`m3.3 7 8.7 5 8.7-5`,key:`g66t2b`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}]]),L=h(`folder-open`,[[`path`,{d:`m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2`,key:`usdka0`}]]),R=h(`lock`,[[`rect`,{width:`18`,height:`11`,x:`3`,y:`11`,rx:`2`,ry:`2`,key:`1w4ew1`}],[`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`,key:`fwvmzm`}]]),re=h(`monitor`,[[`rect`,{width:`20`,height:`14`,x:`2`,y:`3`,rx:`2`,key:`48i651`}],[`line`,{x1:`8`,x2:`16`,y1:`21`,y2:`21`,key:`1svkeh`}],[`line`,{x1:`12`,x2:`12`,y1:`17`,y2:`21`,key:`vw1qmm`}]]),z=r(),B=t(n(),1),V=e(),H=[{name:`Air-gap Support`,key:`airgap`},{name:`NVIDIA Bluefield DPU`,key:`bluefield`},{name:`NVIDIA MIG Support`,key:`mig`}];function U(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600);if(t>0)return`${t}d ${n}h`;let r=Math.floor(e%3600/60);return n>0?`${n}h ${r}m`:`${r}m`}function W(){let e=(0,z.c)(110),{isAdmin:t,version:n,license:r}=u(),[a,o]=(0,B.useState)(!1),l;e[0]===t?l=e[1]:(l={...ee,enabled:t},e[0]=t,e[1]=l);let{data:d}=i(l),p;e[2]===t?p=e[3]:(p={...A,enabled:t},e[2]=t,e[3]=p);let{data:m}=i(p);if(!t||!n)return null;let h,g,_,v,y,b,x,S,C,w,T,E,D,O,k,M,N,I,L;if(e[4]!==r||e[5]!==d?.cluster_mode||e[6]!==d?.nodes||e[7]!==n.arch||e[8]!==n.commit||e[9]!==n.os||e[10]!==n.version||e[11]!==m?.vms){v=d?.nodes??[];let t;e[31]===m?.vms?t=e[32]:(t=m?.vms??[],e[31]=m?.vms,e[32]=t);let i=t,a=d?.cluster_mode??`single-node`;_=r===`open-source`;let o=v[0]?.region??null,s=v.reduce(se,0),c=v.reduce(oe,0),l=v.reduce(ae,0),u=v.reduce(ie,0);T=`mb-6 space-y-4`,S=`grid grid-cols-1 gap-4 md:grid-cols-3`;let p;e[33]===Symbol.for(`react.memo_cache_sentinel`)?(p=(0,V.jsx)(j,{className:`pb-2`,children:(0,V.jsxs)(ne,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(re,{className:`size-3.5`}),`Spinifex Info`]})}),e[33]=p):p=e[33];let A;e[34]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Version`}),e[34]=A):A=e[34];let ee;e[35]===n.version?ee=e[36]:(ee=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[A,(0,V.jsx)(`span`,{className:`font-mono`,children:n.version})]}),e[35]=n.version,e[36]=ee);let R;e[37]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Commit`}),e[37]=R):R=e[37];let z;e[38]===n.commit?z=e[39]:(z=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[R,(0,V.jsx)(`span`,{className:`font-mono`,children:n.commit})]}),e[38]=n.commit,e[39]=z);let B;e[40]===Symbol.for(`react.memo_cache_sentinel`)?(B=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Platform`}),e[40]=B):B=e[40];let H;e[41]!==n.arch||e[42]!==n.os?(H=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[B,(0,V.jsxs)(`span`,{children:[n.os,`/`,n.arch]})]}),e[41]=n.arch,e[42]=n.os,e[43]=H):H=e[43],C=(0,V.jsxs)(F,{children:[p,(0,V.jsxs)(P,{className:`space-y-1 text-xs`,children:[ee,z,H,o&&(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Region`}),(0,V.jsx)(`span`,{children:o})]})]})]});let U;e[44]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,V.jsx)(j,{className:`pb-2`,children:(0,V.jsx)(ne,{className:`text-sm`,children:`License & Support`})}),e[44]=U):U=e[44];let W;e[45]===Symbol.for(`react.memo_cache_sentinel`)?(W=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`License`}),e[45]=W):W=e[45];let ce=_?`secondary`:`default`,G=_?`Open Source (AGPLv3)`:`Commercial`,K;e[46]!==ce||e[47]!==G?(K=(0,V.jsxs)(`div`,{className:`flex items-center justify-between`,children:[W,(0,V.jsx)(te,{variant:ce,children:G})]}),e[46]=ce,e[47]=G,e[48]=K):K=e[48];let q;e[49]===Symbol.for(`react.memo_cache_sentinel`)?(q=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Support`}),e[49]=q):q=e[49];let J=_?`Community`:`Standard`,Y;e[50]===J?Y=e[51]:(Y=(0,V.jsxs)(`div`,{className:`flex items-center justify-between`,children:[q,(0,V.jsx)(`span`,{children:J})]}),e[50]=J,e[51]=Y);let X;e[52]===_?X=e[53]:(X=_&&(0,V.jsx)(`a`,{href:`https://mulgadc.com/purchase`,target:`_blank`,rel:`noopener noreferrer`,className:`mt-2 block text-xs font-medium text-primary hover:underline`,children:`Upgrade to Commercial →`}),e[52]=_,e[53]=X),e[54]!==K||e[55]!==Y||e[56]!==X?(w=(0,V.jsxs)(F,{children:[U,(0,V.jsxs)(P,{className:`space-y-2 text-xs`,children:[K,Y,X]})]}),e[54]=K,e[55]=Y,e[56]=X,e[57]=w):w=e[57],g=F,e[58]===Symbol.for(`react.memo_cache_sentinel`)?(x=(0,V.jsx)(j,{className:`pb-2`,children:(0,V.jsxs)(ne,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(f,{className:`size-3.5`}),`Cluster Status`]})}),e[58]=x):x=e[58],h=P,N=`space-y-2 text-xs`,e[59]===a?I=e[60]:(I=(0,V.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,V.jsx)(te,{variant:`outline`,children:a})}),e[59]=a,e[60]=I);let Z;e[61]===Symbol.for(`react.memo_cache_sentinel`)?(Z=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Nodes`}),e[61]=Z):Z=e[61],L=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[Z,(0,V.jsx)(`span`,{children:v.length})]});let Q;e[62]===Symbol.for(`react.memo_cache_sentinel`)?(Q=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Instances`}),e[62]=Q):Q=e[62],e[63]===i.length?y=e[64]:(y=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[Q,(0,V.jsx)(`span`,{children:i.length})]}),e[63]=i.length,e[64]=y);let $;e[65]===Symbol.for(`react.memo_cache_sentinel`)?($=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`vCPU`}),e[65]=$):$=e[65],b=(0,V.jsxs)(`div`,{className:`flex justify-between`,children:[$,(0,V.jsxs)(`span`,{children:[c,` / `,s]})]}),k=`flex justify-between`,e[66]===Symbol.for(`react.memo_cache_sentinel`)?(M=(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:`Memory`}),e[66]=M):M=e[66],E=u.toFixed(1),D=` / `,O=l.toFixed(1),e[4]=r,e[5]=d?.cluster_mode,e[6]=d?.nodes,e[7]=n.arch,e[8]=n.commit,e[9]=n.os,e[10]=n.version,e[11]=m?.vms,e[12]=h,e[13]=g,e[14]=_,e[15]=v,e[16]=y,e[17]=b,e[18]=x,e[19]=S,e[20]=C,e[21]=w,e[22]=T,e[23]=E,e[24]=D,e[25]=O,e[26]=k,e[27]=M,e[28]=N,e[29]=I,e[30]=L}else h=e[12],g=e[13],_=e[14],v=e[15],y=e[16],b=e[17],x=e[18],S=e[19],C=e[20],w=e[21],T=e[22],E=e[23],D=e[24],O=e[25],k=e[26],M=e[27],N=e[28],I=e[29],L=e[30];let U;e[67]!==E||e[68]!==D||e[69]!==O?(U=(0,V.jsxs)(`span`,{children:[E,D,O,` GB`]}),e[67]=E,e[68]=D,e[69]=O,e[70]=U):U=e[70];let W;e[71]!==U||e[72]!==k||e[73]!==M?(W=(0,V.jsxs)(`div`,{className:k,children:[M,U]}),e[71]=U,e[72]=k,e[73]=M,e[74]=W):W=e[74];let G;e[75]!==h||e[76]!==y||e[77]!==b||e[78]!==W||e[79]!==N||e[80]!==I||e[81]!==L?(G=(0,V.jsxs)(h,{className:N,children:[I,L,y,b,W]}),e[75]=h,e[76]=y,e[77]=b,e[78]=W,e[79]=N,e[80]=I,e[81]=L,e[82]=G):G=e[82];let K;e[83]!==g||e[84]!==x||e[85]!==G?(K=(0,V.jsxs)(g,{children:[x,G]}),e[83]=g,e[84]=x,e[85]=G,e[86]=K):K=e[86];let q;e[87]!==S||e[88]!==C||e[89]!==w||e[90]!==K?(q=(0,V.jsxs)(`div`,{className:S,children:[C,w,K]}),e[87]=S,e[88]=C,e[89]=w,e[90]=K,e[91]=q):q=e[91];let J;e[92]!==v||e[93]!==a?(J=v.length>0&&(0,V.jsxs)(F,{children:[(0,V.jsx)(j,{className:`pb-0`,children:(0,V.jsxs)(`button`,{type:`button`,className:`flex items-center gap-2 text-sm font-medium`,onClick:()=>o(!a),children:[a?(0,V.jsx)(s,{className:`size-3.5`}):(0,V.jsx)(c,{className:`size-3.5`}),`Nodes (`,v.length,`)`]})}),a&&(0,V.jsx)(P,{className:`pt-2`,children:(0,V.jsx)(ce,{nodes:v})})]}),e[92]=v,e[93]=a,e[94]=J):J=e[94];let Y;e[95]===Symbol.for(`react.memo_cache_sentinel`)?(Y=(0,V.jsx)(j,{className:`pb-2`,children:(0,V.jsx)(ne,{className:`text-sm`,children:`Commercial Add-ons`})}),e[95]=Y):Y=e[95];let X;e[96]===_?X=e[97]:(X=H.map(e=>(0,V.jsxs)(`div`,{className:`flex items-center justify-between text-xs`,children:[(0,V.jsx)(`span`,{className:_?`text-muted-foreground`:void 0,children:e.name}),_?(0,V.jsx)(R,{className:`size-3 text-muted-foreground`}):(0,V.jsx)(te,{variant:`outline`,className:`text-[0.625rem]`,children:`Active`})]},e.key)),e[96]=_,e[97]=X);let Z;e[98]===X?Z=e[99]:(Z=(0,V.jsx)(`div`,{className:`space-y-2`,children:X}),e[98]=X,e[99]=Z);let Q;e[100]===_?Q=e[101]:(Q=_&&(0,V.jsx)(`a`,{href:`https://mulgadc.com/purchase`,target:`_blank`,rel:`noopener noreferrer`,className:`mt-3 block text-xs font-medium text-primary hover:underline`,children:`Learn more about commercial features →`}),e[100]=_,e[101]=Q);let $;e[102]!==Z||e[103]!==Q?($=(0,V.jsxs)(F,{children:[Y,(0,V.jsxs)(P,{children:[Z,Q]})]}),e[102]=Z,e[103]=Q,e[104]=$):$=e[104];let le;return e[105]!==T||e[106]!==q||e[107]!==J||e[108]!==$?(le=(0,V.jsxs)(`div`,{className:T,children:[q,J,$]}),e[105]=T,e[106]=q,e[107]=J,e[108]=$,e[109]=le):le=e[109],le}function ie(e,t){return e+t.alloc_mem_gb}function ae(e,t){return e+t.total_mem_gb}function oe(e,t){return e+t.alloc_vcpu}function se(e,t){return e+t.total_vcpu}function ce(e){let t=(0,z.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,V.jsx)(`thead`,{children:(0,V.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Name`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Status`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Roles`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`IP`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Region`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`AZ`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Uptime`}),(0,V.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`EC2`}),(0,V.jsx)(`th`,{className:`pb-1 font-medium`,children:`Services`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(G),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,V.jsx)(`div`,{className:`overflow-x-auto`,children:(0,V.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,V.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function G(e){return(0,V.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,V.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.node}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,V.jsx)(te,{variant:e.status===`Ready`?`default`:`secondary`,className:`text-[0.625rem]`,children:e.status})}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,V.jsxs)(`div`,{className:`flex gap-1`,children:[e.nats_role&&(0,V.jsxs)(`span`,{className:`text-muted-foreground`,children:[`nats:`,e.nats_role]}),e.predastore_role&&(0,V.jsxs)(`span`,{className:`text-muted-foreground`,children:[`ps:`,e.predastore_role]})]})}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.host}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.region}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.az}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:U(e.uptime)}),(0,V.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.vm_count}),(0,V.jsx)(`td`,{className:`py-1.5`,children:(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.services.length})})]},e.node)}var K=5;function q(){let e=(0,z.c)(11),t,n;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,V.jsx)(O,{title:`Dashboard`}),n=(0,V.jsx)(W,{}),e[0]=t,e[1]=n):(t=e[0],n=e[1]);let r;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(Q,{})}),e[2]=r):r=e[2];let i;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(i=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(me,{})}),e[3]=i):i=e[3];let a;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(ye,{})}),e[4]=a):a=e[4];let o;e[5]===Symbol.for(`react.memo_cache_sentinel`)?(o=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(xe,{})}),e[5]=o):o=e[5];let s;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(Ce,{})}),e[6]=s):s=e[6];let c;e[7]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(Te,{})}),e[7]=c):c=e[7];let l;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(De,{})}),e[8]=l):l=e[8];let u;e[9]===Symbol.for(`react.memo_cache_sentinel`)?(u=(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(je,{})}),e[9]=u):u=e[9];let d;return e[10]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,V.jsxs)(V.Fragment,{children:[t,n,(0,V.jsxs)(`div`,{className:`grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3`,children:[r,i,a,o,s,c,l,u,(0,V.jsx)(B.Suspense,{fallback:(0,V.jsx)(J,{}),children:(0,V.jsx)(ke,{})})]})]}),e[10]=d):d=e[10],d}function J(){let e=(0,z.c)(2),t;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,V.jsx)(j,{children:(0,V.jsx)(`div`,{className:`h-4 w-32 animate-pulse rounded bg-muted`})}),e[0]=t):t=e[0];let n;return e[1]===Symbol.for(`react.memo_cache_sentinel`)?(n=(0,V.jsxs)(F,{children:[t,(0,V.jsx)(P,{children:(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsx)(`div`,{className:`h-3 w-48 animate-pulse rounded bg-muted`}),(0,V.jsx)(`div`,{className:`h-3 w-36 animate-pulse rounded bg-muted`})]})})]}),e[1]=n):n=e[1],n}function Y(e){let t=(0,z.c)(16),{title:n,icon:r,count:i,to:o,children:s}=e,c;t[0]!==r||t[1]!==n?(c=(0,V.jsxs)(ne,{className:`flex items-center gap-2`,children:[r,n]}),t[0]=r,t[1]=n,t[2]=c):c=t[2];let l;t[3]===i?l=t[4]:(l=(0,V.jsx)(N,{children:(0,V.jsx)(`span`,{className:`rounded-md bg-muted px-2 py-0.5 text-xs font-semibold tabular-nums`,children:i})}),t[3]=i,t[4]=l);let u;t[5]!==c||t[6]!==l?(u=(0,V.jsxs)(j,{children:[c,l]}),t[5]=c,t[6]=l,t[7]=u):u=t[7];let d;t[8]===s?d=t[9]:(d=(0,V.jsx)(P,{children:s}),t[8]=s,t[9]=d);let f;t[10]===o?f=t[11]:(f=(0,V.jsx)(M,{children:(0,V.jsx)(a,{to:o,className:`text-xs font-medium text-primary hover:underline`,children:`View all →`})}),t[10]=o,t[11]=f);let p;return t[12]!==u||t[13]!==d||t[14]!==f?(p=(0,V.jsxs)(F,{children:[u,d,f]}),t[12]=u,t[13]=d,t[14]=f,t[15]=p):p=t[15],p}function X(e){let t=(0,z.c)(2),{total:n}=e;if(n<=K)return null;let r=n-K,i;return t[0]===r?i=t[1]:(i=(0,V.jsxs)(`p`,{className:`mt-1 text-[0.625rem] text-muted-foreground`,children:[`+`,r,` more`]}),t[0]=r,t[1]=i),i}function Z(e){let t=(0,z.c)(2),{children:n}=e,r;return t[0]===n?r=t[1]:(r=(0,V.jsx)(`div`,{className:`flex items-center justify-between py-0.5 text-xs`,children:n}),t[0]=n,t[1]=r),r}function Q(){let e=(0,z.c)(16),{data:t}=i(b),n,r,a,o,s;if(e[0]!==t?.Reservations){let i=t?.Reservations?.flatMap(pe)??[];n=i.filter(fe),a=i.filter(de),o=i.filter(ue),r=i.filter(le),s=[...a,...r].slice(0,K),e[0]=t?.Reservations,e[1]=n,e[2]=r,e[3]=a,e[4]=o,e[5]=s}else n=e[1],r=e[2],a=e[3],o=e[4],s=e[5];let c=s,u;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(u=(0,V.jsx)(l,{className:`size-3.5`}),e[6]=u):u=e[6];let d;e[7]!==n.length||e[8]!==c||e[9]!==r||e[10]!==a||e[11]!==o?(d=n.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No instances`}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsxs)(`div`,{className:`mb-2 flex gap-3 text-[0.625rem] text-muted-foreground`,children:[(0,V.jsxs)(`span`,{children:[a.length,` running`]}),(0,V.jsxs)(`span`,{children:[o.length,` stopped`]})]}),(0,V.jsx)(`div`,{className:`space-y-0.5`,children:c.map($)}),(0,V.jsx)(X,{total:a.length+r.length})]}),e[7]=n.length,e[8]=c,e[9]=r,e[10]=a,e[11]=o,e[12]=d):d=e[12];let f;return e[13]!==n.length||e[14]!==d?(f=(0,V.jsx)(Y,{title:`Instances`,icon:u,count:n.length,to:`/ec2/describe-instances`,children:d}),e[13]=n.length,e[14]=d,e[15]=f):f=e[15],f}function $(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.InstanceId}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.InstanceType})]},e.InstanceId)}function le(e){return e.State?.Name===`pending`}function ue(e){return e.State?.Name===`stopped`}function de(e){return e.State?.Name===`running`}function fe(e){return e.State?.Name!==`terminated`}function pe(e){return e.Instances??[]}function me(){let e=(0,z.c)(3),{data:t}=i(p),n;if(e[0]!==t?.Volumes){let r=t?.Volumes??[],i=r.reduce(ve,0),a=r.filter(_e),o=r.filter(ge),s=r.slice(0,K),c;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,V.jsx)(I,{className:`size-3.5`}),e[2]=c):c=e[2],n=(0,V.jsx)(Y,{title:`Volumes`,icon:c,count:r.length,to:`/ec2/describe-volumes`,children:r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No volumes`}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsxs)(`div`,{className:`mb-2 flex gap-3 text-[0.625rem] text-muted-foreground`,children:[(0,V.jsxs)(`span`,{children:[i,` GiB total`]}),(0,V.jsxs)(`span`,{children:[a.length,` in-use`]}),(0,V.jsxs)(`span`,{children:[o.length,` available`]})]}),(0,V.jsx)(`div`,{className:`space-y-0.5`,children:s.map(he)}),(0,V.jsx)(X,{total:r.length})]})}),e[0]=t?.Volumes,e[1]=n}else n=e[1];return n}function he(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.VolumeId}),(0,V.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,V.jsxs)(`span`,{className:`text-muted-foreground`,children:[e.Size,` GiB`]}),(0,V.jsx)(k,{state:e.State})]})]},e.VolumeId)}function ge(e){return e.State===`available`}function _e(e){return e.State===`in-use`}function ve(e,t){return e+(t.Size??0)}function ye(){let e=(0,z.c)(3),{data:t}=i(m),n;if(e[0]!==t?.Vpcs){let r=t?.Vpcs??[],i=r.slice(0,K),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(g,{className:`size-3.5`}),e[2]=a):a=e[2],n=(0,V.jsx)(Y,{title:`VPCs`,icon:a,count:r.length,to:`/ec2/describe-vpcs`,children:r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No VPCs`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(be),(0,V.jsx)(X,{total:r.length})]})}),e[0]=t?.Vpcs,e[1]=n}else n=e[1];return n}function be(e){let t=o(e.Tags);return(0,V.jsxs)(Z,{children:[(0,V.jsxs)(`span`,{className:`font-mono`,children:[e.VpcId,t&&(0,V.jsxs)(`span`,{className:`ml-1 font-sans text-muted-foreground`,children:[`(`,t,`)`]})]}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.CidrBlock})]},e.VpcId)}function xe(){let e=(0,z.c)(3),{data:t}=i(y),n;if(e[0]!==t?.KeyPairs){let r=t?.KeyPairs??[],i=r.slice(0,K),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(S,{className:`size-3.5`}),e[2]=a):a=e[2],n=(0,V.jsx)(Y,{title:`Key Pairs`,icon:a,count:r.length,to:`/ec2/describe-key-pairs`,children:r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No key pairs`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Se),(0,V.jsx)(X,{total:r.length})]})}),e[0]=t?.KeyPairs,e[1]=n}else n=e[1];return n}function Se(e){return(0,V.jsx)(Z,{children:(0,V.jsx)(`span`,{className:`font-mono`,children:e.KeyName})},e.KeyPairId)}function Ce(){let e=(0,z.c)(3),{data:t}=i(T),n;if(e[0]!==t?.Buckets){let r=t?.Buckets??[],i=r.slice(0,K),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(L,{className:`size-3.5`}),e[2]=a):a=e[2],n=(0,V.jsx)(Y,{title:`S3 Buckets`,icon:a,count:r.length,to:`/s3/ls`,children:r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No buckets`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(we),(0,V.jsx)(X,{total:r.length})]})}),e[0]=t?.Buckets,e[1]=n}else n=e[1];return n}function we(e){return(0,V.jsx)(Z,{children:(0,V.jsx)(`span`,{className:`font-mono`,children:e.Name})},e.Name)}function Te(){let e=(0,z.c)(3),{data:t}=i(C),n;if(e[0]!==t?.SecurityGroups){let r=t?.SecurityGroups??[],i=r.slice(0,K),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(v,{className:`size-3.5`}),e[2]=a):a=e[2],n=(0,V.jsx)(Y,{title:`Security Groups`,icon:a,count:r.length,to:`/ec2/describe-security-groups`,children:r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No security groups`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Ee),(0,V.jsx)(X,{total:r.length})]})}),e[0]=t?.SecurityGroups,e[1]=n}else n=e[1];return n}function Ee(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.GroupName}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.VpcId})]},e.GroupId)}function De(){let e=(0,z.c)(3),{data:t}=i(_),n;if(e[0]!==t?.NatGateways){let r=t?.NatGateways??[],i=r.slice(0,K),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(D,{className:`size-3.5`}),e[2]=a):a=e[2],n=(0,V.jsx)(Y,{title:`NAT Gateways`,icon:a,count:r.length,to:`/ec2/describe-nat-gateways`,children:r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No NAT gateways`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Oe),(0,V.jsx)(X,{total:r.length})]})}),e[0]=t?.NatGateways,e[1]=n}else n=e[1];return n}function Oe(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.NatGatewayId}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.State})]},e.NatGatewayId)}function ke(){let e=(0,z.c)(3),{data:t}=i(E),n;if(e[0]!==t?.InternetGateways){let r=t?.InternetGateways??[],i=r.slice(0,K),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(d,{className:`size-3.5`}),e[2]=a):a=e[2],n=(0,V.jsx)(Y,{title:`Internet Gateways`,icon:a,count:r.length,to:`/ec2/describe-internet-gateways`,children:r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No internet gateways`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Ae),(0,V.jsx)(X,{total:r.length})]})}),e[0]=t?.InternetGateways,e[1]=n}else n=e[1];return n}function Ae(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.InternetGatewayId}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.Attachments?.[0]?.VpcId?`attached`:`detached`})]},e.InternetGatewayId)}function je(){let e=(0,z.c)(3),{data:t}=i(w),n;if(e[0]!==t?.Addresses){let r=t?.Addresses??[],i=r.slice(0,K),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,V.jsx)(x,{className:`size-3.5`}),e[2]=a):a=e[2],n=(0,V.jsx)(Y,{title:`Elastic IPs`,icon:a,count:r.length,to:`/ec2/describe-addresses`,children:r.length===0?(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No Elastic IPs`}):(0,V.jsxs)(`div`,{className:`space-y-0.5`,children:[i.map(Me),(0,V.jsx)(X,{total:r.length})]})}),e[0]=t?.Addresses,e[1]=n}else n=e[1];return n}function Me(e){return(0,V.jsxs)(Z,{children:[(0,V.jsx)(`span`,{className:`font-mono`,children:e.PublicIp}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.AssociationId?`associated`:`available`})]},e.AllocationId)}export{q as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_bucket.js b/spinifex/services/spinifexui/frontend/dist/assets/_bucket.js index 31e1e407c..74c62c4a3 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_bucket.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_bucket.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{n as r,r as i,t as a}from"./upload-button.js";import{r as o}from"./s3.js";import{D as s,Fo as c,Ho as l,oo as u,z as d}from"./index.js";import{t as f}from"./page-heading.js";var p=c(),m=l();function h(){let c=(0,p.c)(31),{bucket:l}=s.useParams(),h;c[0]===l?h=c[1]:(h=d(l),c[0]=l,c[1]=h);let{data:g}=t(h),_=o(),v;c[2]===g.Contents?v=c[3]:(v=g.Contents??[],c[2]=g.Contents,c[3]=v);let y=v,b;c[4]===g.CommonPrefixes?b=c[5]:(b=g.CommonPrefixes??[],c[4]=g.CommonPrefixes,c[5]=b);let x=b,S;c[6]===Symbol.for(`react.memo_cache_sentinel`)?(S=(0,m.jsx)(n,{to:`/s3/ls`,children:`Back to buckets`}),c[6]=S):S=c[6];let C;c[7]!==l||c[8]!==_.isPending||c[9]!==_.mutateAsync?(C=(0,m.jsx)(a,{bucket:l,isPending:_.isPending,onUpload:_.mutateAsync}),c[7]=l,c[8]=_.isPending,c[9]=_.mutateAsync,c[10]=C):C=c[10];let w;c[11]!==l||c[12]!==C?(w=(0,m.jsx)(f,{actions:C,title:l}),c[11]=l,c[12]=C,c[13]=w):w=c[13];let T;c[14]===_.error?T=c[15]:(T=_.error&&(0,m.jsx)(e,{error:_.error,msg:`Failed to upload object`}),c[14]=_.error,c[15]=T);let E;c[16]!==l||c[17]!==x?(E=x.length>0&&(0,m.jsxs)(`div`,{className:`mb-6`,children:[(0,m.jsx)(`h2`,{className:`mb-3 text-lg font-semibold`,children:`Folders`}),(0,m.jsx)(`div`,{className:`space-y-2`,children:x.map(e=>e.Prefix?(0,m.jsx)(i,{bucketName:l,folderName:u(e.Prefix),fullPrefix:e.Prefix},e.Prefix):null)})]}),c[16]=l,c[17]=x,c[18]=E):E=c[18];let D;c[19]!==l||c[20]!==y?(D=y.length>0&&(0,m.jsxs)(`div`,{className:`space-y-2`,children:[(0,m.jsx)(`h2`,{className:`mb-3 text-lg font-semibold`,children:`Objects`}),y.map(e=>e.Key?(0,m.jsx)(r,{bucketName:l,displayName:e.Key,fullKey:e.Key,object:e},e.Key):null)]}),c[19]=l,c[20]=y,c[21]=D):D=c[21];let O;c[22]!==x.length||c[23]!==y.length?(O=y.length===0&&x.length===0&&(0,m.jsx)(`p`,{className:`text-muted-foreground`,children:`No objects found in this bucket.`}),c[22]=x.length,c[23]=y.length,c[24]=O):O=c[24];let k;return c[25]!==w||c[26]!==T||c[27]!==E||c[28]!==D||c[29]!==O?(k=(0,m.jsxs)(m.Fragment,{children:[S,w,T,E,D,O]}),c[25]=w,c[26]=T,c[27]=E,c[28]=D,c[29]=O,c[30]=k):k=c[30],k}export{h as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{j as i}from"./SignatureV4.js";import{t as a}from"./back-link.js";import{n as o,r as s,t as c}from"./upload-button.js";import{r as l}from"./s3.js";import{N as u,ot as d}from"./index.js";import{t as f}from"./page-heading.js";var p=t(),m=e();function h(){let e=(0,p.c)(31),{bucket:t}=u.useParams(),h;e[0]===t?h=e[1]:(h=d(t),e[0]=t,e[1]=h);let{data:g}=r(h),_=l(),v;e[2]===g.Contents?v=e[3]:(v=g.Contents??[],e[2]=g.Contents,e[3]=v);let y=v,b;e[4]===g.CommonPrefixes?b=e[5]:(b=g.CommonPrefixes??[],e[4]=g.CommonPrefixes,e[5]=b);let x=b,S;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(S=(0,m.jsx)(a,{to:`/s3/ls`,children:`Back to buckets`}),e[6]=S):S=e[6];let C;e[7]!==t||e[8]!==_.isPending||e[9]!==_.mutateAsync?(C=(0,m.jsx)(c,{bucket:t,isPending:_.isPending,onUpload:_.mutateAsync}),e[7]=t,e[8]=_.isPending,e[9]=_.mutateAsync,e[10]=C):C=e[10];let w;e[11]!==t||e[12]!==C?(w=(0,m.jsx)(f,{actions:C,title:t}),e[11]=t,e[12]=C,e[13]=w):w=e[13];let T;e[14]===_.error?T=e[15]:(T=_.error&&(0,m.jsx)(n,{error:_.error,msg:`Failed to upload object`}),e[14]=_.error,e[15]=T);let E;e[16]!==t||e[17]!==x?(E=x.length>0&&(0,m.jsxs)(`div`,{className:`mb-6`,children:[(0,m.jsx)(`h2`,{className:`mb-3 text-lg font-semibold`,children:`Folders`}),(0,m.jsx)(`div`,{className:`space-y-2`,children:x.map(e=>e.Prefix?(0,m.jsx)(s,{bucketName:t,folderName:i(e.Prefix),fullPrefix:e.Prefix},e.Prefix):null)})]}),e[16]=t,e[17]=x,e[18]=E):E=e[18];let D;e[19]!==t||e[20]!==y?(D=y.length>0&&(0,m.jsxs)(`div`,{className:`space-y-2`,children:[(0,m.jsx)(`h2`,{className:`mb-3 text-lg font-semibold`,children:`Objects`}),y.map(e=>e.Key?(0,m.jsx)(o,{bucketName:t,displayName:e.Key,fullKey:e.Key,object:e},e.Key):null)]}),e[19]=t,e[20]=y,e[21]=D):D=e[21];let O;e[22]!==x.length||e[23]!==y.length?(O=y.length===0&&x.length===0&&(0,m.jsx)(`p`,{className:`text-muted-foreground`,children:`No objects found in this bucket.`}),e[22]=x.length,e[23]=y.length,e[24]=O):O=e[24];let k;return e[25]!==w||e[26]!==T||e[27]!==E||e[28]!==D||e[29]!==O?(k=(0,m.jsxs)(m.Fragment,{children:[S,w,T,E,D,O]}),e[25]=w,e[26]=T,e[27]=E,e[28]=D,e[29]=O,e[30]=k):k=e[30],k}export{h as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_clusterName.js b/spinifex/services/spinifexui/frontend/dist/assets/_clusterName.js new file mode 100644 index 000000000..553ce05e7 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/_clusterName.js @@ -0,0 +1 @@ +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{t as a}from"./useQuery.js";import{t as o}from"./useSuspenseQuery.js";import{Or as s}from"./awsClient.js";import{D as c}from"./SignatureV4.js";import{t as l}from"./back-link.js";import{t as u}from"./cli-command-panel.js";import{t as d}from"./trash-2.js";import{t as f}from"./button.js";import{S as p,_ as m,a as h,b as g,d as _,f as v,g as y,h as b,l as x,m as S,n as C,o as w,r as T,s as E,t as D,u as O,v as k,x as A,y as j}from"./eks.js";import{C as M,D as N,E as P,Ir as F,M as I,Nr as L,Nt as R,O as z,Rn as B,T as V,_ as H,j as ee,k as te,rt as ne,w as re}from"./index.js";import{i as U,t as ie}from"./zod.js";import{t as ae}from"./page-heading.js";import{a as W,r as G,t as K}from"./field.js";import{i as oe,l as q,n as se,o as ce,r as le,s as ue,t as J}from"./alert-dialog.js";import{t as de}from"./delete-confirmation-dialog.js";import{n as Y,t as X}from"./detail-row.js";import{t as fe}from"./textarea.js";import{i as pe,n as me,r as he,t as ge}from"./tabs.js";import{t as _e}from"./badge.js";var ve=L(`circle-arrow-up`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m16 12-4-4-4 4`,key:`177agl`}],[`path`,{d:`M12 16V8`,key:`1sbj14`}]]),ye=L(`sliders-horizontal`,[[`path`,{d:`M10 5H3`,key:`1qgfaw`}],[`path`,{d:`M12 19H3`,key:`yhmn1j`}],[`path`,{d:`M14 3v4`,key:`1sua03`}],[`path`,{d:`M16 17v4`,key:`1q0r14`}],[`path`,{d:`M21 12h-9`,key:`1o4lsq`}],[`path`,{d:`M21 19h-5`,key:`1rlt1p`}],[`path`,{d:`M21 5h-7`,key:`1oszz2`}],[`path`,{d:`M8 10v4`,key:`tgpxqk`}],[`path`,{d:`M8 12H3`,key:`a7s4jb`}]]),Z=r(),Q=t(n(),1),$=e();function be(e){return e?.split(`/`).pop()??e??``}function xe(e){let t=(0,Z.c)(55),{clusterName:n,principalArn:r,onDelete:i}=e,o;t[0]!==n||t[1]!==r?(o=re(n,r),t[0]=n,t[1]=r,t[2]=o):o=t[2];let{data:s}=a(o),c;t[3]!==n||t[4]!==r?(c=z(n,r),t[3]=n,t[4]=r,t[5]=c):c=t[5];let{data:l}=a(c),u=O(),p=j(),[m,h]=(0,Q.useState)(D[0].arn),g=s?.accessEntry,_;t[6]===l?.associatedAccessPolicies?_=t[7]:(_=l?.associatedAccessPolicies??[],t[6]=l?.associatedAccessPolicies,t[7]=_);let v=_,y;t[8]===r?y=t[9]:(y=(0,$.jsx)(`span`,{className:`truncate font-mono text-xs`,children:r}),t[8]=r,t[9]=y);let b;t[10]!==i||t[11]!==r?(b=()=>i(r),t[10]=i,t[11]=r,t[12]=b):b=t[12];let x;t[13]===Symbol.for(`react.memo_cache_sentinel`)?(x=(0,$.jsx)(d,{className:`size-4`}),t[13]=x):x=t[13];let S;t[14]===b?S=t[15]:(S=(0,$.jsx)(f,{onClick:b,size:`icon`,variant:`ghost`,children:x}),t[14]=b,t[15]=S);let C;t[16]!==y||t[17]!==S?(C=(0,$.jsx)(Y.Header,{children:(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[y,S]})}),t[16]=y,t[17]=S,t[18]=C):C=t[18];let w=g?.type,T;t[19]===w?T=t[20]:(T=(0,$.jsx)(X,{label:`Type`,value:w}),t[19]=w,t[20]=T);let E;t[21]===g?E=t[22]:(E=g?.kubernetesGroups?.length?g.kubernetesGroups.join(`, `):`—`,t[21]=g,t[22]=E);let k;t[23]===E?k=t[24]:(k=(0,$.jsx)(X,{label:`Kubernetes groups`,value:E}),t[23]=E,t[24]=k);let A;t[25]!==v||t[26]!==n||t[27]!==p||t[28]!==r?(A=v.length>0?(0,$.jsx)(`div`,{className:`space-y-1`,children:v.map(e=>(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,$.jsx)(`span`,{children:be(e.policyArn)}),(0,$.jsx)(f,{onClick:()=>p.mutate({clusterName:n,principalArn:r,policyArn:e.policyArn??``}),size:`sm`,variant:`ghost`,children:`Remove`})]},e.policyArn))}):`—`,t[25]=v,t[26]=n,t[27]=p,t[28]=r,t[29]=A):A=t[29];let M;t[30]===A?M=t[31]:(M=(0,$.jsx)(X,{label:`Access policies`,value:A}),t[30]=A,t[31]=M);let N;t[32]===Symbol.for(`react.memo_cache_sentinel`)?(N=e=>h(e.target.value),t[32]=N):N=t[32];let P;t[33]===Symbol.for(`react.memo_cache_sentinel`)?(P=D.map(Se),t[33]=P):P=t[33];let F;t[34]===m?F=t[35]:(F=(0,$.jsx)(`select`,{"aria-label":`Access policy`,className:`h-7 flex-1 rounded-md border border-input bg-input/20 px-2 text-sm`,onChange:N,value:m,children:P}),t[34]=m,t[35]=F);let I;t[36]!==u||t[37]!==n||t[38]!==m||t[39]!==r?(I=()=>u.mutate({clusterName:n,principalArn:r,policyArn:m,accessScopeType:`cluster`}),t[36]=u,t[37]=n,t[38]=m,t[39]=r,t[40]=I):I=t[40];let L;t[41]!==u.isPending||t[42]!==I?(L=(0,$.jsx)(f,{disabled:u.isPending,onClick:I,size:`sm`,children:`Associate`}),t[41]=u.isPending,t[42]=I,t[43]=L):L=t[43];let R;t[44]!==F||t[45]!==L?(R=(0,$.jsxs)(`div`,{className:`flex items-center gap-2 pt-2`,children:[F,L]}),t[44]=F,t[45]=L,t[46]=R):R=t[46];let B;t[47]!==T||t[48]!==k||t[49]!==M||t[50]!==R?(B=(0,$.jsxs)(Y.Content,{children:[T,k,M,R]}),t[47]=T,t[48]=k,t[49]=M,t[50]=R,t[51]=B):B=t[51];let V;return t[52]!==B||t[53]!==C?(V=(0,$.jsxs)(Y,{children:[C,B]}),t[52]=B,t[53]=C,t[54]=V):V=t[54],V}function Se(e){return(0,$.jsx)(`option`,{value:e.arn,children:e.name},e.arn)}function Ce(e){let t=(0,Z.c)(41),{clusterName:n,open:r,onOpenChange:i}=e,a=_(),o;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(o={resolver:ie(w),defaultValues:{principalArn:``,kubernetesGroups:``}},t[0]=o):o=t[0];let{formState:s,handleSubmit:c,register:l,reset:u}=U(o),{errors:d}=s,f;t[1]!==n||t[2]!==a||t[3]!==i||t[4]!==u?(f=async e=>{await a.mutateAsync({clusterName:n,principalArn:e.principalArn,kubernetesGroups:e.kubernetesGroups.split(`,`).map(we).filter(Boolean)}),u(),i(!1)},t[1]=n,t[2]=a,t[3]=i,t[4]=u,t[5]=f):f=t[5];let p=f,m;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,$.jsx)(ue,{children:(0,$.jsx)(q,{children:`Add access entry`})}),t[6]=m):m=t[6];let h;t[7]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ae-principal`,children:`IAM principal ARN`})}),t[7]=h):h=t[7];let g=!!d.principalArn,v;t[8]===l?v=t[9]:(v=l(`principalArn`),t[8]=l,t[9]=v);let y;t[10]!==g||t[11]!==v?(y=(0,$.jsx)(B,{"aria-invalid":g,id:`ae-principal`,placeholder:`arn:aws:iam::000000000000:role/my-role`,...v}),t[10]=g,t[11]=v,t[12]=y):y=t[12];let b;t[13]===d.principalArn?b=t[14]:(b=(0,$.jsx)(G,{errors:[d.principalArn]}),t[13]=d.principalArn,t[14]=b);let x;t[15]!==y||t[16]!==b?(x=(0,$.jsxs)(K,{children:[h,y,b]}),t[15]=y,t[16]=b,t[17]=x):x=t[17];let S;t[18]===Symbol.for(`react.memo_cache_sentinel`)?(S=(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ae-groups`,children:`Kubernetes groups`})}),t[18]=S):S=t[18];let C;t[19]===l?C=t[20]:(C=l(`kubernetesGroups`),t[19]=l,t[20]=C);let T;t[21]===C?T=t[22]:(T=(0,$.jsxs)(K,{children:[S,(0,$.jsx)(B,{id:`ae-groups`,placeholder:`system:masters`,...C})]}),t[21]=C,t[22]=T);let E;t[23]!==x||t[24]!==T?(E=(0,$.jsxs)(`form`,{className:`space-y-4`,id:`add-access-entry-form`,children:[x,T]}),t[23]=x,t[24]=T,t[25]=E):E=t[25];let D;t[26]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,$.jsx)(le,{children:`Cancel`}),t[26]=D):D=t[26];let O;t[27]!==c||t[28]!==p?(O=e=>{e.preventDefault(),c(p)()},t[27]=c,t[28]=p,t[29]=O):O=t[29];let k=a.isPending?`Creating…`:`Create`,A;t[30]!==a.isPending||t[31]!==O||t[32]!==k?(A=(0,$.jsxs)(ce,{children:[D,(0,$.jsx)(se,{disabled:a.isPending,onClick:O,children:k})]}),t[30]=a.isPending,t[31]=O,t[32]=k,t[33]=A):A=t[33];let j;t[34]!==E||t[35]!==A?(j=(0,$.jsxs)(oe,{children:[m,E,A]}),t[34]=E,t[35]=A,t[36]=j):j=t[36];let M;return t[37]!==i||t[38]!==r||t[39]!==j?(M=(0,$.jsx)(J,{onOpenChange:i,open:r,children:j}),t[37]=i,t[38]=r,t[39]=j,t[40]=M):M=t[40],M}function we(e){return e.trim()}function Te(e){let t=(0,Z.c)(25),{clusterName:n}=e,r;t[0]===n?r=t[1]:(r=M(n),t[0]=n,t[1]=r);let{data:i}=o(r),a=b(),[s,c]=(0,Q.useState)(!1),[l,u]=(0,Q.useState)(),d;t[2]===i.accessEntries?d=t[3]:(d=i.accessEntries??[],t[2]=i.accessEntries,t[3]=d);let p=d,m;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,$.jsx)(`div`,{className:`mb-4 flex justify-end`,children:(0,$.jsx)(f,{onClick:()=>c(!0),size:`sm`,children:`Add access entry`})}),t[4]=m):m=t[4];let h;t[5]!==n||t[6]!==p?(h=p.length>0?(0,$.jsx)(`div`,{className:`space-y-4`,children:p.map(e=>(0,$.jsx)(xe,{clusterName:n,onDelete:u,principalArn:e},e))}):(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`No access entries.`}),t[5]=n,t[6]=p,t[7]=h):h=t[7];let g;t[8]!==n||t[9]!==s?(g=(0,$.jsx)(Ce,{clusterName:n,onOpenChange:c,open:s}),t[8]=n,t[9]=s,t[10]=g):g=t[10];let _=`Delete access entry for "${l??``}"?`,v;t[11]!==n||t[12]!==a||t[13]!==l?(v=()=>{l&&a.mutate({clusterName:n,principalArn:l},{onSuccess:()=>u(void 0)})},t[11]=n,t[12]=a,t[13]=l,t[14]=v):v=t[14];let y;t[15]===Symbol.for(`react.memo_cache_sentinel`)?(y=e=>!e&&u(void 0),t[15]=y):y=t[15];let x=l!==void 0,S;t[16]!==a.isPending||t[17]!==_||t[18]!==v||t[19]!==x?(S=(0,$.jsx)(de,{description:_,isPending:a.isPending,onConfirm:v,onOpenChange:y,open:x,title:`Delete access entry`}),t[16]=a.isPending,t[17]=_,t[18]=v,t[19]=x,t[20]=S):S=t[20];let C;return t[21]!==S||t[22]!==h||t[23]!==g?(C=(0,$.jsxs)($.Fragment,{children:[m,h,g,S]}),t[21]=S,t[22]=h,t[23]=g,t[24]=C):C=t[24],C}var Ee=new Set([`CREATING`,`UPDATING`,`DELETING`]),De=5e3,Oe=`h-7 w-full rounded-md border border-input bg-input/20 px-2 text-sm`;function ke(e){if(e.trim()===``)return!0;try{return JSON.parse(e),!0}catch{return!1}}function Ae({clusterName:e,catalog:t,installed:n,open:r,onOpenChange:i}){let a=v(),o=t.filter(e=>!n.includes(e.addonName??``)),{formState:{errors:s},handleSubmit:c,register:l,reset:u,setValue:d,watch:f}=U({resolver:ie(E),defaultValues:{addonName:``,addonVersion:``,serviceAccountRoleArn:``,configurationValues:``}}),p=f(`addonName`),m=f(`addonVersion`),h=t.find(e=>e.addonName===p)?.addonVersions??[],g=h.find(e=>e.addonVersion===m)?.requiresIamPermissions??!1,_=e=>{let n=t.find(t=>t.addonName===e)?.addonVersions?.[0]?.addonVersion??``;d(`addonName`,e,{shouldValidate:!0}),d(`addonVersion`,n,{shouldValidate:!0})},y=async t=>{await a.mutateAsync({clusterName:e,addonName:t.addonName,addonVersion:t.addonVersion||void 0,serviceAccountRoleArn:t.serviceAccountRoleArn.trim()||void 0,configurationValues:t.configurationValues.trim()||void 0}),u(),i(!1)};return(0,$.jsx)(J,{onOpenChange:i,open:r,children:(0,$.jsxs)(oe,{className:`max-h-[85vh] overflow-y-auto`,children:[(0,$.jsx)(ue,{children:(0,$.jsx)(q,{children:`Add add-on`})}),(0,$.jsxs)(`form`,{className:`space-y-4`,id:`add-addon-form`,children:[(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`addon-name`,children:`Add-on`})}),(0,$.jsxs)(`select`,{"aria-invalid":!!s.addonName,className:Oe,id:`addon-name`,onChange:e=>_(e.target.value),value:p,children:[(0,$.jsx)(`option`,{value:``,children:`Select add-on`}),o.map(e=>(0,$.jsx)(`option`,{value:e.addonName??``,children:e.addonName},e.addonName))]}),(0,$.jsx)(G,{errors:[s.addonName]})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`addon-version`,children:`Version`})}),(0,$.jsx)(`select`,{"aria-invalid":!!s.addonVersion,className:Oe,disabled:h.length===0,id:`addon-version`,onChange:e=>d(`addonVersion`,e.target.value,{shouldValidate:!0}),value:m,children:h.map((e,t)=>(0,$.jsxs)(`option`,{value:e.addonVersion??``,children:[e.addonVersion,t===0?` (latest)`:``]},e.addonVersion))}),(0,$.jsx)(G,{errors:[s.addonVersion]})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`addon-role`,children:`Service account IAM role ARN`})}),(0,$.jsx)(B,{id:`addon-role`,placeholder:`arn:aws:iam::…:role/…`,...l(`serviceAccountRoleArn`)}),g&&(0,$.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`This add-on needs IAM permissions — provide an IRSA role ARN.`})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`addon-config`,children:`Configuration values (JSON)`})}),(0,$.jsx)(fe,{"aria-invalid":!!s.configurationValues,id:`addon-config`,placeholder:`{"replicaCount": 2}`,rows:4,...l(`configurationValues`)}),(0,$.jsx)(G,{errors:[s.configurationValues]})]})]}),(0,$.jsxs)(ce,{children:[(0,$.jsx)(le,{children:`Cancel`}),(0,$.jsx)(se,{disabled:a.isPending,onClick:e=>{e.preventDefault(),c(y)()},children:a.isPending?`Adding…`:`Add`})]})]})})}function je(e){let t=(0,Z.c)(54),{clusterName:n,addonName:r,current:i,versions:a,open:o,onOpenChange:s}=e,c=A(),[l,u]=(0,Q.useState)(i.addonVersion??``),[d,f]=(0,Q.useState)(i.serviceAccountRoleArn??``),[p,m]=(0,Q.useState)(i.configurationValues??``),[h,g]=(0,Q.useState)(),_,v;t[0]!==i.addonVersion||t[1]!==i.configurationValues||t[2]!==i.serviceAccountRoleArn||t[3]!==o?(_=()=>{o&&(u(i.addonVersion??``),f(i.serviceAccountRoleArn??``),m(i.configurationValues??``),g(void 0))},v=[o,i.addonVersion,i.serviceAccountRoleArn,i.configurationValues],t[0]=i.addonVersion,t[1]=i.configurationValues,t[2]=i.serviceAccountRoleArn,t[3]=o,t[4]=_,t[5]=v):(_=t[4],v=t[5]),(0,Q.useEffect)(_,v);let y;t[6]!==r||t[7]!==n||t[8]!==p||t[9]!==s||t[10]!==d||t[11]!==c||t[12]!==l?(y=()=>{if(!ke(p)){g(`Configuration must be valid JSON`);return}c.mutate({clusterName:n,addonName:r,addonVersion:l||void 0,serviceAccountRoleArn:d.trim()||void 0,configurationValues:p.trim()||void 0},{onSuccess:()=>s(!1)})},t[6]=r,t[7]=n,t[8]=p,t[9]=s,t[10]=d,t[11]=c,t[12]=l,t[13]=y):y=t[13];let b=y,x;t[14]===r?x=t[15]:(x=(0,$.jsx)(ue,{children:(0,$.jsxs)(q,{children:[`Update `,r]})}),t[14]=r,t[15]=x);let S;t[16]===Symbol.for(`react.memo_cache_sentinel`)?(S=(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`update-addon-version`,children:`Version`})}),t[16]=S):S=t[16];let C=a.length===0,w;t[17]===Symbol.for(`react.memo_cache_sentinel`)?(w=e=>u(e.target.value),t[17]=w):w=t[17];let T;t[18]===a?T=t[19]:(T=a.map(Me),t[18]=a,t[19]=T);let E;t[20]!==C||t[21]!==T||t[22]!==l?(E=(0,$.jsxs)(K,{children:[S,(0,$.jsx)(`select`,{className:Oe,disabled:C,id:`update-addon-version`,onChange:w,value:l,children:T})]}),t[20]=C,t[21]=T,t[22]=l,t[23]=E):E=t[23];let D;t[24]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`update-addon-role`,children:`Service account IAM role ARN`})}),t[24]=D):D=t[24];let O;t[25]===Symbol.for(`react.memo_cache_sentinel`)?(O=e=>f(e.target.value),t[25]=O):O=t[25];let k;t[26]===d?k=t[27]:(k=(0,$.jsxs)(K,{children:[D,(0,$.jsx)(B,{id:`update-addon-role`,onChange:O,placeholder:`arn:aws:iam::…:role/…`,value:d})]}),t[26]=d,t[27]=k);let j;t[28]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`update-addon-config`,children:`Configuration values (JSON)`})}),t[28]=j):j=t[28];let M;t[29]===Symbol.for(`react.memo_cache_sentinel`)?(M=e=>m(e.target.value),t[29]=M):M=t[29];let N;t[30]===p?N=t[31]:(N=(0,$.jsxs)(K,{children:[j,(0,$.jsx)(fe,{id:`update-addon-config`,onChange:M,rows:4,value:p})]}),t[30]=p,t[31]=N);let P;t[32]!==k||t[33]!==N||t[34]!==E?(P=(0,$.jsxs)(`div`,{className:`space-y-4`,children:[E,k,N]}),t[32]=k,t[33]=N,t[34]=E,t[35]=P):P=t[35];let F;t[36]===h?F=t[37]:(F=h&&(0,$.jsx)(`p`,{className:`mt-2 text-xs text-destructive`,children:h}),t[36]=h,t[37]=F);let I;t[38]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,$.jsx)(le,{children:`Cancel`}),t[38]=I):I=t[38];let L;t[39]===b?L=t[40]:(L=e=>{e.preventDefault(),b()},t[39]=b,t[40]=L);let R=c.isPending?`Updating…`:`Update`,z;t[41]!==L||t[42]!==R||t[43]!==c.isPending?(z=(0,$.jsxs)(ce,{children:[I,(0,$.jsx)(se,{disabled:c.isPending,onClick:L,children:R})]}),t[41]=L,t[42]=R,t[43]=c.isPending,t[44]=z):z=t[44];let V;t[45]!==P||t[46]!==F||t[47]!==z||t[48]!==x?(V=(0,$.jsxs)(oe,{className:`max-h-[85vh] overflow-y-auto`,children:[x,P,F,z]}),t[45]=P,t[46]=F,t[47]=z,t[48]=x,t[49]=V):V=t[49];let H;return t[50]!==s||t[51]!==o||t[52]!==V?(H=(0,$.jsx)(J,{onOpenChange:s,open:o,children:V}),t[50]=s,t[51]=o,t[52]=V,t[53]=H):H=t[53],H}function Me(e){return(0,$.jsx)(`option`,{value:e,children:e},e)}function Ne(e){let t=(0,Z.c)(53),{clusterName:n,addonName:r,catalog:i,onDelete:o}=e,s;t[0]!==r||t[1]!==n?(s=V(n,r),t[0]=r,t[1]=n,t[2]=s):s=t[2];let c;t[3]===s?c=t[4]:(c={...s,refetchInterval:Fe},t[3]=s,t[4]=c);let{data:l}=a(c),[u,p]=(0,Q.useState)(!1),m=l?.addon,g=m?.health?.issues?.[0]?.message,_;t[5]!==r||t[6]!==i?(_=i.find(e=>e.addonName===r)?.addonVersions?.map(Pe).filter(Boolean)??[],t[5]=r,t[6]=i,t[7]=_):_=t[7];let v=_,y;t[8]===r?y=t[9]:(y=(0,$.jsx)(`span`,{children:r}),t[8]=r,t[9]=y);let b=m?.status,x;t[10]===b?x=t[11]:(x=h(b),t[10]=b,t[11]=x);let S=m?.status??`UNKNOWN`,C;t[12]!==x||t[13]!==S?(C=(0,$.jsx)(_e,{variant:x,children:S}),t[12]=x,t[13]=S,t[14]=C):C=t[14];let w;t[15]===Symbol.for(`react.memo_cache_sentinel`)?(w=()=>p(!0),t[15]=w):w=t[15];let T;t[16]===Symbol.for(`react.memo_cache_sentinel`)?(T=(0,$.jsx)(f,{"aria-label":`Update add-on`,onClick:w,size:`icon`,variant:`ghost`,children:(0,$.jsx)(ve,{className:`size-4`})}),t[16]=T):T=t[16];let E;t[17]!==r||t[18]!==o?(E=()=>o(r),t[17]=r,t[18]=o,t[19]=E):E=t[19];let D;t[20]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,$.jsx)(d,{className:`size-4`}),t[20]=D):D=t[20];let O;t[21]===E?O=t[22]:(O=(0,$.jsx)(f,{"aria-label":`Remove add-on`,onClick:E,size:`icon`,variant:`ghost`,children:D}),t[21]=E,t[22]=O);let k;t[23]!==O||t[24]!==C?(k=(0,$.jsxs)(`div`,{className:`flex items-center gap-1`,children:[C,T,O]}),t[23]=O,t[24]=C,t[25]=k):k=t[25];let A;t[26]!==k||t[27]!==y?(A=(0,$.jsx)(Y.Header,{children:(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[y,k]})}),t[26]=k,t[27]=y,t[28]=A):A=t[28];let j=m?.addonVersion,M;t[29]===j?M=t[30]:(M=(0,$.jsx)(X,{label:`Version`,value:j}),t[29]=j,t[30]=M);let N=m?.serviceAccountRoleArn,P;t[31]===N?P=t[32]:(P=(0,$.jsx)(X,{label:`IRSA role`,value:N}),t[31]=N,t[32]=P);let F;t[33]===g?F=t[34]:(F=g&&(0,$.jsx)(X,{label:`Issue`,value:(0,$.jsx)(`span`,{className:`text-destructive`,children:g})}),t[33]=g,t[34]=F);let I;t[35]!==M||t[36]!==P||t[37]!==F?(I=(0,$.jsxs)(Y.Content,{children:[M,P,F]}),t[35]=M,t[36]=P,t[37]=F,t[38]=I):I=t[38];let L=m?.addonVersion,R=m?.serviceAccountRoleArn,z=m?.configurationValues,B;t[39]!==L||t[40]!==R||t[41]!==z?(B={addonVersion:L,serviceAccountRoleArn:R,configurationValues:z},t[39]=L,t[40]=R,t[41]=z,t[42]=B):B=t[42];let H;t[43]!==r||t[44]!==n||t[45]!==u||t[46]!==B||t[47]!==v?(H=(0,$.jsx)(je,{addonName:r,clusterName:n,current:B,onOpenChange:p,open:u,versions:v}),t[43]=r,t[44]=n,t[45]=u,t[46]=B,t[47]=v,t[48]=H):H=t[48];let ee;return t[49]!==A||t[50]!==I||t[51]!==H?(ee=(0,$.jsxs)(Y,{children:[A,I,H]}),t[49]=A,t[50]=I,t[51]=H,t[52]=ee):ee=t[52],ee}function Pe(e){return e.addonVersion??``}function Fe(e){return Ee.has(e.state.data?.addon?.status??``)?De:!1}function Ie(e){let t=(0,Z.c)(31),{clusterName:n}=e,r;t[0]===n?r=t[1]:(r=N(n),t[0]=n,t[1]=r);let{data:i}=o(r),{data:s}=a(P),c=y(),[l,u]=(0,Q.useState)(!1),[d,p]=(0,Q.useState)(),m;t[2]===i.addons?m=t[3]:(m=i.addons??[],t[2]=i.addons,t[3]=m);let h=m,g;t[4]===s?.addons?g=t[5]:(g=s?.addons??[],t[4]=s?.addons,t[5]=g);let _=g,v;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(v=(0,$.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Add-on manifests are staged; activation completes when the cluster reports the workload ready.`}),t[6]=v):v=t[6];let b;t[7]===Symbol.for(`react.memo_cache_sentinel`)?(b=(0,$.jsxs)(`div`,{className:`mb-4 flex items-center justify-between gap-4`,children:[v,(0,$.jsx)(f,{onClick:()=>u(!0),size:`sm`,children:`Add add-on`})]}),t[7]=b):b=t[7];let x;t[8]!==h||t[9]!==_||t[10]!==n?(x=h.length>0?(0,$.jsx)(`div`,{className:`space-y-4`,children:h.map(e=>(0,$.jsx)(Ne,{addonName:e,catalog:_,clusterName:n,onDelete:p},e))}):(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`No add-ons installed.`}),t[8]=h,t[9]=_,t[10]=n,t[11]=x):x=t[11];let S;t[12]!==h||t[13]!==_||t[14]!==n||t[15]!==l?(S=(0,$.jsx)(Ae,{catalog:_,clusterName:n,installed:h,onOpenChange:u,open:l}),t[12]=h,t[13]=_,t[14]=n,t[15]=l,t[16]=S):S=t[16];let C=`Remove add-on "${d??``}" from the cluster?`,w;t[17]!==n||t[18]!==c||t[19]!==d?(w=()=>{d&&c.mutate({clusterName:n,addonName:d},{onSuccess:()=>p(void 0)})},t[17]=n,t[18]=c,t[19]=d,t[20]=w):w=t[20];let T;t[21]===Symbol.for(`react.memo_cache_sentinel`)?(T=e=>!e&&p(void 0),t[21]=T):T=t[21];let E=d!==void 0,D;t[22]!==c.isPending||t[23]!==E||t[24]!==C||t[25]!==w?(D=(0,$.jsx)(de,{confirmLabel:`Remove`,description:C,isPending:c.isPending,onConfirm:w,onOpenChange:T,open:E,pendingLabel:`Removing…`,title:`Remove add-on`}),t[22]=c.isPending,t[23]=E,t[24]=C,t[25]=w,t[26]=D):D=t[26];let O;return t[27]!==D||t[28]!==x||t[29]!==S?(O=(0,$.jsxs)($.Fragment,{children:[b,x,S,D]}),t[27]=D,t[28]=x,t[29]=S,t[30]=O):O=t[30],O}function Le(e){let t=(0,Z.c)(6),{to:n,id:r}=e,i;t[0]===r?i=t[1]:(i={id:r},t[0]=r,t[1]=i);let a;return t[2]!==r||t[3]!==i||t[4]!==n?(a=(0,$.jsx)(s,{className:`font-mono text-primary underline-offset-2 hover:underline`,params:i,to:n,children:r}),t[2]=r,t[3]=i,t[4]=n,t[5]=a):a=t[5],a}function Re(e){let t=(0,Z.c)(45),{cluster:n}=e,r=n?.resourcesVpcConfig,i;t[0]===r?.subnetIds?i=t[1]:(i=r?.subnetIds??[],t[0]=r?.subnetIds,t[1]=i);let a=i,o;t[2]===r?.securityGroupIds?o=t[3]:(o=r?.securityGroupIds??[],t[2]=r?.securityGroupIds,t[3]=o);let s=o,c;t[4]===r?.publicAccessCidrs?c=t[5]:(c=r?.publicAccessCidrs??[],t[4]=r?.publicAccessCidrs,t[5]=c);let l=c,u;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(u=(0,$.jsx)(Y.Header,{children:`VPC`}),t[6]=u):u=t[6];let d;t[7]===r?d=t[8]:(d=r?.vpcId?(0,$.jsx)(Le,{id:r.vpcId,to:`/ec2/describe-vpcs/$id`}):void 0,t[7]=r,t[8]=d);let f;t[9]===d?f=t[10]:(f=(0,$.jsx)(X,{label:`VPC`,value:d}),t[9]=d,t[10]=f);let p=n?.endpoint,m;t[11]===p?m=t[12]:(m=(0,$.jsx)(X,{label:`API server endpoint`,value:p}),t[11]=p,t[12]=m);let h=r?.endpointPublicAccess?`Enabled`:`Disabled`,g;t[13]===h?g=t[14]:(g=(0,$.jsx)(X,{label:`Public access`,value:h}),t[13]=h,t[14]=g);let _=r?.endpointPrivateAccess?`Enabled`:`Disabled`,v;t[15]===_?v=t[16]:(v=(0,$.jsx)(X,{label:`Private access`,value:_}),t[15]=_,t[16]=v);let y;t[17]!==l||t[18]!==r?.endpointPublicAccess?(y=r?.endpointPublicAccess&&(0,$.jsx)(X,{label:`Public access CIDRs`,value:l.length>0?(0,$.jsx)(`div`,{className:`space-y-1 font-mono text-xs`,children:l.map(Ve)}):`Any (0.0.0.0/0)`}),t[17]=l,t[18]=r?.endpointPublicAccess,t[19]=y):y=t[19];let b;t[20]!==g||t[21]!==v||t[22]!==y||t[23]!==f||t[24]!==m?(b=(0,$.jsxs)(Y,{children:[u,(0,$.jsxs)(Y.Content,{children:[f,m,g,v,y]})]}),t[20]=g,t[21]=v,t[22]=y,t[23]=f,t[24]=m,t[25]=b):b=t[25];let x;t[26]===Symbol.for(`react.memo_cache_sentinel`)?(x=(0,$.jsx)(Y.Header,{children:`Subnets`}),t[26]=x):x=t[26];let S;t[27]===a?S=t[28]:(S=(0,$.jsxs)(Y,{children:[x,(0,$.jsx)(Y.Content,{children:a.length>0?(0,$.jsx)(`div`,{className:`space-y-1`,children:a.map(Be)}):(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`No subnets.`})})]}),t[27]=a,t[28]=S);let C;t[29]===Symbol.for(`react.memo_cache_sentinel`)?(C=(0,$.jsx)(Y.Header,{children:`Security groups`}),t[29]=C):C=t[29];let w;t[30]===r?w=t[31]:(w=r?.clusterSecurityGroupId?(0,$.jsx)(Le,{id:r.clusterSecurityGroupId,to:`/ec2/describe-security-groups/$id`}):void 0,t[30]=r,t[31]=w);let T;t[32]===w?T=t[33]:(T=(0,$.jsx)(X,{label:`Cluster security group`,value:w}),t[32]=w,t[33]=T);let E;t[34]===s?E=t[35]:(E=s.length>0?(0,$.jsx)(`div`,{className:`space-y-1`,children:s.map(ze)}):`—`,t[34]=s,t[35]=E);let D;t[36]===E?D=t[37]:(D=(0,$.jsx)(X,{label:`Additional security groups`,value:E}),t[36]=E,t[37]=D);let O;t[38]!==T||t[39]!==D?(O=(0,$.jsxs)(Y,{children:[C,(0,$.jsxs)(Y.Content,{children:[T,D]})]}),t[38]=T,t[39]=D,t[40]=O):O=t[40];let k;return t[41]!==b||t[42]!==S||t[43]!==O?(k=(0,$.jsxs)($.Fragment,{children:[b,S,O]}),t[41]=b,t[42]=S,t[43]=O,t[44]=k):k=t[44],k}function ze(e){return(0,$.jsx)(Le,{id:e,to:`/ec2/describe-security-groups/$id`},e)}function Be(e){return(0,$.jsx)(Le,{id:e,to:`/ec2/describe-subnets/$id`},e)}function Ve(e){return(0,$.jsx)(`div`,{children:e},e)}function He(e){let t=(0,Z.c)(50),{clusterName:n,nodegroupName:r,current:i,open:a,onOpenChange:o}=e,s=g(),[c,l]=(0,Q.useState)(i.minSize??0),[u,d]=(0,Q.useState)(i.desiredSize??0),[f,p]=(0,Q.useState)(i.maxSize??0),[m,h]=(0,Q.useState)(),_,v;t[0]!==i.desiredSize||t[1]!==i.maxSize||t[2]!==i.minSize||t[3]!==a?(_=()=>{a&&(l(i.minSize??0),d(i.desiredSize??0),p(i.maxSize??0),h(void 0))},v=[a,i.minSize,i.desiredSize,i.maxSize],t[0]=i.desiredSize,t[1]=i.maxSize,t[2]=i.minSize,t[3]=a,t[4]=_,t[5]=v):(_=t[4],v=t[5]),(0,Q.useEffect)(_,v);let y;t[6]!==n||t[7]!==u||t[8]!==f||t[9]!==c||t[10]!==r||t[11]!==o||t[12]!==s?(y=()=>{if(!(c<=u&&u<=f)){h(`Sizes must satisfy min ≤ desired ≤ max`);return}s.mutate({clusterName:n,nodegroupName:r,minSize:c,desiredSize:u,maxSize:f},{onSuccess:()=>o(!1)})},t[6]=n,t[7]=u,t[8]=f,t[9]=c,t[10]=r,t[11]=o,t[12]=s,t[13]=y):y=t[13];let b=y,x;t[14]===r?x=t[15]:(x=(0,$.jsx)(ue,{children:(0,$.jsxs)(q,{children:[`Scale `,r]})}),t[14]=r,t[15]=x);let S;t[16]===Symbol.for(`react.memo_cache_sentinel`)?(S=(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`scale-min`,children:`Min`})}),t[16]=S):S=t[16];let C;t[17]===Symbol.for(`react.memo_cache_sentinel`)?(C=e=>l(Number(e.target.value)),t[17]=C):C=t[17];let w;t[18]===c?w=t[19]:(w=(0,$.jsxs)(K,{children:[S,(0,$.jsx)(B,{id:`scale-min`,onChange:C,type:`number`,value:c})]}),t[18]=c,t[19]=w);let T;t[20]===Symbol.for(`react.memo_cache_sentinel`)?(T=(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`scale-desired`,children:`Desired`})}),t[20]=T):T=t[20];let E;t[21]===Symbol.for(`react.memo_cache_sentinel`)?(E=e=>d(Number(e.target.value)),t[21]=E):E=t[21];let D;t[22]===u?D=t[23]:(D=(0,$.jsxs)(K,{children:[T,(0,$.jsx)(B,{id:`scale-desired`,onChange:E,type:`number`,value:u})]}),t[22]=u,t[23]=D);let O;t[24]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`scale-max`,children:`Max`})}),t[24]=O):O=t[24];let k;t[25]===Symbol.for(`react.memo_cache_sentinel`)?(k=e=>p(Number(e.target.value)),t[25]=k):k=t[25];let A;t[26]===f?A=t[27]:(A=(0,$.jsxs)(K,{children:[O,(0,$.jsx)(B,{id:`scale-max`,onChange:k,type:`number`,value:f})]}),t[26]=f,t[27]=A);let j;t[28]!==D||t[29]!==A||t[30]!==w?(j=(0,$.jsxs)(`div`,{className:`grid grid-cols-3 gap-3`,children:[w,D,A]}),t[28]=D,t[29]=A,t[30]=w,t[31]=j):j=t[31];let M;t[32]===m?M=t[33]:(M=m&&(0,$.jsx)(`p`,{className:`mt-2 text-xs text-destructive`,children:m}),t[32]=m,t[33]=M);let N;t[34]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,$.jsx)(le,{children:`Cancel`}),t[34]=N):N=t[34];let P;t[35]===b?P=t[36]:(P=e=>{e.preventDefault(),b()},t[35]=b,t[36]=P);let F=s.isPending?`Scaling…`:`Scale`,I;t[37]!==s.isPending||t[38]!==P||t[39]!==F?(I=(0,$.jsxs)(ce,{children:[N,(0,$.jsx)(se,{disabled:s.isPending,onClick:P,children:F})]}),t[37]=s.isPending,t[38]=P,t[39]=F,t[40]=I):I=t[40];let L;t[41]!==j||t[42]!==M||t[43]!==I||t[44]!==x?(L=(0,$.jsxs)(oe,{children:[x,j,M,I]}),t[41]=j,t[42]=M,t[43]=I,t[44]=x,t[45]=L):L=t[45];let R;return t[46]!==o||t[47]!==a||t[48]!==L?(R=(0,$.jsx)(J,{onOpenChange:o,open:a,children:L}),t[46]=o,t[47]=a,t[48]=L,t[49]=R):R=t[49],R}function Ue(e){let t=(0,Z.c)(64),{clusterName:n,clusterVersion:r,nodegroupName:i,onDelete:o}=e,s;t[0]!==n||t[1]!==i?(s=ee(n,i),t[0]=n,t[1]=i,t[2]=s):s=t[2];let{data:c}=a(s),l=p(),u=c?.nodegroup,m=u?.scalingConfig,[h,g]=(0,Q.useState)(!1),[_,v]=(0,Q.useState)(!1),y=!!r&&!!u?.version&&u.version!==r,b;t[3]===i?b=t[4]:(b=(0,$.jsx)(`span`,{children:i}),t[3]=i,t[4]=b);let x;t[5]===y?x=t[6]:(x=y&&(0,$.jsx)(f,{"aria-label":`Update node group version`,onClick:()=>v(!0),size:`icon`,variant:`ghost`,children:(0,$.jsx)(ve,{className:`size-4`})}),t[5]=y,t[6]=x);let S;t[7]===Symbol.for(`react.memo_cache_sentinel`)?(S=()=>g(!0),t[7]=S):S=t[7];let C;t[8]===Symbol.for(`react.memo_cache_sentinel`)?(C=(0,$.jsx)(f,{"aria-label":`Scale node group`,onClick:S,size:`icon`,variant:`ghost`,children:(0,$.jsx)(ye,{className:`size-4`})}),t[8]=C):C=t[8];let w;t[9]!==i||t[10]!==o?(w=()=>o(i),t[9]=i,t[10]=o,t[11]=w):w=t[11];let T;t[12]===Symbol.for(`react.memo_cache_sentinel`)?(T=(0,$.jsx)(d,{className:`size-4`}),t[12]=T):T=t[12];let E;t[13]===w?E=t[14]:(E=(0,$.jsx)(f,{"aria-label":`Delete node group`,onClick:w,size:`icon`,variant:`ghost`,children:T}),t[13]=w,t[14]=E);let D;t[15]!==x||t[16]!==E?(D=(0,$.jsxs)(`div`,{className:`flex items-center gap-1`,children:[x,C,E]}),t[15]=x,t[16]=E,t[17]=D):D=t[17];let O;t[18]!==b||t[19]!==D?(O=(0,$.jsx)(Y.Header,{children:(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[b,D]})}),t[18]=b,t[19]=D,t[20]=O):O=t[20];let k=u?.status,A;t[21]===k?A=t[22]:(A=(0,$.jsx)(X,{label:`Status`,value:k}),t[21]=k,t[22]=A);let j=y?`${u?.version} (cluster on ${r})`:u?.version,M;t[23]===j?M=t[24]:(M=(0,$.jsx)(X,{label:`Kubernetes version`,value:j}),t[23]=j,t[24]=M);let N;t[25]===u?.instanceTypes?N=t[26]:(N=u?.instanceTypes?.join(`, `),t[25]=u?.instanceTypes,t[26]=N);let P;t[27]===N?P=t[28]:(P=(0,$.jsx)(X,{label:`Instance types`,value:N}),t[27]=N,t[28]=P);let F=u?.amiType,I;t[29]===F?I=t[30]:(I=(0,$.jsx)(X,{label:`AMI type`,value:F}),t[29]=F,t[30]=I);let L=u?.capacityType,R;t[31]===L?R=t[32]:(R=(0,$.jsx)(X,{label:`Capacity type`,value:L}),t[31]=L,t[32]=R);let z=m?`${m.minSize} / ${m.desiredSize} / ${m.maxSize}`:void 0,B;t[33]===z?B=t[34]:(B=(0,$.jsx)(X,{label:`Scaling (min / desired / max)`,value:z}),t[33]=z,t[34]=B);let V;t[35]!==A||t[36]!==M||t[37]!==P||t[38]!==I||t[39]!==R||t[40]!==B?(V=(0,$.jsxs)(Y.Content,{children:[A,M,P,I,R,B]}),t[35]=A,t[36]=M,t[37]=P,t[38]=I,t[39]=R,t[40]=B,t[41]=V):V=t[41];let H;t[42]===m?H=t[43]:(H=m??{},t[42]=m,t[43]=H);let te;t[44]!==n||t[45]!==i||t[46]!==h||t[47]!==H?(te=(0,$.jsx)(He,{clusterName:n,current:H,nodegroupName:i,onOpenChange:g,open:h}),t[44]=n,t[45]=i,t[46]=h,t[47]=H,t[48]=te):te=t[48];let ne=`Update node group "${i}" from ${u?.version} to ${r}? Nodes are replaced in a rolling update.`,re;t[49]!==n||t[50]!==r||t[51]!==i||t[52]!==l?(re=()=>l.mutate({clusterName:n,nodegroupName:i,version:r},{onSuccess:()=>v(!1)}),t[49]=n,t[50]=r,t[51]=i,t[52]=l,t[53]=re):re=t[53];let U;t[54]!==_||t[55]!==ne||t[56]!==re||t[57]!==l.isPending?(U=(0,$.jsx)(de,{confirmLabel:`Update`,description:ne,isPending:l.isPending,onConfirm:re,onOpenChange:v,open:_,title:`Update node group version`}),t[54]=_,t[55]=ne,t[56]=re,t[57]=l.isPending,t[58]=U):U=t[58];let ie;return t[59]!==O||t[60]!==V||t[61]!==te||t[62]!==U?(ie=(0,$.jsxs)(Y,{children:[O,V,te,U]}),t[59]=O,t[60]=V,t[61]=te,t[62]=U,t[63]=ie):ie=t[63],ie}function We({clusterName:e,vpcId:t,open:n,onOpenChange:r}){let{data:i}=a(ne),{data:o}=a(R),s=S(),c=i?.Roles??[],l=(o?.Subnets??[]).filter(e=>e.VpcId===t),{formState:{errors:u},handleSubmit:d,register:f,reset:p,setValue:m,watch:h}=U({resolver:ie(x),defaultValues:{nodegroupName:``,nodeRole:``,subnetIds:[],instanceTypes:`t3.medium`,amiType:`AL2_x86_64`,capacityType:`ON_DEMAND`,diskSize:20,minSize:1,desiredSize:2,maxSize:3}}),g=h(`subnetIds`),_=e=>{m(`subnetIds`,g.includes(e)?g.filter(t=>t!==e):[...g,e],{shouldValidate:!0})},v=async t=>{await s.mutateAsync({clusterName:e,nodegroupName:t.nodegroupName,nodeRole:t.nodeRole,subnetIds:t.subnetIds,instanceTypes:t.instanceTypes.split(`,`).map(e=>e.trim()).filter(Boolean),amiType:t.amiType,capacityType:t.capacityType,diskSize:t.diskSize,minSize:t.minSize,desiredSize:t.desiredSize,maxSize:t.maxSize}),p(),r(!1)};return(0,$.jsx)(J,{onOpenChange:r,open:n,children:(0,$.jsxs)(oe,{className:`max-h-[85vh] overflow-y-auto`,children:[(0,$.jsx)(ue,{children:(0,$.jsx)(q,{children:`Add node group`})}),(0,$.jsxs)(`form`,{className:`space-y-4`,id:`add-nodegroup-form`,children:[(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-name`,children:`Name`})}),(0,$.jsx)(B,{"aria-invalid":!!u.nodegroupName,id:`ng-name`,placeholder:`my-nodegroup`,...f(`nodegroupName`)}),(0,$.jsx)(G,{errors:[u.nodegroupName]})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-role`,children:`Node IAM role`})}),(0,$.jsxs)(`select`,{"aria-invalid":!!u.nodeRole,className:`h-7 w-full rounded-md border border-input bg-input/20 px-2 text-sm`,id:`ng-role`,...f(`nodeRole`),children:[(0,$.jsx)(`option`,{value:``,children:`Select role`}),c.map(e=>(0,$.jsx)(`option`,{value:e.Arn??``,children:e.RoleName},e.Arn))]}),(0,$.jsx)(G,{errors:[u.nodeRole]})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:`Subnets`}),l.length===0?(0,$.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No subnets in the cluster VPC.`}):(0,$.jsx)(`div`,{className:`space-y-1`,children:l.map(e=>(0,$.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,$.jsx)(`input`,{"aria-label":`Subnet ${e.SubnetId}`,checked:g.includes(e.SubnetId??``),onChange:()=>_(e.SubnetId??``),type:`checkbox`}),(0,$.jsxs)(`span`,{className:`font-mono`,children:[e.SubnetId,` · `,e.CidrBlock]})]},e.SubnetId))}),(0,$.jsx)(G,{errors:[u.subnetIds]})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-instance-types`,children:`Instance types`})}),(0,$.jsx)(B,{id:`ng-instance-types`,placeholder:`t3.medium, t3.large`,...f(`instanceTypes`)}),(0,$.jsx)(G,{errors:[u.instanceTypes]})]}),(0,$.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-ami`,children:`AMI type`})}),(0,$.jsx)(`select`,{className:`h-7 w-full rounded-md border border-input bg-input/20 px-2 text-sm`,id:`ng-ami`,...f(`amiType`),children:C.map(e=>(0,$.jsx)(`option`,{value:e,children:e},e))})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-capacity`,children:`Capacity type`})}),(0,$.jsx)(`select`,{className:`h-7 w-full rounded-md border border-input bg-input/20 px-2 text-sm`,id:`ng-capacity`,...f(`capacityType`),children:T.map(e=>(0,$.jsx)(`option`,{value:e,children:e},e))})]})]}),(0,$.jsxs)(`div`,{className:`grid grid-cols-4 gap-3`,children:[(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-disk`,children:`Disk (GB)`})}),(0,$.jsx)(B,{id:`ng-disk`,type:`number`,...f(`diskSize`,{valueAsNumber:!0})})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-min`,children:`Min`})}),(0,$.jsx)(B,{id:`ng-min`,type:`number`,...f(`minSize`,{valueAsNumber:!0})})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-desired`,children:`Desired`})}),(0,$.jsx)(B,{id:`ng-desired`,type:`number`,...f(`desiredSize`,{valueAsNumber:!0})})]}),(0,$.jsxs)(K,{children:[(0,$.jsx)(W,{children:(0,$.jsx)(`label`,{htmlFor:`ng-max`,children:`Max`})}),(0,$.jsx)(B,{id:`ng-max`,type:`number`,...f(`maxSize`,{valueAsNumber:!0})})]})]}),(0,$.jsx)(G,{errors:[u.desiredSize]})]}),(0,$.jsxs)(ce,{children:[(0,$.jsx)(le,{children:`Cancel`}),(0,$.jsx)(se,{disabled:s.isPending,onClick:e=>{e.preventDefault(),d(v)()},children:s.isPending?`Creating…`:`Create`})]})]})})}function Ge(e){let t=(0,Z.c)(27),{clusterName:n,clusterVersion:r,vpcId:i}=e,a;t[0]===n?a=t[1]:(a=I(n),t[0]=n,t[1]=a);let{data:s}=o(a),c=k(),[l,u]=(0,Q.useState)(!1),[d,p]=(0,Q.useState)(),m;t[2]===s.nodegroups?m=t[3]:(m=s.nodegroups??[],t[2]=s.nodegroups,t[3]=m);let h=m,g;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(g=(0,$.jsx)(`div`,{className:`mb-4 flex justify-end`,children:(0,$.jsx)(f,{onClick:()=>u(!0),size:`sm`,children:`Add node group`})}),t[4]=g):g=t[4];let _;t[5]!==n||t[6]!==r||t[7]!==h?(_=h.length>0?(0,$.jsx)(`div`,{className:`space-y-4`,children:h.map(e=>(0,$.jsx)(Ue,{clusterName:n,clusterVersion:r,nodegroupName:e,onDelete:p},e))}):(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`No node groups.`}),t[5]=n,t[6]=r,t[7]=h,t[8]=_):_=t[8];let v;t[9]!==n||t[10]!==l||t[11]!==i?(v=(0,$.jsx)(We,{clusterName:n,onOpenChange:u,open:l,vpcId:i}),t[9]=n,t[10]=l,t[11]=i,t[12]=v):v=t[12];let y=`Delete node group "${d??``}"? This terminates its nodes.`,b;t[13]!==n||t[14]!==c||t[15]!==d?(b=()=>{d&&c.mutate({clusterName:n,nodegroupName:d},{onSuccess:()=>p(void 0)})},t[13]=n,t[14]=c,t[15]=d,t[16]=b):b=t[16];let x;t[17]===Symbol.for(`react.memo_cache_sentinel`)?(x=e=>!e&&p(void 0),t[17]=x):x=t[17];let S=d!==void 0,C;t[18]!==c.isPending||t[19]!==y||t[20]!==b||t[21]!==S?(C=(0,$.jsx)(de,{description:y,isPending:c.isPending,onConfirm:b,onOpenChange:x,open:S,title:`Delete node group`}),t[18]=c.isPending,t[19]=y,t[20]=b,t[21]=S,t[22]=C):C=t[22];let w;return t[23]!==C||t[24]!==_||t[25]!==v?(w=(0,$.jsxs)($.Fragment,{children:[g,_,v,C]}),t[23]=C,t[24]=_,t[25]=v,t[26]=w):w=t[26],w}var Ke=`ap-southeast-2`;function qe(e){switch(e??``){case`ACTIVE`:return`default`;case`FAILED`:return`destructive`;default:return`secondary`}}function Je(e){let t=(0,Z.c)(89),{clusterName:n}=e,r=F(),[a,s]=(0,Q.useState)(!1),p;t[0]===n?p=t[1]:(p=te(n),t[0]=n,t[1]=p);let{data:h}=o(p),g=m(),_=h.cluster,v;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(v={type:`bin`,value:`aws eks update-kubeconfig`},t[2]=v):v=t[2];let y;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(y={type:`flag`,value:` --name `},t[3]=y):y=t[3];let b;t[4]===n?b=t[5]:(b={type:`value`,value:n},t[4]=n,t[5]=b);let x;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(x={type:`flag`,value:` --region `},t[6]=x):x=t[6];let S;t[7]===Symbol.for(`react.memo_cache_sentinel`)?(S={type:`value`,value:Ke},t[7]=S):S=t[7];let C;t[8]===b?C=t[9]:(C={label:`Configure kubectl`,parts:[v,y,b,x,S]},t[8]=b,t[9]=C);let w;t[10]===Symbol.for(`react.memo_cache_sentinel`)?(w={label:`Verify access`,parts:[{type:`bin`,value:`kubectl get nodes`}]},t[10]=w):w=t[10];let T;t[11]===C?T=t[12]:(T=[C,w],t[11]=C,t[12]=T);let E=T,D;t[13]!==n||t[14]!==g||t[15]!==r?(D=function(){g.mutate(n,{onSuccess:()=>{r({to:`/eks/list-clusters`})}})},t[13]=n,t[14]=g,t[15]=r,t[16]=D):D=t[16];let O=D,k;t[17]===Symbol.for(`react.memo_cache_sentinel`)?(k=(0,$.jsx)(l,{to:`/eks/list-clusters`,children:`Clusters`}),t[17]=k):k=t[17];let A;t[18]===Symbol.for(`react.memo_cache_sentinel`)?(A=()=>s(!0),t[18]=A):A=t[18];let j;t[19]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,$.jsxs)(f,{onClick:A,size:`sm`,variant:`destructive`,children:[(0,$.jsx)(d,{className:`size-4`}),`Delete`]}),t[19]=j):j=t[19];let M=_?.status,N;t[20]===M?N=t[21]:(N=qe(M),t[20]=M,t[21]=N);let P=_?.status??`UNKNOWN`,I;t[22]!==N||t[23]!==P?(I=(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[j,(0,$.jsx)(_e,{variant:N,children:P})]}),t[22]=N,t[23]=P,t[24]=I):I=t[24];let L;t[25]!==n||t[26]!==I?(L=(0,$.jsx)(ae,{actions:I,subtitle:`Cluster Details`,title:n}),t[25]=n,t[26]=I,t[27]=L):L=t[27];let R;t[28]!==g.error||t[29]!==g.isError?(R=g.isError&&(0,$.jsx)(i,{error:g.error instanceof Error?g.error:void 0,msg:`Failed to delete cluster.`}),t[28]=g.error,t[29]=g.isError,t[30]=R):R=t[30];let z;t[31]===Symbol.for(`react.memo_cache_sentinel`)?(z=(0,$.jsxs)(me,{children:[(0,$.jsx)(pe,{value:`overview`,children:`Overview`}),(0,$.jsx)(pe,{value:`compute`,children:`Compute`}),(0,$.jsx)(pe,{value:`addons`,children:`Add-ons`}),(0,$.jsx)(pe,{value:`access`,children:`Access`}),(0,$.jsx)(pe,{value:`networking`,children:`Networking`}),(0,$.jsx)(pe,{value:`connect`,children:`Connect`})]}),t[31]=z):z=t[31];let B;t[32]===Symbol.for(`react.memo_cache_sentinel`)?(B=(0,$.jsx)(Y.Header,{children:`Cluster`}),t[32]=B):B=t[32];let V=_?.arn,H;t[33]===V?H=t[34]:(H=(0,$.jsx)(X,{label:`ARN`,value:V}),t[33]=V,t[34]=H);let ee=_?.status,ne;t[35]===ee?ne=t[36]:(ne=(0,$.jsx)(X,{label:`Status`,value:ee}),t[35]=ee,t[36]=ne);let re=_?.version,U;t[37]===re?U=t[38]:(U=(0,$.jsx)(X,{label:`Kubernetes version`,value:re}),t[37]=re,t[38]=U);let ie=_?.platformVersion,W;t[39]===ie?W=t[40]:(W=(0,$.jsx)(X,{label:`Platform version`,value:ie}),t[39]=ie,t[40]=W);let G=_?.endpoint,K;t[41]===G?K=t[42]:(K=(0,$.jsx)(X,{label:`Endpoint`,value:G}),t[41]=G,t[42]=K);let oe=_?.identity?.oidc?.issuer,q;t[43]===oe?q=t[44]:(q=(0,$.jsx)(X,{label:`OIDC issuer`,value:oe}),t[43]=oe,t[44]=q);let se=_?.resourcesVpcConfig?.vpcId,ce;t[45]===se?ce=t[46]:(ce=(0,$.jsx)(X,{label:`VPC`,value:se}),t[45]=se,t[46]=ce);let le=_?.createdAt,ue;t[47]===le?ue=t[48]:(ue=c(le),t[47]=le,t[48]=ue);let J;t[49]===ue?J=t[50]:(J=(0,$.jsx)(X,{label:`Created at`,value:ue}),t[49]=ue,t[50]=J);let fe;t[51]!==H||t[52]!==ne||t[53]!==U||t[54]!==W||t[55]!==K||t[56]!==q||t[57]!==ce||t[58]!==J?(fe=(0,$.jsx)(he,{value:`overview`,children:(0,$.jsxs)(Y,{children:[B,(0,$.jsxs)(Y.Content,{children:[H,ne,U,W,K,q,ce,J]})]})}),t[51]=H,t[52]=ne,t[53]=U,t[54]=W,t[55]=K,t[56]=q,t[57]=ce,t[58]=J,t[59]=fe):fe=t[59];let ve=_?.version,ye=_?.resourcesVpcConfig?.vpcId,be;t[60]!==n||t[61]!==ve||t[62]!==ye?(be=(0,$.jsx)(he,{value:`compute`,children:(0,$.jsx)(Ge,{clusterName:n,clusterVersion:ve,vpcId:ye})}),t[60]=n,t[61]=ve,t[62]=ye,t[63]=be):be=t[63];let xe;t[64]===n?xe=t[65]:(xe=(0,$.jsx)(he,{value:`addons`,children:(0,$.jsx)(Ie,{clusterName:n})}),t[64]=n,t[65]=xe);let Se;t[66]===n?Se=t[67]:(Se=(0,$.jsx)(he,{value:`access`,children:(0,$.jsx)(Te,{clusterName:n})}),t[66]=n,t[67]=Se);let Ce;t[68]===_?Ce=t[69]:(Ce=(0,$.jsx)(he,{value:`networking`,children:(0,$.jsx)(Re,{cluster:_})}),t[68]=_,t[69]=Ce);let we;t[70]===E?we=t[71]:(we=(0,$.jsx)(he,{value:`connect`,children:(0,$.jsx)(u,{commands:E})}),t[70]=E,t[71]=we);let Ee;t[72]!==fe||t[73]!==be||t[74]!==xe||t[75]!==Se||t[76]!==Ce||t[77]!==we?(Ee=(0,$.jsxs)(ge,{defaultValue:`overview`,children:[z,fe,be,xe,Se,Ce,we]}),t[72]=fe,t[73]=be,t[74]=xe,t[75]=Se,t[76]=Ce,t[77]=we,t[78]=Ee):Ee=t[78];let De=`This permanently deletes cluster "${n}" and its control plane. This cannot be undone.`,Oe;t[79]!==g.isPending||t[80]!==O||t[81]!==a||t[82]!==De?(Oe=(0,$.jsx)(de,{description:De,isPending:g.isPending,onConfirm:O,onOpenChange:s,open:a,title:`Delete cluster`}),t[79]=g.isPending,t[80]=O,t[81]=a,t[82]=De,t[83]=Oe):Oe=t[83];let ke;return t[84]!==L||t[85]!==R||t[86]!==Ee||t[87]!==Oe?(ke=(0,$.jsxs)($.Fragment,{children:[k,L,R,Ee,Oe]}),t[84]=L,t[85]=R,t[86]=Ee,t[87]=Oe,t[88]=ke):ke=t[88],ke}function Ye(){let e=(0,Z.c)(2),{clusterName:t}=H.useParams(),n;return e[0]===t?n=e[1]:(n=(0,$.jsx)(Je,{clusterName:t}),e[0]=t,e[1]=n),n}export{Ye as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id.js b/spinifex/services/spinifexui/frontend/dist/assets/_id.js index aa4930931..cc9c1f8c0 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as a,s as o,t as s}from"./select.js";import{t as c}from"./trash-2.js";import{I as l,U as u,n as d}from"./ec2.js";import{Ho as f,J as p,Ro as m,Uo as h,V as g,Xa as _,ao as v,cs as y,t as b}from"./index.js";import{t as x}from"./page-heading.js";import{t as S}from"./delete-confirmation-dialog.js";import{n as C,t as w}from"./detail-row.js";var T=y(h()),E=f();function D(){let{id:f}=b.useParams(),h=m(),{data:y}=t(g(f)),{data:D}=t(p),O=d(),k=l(),A=u(),[j,M]=(0,T.useState)(!1),[N,P]=(0,T.useState)(``),F=y.Addresses?.[0],I=D.Reservations?.flatMap(e=>e.Instances??[]).filter(e=>e.State?.Name===`running`||e.State?.Name===`stopped`)??[],L=async()=>{try{await A.mutateAsync(f),h({to:`/ec2/describe-addresses`})}finally{M(!1)}},R=async()=>{N&&await O.mutateAsync({allocationId:f,instanceId:N})},z=async()=>{F?.AssociationId&&await k.mutateAsync(F.AssociationId)};if(!F?.AllocationId)return(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(n,{to:`/ec2/describe-addresses`,children:`Back to Elastic IPs`}),(0,E.jsx)(`p`,{className:`text-muted-foreground`,children:`Elastic IP not found.`})]});let B=!!F.AssociationId,V=v(F.Tags);return(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(n,{to:`/ec2/describe-addresses`,children:`Back to Elastic IPs`}),(A.error??O.error??k.error)&&(0,E.jsx)(e,{error:A.error??O.error??k.error??void 0,msg:`Elastic IP action failed`}),(0,E.jsxs)(`div`,{className:`space-y-6`,children:[(0,E.jsx)(x,{actions:(0,E.jsxs)(_,{disabled:B||A.isPending,onClick:()=>M(!0),size:`sm`,variant:`destructive`,children:[(0,E.jsx)(c,{className:`size-4`}),`Release`]}),subtitle:`Elastic IP Details`,title:V?`${F.AllocationId} (${V})`:F.AllocationId}),(0,E.jsxs)(C,{children:[(0,E.jsx)(C.Header,{children:`Elastic IP Information`}),(0,E.jsxs)(C.Content,{children:[(0,E.jsx)(w,{label:`Allocation ID`,value:F.AllocationId}),(0,E.jsx)(w,{label:`Public IP`,value:F.PublicIp}),(0,E.jsx)(w,{label:`Domain`,value:F.Domain}),(0,E.jsx)(w,{label:`Association ID`,value:F.AssociationId??`—`}),(0,E.jsx)(w,{label:`Instance ID`,value:F.InstanceId??`—`}),(0,E.jsx)(w,{label:`Private IP`,value:F.PrivateIpAddress??`—`})]})]}),(0,E.jsxs)(C,{children:[(0,E.jsx)(C.Header,{children:`Association`}),(0,E.jsx)(C.Content,{children:B?(0,E.jsx)(_,{disabled:k.isPending,onClick:z,size:`sm`,variant:`outline`,children:`Disassociate`}):(0,E.jsxs)(`div`,{className:`flex items-end gap-2`,children:[(0,E.jsx)(`div`,{className:`flex-1`,children:(0,E.jsxs)(s,{onValueChange:e=>P(e??``),value:N,children:[(0,E.jsx)(o,{className:`w-full`,children:(0,E.jsx)(r,{placeholder:`Select an instance`})}),(0,E.jsx)(a,{children:I.map(e=>{let t=v(e.Tags);return(0,E.jsx)(i,{value:e.InstanceId??``,children:t?`${e.InstanceId} (${t})`:e.InstanceId},e.InstanceId)})})]})}),(0,E.jsx)(_,{disabled:!N||O.isPending,onClick:R,size:`sm`,children:`Associate`})]})})]})]}),(0,E.jsx)(S,{description:`Are you sure you want to release the Elastic IP "${F.AllocationId}" (${F.PublicIp})? This action cannot be undone.`,isPending:A.isPending,onConfirm:L,onOpenChange:M,open:j,title:`Release Elastic IP`})]})}export{D as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{A as a}from"./SignatureV4.js";import{t as o}from"./back-link.js";import{c as s,i as c,n as l,s as u,t as d}from"./select.js";import{t as f}from"./trash-2.js";import{t as p}from"./button.js";import{G as m,L as h,n as g}from"./ec2.js";import{Ir as _,ct as v,gt as y,t as b}from"./index.js";import{t as x}from"./page-heading.js";import{t as S}from"./delete-confirmation-dialog.js";import{n as C,t as w}from"./detail-row.js";var T=t(n()),E=e();function D(){let{id:e}=b.useParams(),t=_(),{data:n}=i(v(e)),{data:D}=i(y),O=g(),k=h(),A=m(),[j,M]=(0,T.useState)(!1),[N,P]=(0,T.useState)(``),F=n.Addresses?.[0],I=D.Reservations?.flatMap(e=>e.Instances??[]).filter(e=>e.State?.Name===`running`||e.State?.Name===`stopped`)??[],L=async()=>{try{await A.mutateAsync(e),t({to:`/ec2/describe-addresses`})}finally{M(!1)}},R=async()=>{N&&await O.mutateAsync({allocationId:e,instanceId:N})},z=async()=>{F?.AssociationId&&await k.mutateAsync(F.AssociationId)};if(!F?.AllocationId)return(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(o,{to:`/ec2/describe-addresses`,children:`Back to Elastic IPs`}),(0,E.jsx)(`p`,{className:`text-muted-foreground`,children:`Elastic IP not found.`})]});let B=!!F.AssociationId,V=a(F.Tags);return(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(o,{to:`/ec2/describe-addresses`,children:`Back to Elastic IPs`}),(A.error??O.error??k.error)&&(0,E.jsx)(r,{error:A.error??O.error??k.error??void 0,msg:`Elastic IP action failed`}),(0,E.jsxs)(`div`,{className:`space-y-6`,children:[(0,E.jsx)(x,{actions:(0,E.jsxs)(p,{disabled:B||A.isPending,onClick:()=>M(!0),size:`sm`,variant:`destructive`,children:[(0,E.jsx)(f,{className:`size-4`}),`Release`]}),subtitle:`Elastic IP Details`,title:V?`${F.AllocationId} (${V})`:F.AllocationId}),(0,E.jsxs)(C,{children:[(0,E.jsx)(C.Header,{children:`Elastic IP Information`}),(0,E.jsxs)(C.Content,{children:[(0,E.jsx)(w,{label:`Allocation ID`,value:F.AllocationId}),(0,E.jsx)(w,{label:`Public IP`,value:F.PublicIp}),(0,E.jsx)(w,{label:`Domain`,value:F.Domain}),(0,E.jsx)(w,{label:`Association ID`,value:F.AssociationId??`—`}),(0,E.jsx)(w,{label:`Instance ID`,value:F.InstanceId??`—`}),(0,E.jsx)(w,{label:`Private IP`,value:F.PrivateIpAddress??`—`})]})]}),(0,E.jsxs)(C,{children:[(0,E.jsx)(C.Header,{children:`Association`}),(0,E.jsx)(C.Content,{children:B?(0,E.jsx)(p,{disabled:k.isPending,onClick:z,size:`sm`,variant:`outline`,children:`Disassociate`}):(0,E.jsxs)(`div`,{className:`flex items-end gap-2`,children:[(0,E.jsx)(`div`,{className:`flex-1`,children:(0,E.jsxs)(d,{onValueChange:e=>P(e??``),value:N,children:[(0,E.jsx)(u,{className:`w-full`,children:(0,E.jsx)(s,{placeholder:`Select an instance`})}),(0,E.jsx)(l,{children:I.map(e=>{let t=a(e.Tags);return(0,E.jsx)(c,{value:e.InstanceId??``,children:t?`${e.InstanceId} (${t})`:e.InstanceId},e.InstanceId)})})]})}),(0,E.jsx)(p,{disabled:!N||O.isPending,onClick:R,size:`sm`,children:`Associate`})]})})]})]}),(0,E.jsx)(S,{description:`Are you sure you want to release the Elastic IP "${F.AllocationId}" (${F.PublicIp})? This action cannot be undone.`,isPending:A.isPending,onConfirm:L,onOpenChange:M,open:j,title:`Release Elastic IP`})]})}export{D as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id10.js b/spinifex/services/spinifexui/frontend/dist/assets/_id10.js index 9e5244beb..34cc7a79d 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id10.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id10.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as a,s as o,t as s}from"./select.js";import{t as c}from"./plus.js";import{t as l}from"./trash-2.js";import{G as u,W as d,k as f,o as p,s as m}from"./ec2.js";import{Ho as h,Lo as g,Ro as _,Uo as v,Xa as y,Xi as b,cs as x,mo as S,ot as C,u as w}from"./index.js";import{i as T,n as E,t as D}from"./zod.js";import{t as O}from"./page-heading.js";import{a as k,r as A,t as j}from"./field.js";import{y as M}from"./ec22.js";import{i as N,l as P,n as F,o as I,r as L,s as R,t as z}from"./alert-dialog.js";import{t as B}from"./delete-confirmation-dialog.js";import{n as V,t as H}from"./detail-row.js";var U=x(v()),W=h();function G(e){if(!e||e===`-1`)return`All traffic`;let t=e.toUpperCase();return t===`TCP`||t===`UDP`||t===`ICMP`?t:e}function K(e,t,n){return!n||n===`-1`||e===void 0||t===void 0||e===-1&&t===-1?`All`:e===t?e.toString():`${e}-${t}`}function q({open:t,onOpenChange:n,direction:c,groupId:l}){let u=m(),d=p(),f=c===`ingress`?u:d,{control:h,handleSubmit:g,register:_,reset:v,watch:y,formState:{errors:x}}=T({resolver:D(M),defaultValues:{ipProtocol:`tcp`,fromPort:0,toPort:0,cidrIp:`0.0.0.0/0`}}),S=y(`ipProtocol`),C=S===`-1`||S===`icmp`;return(0,W.jsx)(z,{onOpenChange:n,open:t,children:(0,W.jsxs)(N,{children:[(0,W.jsx)(R,{children:(0,W.jsxs)(P,{children:[`Add `,c===`ingress`?`Inbound`:`Outbound`,` Rule`]})}),f.error&&(0,W.jsx)(e,{error:f.error,msg:`Failed to add rule`}),(0,W.jsxs)(`form`,{className:`space-y-4`,id:`add-rule-form`,onSubmit:g(async e=>{let t={groupId:l,ipProtocol:e.ipProtocol,fromPort:C?-1:e.fromPort,toPort:C?-1:e.toPort,cidrIp:e.cidrIp};await f.mutateAsync(t),v(),n(!1)}),children:[(0,W.jsxs)(j,{children:[(0,W.jsx)(k,{children:(0,W.jsx)(`label`,{htmlFor:`ipProtocol`,children:`Protocol`})}),(0,W.jsx)(E,{control:h,name:`ipProtocol`,render:({field:e})=>(0,W.jsxs)(s,{onValueChange:t=>e.onChange(t),value:e.value,children:[(0,W.jsx)(o,{className:`w-full`,id:`ipProtocol`,children:(0,W.jsx)(r,{})}),(0,W.jsxs)(a,{children:[(0,W.jsx)(i,{value:`tcp`,children:`TCP`}),(0,W.jsx)(i,{value:`udp`,children:`UDP`}),(0,W.jsx)(i,{value:`icmp`,children:`ICMP`}),(0,W.jsx)(i,{value:`-1`,children:`All traffic (-1)`})]})]})}),(0,W.jsx)(A,{errors:[x.ipProtocol]})]}),(0,W.jsxs)(`div`,{className:`grid grid-cols-2 gap-4`,children:[(0,W.jsxs)(j,{children:[(0,W.jsx)(k,{children:(0,W.jsx)(`label`,{htmlFor:`fromPort`,children:`From Port`})}),(0,W.jsx)(b,{"aria-invalid":!!x.fromPort,disabled:C,id:`fromPort`,type:`number`,..._(`fromPort`,{valueAsNumber:!0})}),(0,W.jsx)(A,{errors:[x.fromPort]})]}),(0,W.jsxs)(j,{children:[(0,W.jsx)(k,{children:(0,W.jsx)(`label`,{htmlFor:`toPort`,children:`To Port`})}),(0,W.jsx)(b,{"aria-invalid":!!x.toPort,disabled:C,id:`toPort`,type:`number`,..._(`toPort`,{valueAsNumber:!0})}),(0,W.jsx)(A,{errors:[x.toPort]})]})]}),(0,W.jsxs)(j,{children:[(0,W.jsx)(k,{children:(0,W.jsxs)(`label`,{htmlFor:`cidrIp`,children:[c===`ingress`?`Source`:`Destination`,` CIDR`]})}),(0,W.jsx)(b,{"aria-invalid":!!x.cidrIp,id:`cidrIp`,placeholder:`0.0.0.0/0`,..._(`cidrIp`)}),(0,W.jsx)(A,{errors:[x.cidrIp]})]})]}),(0,W.jsxs)(I,{children:[(0,W.jsx)(L,{onClick:()=>{v(),f.reset()},children:`Cancel`}),(0,W.jsx)(F,{disabled:f.isPending,form:`add-rule-form`,type:`submit`,children:f.isPending?`Adding…`:`Add Rule`})]})]})})}function J(e){return[...(e.IpRanges??[]).map(e=>({kind:`cidr`,value:e.CidrIp??``})),...(e.UserIdGroupPairs??[]).map(e=>({kind:`group`,value:e.GroupId??``}))]}function Y({rule:t,source:n,groupId:r,direction:i}){let a=u(),o=d(),s=i===`ingress`?a:o,c=async()=>{try{await s.mutateAsync({groupId:r,ipProtocol:t.IpProtocol??`-1`,fromPort:t.FromPort??-1,toPort:t.ToPort??-1,...n.kind===`group`?{sourceGroupId:n.value}:{cidrIp:n.value}})}catch{}},l=n.kind===`group`&&n.value===r?`${n.value} (this security group)`:n.value;return(0,W.jsxs)(`div`,{className:`space-y-0`,children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between border-b px-4 py-3 last:border-b-0`,children:[(0,W.jsxs)(`div`,{className:`grid flex-1 grid-cols-3 gap-4 text-sm`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`span`,{className:`text-muted-foreground`,children:`Protocol: `}),G(t.IpProtocol)]}),(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`span`,{className:`text-muted-foreground`,children:`Port Range: `}),K(t.FromPort,t.ToPort,t.IpProtocol)]}),(0,W.jsxs)(`div`,{children:[(0,W.jsxs)(`span`,{className:`text-muted-foreground`,children:[i===`ingress`?`Source`:`Destination`,`:`,` `]}),l]})]}),(0,W.jsx)(y,{disabled:s.isPending,onClick:c,size:`icon-xs`,variant:`ghost`,children:(0,W.jsx)(S,{className:`size-3`})})]}),s.error&&(0,W.jsx)(`div`,{className:`px-4 pb-2`,children:(0,W.jsx)(e,{error:s.error,msg:`Failed to remove rule`})})]})}function X(){let{id:r}=w.useParams(),i=_(),{data:a}=t(C(r)),o=a.SecurityGroups?.[0],s=f(),[u,d]=(0,U.useState)(!1),[p,m]=(0,U.useState)(!1),[h,v]=(0,U.useState)(!1),b=async()=>{try{await s.mutateAsync(r),i({to:`/ec2/describe-security-groups`})}finally{d(!1)}};if(!o?.GroupId)return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(n,{to:`/ec2/describe-security-groups`,children:`Back to Security Groups`}),(0,W.jsx)(`p`,{className:`text-muted-foreground`,children:`Security group not found.`})]});let x=o.IpPermissions??[],S=o.IpPermissionsEgress??[];return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(n,{to:`/ec2/describe-security-groups`,children:`Back to Security Groups`}),s.error&&(0,W.jsx)(e,{error:s.error,msg:`Failed to delete security group`}),(0,W.jsxs)(`div`,{className:`space-y-6`,children:[(0,W.jsx)(O,{actions:(0,W.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,W.jsxs)(y,{onClick:()=>d(!0),size:`sm`,variant:`destructive`,children:[(0,W.jsx)(l,{className:`size-4`}),`Delete`]})}),subtitle:`Security Group Details`,title:o.GroupName?`${o.GroupId} (${o.GroupName})`:o.GroupId}),(0,W.jsxs)(V,{children:[(0,W.jsx)(V.Header,{children:`Security Group Information`}),(0,W.jsxs)(V.Content,{children:[(0,W.jsx)(H,{label:`Group ID`,value:o.GroupId}),(0,W.jsx)(H,{label:`Group Name`,value:o.GroupName}),(0,W.jsx)(H,{label:`Description`,value:o.Description}),(0,W.jsx)(H,{label:`VPC ID`,value:o.VpcId?(0,W.jsx)(g,{className:`text-primary hover:underline`,params:{id:o.VpcId},to:`/ec2/describe-vpcs/$id`,children:o.VpcId}):`—`}),(0,W.jsx)(H,{label:`Owner ID`,value:o.OwnerId})]})]}),(0,W.jsxs)(V,{children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between border-b p-4`,children:[(0,W.jsxs)(`h2`,{className:`font-semibold`,children:[`Inbound Rules (`,x.length,`)`]}),(0,W.jsxs)(y,{onClick:()=>m(!0),size:`sm`,variant:`outline`,children:[(0,W.jsx)(c,{className:`size-4`}),`Add Rule`]})]}),x.length>0?(0,W.jsx)(`div`,{children:x.flatMap(e=>J(e).map(t=>(0,W.jsx)(Y,{direction:`ingress`,groupId:o.GroupId??``,rule:e,source:t},`${e.IpProtocol}-${e.FromPort}-${e.ToPort}-${t.kind}-${t.value}`)))}):(0,W.jsx)(`p`,{className:`p-4 text-sm text-muted-foreground`,children:`No inbound rules.`})]}),(0,W.jsxs)(V,{children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between border-b p-4`,children:[(0,W.jsxs)(`h2`,{className:`font-semibold`,children:[`Outbound Rules (`,S.length,`)`]}),(0,W.jsxs)(y,{onClick:()=>v(!0),size:`sm`,variant:`outline`,children:[(0,W.jsx)(c,{className:`size-4`}),`Add Rule`]})]}),S.length>0?(0,W.jsx)(`div`,{children:S.flatMap(e=>J(e).map(t=>(0,W.jsx)(Y,{direction:`egress`,groupId:o.GroupId??``,rule:e,source:t},`${e.IpProtocol}-${e.FromPort}-${e.ToPort}-${t.kind}-${t.value}`)))}):(0,W.jsx)(`p`,{className:`p-4 text-sm text-muted-foreground`,children:`No outbound rules.`})]})]}),(0,W.jsx)(B,{description:`Are you sure you want to delete the security group "${o.GroupId}"? This action cannot be undone.`,isPending:s.isPending,onConfirm:b,onOpenChange:d,open:u,title:`Delete Security Group`}),(0,W.jsx)(q,{direction:`ingress`,groupId:o.GroupId,onOpenChange:m,open:p}),(0,W.jsx)(q,{direction:`egress`,groupId:o.GroupId,onOpenChange:v,open:h})]})}export{X as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{t as o}from"./back-link.js";import{c as s,i as c,n as l,s as u,t as d}from"./select.js";import{t as f}from"./plus.js";import{t as p}from"./trash-2.js";import{t as m}from"./button.js";import{A as h,K as g,c as _,q as v,s as y}from"./ec2.js";import{Cr as b,Ir as x,Ot as S,Rn as C,u as w}from"./index.js";import{i as T,n as E,t as D}from"./zod.js";import{t as O}from"./page-heading.js";import{a as k,r as A,t as j}from"./field.js";import{y as M}from"./ec22.js";import{i as N,l as P,n as F,o as I,r as L,s as R,t as z}from"./alert-dialog.js";import{t as B}from"./delete-confirmation-dialog.js";import{n as V,t as H}from"./detail-row.js";var U=t(n()),W=e();function G(e){if(!e||e===`-1`)return`All traffic`;let t=e.toUpperCase();return t===`TCP`||t===`UDP`||t===`ICMP`?t:e}function K(e,t,n){return!n||n===`-1`||e===void 0||t===void 0||e===-1&&t===-1?`All`:e===t?e.toString():`${e}-${t}`}function q({open:e,onOpenChange:t,direction:n,groupId:i}){let a=_(),o=y(),f=n===`ingress`?a:o,{control:p,handleSubmit:m,register:h,reset:g,watch:v,formState:{errors:b}}=T({resolver:D(M),defaultValues:{ipProtocol:`tcp`,fromPort:0,toPort:0,cidrIp:`0.0.0.0/0`}}),x=v(`ipProtocol`),S=x===`-1`||x===`icmp`;return(0,W.jsx)(z,{onOpenChange:t,open:e,children:(0,W.jsxs)(N,{children:[(0,W.jsx)(R,{children:(0,W.jsxs)(P,{children:[`Add `,n===`ingress`?`Inbound`:`Outbound`,` Rule`]})}),f.error&&(0,W.jsx)(r,{error:f.error,msg:`Failed to add rule`}),(0,W.jsxs)(`form`,{className:`space-y-4`,id:`add-rule-form`,onSubmit:m(async e=>{let n={groupId:i,ipProtocol:e.ipProtocol,fromPort:S?-1:e.fromPort,toPort:S?-1:e.toPort,cidrIp:e.cidrIp};await f.mutateAsync(n),g(),t(!1)}),children:[(0,W.jsxs)(j,{children:[(0,W.jsx)(k,{children:(0,W.jsx)(`label`,{htmlFor:`ipProtocol`,children:`Protocol`})}),(0,W.jsx)(E,{control:p,name:`ipProtocol`,render:({field:e})=>(0,W.jsxs)(d,{onValueChange:t=>e.onChange(t),value:e.value,children:[(0,W.jsx)(u,{className:`w-full`,id:`ipProtocol`,children:(0,W.jsx)(s,{})}),(0,W.jsxs)(l,{children:[(0,W.jsx)(c,{value:`tcp`,children:`TCP`}),(0,W.jsx)(c,{value:`udp`,children:`UDP`}),(0,W.jsx)(c,{value:`icmp`,children:`ICMP`}),(0,W.jsx)(c,{value:`-1`,children:`All traffic (-1)`})]})]})}),(0,W.jsx)(A,{errors:[b.ipProtocol]})]}),(0,W.jsxs)(`div`,{className:`grid grid-cols-2 gap-4`,children:[(0,W.jsxs)(j,{children:[(0,W.jsx)(k,{children:(0,W.jsx)(`label`,{htmlFor:`fromPort`,children:`From Port`})}),(0,W.jsx)(C,{"aria-invalid":!!b.fromPort,disabled:S,id:`fromPort`,type:`number`,...h(`fromPort`,{valueAsNumber:!0})}),(0,W.jsx)(A,{errors:[b.fromPort]})]}),(0,W.jsxs)(j,{children:[(0,W.jsx)(k,{children:(0,W.jsx)(`label`,{htmlFor:`toPort`,children:`To Port`})}),(0,W.jsx)(C,{"aria-invalid":!!b.toPort,disabled:S,id:`toPort`,type:`number`,...h(`toPort`,{valueAsNumber:!0})}),(0,W.jsx)(A,{errors:[b.toPort]})]})]}),(0,W.jsxs)(j,{children:[(0,W.jsx)(k,{children:(0,W.jsxs)(`label`,{htmlFor:`cidrIp`,children:[n===`ingress`?`Source`:`Destination`,` CIDR`]})}),(0,W.jsx)(C,{"aria-invalid":!!b.cidrIp,id:`cidrIp`,placeholder:`0.0.0.0/0`,...h(`cidrIp`)}),(0,W.jsx)(A,{errors:[b.cidrIp]})]})]}),(0,W.jsxs)(I,{children:[(0,W.jsx)(L,{onClick:()=>{g(),f.reset()},children:`Cancel`}),(0,W.jsx)(F,{disabled:f.isPending,form:`add-rule-form`,type:`submit`,children:f.isPending?`Adding…`:`Add Rule`})]})]})})}function J(e){return[...(e.IpRanges??[]).map(e=>({kind:`cidr`,value:e.CidrIp??``})),...(e.UserIdGroupPairs??[]).map(e=>({kind:`group`,value:e.GroupId??``}))]}function Y({rule:e,source:t,groupId:n,direction:i}){let a=v(),o=g(),s=i===`ingress`?a:o,c=async()=>{try{await s.mutateAsync({groupId:n,ipProtocol:e.IpProtocol??`-1`,fromPort:e.FromPort??-1,toPort:e.ToPort??-1,...t.kind===`group`?{sourceGroupId:t.value}:{cidrIp:t.value}})}catch{}},l=t.kind===`group`&&t.value===n?`${t.value} (this security group)`:t.value;return(0,W.jsxs)(`div`,{className:`space-y-0`,children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between border-b px-4 py-3 last:border-b-0`,children:[(0,W.jsxs)(`div`,{className:`grid flex-1 grid-cols-3 gap-4 text-sm`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`span`,{className:`text-muted-foreground`,children:`Protocol: `}),G(e.IpProtocol)]}),(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`span`,{className:`text-muted-foreground`,children:`Port Range: `}),K(e.FromPort,e.ToPort,e.IpProtocol)]}),(0,W.jsxs)(`div`,{children:[(0,W.jsxs)(`span`,{className:`text-muted-foreground`,children:[i===`ingress`?`Source`:`Destination`,`:`,` `]}),l]})]}),(0,W.jsx)(m,{disabled:s.isPending,onClick:c,size:`icon-xs`,variant:`ghost`,children:(0,W.jsx)(b,{className:`size-3`})})]}),s.error&&(0,W.jsx)(`div`,{className:`px-4 pb-2`,children:(0,W.jsx)(r,{error:s.error,msg:`Failed to remove rule`})})]})}function X(){let{id:e}=w.useParams(),t=x(),{data:n}=i(S(e)),s=n.SecurityGroups?.[0],c=h(),[l,u]=(0,U.useState)(!1),[d,g]=(0,U.useState)(!1),[_,v]=(0,U.useState)(!1),y=async()=>{try{await c.mutateAsync(e),t({to:`/ec2/describe-security-groups`})}finally{u(!1)}};if(!s?.GroupId)return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(o,{to:`/ec2/describe-security-groups`,children:`Back to Security Groups`}),(0,W.jsx)(`p`,{className:`text-muted-foreground`,children:`Security group not found.`})]});let b=s.IpPermissions??[],C=s.IpPermissionsEgress??[];return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(o,{to:`/ec2/describe-security-groups`,children:`Back to Security Groups`}),c.error&&(0,W.jsx)(r,{error:c.error,msg:`Failed to delete security group`}),(0,W.jsxs)(`div`,{className:`space-y-6`,children:[(0,W.jsx)(O,{actions:(0,W.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,W.jsxs)(m,{onClick:()=>u(!0),size:`sm`,variant:`destructive`,children:[(0,W.jsx)(p,{className:`size-4`}),`Delete`]})}),subtitle:`Security Group Details`,title:s.GroupName?`${s.GroupId} (${s.GroupName})`:s.GroupId}),(0,W.jsxs)(V,{children:[(0,W.jsx)(V.Header,{children:`Security Group Information`}),(0,W.jsxs)(V.Content,{children:[(0,W.jsx)(H,{label:`Group ID`,value:s.GroupId}),(0,W.jsx)(H,{label:`Group Name`,value:s.GroupName}),(0,W.jsx)(H,{label:`Description`,value:s.Description}),(0,W.jsx)(H,{label:`VPC ID`,value:s.VpcId?(0,W.jsx)(a,{className:`text-primary hover:underline`,params:{id:s.VpcId},to:`/ec2/describe-vpcs/$id`,children:s.VpcId}):`—`}),(0,W.jsx)(H,{label:`Owner ID`,value:s.OwnerId})]})]}),(0,W.jsxs)(V,{children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between border-b p-4`,children:[(0,W.jsxs)(`h2`,{className:`font-semibold`,children:[`Inbound Rules (`,b.length,`)`]}),(0,W.jsxs)(m,{onClick:()=>g(!0),size:`sm`,variant:`outline`,children:[(0,W.jsx)(f,{className:`size-4`}),`Add Rule`]})]}),b.length>0?(0,W.jsx)(`div`,{children:b.flatMap(e=>J(e).map(t=>(0,W.jsx)(Y,{direction:`ingress`,groupId:s.GroupId??``,rule:e,source:t},`${e.IpProtocol}-${e.FromPort}-${e.ToPort}-${t.kind}-${t.value}`)))}):(0,W.jsx)(`p`,{className:`p-4 text-sm text-muted-foreground`,children:`No inbound rules.`})]}),(0,W.jsxs)(V,{children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between border-b p-4`,children:[(0,W.jsxs)(`h2`,{className:`font-semibold`,children:[`Outbound Rules (`,C.length,`)`]}),(0,W.jsxs)(m,{onClick:()=>v(!0),size:`sm`,variant:`outline`,children:[(0,W.jsx)(f,{className:`size-4`}),`Add Rule`]})]}),C.length>0?(0,W.jsx)(`div`,{children:C.flatMap(e=>J(e).map(t=>(0,W.jsx)(Y,{direction:`egress`,groupId:s.GroupId??``,rule:e,source:t},`${e.IpProtocol}-${e.FromPort}-${e.ToPort}-${t.kind}-${t.value}`)))}):(0,W.jsx)(`p`,{className:`p-4 text-sm text-muted-foreground`,children:`No outbound rules.`})]})]}),(0,W.jsx)(B,{description:`Are you sure you want to delete the security group "${s.GroupId}"? This action cannot be undone.`,isPending:c.isPending,onConfirm:y,onOpenChange:u,open:l,title:`Delete Security Group`}),(0,W.jsx)(q,{direction:`ingress`,groupId:s.GroupId,onOpenChange:g,open:d}),(0,W.jsx)(q,{direction:`egress`,groupId:s.GroupId,onOpenChange:v,open:_})]})}export{X as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id11.js b/spinifex/services/spinifexui/frontend/dist/assets/_id11.js index c853885f2..a6b30dd1e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id11.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id11.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./copy.js";import{t as i}from"./trash-2.js";import{A as a,c as o}from"./ec2.js";import{Ho as s,Lo as c,Ro as l,Uo as u,Xa as d,Xi as f,cs as p,ct as m,d as h,no as g,rt as _}from"./index.js";import{t as v}from"./page-heading.js";import{a as y,t as b}from"./field.js";import{a as x,i as S,l as C,n as w,o as T,r as E,s as D,t as O}from"./alert-dialog.js";import{t as k}from"./delete-confirmation-dialog.js";import{n as A,t as j}from"./detail-row.js";import{t as M}from"./state-badge.js";var N=p(u()),P=s();function F(){let{id:s}=h.useParams(),u=l(),{data:p}=t(m(s)),{data:F}=t(_),I=p.Snapshots?.[0],L=a(),R=o(),[z,B]=(0,N.useState)(!1),[V,H]=(0,N.useState)(!1),[U,W]=(0,N.useState)(``),G=F.Regions?.[0]?.RegionName??`ap-southeast-2`,K=I?.State===`completed`,q=async()=>{try{await L.mutateAsync(s),u({to:`/ec2/describe-snapshots`})}finally{B(!1)}},J=async()=>{try{await R.mutateAsync({sourceSnapshotId:s,sourceRegion:G,description:U||void 0}),H(!1),W(``)}catch{}};if(!I?.SnapshotId)return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(n,{to:`/ec2/describe-snapshots`,children:`Back to snapshots`}),(0,P.jsx)(`p`,{className:`text-muted-foreground`,children:`Snapshot not found.`})]});let Y=g(I.StartTime);return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(n,{to:`/ec2/describe-snapshots`,children:`Back to snapshots`}),L.error&&(0,P.jsx)(e,{error:L.error,msg:`Failed to delete snapshot`}),R.error&&(0,P.jsx)(e,{error:R.error,msg:`Failed to copy snapshot`}),(0,P.jsxs)(`div`,{className:`space-y-6`,children:[(0,P.jsx)(v,{actions:(0,P.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,P.jsxs)(d,{disabled:!K,onClick:()=>B(!0),size:`sm`,variant:`destructive`,children:[(0,P.jsx)(i,{className:`size-4`}),`Delete`]}),(0,P.jsxs)(d,{disabled:!K,onClick:()=>H(!0),size:`sm`,variant:`outline`,children:[(0,P.jsx)(r,{className:`size-4`}),`Copy Snapshot`]}),(0,P.jsx)(M,{state:I.State})]}),subtitle:`Snapshot Details`,title:I.SnapshotId}),(0,P.jsxs)(A,{children:[(0,P.jsx)(A.Header,{children:`Snapshot Information`}),(0,P.jsxs)(A.Content,{children:[(0,P.jsx)(j,{label:`Snapshot ID`,value:I.SnapshotId}),(0,P.jsx)(j,{label:`Volume ID`,value:I.VolumeId?(0,P.jsx)(c,{className:`text-primary hover:underline`,params:{id:I.VolumeId},to:`/ec2/describe-volumes/$id`,children:I.VolumeId}):void 0}),(0,P.jsx)(j,{label:`Volume Size`,value:`${I.VolumeSize} GiB`}),(0,P.jsx)(j,{label:`State`,value:I.State}),(0,P.jsx)(j,{label:`Description`,value:I.Description}),(0,P.jsx)(j,{label:`Progress`,value:I.Progress}),(0,P.jsx)(j,{label:`Start Time`,value:Y}),(0,P.jsx)(j,{label:`Owner ID`,value:I.OwnerId}),(0,P.jsx)(j,{label:`Encrypted`,value:I.Encrypted?`Yes`:`No`})]})]})]}),(0,P.jsx)(k,{description:`Are you sure you want to delete the snapshot "${I.SnapshotId}"? This action cannot be undone.`,isPending:L.isPending,onConfirm:q,onOpenChange:B,open:z,title:`Delete Snapshot`}),(0,P.jsx)(O,{onOpenChange:H,open:V,children:(0,P.jsxs)(S,{children:[(0,P.jsxs)(D,{children:[(0,P.jsx)(C,{children:`Copy Snapshot`}),(0,P.jsx)(x,{children:`Create a copy of this snapshot in the current region.`})]}),(0,P.jsxs)(b,{children:[(0,P.jsx)(y,{children:(0,P.jsx)(`label`,{htmlFor:`copyDescription`,children:`Description (optional)`})}),(0,P.jsx)(f,{id:`copyDescription`,onChange:e=>W(e.target.value),placeholder:`Description for the snapshot copy`,value:U})]}),(0,P.jsxs)(T,{children:[(0,P.jsx)(E,{children:`Cancel`}),(0,P.jsx)(w,{disabled:R.isPending,onClick:J,children:R.isPending?`Copying…`:`Copy Snapshot`})]})]})})]})}export{F as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{D as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{t as c}from"./copy.js";import{t as l}from"./trash-2.js";import{t as u}from"./button.js";import{j as d,l as f}from"./ec2.js";import{At as p,Ir as m,Rn as h,Tt as g,d as _}from"./index.js";import{t as v}from"./page-heading.js";import{a as y,t as b}from"./field.js";import{a as x,i as S,l as C,n as w,o as T,r as E,s as D,t as O}from"./alert-dialog.js";import{t as k}from"./delete-confirmation-dialog.js";import{n as A,t as j}from"./detail-row.js";import{t as M}from"./state-badge.js";var N=t(n()),P=e();function F(){let{id:e}=_.useParams(),t=m(),{data:n}=i(p(e)),{data:F}=i(g),I=n.Snapshots?.[0],L=d(),R=f(),[z,B]=(0,N.useState)(!1),[V,H]=(0,N.useState)(!1),[U,W]=(0,N.useState)(``),G=F.Regions?.[0]?.RegionName??`ap-southeast-2`,K=I?.State===`completed`,q=async()=>{try{await L.mutateAsync(e),t({to:`/ec2/describe-snapshots`})}finally{B(!1)}},J=async()=>{try{await R.mutateAsync({sourceSnapshotId:e,sourceRegion:G,description:U||void 0}),H(!1),W(``)}catch{}};if(!I?.SnapshotId)return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(s,{to:`/ec2/describe-snapshots`,children:`Back to snapshots`}),(0,P.jsx)(`p`,{className:`text-muted-foreground`,children:`Snapshot not found.`})]});let Y=o(I.StartTime);return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(s,{to:`/ec2/describe-snapshots`,children:`Back to snapshots`}),L.error&&(0,P.jsx)(r,{error:L.error,msg:`Failed to delete snapshot`}),R.error&&(0,P.jsx)(r,{error:R.error,msg:`Failed to copy snapshot`}),(0,P.jsxs)(`div`,{className:`space-y-6`,children:[(0,P.jsx)(v,{actions:(0,P.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,P.jsxs)(u,{disabled:!K,onClick:()=>B(!0),size:`sm`,variant:`destructive`,children:[(0,P.jsx)(l,{className:`size-4`}),`Delete`]}),(0,P.jsxs)(u,{disabled:!K,onClick:()=>H(!0),size:`sm`,variant:`outline`,children:[(0,P.jsx)(c,{className:`size-4`}),`Copy Snapshot`]}),(0,P.jsx)(M,{state:I.State})]}),subtitle:`Snapshot Details`,title:I.SnapshotId}),(0,P.jsxs)(A,{children:[(0,P.jsx)(A.Header,{children:`Snapshot Information`}),(0,P.jsxs)(A.Content,{children:[(0,P.jsx)(j,{label:`Snapshot ID`,value:I.SnapshotId}),(0,P.jsx)(j,{label:`Volume ID`,value:I.VolumeId?(0,P.jsx)(a,{className:`text-primary hover:underline`,params:{id:I.VolumeId},to:`/ec2/describe-volumes/$id`,children:I.VolumeId}):void 0}),(0,P.jsx)(j,{label:`Volume Size`,value:`${I.VolumeSize} GiB`}),(0,P.jsx)(j,{label:`State`,value:I.State}),(0,P.jsx)(j,{label:`Description`,value:I.Description}),(0,P.jsx)(j,{label:`Progress`,value:I.Progress}),(0,P.jsx)(j,{label:`Start Time`,value:Y}),(0,P.jsx)(j,{label:`Owner ID`,value:I.OwnerId}),(0,P.jsx)(j,{label:`Encrypted`,value:I.Encrypted?`Yes`:`No`})]})]})]}),(0,P.jsx)(k,{description:`Are you sure you want to delete the snapshot "${I.SnapshotId}"? This action cannot be undone.`,isPending:L.isPending,onConfirm:q,onOpenChange:B,open:z,title:`Delete Snapshot`}),(0,P.jsx)(O,{onOpenChange:H,open:V,children:(0,P.jsxs)(S,{children:[(0,P.jsxs)(D,{children:[(0,P.jsx)(C,{children:`Copy Snapshot`}),(0,P.jsx)(x,{children:`Create a copy of this snapshot in the current region.`})]}),(0,P.jsxs)(b,{children:[(0,P.jsx)(y,{children:(0,P.jsx)(`label`,{htmlFor:`copyDescription`,children:`Description (optional)`})}),(0,P.jsx)(h,{id:`copyDescription`,onChange:e=>W(e.target.value),placeholder:`Description for the snapshot copy`,value:U})]}),(0,P.jsxs)(T,{children:[(0,P.jsx)(E,{children:`Cancel`}),(0,P.jsx)(w,{disabled:R.isPending,onClick:J,children:R.isPending?`Copying…`:`Copy Snapshot`})]})]})})]})}export{F as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id12.js b/spinifex/services/spinifexui/frontend/dist/assets/_id12.js index c5617764d..ad3a277f1 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id12.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id12.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./trash-2.js";import{j as i}from"./ec2.js";import{Ho as a,Lo as o,Ro as s,Uo as c,Xa as l,ao as u,cs as d,f,ut as p}from"./index.js";import{t as m}from"./page-heading.js";import{t as h}from"./delete-confirmation-dialog.js";import{n as g,t as _}from"./detail-row.js";import{t as v}from"./state-badge.js";var y=d(c()),b=a();function x(){let{id:a}=f.useParams(),c=s(),{data:d}=t(p(a)),x=d.Subnets?.[0],S=i(),[C,w]=(0,y.useState)(!1),T=async()=>{try{await S.mutateAsync(a),c({to:`/ec2/describe-subnets`})}finally{w(!1)}};if(!x?.SubnetId)return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(n,{to:`/ec2/describe-subnets`,children:`Back to Subnets`}),(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`Subnet not found.`})]});let E=u(x.Tags);return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(n,{to:`/ec2/describe-subnets`,children:`Back to Subnets`}),S.error&&(0,b.jsx)(e,{error:S.error,msg:`Failed to delete subnet`}),(0,b.jsxs)(`div`,{className:`space-y-6`,children:[(0,b.jsx)(m,{actions:(0,b.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,b.jsxs)(l,{onClick:()=>w(!0),size:`sm`,variant:`destructive`,children:[(0,b.jsx)(r,{className:`size-4`}),`Delete`]}),(0,b.jsx)(v,{state:x.State})]}),subtitle:`Subnet Details`,title:E?`${x.SubnetId} (${E})`:x.SubnetId}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`Subnet Information`}),(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`Subnet ID`,value:x.SubnetId}),(0,b.jsx)(_,{label:`VPC ID`,value:x.VpcId?(0,b.jsx)(o,{className:`text-primary hover:underline`,params:{id:x.VpcId},to:`/ec2/describe-vpcs/$id`,children:x.VpcId}):`—`}),(0,b.jsx)(_,{label:`State`,value:x.State}),(0,b.jsx)(_,{label:`CIDR Block`,value:x.CidrBlock}),(0,b.jsx)(_,{label:`Availability Zone`,value:x.AvailabilityZone}),(0,b.jsx)(_,{label:`Available IP Count`,value:x.AvailableIpAddressCount?.toString()}),(0,b.jsx)(_,{label:`Map Public IP on Launch`,value:x.MapPublicIpOnLaunch?`Yes`:`No`}),(0,b.jsx)(_,{label:`Default for AZ`,value:x.DefaultForAz?`Yes`:`No`})]})]})]}),(0,b.jsx)(h,{description:`Are you sure you want to delete the subnet "${x.SubnetId}"? This action cannot be undone.`,isPending:S.isPending,onConfirm:T,onOpenChange:w,open:C,title:`Delete Subnet`})]})}export{x as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{A as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{t as c}from"./trash-2.js";import{t as l}from"./button.js";import{M as u}from"./ec2.js";import{Ir as d,Mt as f,f as p}from"./index.js";import{t as m}from"./page-heading.js";import{t as h}from"./delete-confirmation-dialog.js";import{n as g,t as _}from"./detail-row.js";import{t as v}from"./state-badge.js";var y=t(n()),b=e();function x(){let{id:e}=p.useParams(),t=d(),{data:n}=i(f(e)),x=n.Subnets?.[0],S=u(),[C,w]=(0,y.useState)(!1),T=async()=>{try{await S.mutateAsync(e),t({to:`/ec2/describe-subnets`})}finally{w(!1)}};if(!x?.SubnetId)return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(s,{to:`/ec2/describe-subnets`,children:`Back to Subnets`}),(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`Subnet not found.`})]});let E=o(x.Tags);return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(s,{to:`/ec2/describe-subnets`,children:`Back to Subnets`}),S.error&&(0,b.jsx)(r,{error:S.error,msg:`Failed to delete subnet`}),(0,b.jsxs)(`div`,{className:`space-y-6`,children:[(0,b.jsx)(m,{actions:(0,b.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,b.jsxs)(l,{onClick:()=>w(!0),size:`sm`,variant:`destructive`,children:[(0,b.jsx)(c,{className:`size-4`}),`Delete`]}),(0,b.jsx)(v,{state:x.State})]}),subtitle:`Subnet Details`,title:E?`${x.SubnetId} (${E})`:x.SubnetId}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`Subnet Information`}),(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`Subnet ID`,value:x.SubnetId}),(0,b.jsx)(_,{label:`VPC ID`,value:x.VpcId?(0,b.jsx)(a,{className:`text-primary hover:underline`,params:{id:x.VpcId},to:`/ec2/describe-vpcs/$id`,children:x.VpcId}):`—`}),(0,b.jsx)(_,{label:`State`,value:x.State}),(0,b.jsx)(_,{label:`CIDR Block`,value:x.CidrBlock}),(0,b.jsx)(_,{label:`Availability Zone`,value:x.AvailabilityZone}),(0,b.jsx)(_,{label:`Available IP Count`,value:x.AvailableIpAddressCount?.toString()}),(0,b.jsx)(_,{label:`Map Public IP on Launch`,value:x.MapPublicIpOnLaunch?`Yes`:`No`}),(0,b.jsx)(_,{label:`Default for AZ`,value:x.DefaultForAz?`Yes`:`No`})]})]})]}),(0,b.jsx)(h,{description:`Are you sure you want to delete the subnet "${x.SubnetId}"? This action cannot be undone.`,isPending:S.isPending,onConfirm:T,onOpenChange:w,open:C,title:`Delete Subnet`})]})}export{x as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id13.js b/spinifex/services/spinifexui/frontend/dist/assets/_id13.js index 6aa89ad12..a426008ea 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id13.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id13.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useQuery.js";import{t as n}from"./useSuspenseQuery.js";import{t as r}from"./back-link.js";import{t as i}from"./trash-2.js";import{c as a,l as o,m as s,p as c}from"./elbv2.js";import{$a as l,F as u,Fo as d,Ho as f,J as p,L as m,N as h,P as g,Ro as _,Uo as v,Xa as y,Xi as b,ao as x,cs as S,p as C}from"./index.js";import{t as w}from"./page-heading.js";import{a as T,i as E,l as D,n as O,o as k,r as A,s as j,t as M}from"./alert-dialog.js";import{t as N}from"./delete-confirmation-dialog.js";import{n as P,t as F}from"./detail-row.js";import{a as I,i as L,n as R,r as z,s as B,t as V}from"./tabs.js";var H=d(),U=S(v(),1),W=f();function G(e){let t=e.trim();if(t===``)return;let n=Number(t);if(!(!Number.isInteger(n)||n<1||n>65535))return n}function K(e){let t=(0,H.c)(36),{open:n,onOpenChange:r,instances:i,defaultPort:a,isPending:o,onConfirm:s}=e,c;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(c={},t[0]=c):c=t[0];let[l,u]=(0,U.useState)(c),[d,f]=(0,U.useState)(),p,m;t[1]===n?(p=t[2],m=t[3]):(p=()=>{n||(u({}),f(void 0))},m=[n],t[1]=n,t[2]=p,t[3]=m),(0,U.useEffect)(p,m);let h;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(h=e=>{u(t=>{let n=t[e];return n?.checked?{...t,[e]:{...n,checked:!1}}:{...t,[e]:{checked:!0,port:n?.port??``}}})},t[4]=h):h=t[4];let g=h,_;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(_=(e,t)=>{u(n=>({...n,[e]:{checked:n[e]?.checked??!0,port:t}}))},t[5]=_):_=t[5];let v=_,y;t[6]!==s||t[7]!==l?(y=()=>{let e=Object.entries(l).filter(q);if(e.length===0){f(`Select at least one instance.`);return}let t=[];for(let[n,r]of e){if(r.port.trim()===``){t.push({id:n});continue}let e=G(r.port);if(e===void 0){f(`Port for ${n} must be an integer between 1 and 65535.`);return}t.push({id:n,port:e})}f(void 0),s(t)},t[6]=s,t[7]=l,t[8]=y):y=t[8];let S=y,C;t[9]===Symbol.for(`react.memo_cache_sentinel`)?(C=(0,W.jsx)(D,{children:`Register targets`}),t[9]=C):C=t[9];let w;t[10]===a?w=t[11]:(w=(0,W.jsxs)(j,{children:[C,(0,W.jsxs)(T,{children:[`Select instances to register. Leave port blank to use the target group port (`,a,`).`]})]}),t[10]=a,t[11]=w);let N;t[12]!==a||t[13]!==i||t[14]!==l?(N=i.length===0?(0,W.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No instances available in this VPC.`}):(0,W.jsx)(`div`,{className:`max-h-72 space-y-1 overflow-y-auto`,children:i.map(e=>{let t=e.InstanceId??``,n=l[t],r=n?.checked??!1,i=x(e.Tags);return(0,W.jsxs)(`div`,{className:`flex items-center gap-2 rounded px-1 py-1 text-xs hover:bg-accent`,children:[(0,W.jsx)(`input`,{"aria-label":`Register target ${t}`,checked:r,id:`register-target-${t}`,onChange:()=>g(t),type:`checkbox`}),(0,W.jsxs)(`label`,{className:`flex flex-1 items-center gap-2`,htmlFor:`register-target-${t}`,children:[(0,W.jsx)(`span`,{className:`font-mono`,children:t}),i&&(0,W.jsxs)(`span`,{className:`text-muted-foreground`,children:[`(`,i,`)`]}),(0,W.jsx)(`span`,{className:`text-muted-foreground`,children:e.State?.Name??``})]}),r&&(0,W.jsx)(b,{"aria-label":`Port override for ${t}`,className:`w-24`,onChange:e=>v(t,e.target.value),placeholder:String(a),value:n?.port??``})]},t)})}),t[12]=a,t[13]=i,t[14]=l,t[15]=N):N=t[15];let P;t[16]===d?P=t[17]:(P=d&&(0,W.jsx)(`p`,{className:`text-xs text-destructive`,role:`alert`,children:d}),t[16]=d,t[17]=P);let F;t[18]===o?F=t[19]:(F=(0,W.jsx)(A,{disabled:o,children:`Cancel`}),t[18]=o,t[19]=F);let I=o?`Registering…`:`Register`,L;t[20]!==S||t[21]!==o||t[22]!==I?(L=(0,W.jsx)(O,{disabled:o,onClick:S,children:I}),t[20]=S,t[21]=o,t[22]=I,t[23]=L):L=t[23];let R;t[24]!==F||t[25]!==L?(R=(0,W.jsxs)(k,{children:[F,L]}),t[24]=F,t[25]=L,t[26]=R):R=t[26];let z;t[27]!==P||t[28]!==R||t[29]!==w||t[30]!==N?(z=(0,W.jsxs)(E,{className:`sm:max-w-lg`,children:[w,N,P,R]}),t[27]=P,t[28]=R,t[29]=w,t[30]=N,t[31]=z):z=t[31];let B;return t[32]!==r||t[33]!==n||t[34]!==z?(B=(0,W.jsx)(M,{onOpenChange:r,open:n,children:z}),t[32]=r,t[33]=n,t[34]=z,t[35]=B):B=t[35],B}function q(e){let[,t]=e;return t.checked&&t!==void 0}var J=5e3,Y=new Set([`healthy`]),X=new Set([`unhealthy`,`unavailable`]),Z=new Set([`initial`,`draining`]);function Q(e){return e&&Y.has(e)?`bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100`:e&&X.has(e)?`bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100`:e&&Z.has(e)?`bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100`:`bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100`}function $(e){return`${e.Id??``}:${e.Port??``}`}function ee({targetGroupArn:r,vpcId:a,defaultPort:c,isActive:u}){let{data:d}=n(p),{data:f,isLoading:h,error:g}=t({...m(r),refetchInterval:u?J:!1,refetchIntervalInBackground:!1}),_=s(),v=o(),[b,x]=(0,U.useState)(!1),[S,C]=(0,U.useState)(),w=(d.Reservations?.flatMap(e=>e.Instances??[])??[]).filter(e=>e.VpcId===a&&e.State?.Name!==`terminated`),T=f?.TargetHealthDescriptions??[];return(0,W.jsxs)(`div`,{className:`space-y-3`,children:[_.error&&(0,W.jsx)(e,{error:_.error,msg:`Failed to register targets`}),v.error&&(0,W.jsx)(e,{error:v.error,msg:`Failed to deregister target`}),g&&(0,W.jsx)(e,{error:g,msg:`Failed to load target health`}),(0,W.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,W.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:h?`Loading target health…`:`${T.length} target${T.length===1?``:`s`}`}),(0,W.jsx)(y,{onClick:()=>x(!0),size:`sm`,children:`Register targets`})]}),T.length===0?(0,W.jsx)(`p`,{className:`text-muted-foreground`,children:`No targets registered.`}):(0,W.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,W.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,W.jsx)(`thead`,{children:(0,W.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,W.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Target`}),(0,W.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Port`}),(0,W.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`AZ`}),(0,W.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Health`}),(0,W.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Reason`}),(0,W.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Description`}),(0,W.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:(0,W.jsx)(`span`,{className:`sr-only`,children:`Actions`})})]})}),(0,W.jsx)(`tbody`,{children:T.map(e=>{let t=e.Target??{Id:``},n=e.TargetHealth?.State;return(0,W.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,W.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:t.Id??``}),(0,W.jsx)(`td`,{className:`px-4 py-2`,children:t.Port??``}),(0,W.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:t.AvailabilityZone??``}),(0,W.jsx)(`td`,{className:`px-4 py-2`,children:(0,W.jsx)(`span`,{className:l(`rounded-full px-2 py-0.5 text-xs`,Q(n)),children:n??`unknown`})}),(0,W.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:e.TargetHealth?.Reason??``}),(0,W.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:e.TargetHealth?.Description??``}),(0,W.jsx)(`td`,{className:`px-4 py-2 text-right`,children:(0,W.jsx)(y,{"aria-label":`Deregister ${t.Id}`,onClick:()=>C(t),size:`sm`,variant:`ghost`,children:(0,W.jsx)(i,{className:`size-4`})})})]},$(t))})})]})}),(0,W.jsx)(K,{defaultPort:c,instances:w,isPending:_.isPending,onConfirm:async e=>{try{await _.mutateAsync({targetGroupArn:r,targets:e}),x(!1)}catch{}},onOpenChange:x,open:b}),(0,W.jsx)(N,{description:S?`Deregister ${S.Id}${S.Port?`:${S.Port}`:``} from this target group?`:``,isPending:v.isPending,onConfirm:async()=>{if(S?.Id)try{await v.mutateAsync({targetGroupArn:r,targets:[{id:S.Id,port:S.Port}]})}finally{C(void 0)}},onOpenChange:e=>!e&&C(void 0),open:S!==void 0,title:`Deregister target`})]})}function te({arn:t}){let o=_(),{data:s}=n(u(t)),{data:l}=n(g(t)),{data:d}=n(h([t])),f=a(),p=c(),[m,v]=(0,U.useState)(!1),[b,x]=(0,U.useState)(`overview`),S=s.TargetGroups?.[0];if(!S?.TargetGroupArn)return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(r,{to:`/ec2/describe-target-groups`,children:`Back to target groups`}),(0,W.jsx)(`p`,{className:`text-muted-foreground`,children:`Target group not found.`})]});let C=async()=>{try{await f.mutateAsync(t),o({to:`/ec2/describe-target-groups`})}finally{v(!1)}},T=l.Attributes??[],E=d?.TagDescriptions?.find(e=>e.ResourceArn===t)?.Tags??[];return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(r,{to:`/ec2/describe-target-groups`,children:`Back to target groups`}),f.error&&(0,W.jsx)(e,{error:f.error,msg:`Failed to delete target group`}),(0,W.jsxs)(`div`,{className:`space-y-6`,children:[(0,W.jsx)(w,{actions:(0,W.jsxs)(y,{onClick:()=>v(!0),size:`sm`,variant:`destructive`,children:[(0,W.jsx)(i,{className:`size-4`}),`Delete`]}),subtitle:`Target Group Details`,title:S.TargetGroupName??S.TargetGroupArn}),(0,W.jsxs)(V,{onValueChange:x,value:b,children:[(0,W.jsxs)(R,{children:[(0,W.jsx)(L,{value:`overview`,children:`Overview`}),(0,W.jsx)(L,{value:`targets`,children:`Targets`}),(0,W.jsx)(L,{value:`health-checks`,children:`Health checks`}),(0,W.jsx)(L,{value:`attributes`,children:`Attributes`}),(0,W.jsx)(L,{value:`tags`,children:`Tags`})]}),(0,W.jsx)(z,{value:`overview`,children:(0,W.jsxs)(P,{children:[(0,W.jsx)(P.Header,{children:`Target group`}),(0,W.jsxs)(P.Content,{children:[(0,W.jsx)(F,{label:`ARN`,value:S.TargetGroupArn}),(0,W.jsx)(F,{label:`Protocol`,value:S.Protocol}),(0,W.jsx)(F,{label:`Port`,value:S.Port?.toString()}),(0,W.jsx)(F,{label:`VPC`,value:S.VpcId}),(0,W.jsx)(F,{label:`Target type`,value:S.TargetType}),(0,W.jsx)(F,{label:`Protocol version`,value:S.ProtocolVersion}),(0,W.jsx)(F,{label:`IP address type`,value:S.IpAddressType})]})]})}),(0,W.jsx)(z,{value:`targets`,children:(0,W.jsx)(ee,{defaultPort:S.Port??80,isActive:b===`targets`,targetGroupArn:S.TargetGroupArn,vpcId:S.VpcId})}),(0,W.jsxs)(z,{value:`health-checks`,children:[(0,W.jsx)(`p`,{className:`mb-3 text-sm text-muted-foreground`,children:`Health-check settings are set at creation time. Editing lands in a future slice (mulga-948).`}),(0,W.jsxs)(P,{children:[(0,W.jsx)(P.Header,{children:`Health check`}),(0,W.jsxs)(P.Content,{children:[(0,W.jsx)(F,{label:`Enabled`,value:S.HealthCheckEnabled===void 0?void 0:String(S.HealthCheckEnabled)}),(0,W.jsx)(F,{label:`Protocol`,value:S.HealthCheckProtocol}),(0,W.jsx)(F,{label:`Path`,value:S.HealthCheckPath}),(0,W.jsx)(F,{label:`Port`,value:S.HealthCheckPort}),(0,W.jsx)(F,{label:`Interval (s)`,value:S.HealthCheckIntervalSeconds?.toString()}),(0,W.jsx)(F,{label:`Timeout (s)`,value:S.HealthCheckTimeoutSeconds?.toString()}),(0,W.jsx)(F,{label:`Healthy threshold`,value:S.HealthyThresholdCount?.toString()}),(0,W.jsx)(F,{label:`Unhealthy threshold`,value:S.UnhealthyThresholdCount?.toString()}),(0,W.jsx)(F,{label:`Matcher (HTTP codes)`,value:S.Matcher?.HttpCode??S.Matcher?.GrpcCode})]})]})]}),(0,W.jsx)(z,{value:`attributes`,children:(0,W.jsx)(I,{attributes:T,error:p.error,isPending:p.isPending,isSuccess:p.isSuccess,onSubmit:e=>{p.mutate({targetGroupArn:t,attributes:e})},specs:B})}),(0,W.jsx)(z,{value:`tags`,children:E.length>0?(0,W.jsxs)(P,{children:[(0,W.jsx)(P.Header,{children:`Tags`}),(0,W.jsx)(P.Content,{children:E.map(e=>(0,W.jsx)(F,{label:e.Key??``,value:e.Value},e.Key??``))})]}):(0,W.jsx)(`p`,{className:`text-muted-foreground`,children:`No tags.`})})]})]}),(0,W.jsx)(N,{description:`Are you sure you want to delete target group "${S.TargetGroupName??S.TargetGroupArn}"? This action cannot be undone.`,isPending:f.isPending,onConfirm:C,onOpenChange:v,open:m,title:`Delete Target Group`})]})}function ne(){let e=(0,H.c)(2),{id:t}=C.useParams(),n=decodeURIComponent(t),r;return e[0]===n?r=e[1]:(r=(0,W.jsx)(te,{arn:n}),e[0]=n,e[1]=r),r}export{ne as component}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{t as a}from"./useQuery.js";import{t as o}from"./useSuspenseQuery.js";import{A as s,w as c}from"./SignatureV4.js";import{t as l}from"./back-link.js";import{t as u}from"./trash-2.js";import{t as d}from"./button.js";import{_ as f,c as p,l as m,m as h,p as g}from"./elbv2.js";import{B as _,H as v,Ir as y,Rn as b,V as x,W as S,gt as C,p as w}from"./index.js";import{t as T}from"./page-heading.js";import{a as E,i as D,l as O,n as k,o as A,r as j,s as M,t as N}from"./alert-dialog.js";import{t as P}from"./delete-confirmation-dialog.js";import{n as F,t as I}from"./detail-row.js";import{i as L,n as R,t as z}from"./tags-editor.js";import{i as B,n as V,r as H,t as U}from"./tabs.js";var W=r(),G=t(n(),1),K=e();function q(e){let t=e.trim();if(t===``)return;let n=Number(t);if(!(!Number.isInteger(n)||n<1||n>65535))return n}function J(e){let t=(0,W.c)(36),{open:n,onOpenChange:r,instances:i,defaultPort:a,isPending:o,onConfirm:c}=e,l;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(l={},t[0]=l):l=t[0];let[u,d]=(0,G.useState)(l),[f,p]=(0,G.useState)(),m,h;t[1]===n?(m=t[2],h=t[3]):(m=()=>{n||(d({}),p(void 0))},h=[n],t[1]=n,t[2]=m,t[3]=h),(0,G.useEffect)(m,h);let g;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(g=e=>{d(t=>{let n=t[e];return n?.checked?{...t,[e]:{...n,checked:!1}}:{...t,[e]:{checked:!0,port:n?.port??``}}})},t[4]=g):g=t[4];let _=g,v;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(v=(e,t)=>{d(n=>({...n,[e]:{checked:n[e]?.checked??!0,port:t}}))},t[5]=v):v=t[5];let y=v,x;t[6]!==c||t[7]!==u?(x=()=>{let e=Object.entries(u).filter(Y);if(e.length===0){p(`Select at least one instance.`);return}let t=[];for(let[n,r]of e){if(r.port.trim()===``){t.push({id:n});continue}let e=q(r.port);if(e===void 0){p(`Port for ${n} must be an integer between 1 and 65535.`);return}t.push({id:n,port:e})}p(void 0),c(t)},t[6]=c,t[7]=u,t[8]=x):x=t[8];let S=x,C;t[9]===Symbol.for(`react.memo_cache_sentinel`)?(C=(0,K.jsx)(O,{children:`Register targets`}),t[9]=C):C=t[9];let w;t[10]===a?w=t[11]:(w=(0,K.jsxs)(M,{children:[C,(0,K.jsxs)(E,{children:[`Select instances to register. Leave port blank to use the target group port (`,a,`).`]})]}),t[10]=a,t[11]=w);let T;t[12]!==a||t[13]!==i||t[14]!==u?(T=i.length===0?(0,K.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No instances available in this VPC.`}):(0,K.jsx)(`div`,{className:`max-h-72 space-y-1 overflow-y-auto`,children:i.map(e=>{let t=e.InstanceId??``,n=u[t],r=n?.checked??!1,i=s(e.Tags);return(0,K.jsxs)(`div`,{className:`flex items-center gap-2 rounded px-1 py-1 text-xs hover:bg-accent`,children:[(0,K.jsx)(`input`,{"aria-label":`Register target ${t}`,checked:r,id:`register-target-${t}`,onChange:()=>_(t),type:`checkbox`}),(0,K.jsxs)(`label`,{className:`flex flex-1 items-center gap-2`,htmlFor:`register-target-${t}`,children:[(0,K.jsx)(`span`,{className:`font-mono`,children:t}),i&&(0,K.jsxs)(`span`,{className:`text-muted-foreground`,children:[`(`,i,`)`]}),(0,K.jsx)(`span`,{className:`text-muted-foreground`,children:e.State?.Name??``})]}),r&&(0,K.jsx)(b,{"aria-label":`Port override for ${t}`,className:`w-24`,onChange:e=>y(t,e.target.value),placeholder:String(a),value:n?.port??``})]},t)})}),t[12]=a,t[13]=i,t[14]=u,t[15]=T):T=t[15];let P;t[16]===f?P=t[17]:(P=f&&(0,K.jsx)(`p`,{className:`text-xs text-destructive`,role:`alert`,children:f}),t[16]=f,t[17]=P);let F;t[18]===o?F=t[19]:(F=(0,K.jsx)(j,{disabled:o,children:`Cancel`}),t[18]=o,t[19]=F);let I=o?`Registering…`:`Register`,L;t[20]!==S||t[21]!==o||t[22]!==I?(L=(0,K.jsx)(k,{disabled:o,onClick:S,children:I}),t[20]=S,t[21]=o,t[22]=I,t[23]=L):L=t[23];let R;t[24]!==F||t[25]!==L?(R=(0,K.jsxs)(A,{children:[F,L]}),t[24]=F,t[25]=L,t[26]=R):R=t[26];let z;t[27]!==P||t[28]!==R||t[29]!==w||t[30]!==T?(z=(0,K.jsxs)(D,{className:`sm:max-w-lg`,children:[w,T,P,R]}),t[27]=P,t[28]=R,t[29]=w,t[30]=T,t[31]=z):z=t[31];let B;return t[32]!==r||t[33]!==n||t[34]!==z?(B=(0,K.jsx)(N,{onOpenChange:r,open:n,children:z}),t[32]=r,t[33]=n,t[34]=z,t[35]=B):B=t[35],B}function Y(e){let[,t]=e;return t.checked&&t!==void 0}var X=5e3,Z=new Set([`healthy`]),Q=new Set([`unhealthy`,`unavailable`]),$=new Set([`initial`,`draining`]);function ee(e){return e&&Z.has(e)?`bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100`:e&&Q.has(e)?`bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100`:e&&$.has(e)?`bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100`:`bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100`}function te(e){return`${e.Id??``}:${e.Port??``}`}function ne({targetGroupArn:e,vpcId:t,defaultPort:n,isActive:r}){let{data:s}=o(C),{data:l,isLoading:f,error:p}=a({...S(e),refetchInterval:r?X:!1,refetchIntervalInBackground:!1}),g=h(),_=m(),[v,y]=(0,G.useState)(!1),[b,x]=(0,G.useState)(),w=(s.Reservations?.flatMap(e=>e.Instances??[])??[]).filter(e=>e.VpcId===t&&e.State?.Name!==`terminated`),T=l?.TargetHealthDescriptions??[];return(0,K.jsxs)(`div`,{className:`space-y-3`,children:[g.error&&(0,K.jsx)(i,{error:g.error,msg:`Failed to register targets`}),_.error&&(0,K.jsx)(i,{error:_.error,msg:`Failed to deregister target`}),p&&(0,K.jsx)(i,{error:p,msg:`Failed to load target health`}),(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:f?`Loading target health…`:`${T.length} target${T.length===1?``:`s`}`}),(0,K.jsx)(d,{onClick:()=>y(!0),size:`sm`,children:`Register targets`})]}),T.length===0?(0,K.jsx)(`p`,{className:`text-muted-foreground`,children:`No targets registered.`}):(0,K.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,K.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,K.jsx)(`thead`,{children:(0,K.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,K.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Target`}),(0,K.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Port`}),(0,K.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`AZ`}),(0,K.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Health`}),(0,K.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Reason`}),(0,K.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Description`}),(0,K.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:(0,K.jsx)(`span`,{className:`sr-only`,children:`Actions`})})]})}),(0,K.jsx)(`tbody`,{children:T.map(e=>{let t=e.Target??{Id:``},n=e.TargetHealth?.State;return(0,K.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,K.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:t.Id??``}),(0,K.jsx)(`td`,{className:`px-4 py-2`,children:t.Port??``}),(0,K.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:t.AvailabilityZone??``}),(0,K.jsx)(`td`,{className:`px-4 py-2`,children:(0,K.jsx)(`span`,{className:c(`rounded-full px-2 py-0.5 text-xs`,ee(n)),children:n??`unknown`})}),(0,K.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:e.TargetHealth?.Reason??``}),(0,K.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:e.TargetHealth?.Description??``}),(0,K.jsx)(`td`,{className:`px-4 py-2 text-right`,children:(0,K.jsx)(d,{"aria-label":`Deregister ${t.Id}`,onClick:()=>x(t),size:`sm`,variant:`ghost`,children:(0,K.jsx)(u,{className:`size-4`})})})]},te(t))})})]})}),(0,K.jsx)(J,{defaultPort:n,instances:w,isPending:g.isPending,onConfirm:async t=>{try{await g.mutateAsync({targetGroupArn:e,targets:t}),y(!1)}catch{}},onOpenChange:y,open:v}),(0,K.jsx)(P,{description:b?`Deregister ${b.Id}${b.Port?`:${b.Port}`:``} from this target group?`:``,isPending:_.isPending,onConfirm:async()=>{if(b?.Id)try{await _.mutateAsync({targetGroupArn:e,targets:[{id:b.Id,port:b.Port}]})}finally{x(void 0)}},onOpenChange:e=>!e&&x(void 0),open:b!==void 0,title:`Deregister target`})]})}function re({arn:e}){let t=y(),{data:n}=o(v(e)),{data:r}=o(x(e)),{data:a}=o(_([e])),s=p(),c=g(),m=f(),[h,b]=(0,G.useState)(!1),[S,C]=(0,G.useState)(`overview`),w=n.TargetGroups?.[0];if(!w?.TargetGroupArn)return(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(l,{to:`/ec2/describe-target-groups`,children:`Back to target groups`}),(0,K.jsx)(`p`,{className:`text-muted-foreground`,children:`Target group not found.`})]});let E=async()=>{try{await s.mutateAsync(e),t({to:`/ec2/describe-target-groups`})}finally{b(!1)}},D=r.Attributes??[],O=a?.TagDescriptions?.find(t=>t.ResourceArn===e)?.Tags??[];return(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(l,{to:`/ec2/describe-target-groups`,children:`Back to target groups`}),s.error&&(0,K.jsx)(i,{error:s.error,msg:`Failed to delete target group`}),(0,K.jsxs)(`div`,{className:`space-y-6`,children:[(0,K.jsx)(T,{actions:(0,K.jsxs)(d,{onClick:()=>b(!0),size:`sm`,variant:`destructive`,children:[(0,K.jsx)(u,{className:`size-4`}),`Delete`]}),subtitle:`Target Group Details`,title:w.TargetGroupName??w.TargetGroupArn}),(0,K.jsxs)(U,{onValueChange:C,value:S,children:[(0,K.jsxs)(V,{children:[(0,K.jsx)(B,{value:`overview`,children:`Overview`}),(0,K.jsx)(B,{value:`targets`,children:`Targets`}),(0,K.jsx)(B,{value:`health-checks`,children:`Health checks`}),(0,K.jsx)(B,{value:`attributes`,children:`Attributes`}),(0,K.jsx)(B,{value:`tags`,children:`Tags`})]}),(0,K.jsx)(H,{value:`overview`,children:(0,K.jsxs)(F,{children:[(0,K.jsx)(F.Header,{children:`Target group`}),(0,K.jsxs)(F.Content,{children:[(0,K.jsx)(I,{label:`ARN`,value:w.TargetGroupArn}),(0,K.jsx)(I,{label:`Protocol`,value:w.Protocol}),(0,K.jsx)(I,{label:`Port`,value:w.Port?.toString()}),(0,K.jsx)(I,{label:`VPC`,value:w.VpcId}),(0,K.jsx)(I,{label:`Target type`,value:w.TargetType}),(0,K.jsx)(I,{label:`Protocol version`,value:w.ProtocolVersion}),(0,K.jsx)(I,{label:`IP address type`,value:w.IpAddressType})]})]})}),(0,K.jsx)(H,{value:`targets`,children:(0,K.jsx)(ne,{defaultPort:w.Port??80,isActive:S===`targets`,targetGroupArn:w.TargetGroupArn,vpcId:w.VpcId})}),(0,K.jsxs)(H,{value:`health-checks`,children:[(0,K.jsx)(`p`,{className:`mb-3 text-sm text-muted-foreground`,children:`Health-check settings are set at creation time. Editing lands in a future slice (mulga-948).`}),(0,K.jsxs)(F,{children:[(0,K.jsx)(F.Header,{children:`Health check`}),(0,K.jsxs)(F.Content,{children:[(0,K.jsx)(I,{label:`Enabled`,value:w.HealthCheckEnabled===void 0?void 0:String(w.HealthCheckEnabled)}),(0,K.jsx)(I,{label:`Protocol`,value:w.HealthCheckProtocol}),(0,K.jsx)(I,{label:`Path`,value:w.HealthCheckPath}),(0,K.jsx)(I,{label:`Port`,value:w.HealthCheckPort}),(0,K.jsx)(I,{label:`Interval (s)`,value:w.HealthCheckIntervalSeconds?.toString()}),(0,K.jsx)(I,{label:`Timeout (s)`,value:w.HealthCheckTimeoutSeconds?.toString()}),(0,K.jsx)(I,{label:`Healthy threshold`,value:w.HealthyThresholdCount?.toString()}),(0,K.jsx)(I,{label:`Unhealthy threshold`,value:w.UnhealthyThresholdCount?.toString()}),(0,K.jsx)(I,{label:`Matcher (HTTP codes)`,value:w.Matcher?.HttpCode??w.Matcher?.GrpcCode})]})]})]}),(0,K.jsx)(H,{value:`attributes`,children:(0,K.jsx)(R,{attributes:D,error:c.error,isPending:c.isPending,isSuccess:c.isSuccess,onSubmit:t=>{c.mutate({targetGroupArn:e,attributes:t})},specs:L})}),(0,K.jsx)(H,{value:`tags`,children:(0,K.jsx)(z,{error:m.error,isPending:m.isPending,isSuccess:m.isSuccess,onSubmit:t=>m.mutate({resourceArn:e,tags:t,initialKeys:O.map(e=>e.Key??``).filter(e=>e.length>0)}),tags:O})})]})]}),(0,K.jsx)(P,{description:`Are you sure you want to delete target group "${w.TargetGroupName??w.TargetGroupArn}"? This action cannot be undone.`,isPending:s.isPending,onConfirm:E,onOpenChange:b,open:h,title:`Delete Target Group`})]})}function ie(){let e=(0,W.c)(2),{id:t}=w.useParams(),n=decodeURIComponent(t),r;return e[0]===n?r=e[1]:(r=(0,K.jsx)(re,{arn:n}),e[0]=n,e[1]=r),r}export{ie as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id14.js b/spinifex/services/spinifexui/frontend/dist/assets/_id14.js index 4892f76e3..2cabd19c2 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id14.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id14.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as ee,i as r,n as i,s as a,t as te}from"./select.js";import{t as o}from"./trash-2.js";import{F as s,M as c,a as l}from"./ec2.js";import{Co as u,Ho as d,J as f,Lo as p,Ro as m,Uo as h,Xa as g,Xi as _,Za as v,ao as ne,cs as y,ft as re,m as b,no as x,wo as S}from"./index.js";import{t as ie}from"./page-heading.js";import{a as C,t as w}from"./field.js";import{a as T,i as E,l as D,n as O,o as k,r as A,s as j,t as M}from"./alert-dialog.js";import{t as ae}from"./delete-confirmation-dialog.js";import{n as N,t as P}from"./detail-row.js";import{t as F}from"./state-badge.js";var I=S(`link-2-off`,[[`path`,{d:`M9 17H7A5 5 0 0 1 7 7`,key:`10o201`}],[`path`,{d:`M15 7h2a5 5 0 0 1 4 8`,key:`1d3206`}],[`line`,{x1:`8`,x2:`12`,y1:`12`,y2:`12`,key:`rvw6j4`}],[`line`,{x1:`2`,x2:`22`,y1:`2`,y2:`22`,key:`a6p6uj`}]]),L=S(`link-2`,[[`path`,{d:`M9 17H7A5 5 0 0 1 7 7h2`,key:`8i5ue5`}],[`path`,{d:`M15 7h2a5 5 0 1 1 0 10h-2`,key:`1b9ql8`}],[`line`,{x1:`8`,x2:`16`,y1:`12`,y2:`12`,key:`1jonct`}]]),R=y(h()),z=d();function B(){let{id:d}=b.useParams(),h=m(),{data:y}=t(re(d)),{data:S}=t(f),B=y.Volumes?.[0],V=c(),H=l(),U=s(),[W,G]=(0,R.useState)(null),[K,q]=(0,R.useState)(``),[J,Y]=(0,R.useState)(``),[X,Z]=(0,R.useState)(!1),oe=S.Reservations?.flatMap(e=>e.Instances?.filter(e=>e.State?.Name===`running`)??[])??[],Q=B?.State===`available`&&(!B.Attachments||B.Attachments.length===0),$=()=>G(null);return B?.VolumeId?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(n,{to:`/ec2/describe-volumes`,children:`Back to volumes`}),V.error&&(0,z.jsx)(e,{error:V.error,msg:`Failed to delete volume`}),H.error&&(0,z.jsx)(e,{error:H.error,msg:`Failed to attach volume`}),U.error&&(0,z.jsx)(e,{error:U.error,msg:`Failed to detach volume`}),(0,z.jsxs)(`div`,{className:`space-y-6`,children:[(0,z.jsx)(ie,{actions:(0,z.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,z.jsxs)(g,{disabled:!Q,onClick:()=>G(`delete`),size:`sm`,variant:`destructive`,children:[(0,z.jsx)(o,{className:`size-4`}),`Delete`]}),Q?(0,z.jsx)(p,{className:v({variant:`outline`}),params:{id:B.VolumeId},to:`/ec2/modify-volume/$id`,children:`Resize Volume`}):(0,z.jsx)(g,{disabled:!0,variant:`outline`,children:`Resize Volume`}),Q?(0,z.jsxs)(g,{onClick:()=>G(`attach`),size:`sm`,variant:`outline`,children:[(0,z.jsx)(L,{className:`size-4`}),`Attach`]}):null,B.Attachments&&B.Attachments.length>0?(0,z.jsxs)(g,{onClick:()=>G(`detach`),size:`sm`,variant:`outline`,children:[(0,z.jsx)(I,{className:`size-4`}),`Detach`]}):null,(0,z.jsxs)(p,{className:v({variant:`outline`,size:`sm`}),search:{volumeId:B.VolumeId},to:`/ec2/create-snapshot`,children:[(0,z.jsx)(u,{className:`size-4`}),`Create Snapshot`]}),(0,z.jsx)(F,{state:B.State})]}),subtitle:`Volume Details`,title:B.VolumeId}),(0,z.jsxs)(N,{children:[(0,z.jsx)(N.Header,{children:`Volume Information`}),(0,z.jsxs)(N.Content,{children:[(0,z.jsx)(P,{label:`Volume ID`,value:B.VolumeId}),(0,z.jsx)(P,{label:`Size`,value:`${B.Size} GiB`}),(0,z.jsx)(P,{label:`Volume Type`,value:B.VolumeType}),(0,z.jsx)(P,{label:`State`,value:B.State}),(0,z.jsx)(P,{label:`Availability Zone`,value:B.AvailabilityZone}),(0,z.jsx)(P,{label:`Create Time`,value:x(B.CreateTime)}),(0,z.jsx)(P,{label:`Encrypted`,value:B.Encrypted?`Yes`:`No`}),(0,z.jsx)(P,{label:`KMS Key ID`,value:B.KmsKeyId})]})]}),B.Attachments&&B.Attachments.length>0&&(0,z.jsxs)(N,{children:[(0,z.jsx)(N.Header,{children:`Attachments`}),B.Attachments.map(e=>(0,z.jsxs)(N.Content,{children:[(0,z.jsx)(P,{label:`Instance ID`,value:e.InstanceId?(0,z.jsx)(p,{className:`text-primary hover:underline`,params:{id:e.InstanceId},to:`/ec2/describe-instances/$id`,children:e.InstanceId}):void 0}),(0,z.jsx)(P,{label:`Device`,value:e.Device}),(0,z.jsx)(P,{label:`Status`,value:e.State}),(0,z.jsx)(P,{label:`Attach Time`,value:x(e.AttachTime)}),(0,z.jsx)(P,{label:`Delete on Termination`,value:e.DeleteOnTermination?`Yes`:`No`})]},e.InstanceId))]})]}),(0,z.jsx)(ae,{description:`Are you sure you want to delete the volume "${B.VolumeId}"? This action cannot be undone.`,isPending:V.isPending,onConfirm:async()=>{try{await V.mutateAsync(d),h({to:`/ec2/describe-volumes`})}finally{$()}},onOpenChange:e=>{e||$()},open:W===`delete`,title:`Delete Volume`}),(0,z.jsx)(M,{onOpenChange:e=>{e||($(),q(``),Y(``))},open:W===`attach`,children:(0,z.jsxs)(E,{children:[(0,z.jsxs)(j,{children:[(0,z.jsx)(D,{children:`Attach Volume`}),(0,z.jsx)(T,{children:`Attach this volume to a running instance.`})]}),(0,z.jsxs)(w,{children:[(0,z.jsx)(C,{children:(0,z.jsx)(`label`,{htmlFor:`attachInstance`,children:`Instance`})}),(0,z.jsxs)(te,{onValueChange:e=>q(e??``),value:K,children:[(0,z.jsx)(a,{className:`w-full`,id:`attachInstance`,children:(0,z.jsx)(ee,{placeholder:`Select an instance`})}),(0,z.jsx)(i,{children:oe.map(e=>{let t=ne(e.Tags);return(0,z.jsxs)(r,{value:e.InstanceId??``,children:[e.InstanceId,t?` (${t})`:``]},e.InstanceId)})})]})]}),(0,z.jsxs)(w,{children:[(0,z.jsx)(C,{children:(0,z.jsx)(`label`,{htmlFor:`attachDevice`,children:`Device (optional)`})}),(0,z.jsx)(_,{id:`attachDevice`,onChange:e=>Y(e.target.value),placeholder:`/dev/sdf`,value:J})]}),(0,z.jsxs)(k,{children:[(0,z.jsx)(A,{children:`Cancel`}),(0,z.jsx)(O,{disabled:H.isPending||!K,onClick:async()=>{try{await H.mutateAsync({volumeId:d,instanceId:K,device:J||void 0}),$(),q(``),Y(``)}catch{}},children:H.isPending?`Attaching…`:`Attach`})]})]})}),(0,z.jsx)(M,{onOpenChange:e=>{e||($(),Z(!1))},open:W===`detach`,children:(0,z.jsxs)(E,{children:[(0,z.jsxs)(j,{children:[(0,z.jsx)(D,{children:`Detach Volume`}),(0,z.jsxs)(T,{children:[`Are you sure you want to detach the volume "`,B.VolumeId,`" from instance "`,B.Attachments?.[0]?.InstanceId,`"?`]})]}),(0,z.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,z.jsx)(`input`,{"aria-label":`Force detach`,checked:X,onChange:e=>Z(e.target.checked),type:`checkbox`}),`Force detach`]}),(0,z.jsxs)(k,{children:[(0,z.jsx)(A,{children:`Cancel`}),(0,z.jsx)(O,{disabled:U.isPending,onClick:async()=>{let e=B?.Attachments?.[0]?.InstanceId;try{await U.mutateAsync({volumeId:d,instanceId:e??void 0,force:X||void 0}),$(),Z(!1)}catch{}},children:U.isPending?`Detaching…`:`Detach`})]})]})})]}):(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(n,{to:`/ec2/describe-volumes`,children:`Back to volumes`}),(0,z.jsx)(`p`,{className:`text-muted-foreground`,children:`Volume not found.`})]})}export{B as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{A as o,D as s}from"./SignatureV4.js";import{t as c}from"./back-link.js";import{c as l,i as ee,n as u,s as d,t as f}from"./select.js";import{t as p}from"./trash-2.js";import{n as m,t as h}from"./button.js";import{I as g,N as _,o as v}from"./ec2.js";import{Ir as y,Mr as te,Nr as b,Pt as ne,Rn as x,gt as S,m as re}from"./index.js";import{t as ie}from"./page-heading.js";import{a as C,t as w}from"./field.js";import{a as T,i as E,l as D,n as O,o as k,r as A,s as j,t as M}from"./alert-dialog.js";import{t as ae}from"./delete-confirmation-dialog.js";import{n as N,t as P}from"./detail-row.js";import{t as F}from"./state-badge.js";var I=b(`link-2-off`,[[`path`,{d:`M9 17H7A5 5 0 0 1 7 7`,key:`10o201`}],[`path`,{d:`M15 7h2a5 5 0 0 1 4 8`,key:`1d3206`}],[`line`,{x1:`8`,x2:`12`,y1:`12`,y2:`12`,key:`rvw6j4`}],[`line`,{x1:`2`,x2:`22`,y1:`2`,y2:`22`,key:`a6p6uj`}]]),L=b(`link-2`,[[`path`,{d:`M9 17H7A5 5 0 0 1 7 7h2`,key:`8i5ue5`}],[`path`,{d:`M15 7h2a5 5 0 1 1 0 10h-2`,key:`1b9ql8`}],[`line`,{x1:`8`,x2:`16`,y1:`12`,y2:`12`,key:`1jonct`}]]),R=t(n()),z=e();function B(){let{id:e}=re.useParams(),t=y(),{data:n}=i(ne(e)),{data:b}=i(S),B=n.Volumes?.[0],V=_(),H=v(),U=g(),[W,G]=(0,R.useState)(null),[K,q]=(0,R.useState)(``),[J,Y]=(0,R.useState)(``),[X,Z]=(0,R.useState)(!1),oe=b.Reservations?.flatMap(e=>e.Instances?.filter(e=>e.State?.Name===`running`)??[])??[],Q=B?.State===`available`&&(!B.Attachments||B.Attachments.length===0),$=()=>G(null);return B?.VolumeId?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(c,{to:`/ec2/describe-volumes`,children:`Back to volumes`}),V.error&&(0,z.jsx)(r,{error:V.error,msg:`Failed to delete volume`}),H.error&&(0,z.jsx)(r,{error:H.error,msg:`Failed to attach volume`}),U.error&&(0,z.jsx)(r,{error:U.error,msg:`Failed to detach volume`}),(0,z.jsxs)(`div`,{className:`space-y-6`,children:[(0,z.jsx)(ie,{actions:(0,z.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,z.jsxs)(h,{disabled:!Q,onClick:()=>G(`delete`),size:`sm`,variant:`destructive`,children:[(0,z.jsx)(p,{className:`size-4`}),`Delete`]}),Q?(0,z.jsx)(a,{className:m({variant:`outline`}),params:{id:B.VolumeId},to:`/ec2/modify-volume/$id`,children:`Resize Volume`}):(0,z.jsx)(h,{disabled:!0,variant:`outline`,children:`Resize Volume`}),Q?(0,z.jsxs)(h,{onClick:()=>G(`attach`),size:`sm`,variant:`outline`,children:[(0,z.jsx)(L,{className:`size-4`}),`Attach`]}):null,B.Attachments&&B.Attachments.length>0?(0,z.jsxs)(h,{onClick:()=>G(`detach`),size:`sm`,variant:`outline`,children:[(0,z.jsx)(I,{className:`size-4`}),`Detach`]}):null,(0,z.jsxs)(a,{className:m({variant:`outline`,size:`sm`}),search:{volumeId:B.VolumeId},to:`/ec2/create-snapshot`,children:[(0,z.jsx)(te,{className:`size-4`}),`Create Snapshot`]}),(0,z.jsx)(F,{state:B.State})]}),subtitle:`Volume Details`,title:B.VolumeId}),(0,z.jsxs)(N,{children:[(0,z.jsx)(N.Header,{children:`Volume Information`}),(0,z.jsxs)(N.Content,{children:[(0,z.jsx)(P,{label:`Volume ID`,value:B.VolumeId}),(0,z.jsx)(P,{label:`Size`,value:`${B.Size} GiB`}),(0,z.jsx)(P,{label:`Volume Type`,value:B.VolumeType}),(0,z.jsx)(P,{label:`State`,value:B.State}),(0,z.jsx)(P,{label:`Availability Zone`,value:B.AvailabilityZone}),(0,z.jsx)(P,{label:`Create Time`,value:s(B.CreateTime)}),(0,z.jsx)(P,{label:`Encrypted`,value:B.Encrypted?`Yes`:`No`}),(0,z.jsx)(P,{label:`KMS Key ID`,value:B.KmsKeyId})]})]}),B.Attachments&&B.Attachments.length>0&&(0,z.jsxs)(N,{children:[(0,z.jsx)(N.Header,{children:`Attachments`}),B.Attachments.map(e=>(0,z.jsxs)(N.Content,{children:[(0,z.jsx)(P,{label:`Instance ID`,value:e.InstanceId?(0,z.jsx)(a,{className:`text-primary hover:underline`,params:{id:e.InstanceId},to:`/ec2/describe-instances/$id`,children:e.InstanceId}):void 0}),(0,z.jsx)(P,{label:`Device`,value:e.Device}),(0,z.jsx)(P,{label:`Status`,value:e.State}),(0,z.jsx)(P,{label:`Attach Time`,value:s(e.AttachTime)}),(0,z.jsx)(P,{label:`Delete on Termination`,value:e.DeleteOnTermination?`Yes`:`No`})]},e.InstanceId))]})]}),(0,z.jsx)(ae,{description:`Are you sure you want to delete the volume "${B.VolumeId}"? This action cannot be undone.`,isPending:V.isPending,onConfirm:async()=>{try{await V.mutateAsync(e),t({to:`/ec2/describe-volumes`})}finally{$()}},onOpenChange:e=>{e||$()},open:W===`delete`,title:`Delete Volume`}),(0,z.jsx)(M,{onOpenChange:e=>{e||($(),q(``),Y(``))},open:W===`attach`,children:(0,z.jsxs)(E,{children:[(0,z.jsxs)(j,{children:[(0,z.jsx)(D,{children:`Attach Volume`}),(0,z.jsx)(T,{children:`Attach this volume to a running instance.`})]}),(0,z.jsxs)(w,{children:[(0,z.jsx)(C,{children:(0,z.jsx)(`label`,{htmlFor:`attachInstance`,children:`Instance`})}),(0,z.jsxs)(f,{onValueChange:e=>q(e??``),value:K,children:[(0,z.jsx)(d,{className:`w-full`,id:`attachInstance`,children:(0,z.jsx)(l,{placeholder:`Select an instance`})}),(0,z.jsx)(u,{children:oe.map(e=>{let t=o(e.Tags);return(0,z.jsxs)(ee,{value:e.InstanceId??``,children:[e.InstanceId,t?` (${t})`:``]},e.InstanceId)})})]})]}),(0,z.jsxs)(w,{children:[(0,z.jsx)(C,{children:(0,z.jsx)(`label`,{htmlFor:`attachDevice`,children:`Device (optional)`})}),(0,z.jsx)(x,{id:`attachDevice`,onChange:e=>Y(e.target.value),placeholder:`/dev/sdf`,value:J})]}),(0,z.jsxs)(k,{children:[(0,z.jsx)(A,{children:`Cancel`}),(0,z.jsx)(O,{disabled:H.isPending||!K,onClick:async()=>{try{await H.mutateAsync({volumeId:e,instanceId:K,device:J||void 0}),$(),q(``),Y(``)}catch{}},children:H.isPending?`Attaching…`:`Attach`})]})]})}),(0,z.jsx)(M,{onOpenChange:e=>{e||($(),Z(!1))},open:W===`detach`,children:(0,z.jsxs)(E,{children:[(0,z.jsxs)(j,{children:[(0,z.jsx)(D,{children:`Detach Volume`}),(0,z.jsxs)(T,{children:[`Are you sure you want to detach the volume "`,B.VolumeId,`" from instance "`,B.Attachments?.[0]?.InstanceId,`"?`]})]}),(0,z.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,z.jsx)(`input`,{"aria-label":`Force detach`,checked:X,onChange:e=>Z(e.target.checked),type:`checkbox`}),`Force detach`]}),(0,z.jsxs)(k,{children:[(0,z.jsx)(A,{children:`Cancel`}),(0,z.jsx)(O,{disabled:U.isPending,onClick:async()=>{let t=B?.Attachments?.[0]?.InstanceId;try{await U.mutateAsync({volumeId:e,instanceId:t??void 0,force:X||void 0}),$(),Z(!1)}catch{}},children:U.isPending?`Detaching…`:`Detach`})]})]})})]}):(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(c,{to:`/ec2/describe-volumes`,children:`Back to volumes`}),(0,z.jsx)(`p`,{className:`text-muted-foreground`,children:`Volume not found.`})]})}export{B as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id15.js b/spinifex/services/spinifexui/frontend/dist/assets/_id15.js index ddd546de0..43863d23b 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id15.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id15.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{V as r}from"./ec2.js";import{Fo as i,Ho as a,Ro as o,Xi as s,ft as c,h as l}from"./index.js";import{i as u,t as d}from"./zod.js";import{t as f}from"./form-actions.js";import{t as p}from"./page-heading.js";import{a as m,r as h,t as g}from"./field.js";import{_}from"./ec22.js";var v=i(),y=a();function b(){let i=(0,v.c)(53),{id:a}=l.useParams(),b=o(),x;i[0]===a?x=i[1]:(x=c(a),i[0]=a,i[1]=x);let{data:S}=t(x),C=S.Volumes?.[0],w=C?.VolumeId,T=r(),E=C?.Size??1,D;i[2]===E?D=i[3]:(D=d(_.refine(e=>e.size>E,{message:`New size must be greater than current size (${E} GiB)`,path:[`size`]})),i[2]=E,i[3]=D);let O;i[4]===E?O=i[5]:(O={size:E},i[4]=E,i[5]=O);let k;i[6]!==D||i[7]!==O?(k={resolver:D,defaultValues:O},i[6]=D,i[7]=O,i[8]=k):k=i[8];let{handleSubmit:A,register:j,formState:M}=u(k),{errors:N,isSubmitting:P}=M;if(!w){let e;return i[9]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(n,{to:`/ec2/describe-volumes`,children:`Back to volumes`}),(0,y.jsx)(`p`,{className:`text-muted-foreground`,children:`Volume not found.`})]}),i[9]=e):e=i[9],e}let F;i[10]!==T||i[11]!==b||i[12]!==w?(F=async e=>{await T.mutateAsync({...e,volumeId:w}),b({to:`/ec2/describe-volumes/$id`,params:{id:w}})},i[10]=T,i[11]=b,i[12]=w,i[13]=F):F=i[13];let I=F,L,R;i[14]===w?(L=i[15],R=i[16]):(L=(0,y.jsx)(n,{params:{id:w},to:`/ec2/describe-volumes/$id`,children:`Back to volume`}),R=(0,y.jsx)(p,{subtitle:w,title:`Resize Volume`}),i[14]=w,i[15]=L,i[16]=R);let z;i[17]===T.error?z=i[18]:(z=T.error&&(0,y.jsx)(e,{error:T.error,msg:`Failed to resize volume`}),i[17]=T.error,i[18]=z);let B;i[19]!==A||i[20]!==I?(B=A(I),i[19]=A,i[20]=I,i[21]=B):B=i[21];let V;i[22]===E?V=i[23]:(V=(0,y.jsx)(`div`,{className:`space-y-2`,children:(0,y.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[`Current size: `,E,` GiB`]})}),i[22]=E,i[23]=V);let H;i[24]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,y.jsx)(m,{children:(0,y.jsx)(`label`,{htmlFor:`size`,children:`New size (GiB)`})}),i[24]=H):H=i[24];let U=!!N.size,W=E+1,G;i[25]===j?G=i[26]:(G=j(`size`,{valueAsNumber:!0}),i[25]=j,i[26]=G);let K;i[27]!==U||i[28]!==W||i[29]!==G?(K=(0,y.jsx)(s,{"aria-invalid":U,id:`size`,min:W,type:`number`,...G}),i[27]=U,i[28]=W,i[29]=G,i[30]=K):K=i[30];let q;i[31]===N.size?q=i[32]:(q=(0,y.jsx)(h,{errors:[N.size]}),i[31]=N.size,i[32]=q);let J;i[33]!==K||i[34]!==q?(J=(0,y.jsxs)(g,{children:[H,K,q]}),i[33]=K,i[34]=q,i[35]=J):J=i[35];let Y;i[36]!==b||i[37]!==w?(Y=async()=>await b({to:`/ec2/describe-volumes/$id`,params:{id:w}}),i[36]=b,i[37]=w,i[38]=Y):Y=i[38];let X;i[39]!==P||i[40]!==T.isPending||i[41]!==Y?(X=(0,y.jsx)(f,{isPending:T.isPending,isSubmitting:P,onCancel:Y,pendingLabel:`Resizing\\u2026`,submitLabel:`Resize Volume`}),i[39]=P,i[40]=T.isPending,i[41]=Y,i[42]=X):X=i[42];let Z;i[43]!==V||i[44]!==J||i[45]!==X||i[46]!==B?(Z=(0,y.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:B,children:[V,J,X]}),i[43]=V,i[44]=J,i[45]=X,i[46]=B,i[47]=Z):Z=i[47];let Q;return i[48]!==Z||i[49]!==L||i[50]!==R||i[51]!==z?(Q=(0,y.jsxs)(y.Fragment,{children:[L,R,z,Z]}),i[48]=Z,i[49]=L,i[50]=R,i[51]=z,i[52]=Q):Q=i[52],Q}export{b as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{t as i}from"./back-link.js";import{U as a}from"./ec2.js";import{Ir as o,Pt as s,Rn as c,h as l}from"./index.js";import{i as u,t as d}from"./zod.js";import{t as f}from"./form-actions.js";import{t as p}from"./page-heading.js";import{a as m,r as h,t as g}from"./field.js";import{_}from"./ec22.js";var v=t(),y=e();function b(){let e=(0,v.c)(53),{id:t}=l.useParams(),b=o(),x;e[0]===t?x=e[1]:(x=s(t),e[0]=t,e[1]=x);let{data:S}=r(x),C=S.Volumes?.[0],w=C?.VolumeId,T=a(),E=C?.Size??1,D;e[2]===E?D=e[3]:(D=d(_.refine(e=>e.size>E,{message:`New size must be greater than current size (${E} GiB)`,path:[`size`]})),e[2]=E,e[3]=D);let O;e[4]===E?O=e[5]:(O={size:E},e[4]=E,e[5]=O);let k;e[6]!==D||e[7]!==O?(k={resolver:D,defaultValues:O},e[6]=D,e[7]=O,e[8]=k):k=e[8];let{handleSubmit:A,register:j,formState:M}=u(k),{errors:N,isSubmitting:P}=M;if(!w){let t;return e[9]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(i,{to:`/ec2/describe-volumes`,children:`Back to volumes`}),(0,y.jsx)(`p`,{className:`text-muted-foreground`,children:`Volume not found.`})]}),e[9]=t):t=e[9],t}let F;e[10]!==T||e[11]!==b||e[12]!==w?(F=async e=>{await T.mutateAsync({...e,volumeId:w}),b({to:`/ec2/describe-volumes/$id`,params:{id:w}})},e[10]=T,e[11]=b,e[12]=w,e[13]=F):F=e[13];let I=F,L,R;e[14]===w?(L=e[15],R=e[16]):(L=(0,y.jsx)(i,{params:{id:w},to:`/ec2/describe-volumes/$id`,children:`Back to volume`}),R=(0,y.jsx)(p,{subtitle:w,title:`Resize Volume`}),e[14]=w,e[15]=L,e[16]=R);let z;e[17]===T.error?z=e[18]:(z=T.error&&(0,y.jsx)(n,{error:T.error,msg:`Failed to resize volume`}),e[17]=T.error,e[18]=z);let B;e[19]!==A||e[20]!==I?(B=A(I),e[19]=A,e[20]=I,e[21]=B):B=e[21];let V;e[22]===E?V=e[23]:(V=(0,y.jsx)(`div`,{className:`space-y-2`,children:(0,y.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[`Current size: `,E,` GiB`]})}),e[22]=E,e[23]=V);let H;e[24]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,y.jsx)(m,{children:(0,y.jsx)(`label`,{htmlFor:`size`,children:`New size (GiB)`})}),e[24]=H):H=e[24];let U=!!N.size,W=E+1,G;e[25]===j?G=e[26]:(G=j(`size`,{valueAsNumber:!0}),e[25]=j,e[26]=G);let K;e[27]!==U||e[28]!==W||e[29]!==G?(K=(0,y.jsx)(c,{"aria-invalid":U,id:`size`,min:W,type:`number`,...G}),e[27]=U,e[28]=W,e[29]=G,e[30]=K):K=e[30];let q;e[31]===N.size?q=e[32]:(q=(0,y.jsx)(h,{errors:[N.size]}),e[31]=N.size,e[32]=q);let J;e[33]!==K||e[34]!==q?(J=(0,y.jsxs)(g,{children:[H,K,q]}),e[33]=K,e[34]=q,e[35]=J):J=e[35];let Y;e[36]!==b||e[37]!==w?(Y=async()=>await b({to:`/ec2/describe-volumes/$id`,params:{id:w}}),e[36]=b,e[37]=w,e[38]=Y):Y=e[38];let X;e[39]!==P||e[40]!==T.isPending||e[41]!==Y?(X=(0,y.jsx)(f,{isPending:T.isPending,isSubmitting:P,onCancel:Y,pendingLabel:`Resizing\\u2026`,submitLabel:`Resize Volume`}),e[39]=P,e[40]=T.isPending,e[41]=Y,e[42]=X):X=e[42];let Z;e[43]!==V||e[44]!==J||e[45]!==X||e[46]!==B?(Z=(0,y.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:B,children:[V,J,X]}),e[43]=V,e[44]=J,e[45]=X,e[46]=B,e[47]=Z):Z=e[47];let Q;return e[48]!==Z||e[49]!==L||e[50]!==R||e[51]!==z?(Q=(0,y.jsxs)(y.Fragment,{children:[L,R,z,Z]}),e[48]=Z,e[49]=L,e[50]=R,e[51]=z,e[52]=Q):Q=e[52],Q}export{b as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id16.js b/spinifex/services/spinifexui/frontend/dist/assets/_id16.js index bb3486afb..d51b86509 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id16.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id16.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./trash-2.js";import{N as i}from"./ec2.js";import{Ho as a,Lo as o,Ro as s,Uo as c,Xa as l,ao as u,cs as d,dt as f,g as p,mt as m}from"./index.js";import{t as h}from"./page-heading.js";import{t as g}from"./delete-confirmation-dialog.js";import{n as _,t as v}from"./detail-row.js";import{t as y}from"./state-badge.js";var b=d(c()),x=a();function S(){let{id:a}=p.useParams(),c=s(),{data:d}=t(m(a)),{data:S}=t(f),C=d.Vpcs?.[0],w=i(),[T,E]=(0,b.useState)(!1),D=S.Subnets?.filter(e=>e.VpcId===a)??[],O=async()=>{try{await w.mutateAsync(a),c({to:`/ec2/describe-vpcs`})}finally{E(!1)}};if(!C?.VpcId)return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(n,{to:`/ec2/describe-vpcs`,children:`Back to VPCs`}),(0,x.jsx)(`p`,{className:`text-muted-foreground`,children:`VPC not found.`})]});let k=u(C.Tags);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(n,{to:`/ec2/describe-vpcs`,children:`Back to VPCs`}),w.error&&(0,x.jsx)(e,{error:w.error,msg:`Failed to delete VPC`}),(0,x.jsxs)(`div`,{className:`space-y-6`,children:[(0,x.jsx)(h,{actions:(0,x.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,x.jsxs)(l,{onClick:()=>E(!0),size:`sm`,variant:`destructive`,children:[(0,x.jsx)(r,{className:`size-4`}),`Delete`]}),(0,x.jsx)(y,{state:C.State})]}),subtitle:`VPC Details`,title:k?`${C.VpcId} (${k})`:C.VpcId}),(0,x.jsxs)(_,{children:[(0,x.jsx)(_.Header,{children:`VPC Information`}),(0,x.jsxs)(_.Content,{children:[(0,x.jsx)(v,{label:`VPC ID`,value:C.VpcId}),(0,x.jsx)(v,{label:`State`,value:C.State}),(0,x.jsx)(v,{label:`CIDR Block`,value:C.CidrBlock}),(0,x.jsx)(v,{label:`Is Default`,value:C.IsDefault?`Yes`:`No`}),(0,x.jsx)(v,{label:`DHCP Options ID`,value:C.DhcpOptionsId}),(0,x.jsx)(v,{label:`Instance Tenancy`,value:C.InstanceTenancy}),(0,x.jsx)(v,{label:`Owner ID`,value:C.OwnerId})]})]}),D.length>0&&(0,x.jsxs)(_,{children:[(0,x.jsxs)(_.Header,{children:[`Subnets (`,D.length,`)`]}),D.map(e=>e.SubnetId?(0,x.jsxs)(_.Content,{children:[(0,x.jsx)(v,{label:`Subnet ID`,value:(0,x.jsx)(o,{className:`text-primary hover:underline`,params:{id:e.SubnetId},to:`/ec2/describe-subnets/$id`,children:e.SubnetId})}),(0,x.jsx)(v,{label:`CIDR Block`,value:e.CidrBlock}),(0,x.jsx)(v,{label:`Availability Zone`,value:e.AvailabilityZone}),(0,x.jsx)(v,{label:`State`,value:e.State})]},e.SubnetId):null)]})]}),(0,x.jsx)(g,{description:`Are you sure you want to delete the VPC "${C.VpcId}"? This action cannot be undone. The VPC must have no associated subnets or internet gateways.`,isPending:w.isPending,onConfirm:O,onOpenChange:E,open:T,title:`Delete VPC`})]})}export{S as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{A as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{t as c}from"./trash-2.js";import{t as l}from"./button.js";import{P as u}from"./ec2.js";import{Ir as d,It as f,Nt as p,g as m}from"./index.js";import{t as h}from"./page-heading.js";import{t as g}from"./delete-confirmation-dialog.js";import{n as _,t as v}from"./detail-row.js";import{t as y}from"./state-badge.js";var b=t(n()),x=e();function S(){let{id:e}=m.useParams(),t=d(),{data:n}=i(f(e)),{data:S}=i(p),C=n.Vpcs?.[0],w=u(),[T,E]=(0,b.useState)(!1),D=S.Subnets?.filter(t=>t.VpcId===e)??[],O=async()=>{try{await w.mutateAsync(e),t({to:`/ec2/describe-vpcs`})}finally{E(!1)}};if(!C?.VpcId)return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(s,{to:`/ec2/describe-vpcs`,children:`Back to VPCs`}),(0,x.jsx)(`p`,{className:`text-muted-foreground`,children:`VPC not found.`})]});let k=o(C.Tags);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(s,{to:`/ec2/describe-vpcs`,children:`Back to VPCs`}),w.error&&(0,x.jsx)(r,{error:w.error,msg:`Failed to delete VPC`}),(0,x.jsxs)(`div`,{className:`space-y-6`,children:[(0,x.jsx)(h,{actions:(0,x.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,x.jsxs)(l,{onClick:()=>E(!0),size:`sm`,variant:`destructive`,children:[(0,x.jsx)(c,{className:`size-4`}),`Delete`]}),(0,x.jsx)(y,{state:C.State})]}),subtitle:`VPC Details`,title:k?`${C.VpcId} (${k})`:C.VpcId}),(0,x.jsxs)(_,{children:[(0,x.jsx)(_.Header,{children:`VPC Information`}),(0,x.jsxs)(_.Content,{children:[(0,x.jsx)(v,{label:`VPC ID`,value:C.VpcId}),(0,x.jsx)(v,{label:`State`,value:C.State}),(0,x.jsx)(v,{label:`CIDR Block`,value:C.CidrBlock}),(0,x.jsx)(v,{label:`Is Default`,value:C.IsDefault?`Yes`:`No`}),(0,x.jsx)(v,{label:`DHCP Options ID`,value:C.DhcpOptionsId}),(0,x.jsx)(v,{label:`Instance Tenancy`,value:C.InstanceTenancy}),(0,x.jsx)(v,{label:`Owner ID`,value:C.OwnerId})]})]}),D.length>0&&(0,x.jsxs)(_,{children:[(0,x.jsxs)(_.Header,{children:[`Subnets (`,D.length,`)`]}),D.map(e=>e.SubnetId?(0,x.jsxs)(_.Content,{children:[(0,x.jsx)(v,{label:`Subnet ID`,value:(0,x.jsx)(a,{className:`text-primary hover:underline`,params:{id:e.SubnetId},to:`/ec2/describe-subnets/$id`,children:e.SubnetId})}),(0,x.jsx)(v,{label:`CIDR Block`,value:e.CidrBlock}),(0,x.jsx)(v,{label:`Availability Zone`,value:e.AvailabilityZone}),(0,x.jsx)(v,{label:`State`,value:e.State})]},e.SubnetId):null)]})]}),(0,x.jsx)(g,{description:`Are you sure you want to delete the VPC "${C.VpcId}"? This action cannot be undone. The VPC must have no associated subnets or internet gateways.`,isPending:w.isPending,onConfirm:O,onOpenChange:E,open:T,title:`Delete VPC`})]})}export{S as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id2.js b/spinifex/services/spinifexui/frontend/dist/assets/_id2.js index 3d1dabace..dcd8de4a6 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id2.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id2.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{t}from"./back-link.js";import{Fo as n,Ho as r,W as i,n as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./state-badge.js";import{t as c}from"./system-managed.js";import{t as l}from"./ami-details.js";var u=n(),d=r();function f(){let n=(0,u.c)(14),{id:r}=a.useParams(),f;n[0]===r?f=n[1]:(f=i(r),n[0]=r,n[1]=f);let{data:p}=e(f),m=p?.Images?.[0];if(!m?.ImageId||c(m)){let e;return n[2]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(t,{to:`/ec2/describe-images`,children:`Back to images`}),(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`Image not found.`})]}),n[2]=e):e=n[2],e}let h;n[3]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,d.jsx)(t,{to:`/ec2/describe-images`,children:`Back to images`}),n[3]=h):h=n[3];let g;n[4]===m.State?g=n[5]:(g=(0,d.jsx)(s,{state:m.State}),n[4]=m.State,n[5]=g);let _;n[6]!==m.ImageId||n[7]!==g?(_=(0,d.jsx)(o,{actions:g,subtitle:`Image Details`,title:m.ImageId}),n[6]=m.ImageId,n[7]=g,n[8]=_):_=n[8];let v;n[9]===m?v=n[10]:(v=(0,d.jsx)(l,{image:m,showExtendedDetails:!0}),n[9]=m,n[10]=v);let y;return n[11]!==_||n[12]!==v?(y=(0,d.jsxs)(d.Fragment,{children:[h,(0,d.jsxs)(`div`,{className:`space-y-6`,children:[_,v]})]}),n[11]=_,n[12]=v,n[13]=y):y=n[13],y}export{f as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{t as r}from"./back-link.js";import{ft as i,n as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./state-badge.js";import{n as c}from"./system-managed.js";import{t as l}from"./ami-details.js";var u=t(),d=e();function f(){let e=(0,u.c)(14),{id:t}=a.useParams(),f;e[0]===t?f=e[1]:(f=i(t),e[0]=t,e[1]=f);let{data:p}=n(f),m=p?.Images?.[0];if(!m?.ImageId||c(m)){let t;return e[2]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(r,{to:`/ec2/describe-images`,children:`Back to images`}),(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`Image not found.`})]}),e[2]=t):t=e[2],t}let h;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,d.jsx)(r,{to:`/ec2/describe-images`,children:`Back to images`}),e[3]=h):h=e[3];let g;e[4]===m.State?g=e[5]:(g=(0,d.jsx)(s,{state:m.State}),e[4]=m.State,e[5]=g);let _;e[6]!==m.ImageId||e[7]!==g?(_=(0,d.jsx)(o,{actions:g,subtitle:`Image Details`,title:m.ImageId}),e[6]=m.ImageId,e[7]=g,e[8]=_):_=e[8];let v;e[9]===m?v=e[10]:(v=(0,d.jsx)(l,{image:m,showExtendedDetails:!0}),e[9]=m,e[10]=v);let y;return e[11]!==_||e[12]!==v?(y=(0,d.jsxs)(d.Fragment,{children:[h,(0,d.jsxs)(`div`,{className:`space-y-6`,children:[_,v]})]}),e[11]=_,e[12]=v,e[13]=y):y=e[13],y}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id3.js b/spinifex/services/spinifexui/frontend/dist/assets/_id3.js index 0e77dfdfd..8b0b6267d 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id3.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id3.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useQuery.js";import{t as n}from"./useSuspenseQuery.js";import{t as r}from"./back-link.js";import{c as i,i as a,n as o,s,t as c}from"./select.js";import{t as l}from"./trash-2.js";import{B as u,H as d,J as f,K as p,R as m,l as h,q as g}from"./ec2.js";import{Fo as _,Ho as v,K as y,Lo as b,Uo as x,W as S,Xa as C,Xi as w,cs as T,gr as E,io as D,no as O,q as k,r as A,wo as j}from"./index.js";import{t as ee}from"./page-heading.js";import{a as M,t as N}from"./field.js";import{a as P,i as F,l as I,n as L,o as R,r as z,s as B,t as V}from"./alert-dialog.js";import{t as H}from"./delete-confirmation-dialog.js";import{n as U,t as W}from"./detail-row.js";import{t as te}from"./state-badge.js";import{t as ne}from"./ami-details.js";import{r as re}from"./admin.js";import{t as G}from"./textarea.js";var ie=j(`image-plus`,[[`path`,{d:`M16 5h6`,key:`1vod17`}],[`path`,{d:`M19 2v6`,key:`4bpg5p`}],[`path`,{d:`M21 11.5V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7.5`,key:`1ue2ih`}],[`path`,{d:`m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21`,key:`1xmnt7`}],[`circle`,{cx:`9`,cy:`9`,r:`2`,key:`af1f0g`}]]),K=j(`pause`,[[`rect`,{x:`14`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`kaeet6`}],[`rect`,{x:`5`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`1wsw3u`}]]),q=j(`play`,[[`path`,{d:`M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z`,key:`10ikf1`}]]),J=j(`rotate-cw`,[[`path`,{d:`M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8`,key:`1p45f6`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}]]),ae=j(`settings-2`,[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]),oe=j(`terminal`,[[`path`,{d:`M12 19h8`,key:`baeox8`}],[`path`,{d:`m4 17 6-6-6-6`,key:`1yngyt`}]]),Y=T(x(),1),X=v();function se({instanceId:e,open:t,onOpenChange:n}){let r=h(),[i,a]=(0,Y.useState)(``),[o,s]=(0,Y.useState)(``),[c,l]=(0,Y.useState)(null);function u(e){e||(a(``),s(``),l(null)),n(e)}async function d(){try{l((await r.mutateAsync({instanceId:e,name:i,description:o||void 0})).ImageId??null),a(``),s(``)}catch{}}return(0,X.jsx)(V,{onOpenChange:u,open:t,children:(0,X.jsxs)(F,{children:[(0,X.jsxs)(B,{children:[(0,X.jsx)(I,{children:c?`Image Created`:`Create Image (AMI)`}),(0,X.jsx)(P,{children:c?`Image "${c}" was created successfully.`:`Create an AMI from instance "${e}".`})]}),!c&&(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(N,{children:[(0,X.jsx)(M,{children:(0,X.jsx)(`label`,{htmlFor:`imageName`,children:`Name`})}),(0,X.jsx)(w,{id:`imageName`,onChange:e=>a(e.target.value),placeholder:`my-image`,value:i})]}),(0,X.jsxs)(N,{children:[(0,X.jsx)(M,{children:(0,X.jsx)(`label`,{htmlFor:`imageDescription`,children:`Description`})}),(0,X.jsx)(G,{id:`imageDescription`,onChange:e=>s(e.target.value),placeholder:`Optional description`,rows:2,value:o})]})]}),(0,X.jsxs)(R,{children:[(0,X.jsx)(z,{children:c?`Close`:`Cancel`}),!c&&(0,X.jsx)(L,{disabled:r.isPending||!i.trim(),onClick:d,children:r.isPending?`Creating…`:`Create Image`})]})]})})}var Z=_(),Q=new Set([`pending`,`stopping`,`shutting-down`,`terminated`]);function ce(t){let n=(0,Z.c)(43),{instanceId:r,state:i}=t,a=p(),o=g(),s=d(),c=f(),[u,m]=(0,Y.useState)(!1),h=Q.has(i??``),_;n[0]!==r||n[1]!==c?(_=()=>{c.mutate(r,{onSettled:()=>m(!1)})},n[0]=r,n[1]=c,n[2]=_):_=n[2];let v=_;if(h&&i!==`terminated`){let e;return n[3]===i?e=n[4]:(e=(0,X.jsx)(`div`,{className:`rounded-lg border bg-muted/50 p-4`,children:(0,X.jsxs)(`p`,{className:`text-center text-sm text-muted-foreground`,children:[`Instance is `,i,`. Actions will be available once the operation completes.`]})}),n[3]=i,n[4]=e),e}if(i===`terminated`){let e;return n[5]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,X.jsx)(`div`,{className:`rounded-lg border bg-muted/50 p-4`,children:(0,X.jsx)(`p`,{className:`text-center text-sm text-muted-foreground`,children:`This instance has been terminated and cannot be managed.`})}),n[5]=e):e=n[5],e}let y;n[6]===Symbol.for(`react.memo_cache_sentinel`)?(y=(0,X.jsx)(`h2`,{className:`mb-4 font-semibold`,children:`Instance Actions`}),n[6]=y):y=n[6];let b;n[7]===a.error?b=n[8]:(b=a.error&&(0,X.jsx)(e,{error:a.error,msg:`Failed to start instance`}),n[7]=a.error,n[8]=b);let x;n[9]===o.error?x=n[10]:(x=o.error&&(0,X.jsx)(e,{error:o.error,msg:`Failed to stop instance`}),n[9]=o.error,n[10]=x);let S;n[11]===s.error?S=n[12]:(S=s.error&&(0,X.jsx)(e,{error:s.error,msg:`Failed to reboot instance`}),n[11]=s.error,n[12]=S);let w;n[13]===c.error?w=n[14]:(w=c.error&&(0,X.jsx)(e,{error:c.error,msg:`Failed to terminate instance`}),n[13]=c.error,n[14]=w);let T;n[15]!==r||n[16]!==a||n[17]!==i?(T=i===`stopped`&&(0,X.jsxs)(C,{disabled:a.isPending,onClick:()=>a.mutate(r),size:`sm`,variant:`default`,children:[(0,X.jsx)(q,{className:`size-4`}),a.isPending?`Starting`:`Start`]}),n[15]=r,n[16]=a,n[17]=i,n[18]=T):T=n[18];let E;n[19]!==r||n[20]!==s||n[21]!==i||n[22]!==o?(E=i===`running`&&(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(C,{disabled:o.isPending,onClick:()=>o.mutate(r),size:`sm`,variant:`outline`,children:[(0,X.jsx)(K,{className:`size-4`}),o.isPending?`Stopping`:`Stop`]}),(0,X.jsxs)(C,{disabled:s.isPending,onClick:()=>s.mutate(r),size:`sm`,variant:`outline`,children:[(0,X.jsx)(J,{className:`size-4`}),s.isPending?`Rebooting`:`Reboot`]})]}),n[19]=r,n[20]=s,n[21]=i,n[22]=o,n[23]=E):E=n[23];let D;n[24]!==i||n[25]!==c.isPending?(D=(i===`stopped`||i===`running`)&&(0,X.jsxs)(C,{disabled:c.isPending,onClick:()=>m(!0),size:`sm`,variant:`destructive`,children:[(0,X.jsx)(l,{className:`size-4`}),c.isPending?`Terminating`:`Terminate`]}),n[24]=i,n[25]=c.isPending,n[26]=D):D=n[26];let O;n[27]!==T||n[28]!==E||n[29]!==D?(O=(0,X.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[T,E,D]}),n[27]=T,n[28]=E,n[29]=D,n[30]=O):O=n[30];let k=`Are you sure you want to terminate the instance "${r}"? This action cannot be undone.`,A;n[31]!==v||n[32]!==u||n[33]!==k||n[34]!==c.isPending?(A=(0,X.jsx)(H,{confirmLabel:`Terminate`,description:k,isPending:c.isPending,onConfirm:v,onOpenChange:m,open:u,pendingLabel:`Terminating…`,title:`Terminate Instance`}),n[31]=v,n[32]=u,n[33]=k,n[34]=c.isPending,n[35]=A):A=n[35];let j;return n[36]!==O||n[37]!==A||n[38]!==b||n[39]!==x||n[40]!==S||n[41]!==w?(j=(0,X.jsxs)(`div`,{className:`rounded-lg border bg-card p-4`,children:[y,b,x,S,w,O,A]}),n[36]=O,n[37]=A,n[38]=b,n[39]=x,n[40]=S,n[41]=w,n[42]=j):j=n[42],j}function $(){let{id:l}=A.useParams(),{isAdmin:d}=E(),{data:f}=n(y(l)),p=f.Reservations?.[0]?.Instances?.[0],{data:h}=n(S(p?.ImageId)),g=h?.Images?.[0],{data:_}=n(k),{data:v}=t({...re,enabled:d,refetchInterval:5e3}),x=v?.vms?.find(e=>e.instance_id===l),w=u(),T=m(),[j,H]=(0,Y.useState)(!1),[G,K]=(0,Y.useState)(``),[q,J]=(0,Y.useState)(!1),[Z,Q]=(0,Y.useState)(null),[$,le]=(0,Y.useState)(!1),ue=[...new Set(_.InstanceTypes?.flatMap(e=>e.InstanceType?[e.InstanceType]:[]))].toSorted();if(!p?.InstanceId)return(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(r,{to:`/ec2/describe-instances`,children:`Back to instances`}),(0,X.jsx)(`p`,{className:`text-muted-foreground`,children:`Instance not found.`})]});let de=O(p.LaunchTime),fe=p.State?.Name===`running`,pe=p.State?.Name===`stopped`;async function me(){try{await w.mutateAsync({instanceId:l,instanceType:G}),H(!1),K(``)}catch{}}async function he(){try{let e=await T.mutateAsync(l);Q(e.Output?atob(e.Output):`(no output)`),J(!0)}catch{}}return(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(r,{to:`/ec2/describe-instances`,children:`Back to instances`}),w.error&&(0,X.jsx)(e,{error:w.error,msg:`Failed to modify instance`}),T.error&&(0,X.jsx)(e,{error:T.error,msg:`Failed to get console output`}),(0,X.jsxs)(`div`,{className:`space-y-6`,children:[(0,X.jsx)(ee,{actions:(0,X.jsxs)(`div`,{className:`flex items-center gap-2`,children:[fe&&(0,X.jsxs)(C,{disabled:T.isPending,onClick:he,size:`sm`,variant:`outline`,children:[(0,X.jsx)(oe,{className:`size-4`}),T.isPending?`Fetching…`:`View Console`]}),(fe||pe)&&(0,X.jsxs)(C,{onClick:()=>le(!0),size:`sm`,variant:`outline`,children:[(0,X.jsx)(ie,{className:`size-4`}),`Create Image`]}),pe&&(0,X.jsxs)(C,{onClick:()=>{K(p.InstanceType??``),H(!0)},size:`sm`,variant:`outline`,children:[(0,X.jsx)(ae,{className:`size-4`}),`Modify Instance`]}),(0,X.jsx)(te,{state:p.State?.Name})]}),subtitle:`Instance Details`,title:p.InstanceId}),(0,X.jsx)(ce,{instanceId:p.InstanceId,state:p.State?.Name}),q&&Z!==null&&(0,X.jsxs)(U,{children:[(0,X.jsx)(U.Header,{children:(0,X.jsxs)(`div`,{className:`flex items-center justify-between`,children:[`Console Output`,(0,X.jsx)(C,{onClick:()=>{J(!1),Q(null)},size:`sm`,variant:`ghost`,children:`Hide`})]})}),(0,X.jsx)(`div`,{className:`p-4`,children:(0,X.jsx)(`pre`,{className:`max-h-96 overflow-y-auto rounded bg-muted p-3 font-mono text-xs whitespace-pre-wrap`,children:Z})})]}),(0,X.jsxs)(U,{children:[(0,X.jsx)(U.Header,{children:`Instance Information`}),(0,X.jsxs)(U.Content,{children:[(0,X.jsx)(W,{label:`Instance ID`,value:p.InstanceId}),(0,X.jsx)(W,{label:`Instance Type`,value:p.InstanceType}),(0,X.jsx)(W,{label:`Instance State`,value:p.State?.Name}),(0,X.jsx)(W,{label:`State Code`,value:p.State?.Code?.toString()}),(0,X.jsx)(W,{label:`Launch Time`,value:de}),(0,X.jsx)(W,{label:`Availability Zone`,value:p.Placement?.AvailabilityZone}),(0,X.jsx)(W,{label:`Placement Group`,value:p.Placement?.GroupName?(0,X.jsx)(b,{className:`text-primary hover:underline`,to:`/ec2/describe-placement-groups`,children:p.Placement.GroupName}):void 0})]})]}),(p.PrivateIpAddress||p.PublicIpAddress||p.VpcId||p.SubnetId||p.SecurityGroups&&p.SecurityGroups.length>0)&&(0,X.jsxs)(U,{children:[(0,X.jsx)(U.Header,{children:`Network & Security`}),(0,X.jsxs)(U.Content,{children:[(0,X.jsx)(W,{label:`Private IP Address`,value:p.PrivateIpAddress}),(0,X.jsx)(W,{label:`Public IP Address`,value:p.PublicIpAddress}),(0,X.jsx)(W,{label:`Private DNS`,value:p.PrivateDnsName}),(0,X.jsx)(W,{label:`Public DNS`,value:p.PublicDnsName}),(0,X.jsx)(W,{label:`VPC ID`,value:p.VpcId?(0,X.jsx)(b,{className:`text-primary hover:underline`,params:{id:p.VpcId},to:`/ec2/describe-vpcs/$id`,children:p.VpcId}):void 0}),(0,X.jsx)(W,{label:`Subnet ID`,value:p.SubnetId?(0,X.jsx)(b,{className:`text-primary hover:underline`,params:{id:p.SubnetId},to:`/ec2/describe-subnets/$id`,children:p.SubnetId}):void 0}),(0,X.jsx)(W,{label:`Key Name`,value:p.KeyName}),(0,X.jsx)(W,{label:`Security Groups`,value:p.SecurityGroups?.map(e=>e.GroupName).join(`, `)})]})]}),d&&x?.gpu&&(0,X.jsxs)(U,{children:[(0,X.jsx)(U.Header,{children:`GPU`}),(0,X.jsxs)(U.Content,{children:[(0,X.jsx)(W,{label:`Model`,value:x.gpu.model}),(0,X.jsx)(W,{label:`VRAM`,value:D(x.gpu.vram_mib)}),(0,X.jsx)(W,{label:`Attachment`,value:x.gpu.profile?`MIG slice`:`PCIe passthrough`}),x.gpu.profile&&(0,X.jsx)(W,{label:`Profile`,value:x.gpu.profile}),x.gpu.mdev_path&&(0,X.jsx)(W,{label:`Mdev path`,value:x.gpu.mdev_path}),x.gpu.pci_address&&(0,X.jsx)(W,{label:`PCI address`,value:x.gpu.pci_address})]})]}),p.BlockDeviceMappings&&p.BlockDeviceMappings.length>0&&(0,X.jsxs)(U,{children:[(0,X.jsx)(U.Header,{children:`Block Device Mappings`}),p.BlockDeviceMappings.map(e=>(0,X.jsxs)(U.Content,{children:[(0,X.jsx)(W,{label:`Device Name`,value:e.DeviceName}),(0,X.jsx)(W,{label:`Volume ID`,value:e.Ebs?.VolumeId?(0,X.jsx)(b,{className:`text-primary hover:underline`,params:{id:e.Ebs.VolumeId},to:`/ec2/describe-volumes/$id`,children:e.Ebs.VolumeId}):void 0}),(0,X.jsx)(W,{label:`Status`,value:e.Ebs?.Status}),(0,X.jsx)(W,{label:`Attach Time`,value:O(e.Ebs?.AttachTime)}),(0,X.jsx)(W,{label:`Delete on Termination`,value:e.Ebs?.DeleteOnTermination?`Yes`:`No`})]},e.DeviceName))]}),g&&(0,X.jsx)(ne,{image:g})]}),(0,X.jsx)(V,{onOpenChange:e=>{e||(H(!1),K(``))},open:j,children:(0,X.jsxs)(F,{children:[(0,X.jsxs)(B,{children:[(0,X.jsx)(I,{children:`Modify Instance`}),(0,X.jsxs)(P,{children:[`Select a new instance type for "`,p.InstanceId,`". The instance must be stopped to modify its attributes.`]})]}),(0,X.jsxs)(N,{children:[(0,X.jsx)(M,{children:(0,X.jsx)(`label`,{htmlFor:`instanceType`,children:`Instance Type`})}),(0,X.jsxs)(c,{onValueChange:e=>K(e??``),value:G,children:[(0,X.jsx)(s,{className:`w-full`,id:`instanceType`,children:(0,X.jsx)(i,{})}),(0,X.jsx)(o,{children:ue.map(e=>(0,X.jsx)(a,{value:e,children:e},e))})]})]}),(0,X.jsxs)(R,{children:[(0,X.jsx)(z,{children:`Cancel`}),(0,X.jsx)(L,{disabled:w.isPending||!G||G===p.InstanceType,onClick:me,children:w.isPending?`Saving…`:`Save Changes`})]})]})}),(0,X.jsx)(se,{instanceId:p.InstanceId,onOpenChange:le,open:$})]})}export{$ as component}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{t as a}from"./useQuery.js";import{t as o}from"./useSuspenseQuery.js";import{Or as s}from"./awsClient.js";import{D as c,k as l}from"./SignatureV4.js";import{t as u}from"./back-link.js";import{c as d,i as f,n as p,s as m,t as h}from"./select.js";import{t as g}from"./rotate-cw.js";import{t as _}from"./trash-2.js";import{t as v}from"./button.js";import{B as y,H as b,J as x,R as S,W as C,X as w,Y as T,r as E,u as D}from"./ec2.js";import{Bt as O,Nr as k,Q as A,Rn as j,dt as M,ft as N,ht as P,mt as ee,r as te}from"./index.js";import{t as ne}from"./page-heading.js";import{a as F,t as I}from"./field.js";import{a as L,i as R,l as z,n as B,o as V,r as H,s as U,t as re}from"./alert-dialog.js";import{t as W}from"./delete-confirmation-dialog.js";import{n as G,t as K}from"./detail-row.js";import{t as ie}from"./state-badge.js";import{t as ae}from"./ami-details.js";import{r as oe}from"./admin.js";import{t as q}from"./textarea.js";var se=k(`image-plus`,[[`path`,{d:`M16 5h6`,key:`1vod17`}],[`path`,{d:`M19 2v6`,key:`4bpg5p`}],[`path`,{d:`M21 11.5V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7.5`,key:`1ue2ih`}],[`path`,{d:`m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21`,key:`1xmnt7`}],[`circle`,{cx:`9`,cy:`9`,r:`2`,key:`af1f0g`}]]),ce=k(`pause`,[[`rect`,{x:`14`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`kaeet6`}],[`rect`,{x:`5`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`1wsw3u`}]]),J=k(`play`,[[`path`,{d:`M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z`,key:`10ikf1`}]]),le=k(`settings-2`,[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]),ue=k(`terminal`,[[`path`,{d:`M12 19h8`,key:`baeox8`}],[`path`,{d:`m4 17 6-6-6-6`,key:`1yngyt`}]]),Y=t(n(),1),X=e();function de({instanceId:e,open:t,onOpenChange:n}){let r=D(),[i,a]=(0,Y.useState)(``),[o,s]=(0,Y.useState)(``),[c,l]=(0,Y.useState)(null);function u(e){e||(a(``),s(``),l(null)),n(e)}async function d(){try{l((await r.mutateAsync({instanceId:e,name:i,description:o||void 0})).ImageId??null),a(``),s(``)}catch{}}return(0,X.jsx)(re,{onOpenChange:u,open:t,children:(0,X.jsxs)(R,{children:[(0,X.jsxs)(U,{children:[(0,X.jsx)(z,{children:c?`Image Created`:`Create Image (AMI)`}),(0,X.jsx)(L,{children:c?`Image "${c}" was created successfully.`:`Create an AMI from instance "${e}".`})]}),!c&&(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(I,{children:[(0,X.jsx)(F,{children:(0,X.jsx)(`label`,{htmlFor:`imageName`,children:`Name`})}),(0,X.jsx)(j,{id:`imageName`,onChange:e=>a(e.target.value),placeholder:`my-image`,value:i})]}),(0,X.jsxs)(I,{children:[(0,X.jsx)(F,{children:(0,X.jsx)(`label`,{htmlFor:`imageDescription`,children:`Description`})}),(0,X.jsx)(q,{id:`imageDescription`,onChange:e=>s(e.target.value),placeholder:`Optional description`,rows:2,value:o})]})]}),(0,X.jsxs)(V,{children:[(0,X.jsx)(H,{children:c?`Close`:`Cancel`}),!c&&(0,X.jsx)(B,{disabled:r.isPending||!i.trim(),onClick:d,children:r.isPending?`Creating…`:`Create Image`})]})]})})}var Z=r();function fe(e){let t=(0,Z.c)(31),{instanceId:n}=e,r;t[0]===n?r=t[1]:(r=M(n),t[0]=n,t[1]=r);let{data:o}=a(r),{data:c}=a(A),l=E(),u=S(),[f,g]=(0,Y.useState)(``),_;t[2]===o?.IamInstanceProfileAssociations?_=t[3]:(_=o?.IamInstanceProfileAssociations?.find(Q),t[2]=o?.IamInstanceProfileAssociations,t[3]=_);let y=_,b;t[4]===c?.InstanceProfiles?b=t[5]:(b=c?.InstanceProfiles??[],t[4]=c?.InstanceProfiles,t[5]=b);let x=b,C;t[6]!==l||t[7]!==n||t[8]!==f?(C=async()=>{f&&(await l.mutateAsync({instanceId:n,instanceProfileName:f}),g(``))},t[6]=l,t[7]=n,t[8]=f,t[9]=C):C=t[9];let w=C,T;t[10]!==y||t[11]!==u||t[12]!==n?(T=async()=>{y?.AssociationId&&await u.mutateAsync({associationId:y.AssociationId,instanceId:n})},t[10]=y,t[11]=u,t[12]=n,t[13]=T):T=t[13];let D=T,O;t[14]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,X.jsx)(G.Header,{children:`IAM Role`}),t[14]=O):O=t[14];let k;t[15]===l.error?k=t[16]:(k=l.error&&(0,X.jsx)(`div`,{className:`col-span-2`,children:(0,X.jsx)(i,{error:l.error,msg:`Failed to associate instance profile`})}),t[15]=l.error,t[16]=k);let j;t[17]===u.error?j=t[18]:(j=u.error&&(0,X.jsx)(`div`,{className:`col-span-2`,children:(0,X.jsx)(i,{error:u.error,msg:`Failed to disassociate instance profile`})}),t[17]=u.error,t[18]=j);let N;t[19]!==l.isPending||t[20]!==y||t[21]!==u.isPending||t[22]!==w||t[23]!==D||t[24]!==x||t[25]!==f?(N=y?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(K,{label:`Instance Profile ARN`,value:y.IamInstanceProfile?.Arn}),(0,X.jsx)(K,{label:`State`,value:(0,X.jsx)(ie,{state:y.State})}),(0,X.jsx)(`div`,{className:`col-span-2 flex justify-end`,children:(0,X.jsx)(v,{disabled:u.isPending,onClick:D,size:`sm`,variant:`destructive`,children:u.isPending?`Detaching…`:`Detach`})})]}):(0,X.jsxs)(`div`,{className:`col-span-2 space-y-3`,children:[(0,X.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`No instance profile is associated. Associate one to flow role credentials to this instance via IMDS.`}),x.length>0?(0,X.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,X.jsxs)(h,{onValueChange:e=>g(e??``),value:f,children:[(0,X.jsx)(m,{className:`w-full`,children:(0,X.jsx)(d,{placeholder:`Select an instance profile`})}),(0,X.jsx)(p,{children:x.map(pe)})]}),(0,X.jsx)(v,{disabled:!f||l.isPending,onClick:w,size:`sm`,children:l.isPending?`Associating…`:`Associate`})]}):(0,X.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[`No instance profiles exist.`,` `,(0,X.jsx)(s,{className:`text-primary hover:underline`,to:`/iam/create-instance-profile`,children:`Create one`}),` `,`first.`]})]}),t[19]=l.isPending,t[20]=y,t[21]=u.isPending,t[22]=w,t[23]=D,t[24]=x,t[25]=f,t[26]=N):N=t[26];let P;return t[27]!==k||t[28]!==j||t[29]!==N?(P=(0,X.jsxs)(G,{children:[O,(0,X.jsxs)(G.Content,{children:[k,j,N]})]}),t[27]=k,t[28]=j,t[29]=N,t[30]=P):P=t[30],P}function pe(e){return(0,X.jsx)(f,{value:e.InstanceProfileName??``,children:e.InstanceProfileName},e.InstanceProfileName)}function Q(e){return e.State===`associating`||e.State===`associated`}var $=new Set([`pending`,`stopping`,`shutting-down`,`terminated`]);function me(e){let t=(0,Z.c)(43),{instanceId:n,state:r}=e,a=x(),o=T(),s=C(),c=w(),[l,u]=(0,Y.useState)(!1),d=$.has(r??``),f;t[0]!==n||t[1]!==c?(f=()=>{c.mutate(n,{onSettled:()=>u(!1)})},t[0]=n,t[1]=c,t[2]=f):f=t[2];let p=f;if(d&&r!==`terminated`){let e;return t[3]===r?e=t[4]:(e=(0,X.jsx)(`div`,{className:`rounded-lg border bg-muted/50 p-4`,children:(0,X.jsxs)(`p`,{className:`text-center text-sm text-muted-foreground`,children:[`Instance is `,r,`. Actions will be available once the operation completes.`]})}),t[3]=r,t[4]=e),e}if(r===`terminated`){let e;return t[5]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,X.jsx)(`div`,{className:`rounded-lg border bg-muted/50 p-4`,children:(0,X.jsx)(`p`,{className:`text-center text-sm text-muted-foreground`,children:`This instance has been terminated and cannot be managed.`})}),t[5]=e):e=t[5],e}let m;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,X.jsx)(`h2`,{className:`mb-4 font-semibold`,children:`Instance Actions`}),t[6]=m):m=t[6];let h;t[7]===a.error?h=t[8]:(h=a.error&&(0,X.jsx)(i,{error:a.error,msg:`Failed to start instance`}),t[7]=a.error,t[8]=h);let y;t[9]===o.error?y=t[10]:(y=o.error&&(0,X.jsx)(i,{error:o.error,msg:`Failed to stop instance`}),t[9]=o.error,t[10]=y);let b;t[11]===s.error?b=t[12]:(b=s.error&&(0,X.jsx)(i,{error:s.error,msg:`Failed to reboot instance`}),t[11]=s.error,t[12]=b);let S;t[13]===c.error?S=t[14]:(S=c.error&&(0,X.jsx)(i,{error:c.error,msg:`Failed to terminate instance`}),t[13]=c.error,t[14]=S);let E;t[15]!==n||t[16]!==a||t[17]!==r?(E=r===`stopped`&&(0,X.jsxs)(v,{disabled:a.isPending,onClick:()=>a.mutate(n),size:`sm`,variant:`default`,children:[(0,X.jsx)(J,{className:`size-4`}),a.isPending?`Starting`:`Start`]}),t[15]=n,t[16]=a,t[17]=r,t[18]=E):E=t[18];let D;t[19]!==n||t[20]!==s||t[21]!==r||t[22]!==o?(D=r===`running`&&(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(v,{disabled:o.isPending,onClick:()=>o.mutate(n),size:`sm`,variant:`outline`,children:[(0,X.jsx)(ce,{className:`size-4`}),o.isPending?`Stopping`:`Stop`]}),(0,X.jsxs)(v,{disabled:s.isPending,onClick:()=>s.mutate(n),size:`sm`,variant:`outline`,children:[(0,X.jsx)(g,{className:`size-4`}),s.isPending?`Rebooting`:`Reboot`]})]}),t[19]=n,t[20]=s,t[21]=r,t[22]=o,t[23]=D):D=t[23];let O;t[24]!==r||t[25]!==c.isPending?(O=(r===`stopped`||r===`running`)&&(0,X.jsxs)(v,{disabled:c.isPending,onClick:()=>u(!0),size:`sm`,variant:`destructive`,children:[(0,X.jsx)(_,{className:`size-4`}),c.isPending?`Terminating`:`Terminate`]}),t[24]=r,t[25]=c.isPending,t[26]=O):O=t[26];let k;t[27]!==E||t[28]!==D||t[29]!==O?(k=(0,X.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[E,D,O]}),t[27]=E,t[28]=D,t[29]=O,t[30]=k):k=t[30];let A=`Are you sure you want to terminate the instance "${n}"? This action cannot be undone.`,j;t[31]!==p||t[32]!==l||t[33]!==A||t[34]!==c.isPending?(j=(0,X.jsx)(W,{confirmLabel:`Terminate`,description:A,isPending:c.isPending,onConfirm:p,onOpenChange:u,open:l,pendingLabel:`Terminating…`,title:`Terminate Instance`}),t[31]=p,t[32]=l,t[33]=A,t[34]=c.isPending,t[35]=j):j=t[35];let M;return t[36]!==k||t[37]!==j||t[38]!==h||t[39]!==y||t[40]!==b||t[41]!==S?(M=(0,X.jsxs)(`div`,{className:`rounded-lg border bg-card p-4`,children:[m,h,y,b,S,k,j]}),t[36]=k,t[37]=j,t[38]=h,t[39]=y,t[40]=b,t[41]=S,t[42]=M):M=t[42],M}function he(){let{id:e}=te.useParams(),{isAdmin:t}=O(),{data:n}=o(ee(e)),r=n.Reservations?.[0]?.Instances?.[0],{data:g}=o(N(r?.ImageId)),_=g?.Images?.[0],{data:x}=o(P),{data:S}=a({...oe,enabled:t,refetchInterval:5e3}),C=S?.vms?.find(t=>t.instance_id===e),w=b(),T=y(),[E,D]=(0,Y.useState)(!1),[k,A]=(0,Y.useState)(``),[j,M]=(0,Y.useState)(!1),[W,q]=(0,Y.useState)(null),[ce,J]=(0,Y.useState)(!1),Z=[...new Set(x.InstanceTypes?.flatMap(e=>e.InstanceType?[e.InstanceType]:[]))].toSorted();if(!r?.InstanceId)return(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(u,{to:`/ec2/describe-instances`,children:`Back to instances`}),(0,X.jsx)(`p`,{className:`text-muted-foreground`,children:`Instance not found.`})]});let pe=c(r.LaunchTime),Q=r.State?.Name===`running`,$=r.State?.Name===`stopped`;async function he(){try{await w.mutateAsync({instanceId:e,instanceType:k}),D(!1),A(``)}catch{}}async function ge(){try{let t=await T.mutateAsync(e);q(t.Output?atob(t.Output):`(no output)`),M(!0)}catch{}}return(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(u,{to:`/ec2/describe-instances`,children:`Back to instances`}),w.error&&(0,X.jsx)(i,{error:w.error,msg:`Failed to modify instance`}),T.error&&(0,X.jsx)(i,{error:T.error,msg:`Failed to get console output`}),(0,X.jsxs)(`div`,{className:`space-y-6`,children:[(0,X.jsx)(ne,{actions:(0,X.jsxs)(`div`,{className:`flex items-center gap-2`,children:[Q&&(0,X.jsxs)(v,{disabled:T.isPending,onClick:ge,size:`sm`,variant:`outline`,children:[(0,X.jsx)(ue,{className:`size-4`}),T.isPending?`Fetching…`:`View Console`]}),(Q||$)&&(0,X.jsxs)(v,{onClick:()=>J(!0),size:`sm`,variant:`outline`,children:[(0,X.jsx)(se,{className:`size-4`}),`Create Image`]}),$&&(0,X.jsxs)(v,{onClick:()=>{A(r.InstanceType??``),D(!0)},size:`sm`,variant:`outline`,children:[(0,X.jsx)(le,{className:`size-4`}),`Modify Instance`]}),(0,X.jsx)(ie,{state:r.State?.Name})]}),subtitle:`Instance Details`,title:r.InstanceId}),(0,X.jsx)(me,{instanceId:r.InstanceId,state:r.State?.Name}),j&&W!==null&&(0,X.jsxs)(G,{children:[(0,X.jsx)(G.Header,{children:(0,X.jsxs)(`div`,{className:`flex items-center justify-between`,children:[`Console Output`,(0,X.jsx)(v,{onClick:()=>{M(!1),q(null)},size:`sm`,variant:`ghost`,children:`Hide`})]})}),(0,X.jsx)(`div`,{className:`p-4`,children:(0,X.jsx)(`pre`,{className:`max-h-96 overflow-y-auto rounded bg-muted p-3 font-mono text-xs whitespace-pre-wrap`,children:W})})]}),(0,X.jsxs)(G,{children:[(0,X.jsx)(G.Header,{children:`Instance Information`}),(0,X.jsxs)(G.Content,{children:[(0,X.jsx)(K,{label:`Instance ID`,value:r.InstanceId}),(0,X.jsx)(K,{label:`Instance Type`,value:r.InstanceType}),(0,X.jsx)(K,{label:`Instance State`,value:r.State?.Name}),(0,X.jsx)(K,{label:`State Code`,value:r.State?.Code?.toString()}),(0,X.jsx)(K,{label:`Launch Time`,value:pe}),(0,X.jsx)(K,{label:`Availability Zone`,value:r.Placement?.AvailabilityZone}),(0,X.jsx)(K,{label:`Placement Group`,value:r.Placement?.GroupName?(0,X.jsx)(s,{className:`text-primary hover:underline`,to:`/ec2/describe-placement-groups`,children:r.Placement.GroupName}):void 0})]})]}),(r.PrivateIpAddress||r.PublicIpAddress||r.VpcId||r.SubnetId||r.SecurityGroups&&r.SecurityGroups.length>0)&&(0,X.jsxs)(G,{children:[(0,X.jsx)(G.Header,{children:`Network & Security`}),(0,X.jsxs)(G.Content,{children:[(0,X.jsx)(K,{label:`Private IP Address`,value:r.PrivateIpAddress}),(0,X.jsx)(K,{label:`Public IP Address`,value:r.PublicIpAddress}),(0,X.jsx)(K,{label:`Private DNS`,value:r.PrivateDnsName}),(0,X.jsx)(K,{label:`Public DNS`,value:r.PublicDnsName}),(0,X.jsx)(K,{label:`VPC ID`,value:r.VpcId?(0,X.jsx)(s,{className:`text-primary hover:underline`,params:{id:r.VpcId},to:`/ec2/describe-vpcs/$id`,children:r.VpcId}):void 0}),(0,X.jsx)(K,{label:`Subnet ID`,value:r.SubnetId?(0,X.jsx)(s,{className:`text-primary hover:underline`,params:{id:r.SubnetId},to:`/ec2/describe-subnets/$id`,children:r.SubnetId}):void 0}),(0,X.jsx)(K,{label:`Key Name`,value:r.KeyName}),(0,X.jsx)(K,{label:`Security Groups`,value:r.SecurityGroups?.map(e=>e.GroupName).join(`, `)})]})]}),(0,X.jsx)(fe,{instanceId:r.InstanceId}),t&&C?.gpu&&(0,X.jsxs)(G,{children:[(0,X.jsx)(G.Header,{children:`GPU`}),(0,X.jsxs)(G.Content,{children:[(0,X.jsx)(K,{label:`Model`,value:C.gpu.model}),(0,X.jsx)(K,{label:`VRAM`,value:l(C.gpu.vram_mib)}),(0,X.jsx)(K,{label:`Attachment`,value:C.gpu.profile?`MIG slice`:`PCIe passthrough`}),C.gpu.profile&&(0,X.jsx)(K,{label:`Profile`,value:C.gpu.profile}),C.gpu.mdev_path&&(0,X.jsx)(K,{label:`Mdev path`,value:C.gpu.mdev_path}),C.gpu.pci_address&&(0,X.jsx)(K,{label:`PCI address`,value:C.gpu.pci_address})]})]}),r.BlockDeviceMappings&&r.BlockDeviceMappings.length>0&&(0,X.jsxs)(G,{children:[(0,X.jsx)(G.Header,{children:`Block Device Mappings`}),r.BlockDeviceMappings.map(e=>(0,X.jsxs)(G.Content,{children:[(0,X.jsx)(K,{label:`Device Name`,value:e.DeviceName}),(0,X.jsx)(K,{label:`Volume ID`,value:e.Ebs?.VolumeId?(0,X.jsx)(s,{className:`text-primary hover:underline`,params:{id:e.Ebs.VolumeId},to:`/ec2/describe-volumes/$id`,children:e.Ebs.VolumeId}):void 0}),(0,X.jsx)(K,{label:`Status`,value:e.Ebs?.Status}),(0,X.jsx)(K,{label:`Attach Time`,value:c(e.Ebs?.AttachTime)}),(0,X.jsx)(K,{label:`Delete on Termination`,value:e.Ebs?.DeleteOnTermination?`Yes`:`No`})]},e.DeviceName))]}),_&&(0,X.jsx)(ae,{image:_})]}),(0,X.jsx)(re,{onOpenChange:e=>{e||(D(!1),A(``))},open:E,children:(0,X.jsxs)(R,{children:[(0,X.jsxs)(U,{children:[(0,X.jsx)(z,{children:`Modify Instance`}),(0,X.jsxs)(L,{children:[`Select a new instance type for "`,r.InstanceId,`". The instance must be stopped to modify its attributes.`]})]}),(0,X.jsxs)(I,{children:[(0,X.jsx)(F,{children:(0,X.jsx)(`label`,{htmlFor:`instanceType`,children:`Instance Type`})}),(0,X.jsxs)(h,{onValueChange:e=>A(e??``),value:k,children:[(0,X.jsx)(m,{className:`w-full`,id:`instanceType`,children:(0,X.jsx)(d,{})}),(0,X.jsx)(p,{children:Z.map(e=>(0,X.jsx)(f,{value:e,children:e},e))})]})]}),(0,X.jsxs)(V,{children:[(0,X.jsx)(H,{children:`Cancel`}),(0,X.jsx)(B,{disabled:w.isPending||!k||k===r.InstanceType,onClick:he,children:w.isPending?`Saving…`:`Save Changes`})]})]})}),(0,X.jsx)(de,{instanceId:r.InstanceId,onOpenChange:J,open:ce})]})}export{he as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id4.js b/spinifex/services/spinifexui/frontend/dist/assets/_id4.js index 42efe1562..e4dcbd48c 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id4.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id4.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as a,s as o,t as s}from"./select.js";import{t as c}from"./trash-2.js";import{C as l,P as u,i as d}from"./ec2.js";import{Ho as f,Lo as p,Ro as m,Uo as h,Xa as g,Y as _,ao as v,cs as y,ht as b,i as x}from"./index.js";import{t as S}from"./page-heading.js";import{t as C}from"./delete-confirmation-dialog.js";import{n as w,t as T}from"./detail-row.js";var E=y(h()),D=f();function O(){let{id:f}=x.useParams(),h=m(),{data:y}=t(_(f)),{data:O}=t(b),k=d(),A=u(),j=l(),[M,N]=(0,E.useState)(!1),[P,F]=(0,E.useState)(``),I=y.InternetGateways?.[0],L=O.Vpcs??[],R=async()=>{try{await j.mutateAsync(f),h({to:`/ec2/describe-internet-gateways`})}finally{N(!1)}},z=async()=>{P&&await k.mutateAsync({internetGatewayId:f,vpcId:P})},B=async e=>{await A.mutateAsync({internetGatewayId:f,vpcId:e})};if(!I?.InternetGatewayId)return(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(n,{to:`/ec2/describe-internet-gateways`,children:`Back to Internet Gateways`}),(0,D.jsx)(`p`,{className:`text-muted-foreground`,children:`Internet Gateway not found.`})]});let V=I.Attachments?.[0],H=!!V?.VpcId,U=v(I.Tags);return(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(n,{to:`/ec2/describe-internet-gateways`,children:`Back to Internet Gateways`}),(j.error??k.error??A.error)&&(0,D.jsx)(e,{error:j.error??k.error??A.error??void 0,msg:`Internet Gateway action failed`}),(0,D.jsxs)(`div`,{className:`space-y-6`,children:[(0,D.jsx)(S,{actions:(0,D.jsxs)(g,{disabled:H||j.isPending,onClick:()=>N(!0),size:`sm`,variant:`destructive`,children:[(0,D.jsx)(c,{className:`size-4`}),`Delete`]}),subtitle:`Internet Gateway Details`,title:U?`${I.InternetGatewayId} (${U})`:I.InternetGatewayId}),(0,D.jsxs)(w,{children:[(0,D.jsx)(w.Header,{children:`Internet Gateway Information`}),(0,D.jsxs)(w.Content,{children:[(0,D.jsx)(T,{label:`Internet Gateway ID`,value:I.InternetGatewayId}),(0,D.jsx)(T,{label:`Owner ID`,value:I.OwnerId??`—`}),(0,D.jsx)(T,{label:`State`,value:V?.State??`detached`})]})]}),(0,D.jsxs)(w,{children:[(0,D.jsx)(w.Header,{children:`VPC Attachment`}),(0,D.jsx)(w.Content,{children:H?(0,D.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,D.jsx)(p,{className:`text-primary hover:underline`,params:{id:V.VpcId??``},to:`/ec2/describe-vpcs/$id`,children:V.VpcId}),(0,D.jsx)(g,{disabled:A.isPending,onClick:async()=>await B(V.VpcId??``),size:`sm`,variant:`outline`,children:`Detach`})]}):(0,D.jsxs)(`div`,{className:`flex items-end gap-2`,children:[(0,D.jsx)(`div`,{className:`flex-1`,children:(0,D.jsxs)(s,{onValueChange:e=>F(e??``),value:P,children:[(0,D.jsx)(o,{className:`w-full`,children:(0,D.jsx)(r,{placeholder:`Select a VPC`})}),(0,D.jsx)(a,{children:L.map(e=>{let t=v(e.Tags);return(0,D.jsx)(i,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)})})]})}),(0,D.jsx)(g,{disabled:!P||k.isPending,onClick:z,size:`sm`,children:`Attach`})]})})]})]}),(0,D.jsx)(C,{description:`Are you sure you want to delete the Internet Gateway "${I.InternetGatewayId}"? This action cannot be undone.`,isPending:j.isPending,onConfirm:R,onOpenChange:N,open:M,title:`Delete Internet Gateway`})]})}export{O as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{A as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{c,i as l,n as u,s as d,t as f}from"./select.js";import{t as p}from"./trash-2.js";import{t as m}from"./button.js";import{F as h,a as g,w as _}from"./ec2.js";import{Ir as v,Lt as y,_t as b,i as x}from"./index.js";import{t as S}from"./page-heading.js";import{t as C}from"./delete-confirmation-dialog.js";import{n as w,t as T}from"./detail-row.js";var E=t(n()),D=e();function O(){let{id:e}=x.useParams(),t=v(),{data:n}=i(b(e)),{data:O}=i(y),k=g(),A=h(),j=_(),[M,N]=(0,E.useState)(!1),[P,F]=(0,E.useState)(``),I=n.InternetGateways?.[0],L=O.Vpcs??[],R=async()=>{try{await j.mutateAsync(e),t({to:`/ec2/describe-internet-gateways`})}finally{N(!1)}},z=async()=>{P&&await k.mutateAsync({internetGatewayId:e,vpcId:P})},B=async t=>{await A.mutateAsync({internetGatewayId:e,vpcId:t})};if(!I?.InternetGatewayId)return(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(s,{to:`/ec2/describe-internet-gateways`,children:`Back to Internet Gateways`}),(0,D.jsx)(`p`,{className:`text-muted-foreground`,children:`Internet Gateway not found.`})]});let V=I.Attachments?.[0],H=!!V?.VpcId,U=o(I.Tags);return(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(s,{to:`/ec2/describe-internet-gateways`,children:`Back to Internet Gateways`}),(j.error??k.error??A.error)&&(0,D.jsx)(r,{error:j.error??k.error??A.error??void 0,msg:`Internet Gateway action failed`}),(0,D.jsxs)(`div`,{className:`space-y-6`,children:[(0,D.jsx)(S,{actions:(0,D.jsxs)(m,{disabled:H||j.isPending,onClick:()=>N(!0),size:`sm`,variant:`destructive`,children:[(0,D.jsx)(p,{className:`size-4`}),`Delete`]}),subtitle:`Internet Gateway Details`,title:U?`${I.InternetGatewayId} (${U})`:I.InternetGatewayId}),(0,D.jsxs)(w,{children:[(0,D.jsx)(w.Header,{children:`Internet Gateway Information`}),(0,D.jsxs)(w.Content,{children:[(0,D.jsx)(T,{label:`Internet Gateway ID`,value:I.InternetGatewayId}),(0,D.jsx)(T,{label:`Owner ID`,value:I.OwnerId??`—`}),(0,D.jsx)(T,{label:`State`,value:V?.State??`detached`})]})]}),(0,D.jsxs)(w,{children:[(0,D.jsx)(w.Header,{children:`VPC Attachment`}),(0,D.jsx)(w.Content,{children:H?(0,D.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,D.jsx)(a,{className:`text-primary hover:underline`,params:{id:V.VpcId??``},to:`/ec2/describe-vpcs/$id`,children:V.VpcId}),(0,D.jsx)(m,{disabled:A.isPending,onClick:async()=>await B(V.VpcId??``),size:`sm`,variant:`outline`,children:`Detach`})]}):(0,D.jsxs)(`div`,{className:`flex items-end gap-2`,children:[(0,D.jsx)(`div`,{className:`flex-1`,children:(0,D.jsxs)(f,{onValueChange:e=>F(e??``),value:P,children:[(0,D.jsx)(d,{className:`w-full`,children:(0,D.jsx)(c,{placeholder:`Select a VPC`})}),(0,D.jsx)(u,{children:L.map(e=>{let t=o(e.Tags);return(0,D.jsx)(l,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)})})]})}),(0,D.jsx)(m,{disabled:!P||k.isPending,onClick:z,size:`sm`,children:`Attach`})]})})]})]}),(0,D.jsx)(C,{description:`Are you sure you want to delete the Internet Gateway "${I.InternetGatewayId}"? This action cannot be undone.`,isPending:j.isPending,onConfirm:R,onOpenChange:N,open:M,title:`Delete Internet Gateway`})]})}export{O as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id5.js b/spinifex/services/spinifexui/frontend/dist/assets/_id5.js index d6b3ba743..2e47fcf07 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id5.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id5.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./trash-2.js";import{w as i}from"./ec2.js";import{Ho as a,Ro as o,Uo as s,Xa as c,Z as l,a as u,cs as d,no as f}from"./index.js";import{t as p}from"./page-heading.js";import{t as m}from"./delete-confirmation-dialog.js";import{n as h,t as g}from"./detail-row.js";var _=d(s()),v=a();function y(){let{id:a}=u.useParams(),s=o(),{data:d}=t(l(a)),y=d.KeyPairs?.[0],b=i(),[x,S]=(0,_.useState)(!1),C=async()=>{try{await b.mutateAsync(a),s({to:`/ec2/describe-key-pairs`})}finally{S(!1)}};if(!y?.KeyPairId)return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(n,{to:`/ec2/describe-key-pairs`,children:`Back to key pairs`}),(0,v.jsx)(`p`,{className:`text-muted-foreground`,children:`Key pair not found.`})]});let w=f(y.CreateTime);return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(n,{to:`/ec2/describe-key-pairs`,children:`Back to key pairs`}),b.error&&(0,v.jsx)(e,{error:b.error,msg:`Failed to delete key pair`}),(0,v.jsxs)(`div`,{className:`space-y-6`,children:[(0,v.jsx)(p,{actions:(0,v.jsxs)(c,{onClick:()=>S(!0),size:`sm`,variant:`destructive`,children:[(0,v.jsx)(r,{className:`size-4`}),`Delete`]}),subtitle:`Key Pair Details`,title:y.KeyName??``}),(0,v.jsxs)(h,{children:[(0,v.jsx)(h.Header,{children:`Key Pair Information`}),(0,v.jsxs)(h.Content,{children:[(0,v.jsx)(g,{label:`Key Pair ID`,value:y.KeyPairId}),(0,v.jsx)(g,{label:`Key Name`,value:y.KeyName}),(0,v.jsx)(g,{label:`Key Type`,value:y.KeyType}),(0,v.jsx)(g,{label:`Key Fingerprint`,value:y.KeyFingerprint}),(0,v.jsx)(g,{label:`Creation Date`,value:w})]})]}),y.Tags&&y.Tags.length>0&&(0,v.jsxs)(h,{children:[(0,v.jsx)(h.Header,{children:`Tags`}),(0,v.jsx)(h.Content,{children:y.Tags.map(e=>(0,v.jsx)(g,{label:e.Key??``,value:e.Value},e.Key))})]})]}),(0,v.jsx)(m,{description:`Are you sure you want to delete the key pair "${y.KeyName}"? This action cannot be undone. Any instances using this key pair will no longer be able to be accessed with it.`,isPending:b.isPending,onConfirm:C,onOpenChange:S,open:x,title:`Delete Key Pair`})]})}export{y as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{D as a}from"./SignatureV4.js";import{t as o}from"./back-link.js";import{t as s}from"./trash-2.js";import{t as c}from"./button.js";import{T as l}from"./ec2.js";import{Ir as u,a as d,yt as f}from"./index.js";import{t as p}from"./page-heading.js";import{t as m}from"./delete-confirmation-dialog.js";import{n as h,t as g}from"./detail-row.js";var _=t(n()),v=e();function y(){let{id:e}=d.useParams(),t=u(),{data:n}=i(f(e)),y=n.KeyPairs?.[0],b=l(),[x,S]=(0,_.useState)(!1),C=async()=>{try{await b.mutateAsync(e),t({to:`/ec2/describe-key-pairs`})}finally{S(!1)}};if(!y?.KeyPairId)return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(o,{to:`/ec2/describe-key-pairs`,children:`Back to key pairs`}),(0,v.jsx)(`p`,{className:`text-muted-foreground`,children:`Key pair not found.`})]});let w=a(y.CreateTime);return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(o,{to:`/ec2/describe-key-pairs`,children:`Back to key pairs`}),b.error&&(0,v.jsx)(r,{error:b.error,msg:`Failed to delete key pair`}),(0,v.jsxs)(`div`,{className:`space-y-6`,children:[(0,v.jsx)(p,{actions:(0,v.jsxs)(c,{onClick:()=>S(!0),size:`sm`,variant:`destructive`,children:[(0,v.jsx)(s,{className:`size-4`}),`Delete`]}),subtitle:`Key Pair Details`,title:y.KeyName??``}),(0,v.jsxs)(h,{children:[(0,v.jsx)(h.Header,{children:`Key Pair Information`}),(0,v.jsxs)(h.Content,{children:[(0,v.jsx)(g,{label:`Key Pair ID`,value:y.KeyPairId}),(0,v.jsx)(g,{label:`Key Name`,value:y.KeyName}),(0,v.jsx)(g,{label:`Key Type`,value:y.KeyType}),(0,v.jsx)(g,{label:`Key Fingerprint`,value:y.KeyFingerprint}),(0,v.jsx)(g,{label:`Creation Date`,value:w})]})]}),y.Tags&&y.Tags.length>0&&(0,v.jsxs)(h,{children:[(0,v.jsx)(h.Header,{children:`Tags`}),(0,v.jsx)(h.Content,{children:y.Tags.map(e=>(0,v.jsx)(g,{label:e.Key??``,value:e.Value},e.Key))})]})]}),(0,v.jsx)(m,{description:`Are you sure you want to delete the key pair "${y.KeyName}"? This action cannot be undone. Any instances using this key pair will no longer be able to be accessed with it.`,isPending:b.isPending,onConfirm:C,onOpenChange:S,open:x,title:`Delete Key Pair`})]})}export{y as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id6.js b/spinifex/services/spinifexui/frontend/dist/assets/_id6.js index 1f2639ecc..dd65487b1 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id6.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id6.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as a,s as o,t as s}from"./select.js";import{t as c}from"./trash-2.js";import{a as l,d as u,f as d,o as f,r as p,s as m,t as h,u as g}from"./elbv2.js";import{A as _,Fo as v,Ho as y,I as b,N as x,O as S,Ro as C,Uo as w,Xa as T,Xi as E,ao as D,cs as O,dt as k,j as A,k as j,no as ee,o as te,wo as ne}from"./index.js";import{i as M,n as N,r as re,t as P}from"./zod.js";import{t as ie}from"./page-heading.js";import{a as F,r as I,t as L}from"./field.js";import{a as R,i as z,l as B,n as V,o as H,r as U,s as W,t as G}from"./alert-dialog.js";import{t as K}from"./delete-confirmation-dialog.js";import{n as q,t as J}from"./detail-row.js";import{t as ae}from"./state-badge.js";import{r as oe,t as se}from"./elbv22.js";import{a as ce,i as Y,n as le,o as ue,r as X,t as de}from"./tabs.js";var fe=ne(`pencil`,[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]),Z=O(w(),1),Q=v(),$=y();function pe(e){let t=(0,Q.c)(33),{form:n,targetGroups:i}=e,{control:c,register:l,formState:u}=n,{errors:d}=u,f;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(f=(0,$.jsx)(F,{children:(0,$.jsx)(`label`,{htmlFor:`listener-protocol`,children:`Protocol`})}),t[0]=f):f=t[0];let p;t[1]===c?p=t[2]:(p=(0,$.jsxs)(L,{children:[f,(0,$.jsx)(N,{control:c,name:`protocol`,render:he})]}),t[1]=c,t[2]=p);let m;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,$.jsx)(F,{children:(0,$.jsx)(`label`,{htmlFor:`listener-port`,children:`Port`})}),t[3]=m):m=t[3];let h=!!d.port,g;t[4]===l?g=t[5]:(g=l(`port`,{valueAsNumber:!0}),t[4]=l,t[5]=g);let _;t[6]!==h||t[7]!==g?(_=(0,$.jsx)(E,{"aria-invalid":h,id:`listener-port`,inputMode:`numeric`,placeholder:`80`,type:`number`,...g}),t[6]=h,t[7]=g,t[8]=_):_=t[8];let v;t[9]===d.port?v=t[10]:(v=(0,$.jsx)(I,{errors:[d.port]}),t[9]=d.port,t[10]=v);let y;t[11]!==_||t[12]!==v?(y=(0,$.jsxs)(L,{children:[m,_,v]}),t[11]=_,t[12]=v,t[13]=y):y=t[13];let b;t[14]===Symbol.for(`react.memo_cache_sentinel`)?(b=(0,$.jsx)(F,{children:(0,$.jsx)(`label`,{htmlFor:`listener-default-tg`,children:`Default target group`})}),t[14]=b):b=t[14];let x;t[15]!==d.defaultTargetGroupArn||t[16]!==i?(x=e=>{let{field:t}=e;return(0,$.jsxs)(s,{onValueChange:t.onChange,value:t.value??``,children:[(0,$.jsx)(o,{"aria-invalid":!!d.defaultTargetGroupArn,className:`w-full`,id:`listener-default-tg`,children:(0,$.jsx)(r,{placeholder:`Select target group`})}),(0,$.jsx)(a,{children:i.map(me)})]})},t[15]=d.defaultTargetGroupArn,t[16]=i,t[17]=x):x=t[17];let S;t[18]!==c||t[19]!==x?(S=(0,$.jsx)(N,{control:c,name:`defaultTargetGroupArn`,render:x}),t[18]=c,t[19]=x,t[20]=S):S=t[20];let C;t[21]===d.defaultTargetGroupArn?C=t[22]:(C=(0,$.jsx)(I,{errors:[d.defaultTargetGroupArn]}),t[21]=d.defaultTargetGroupArn,t[22]=C);let w;t[23]===i.length?w=t[24]:(w=i.length===0&&(0,$.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`No target groups available in this VPC. Create one first.`}),t[23]=i.length,t[24]=w);let T;t[25]!==S||t[26]!==C||t[27]!==w?(T=(0,$.jsxs)(L,{children:[b,S,C,w]}),t[25]=S,t[26]=C,t[27]=w,t[28]=T):T=t[28];let D;return t[29]!==T||t[30]!==p||t[31]!==y?(D=(0,$.jsxs)($.Fragment,{children:[p,y,T]}),t[29]=T,t[30]=p,t[31]=y,t[32]=D):D=t[32],D}function me(e){return(0,$.jsxs)(i,{value:e.TargetGroupArn??``,children:[e.TargetGroupName,` · `,e.Protocol,`:`,e.Port]},e.TargetGroupArn)}function he(e){let{field:t}=e;return(0,$.jsxs)(s,{onValueChange:t.onChange,value:t.value,children:[(0,$.jsx)(o,{className:`w-full`,id:`listener-protocol`,children:(0,$.jsx)(r,{})}),(0,$.jsx)(a,{children:(0,$.jsx)(i,{value:`HTTP`,children:`HTTP`})})]})}function ge({listenerArn:n,targetGroups:r}){let{data:i}=t(S(n)),a=p(),o=d(),s=m(),[l,u]=(0,Z.useState)(!1),[f,h]=(0,Z.useState)(),[g,_]=(0,Z.useState)(),v=i.Rules??[],y=new Map;for(let e of r)e.TargetGroupArn&&e.TargetGroupName&&y.set(e.TargetGroupArn,e.TargetGroupName);return(0,$.jsxs)(`div`,{className:`space-y-3`,children:[a.error&&(0,$.jsx)(e,{error:a.error,msg:`Failed to create rule`}),o.error&&(0,$.jsx)(e,{error:o.error,msg:`Failed to modify rule`}),s.error&&(0,$.jsx)(e,{error:s.error,msg:`Failed to delete rule`}),(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[v.length,` rule`,v.length===1?``:`s`,` (including default)`]}),(0,$.jsx)(T,{onClick:()=>u(!0),size:`sm`,children:`Add rule`})]}),(0,$.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,$.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Priority`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Conditions`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Forward to`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:(0,$.jsx)(`span`,{className:`sr-only`,children:`Actions`})})]})}),(0,$.jsx)(`tbody`,{children:v.map(e=>(0,$.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,$.jsx)(`td`,{className:`px-4 py-2`,children:e.Priority}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:_e(e.Conditions,e.IsDefault===!0)}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:ye(e,y)}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-right`,children:e.IsDefault===!0?(0,$.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`Edit via listener`}):(0,$.jsxs)(`div`,{className:`flex justify-end gap-1`,children:[(0,$.jsx)(T,{"aria-label":`Edit rule ${e.Priority}`,onClick:()=>h(e),size:`sm`,variant:`ghost`,children:(0,$.jsx)(fe,{className:`size-4`})}),(0,$.jsx)(T,{"aria-label":`Delete rule ${e.Priority}`,onClick:()=>_(e),size:`sm`,variant:`ghost`,children:(0,$.jsx)(c,{className:`size-4`})})]})})]},e.RuleArn??e.Priority??`default`))})]})}),(0,$.jsx)(xe,{isPending:a.isPending,mode:`add`,onOpenChange:u,onSubmit:async e=>{try{await a.mutateAsync({listenerArn:n,priority:e.priority,conditions:e.conditions.map(be),forwardTargetGroupArn:e.forwardTargetGroupArn}),u(!1)}catch{}},open:l,targetGroups:r}),(0,$.jsx)(xe,{initialRule:f,isPending:o.isPending,mode:`edit`,onOpenChange:e=>!e&&h(void 0),onSubmit:async e=>{if(f?.RuleArn)try{await o.mutateAsync({ruleArn:f.RuleArn,listenerArn:n,conditions:e.conditions.map(be),forwardTargetGroupArn:e.forwardTargetGroupArn}),h(void 0)}catch{}},open:f!==void 0,targetGroups:r}),(0,$.jsx)(K,{description:g?`Delete rule with priority ${g.Priority}? This cannot be undone.`:``,isPending:s.isPending,onConfirm:async()=>{if(g?.RuleArn)try{await s.mutateAsync({ruleArn:g.RuleArn,listenerArn:n})}finally{_(void 0)}},onOpenChange:e=>!e&&_(void 0),open:g!==void 0,title:`Delete rule`})]})}function _e(e,t){return t?`default`:!e||e.length===0?`—`:e.map(e=>{let t=ve(e);return`${e.Field}: ${t.join(`, `)}`}).join(` AND `)}function ve(e){if(e.Field===void 0)return e.Values??[];switch(e.Field){case`host-header`:return e.HostHeaderConfig?.Values??e.Values??[];case`path-pattern`:return e.PathPatternConfig?.Values??e.Values??[];case`http-header`:{let t=e.HttpHeaderConfig?.HttpHeaderName??`?`;return(e.HttpHeaderConfig?.Values??[]).map(e=>`${t}=${e}`)}case`http-request-method`:return e.HttpRequestMethodConfig?.Values??[];case`source-ip`:return e.SourceIpConfig?.Values??[];case`query-string`:return(e.QueryStringConfig?.Values??[]).map(e=>e.Key?`${e.Key}=${e.Value??``}`:e.Value??``);default:return e.Values??[]}}function ye(e,t){let n=e.Actions?.[0];return n?n.Type===`forward`&&n.TargetGroupArn?t.get(n.TargetGroupArn)??n.TargetGroupArn:n.Type??`—`:`—`}function be(e){let t=e.rawValues.split(/[\n,]/).map(e=>e.trim()).filter(e=>e.length>0);return e.field===`query-string`?{field:e.field,values:[],queryStringPairs:t.map(e=>{let t=e.indexOf(`=`);return t===-1?{value:e}:{key:e.slice(0,t),value:e.slice(t+1)}})}:{field:e.field,values:t,httpHeaderName:e.httpHeaderName}}function xe({open:e,onOpenChange:t,onSubmit:n,isPending:c,targetGroups:l,mode:u,initialRule:d}){let f=[{field:`path-pattern`,rawValues:``}],p=M({resolver:P(oe),defaultValues:{priority:1,conditions:f,forwardTargetGroupArn:``}}),{fields:m,append:h,remove:g}=re({control:p.control,name:`conditions`});(0,Z.useEffect)(()=>{if(u===`add`){e||p.reset({priority:1,conditions:f,forwardTargetGroupArn:``});return}if(!d)return;let t=Number(d.Priority??1),n=d.Conditions?.map(e=>Ee(e))??f;p.reset({priority:Number.isFinite(t)?t:1,conditions:n.length>0?n:f,forwardTargetGroupArn:d.Actions?.[0]?.TargetGroupArn??``})},[d,e,u]);let _=p.handleSubmit(n);return(0,$.jsx)(G,{onOpenChange:t,open:e,children:(0,$.jsxs)(z,{className:`grid-cols-[minmax(0,1fr)]`,style:{maxWidth:`40rem`,width:`calc(100vw - 2rem)`},children:[(0,$.jsxs)(W,{children:[(0,$.jsx)(B,{children:u===`add`?`Add rule`:`Edit rule`}),(0,$.jsx)(R,{children:`Routing rules match incoming requests and forward them to a target group. Rules evaluate in priority order; the listener default applies when no rule matches.`})]}),(0,$.jsxs)(`form`,{className:`min-w-0 space-y-4`,onSubmit:e=>{e.preventDefault(),_()},children:[(0,$.jsxs)(L,{children:[(0,$.jsx)(F,{children:(0,$.jsx)(`label`,{htmlFor:`rule-priority`,children:`Priority`})}),(0,$.jsx)(E,{"aria-invalid":!!p.formState.errors.priority,id:`rule-priority`,inputMode:`numeric`,type:`number`,...p.register(`priority`,{valueAsNumber:!0})}),(0,$.jsx)(I,{errors:[p.formState.errors.priority]})]}),(0,$.jsxs)(`div`,{className:`space-y-3`,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsx)(F,{children:`Conditions`}),(0,$.jsx)(T,{disabled:m.length>=5,onClick:()=>h({field:`path-pattern`,rawValues:``}),size:`sm`,type:`button`,variant:`outline`,children:`Add condition`})]}),m.map((e,t)=>(0,$.jsx)(Se,{form:p,index:t,onRemove:m.length>1?()=>g(t):void 0},e.id))]}),(0,$.jsxs)(L,{children:[(0,$.jsx)(F,{children:(0,$.jsx)(`label`,{htmlFor:`rule-forward-tg`,children:`Forward to target group`})}),(0,$.jsx)(N,{control:p.control,name:`forwardTargetGroupArn`,render:({field:e})=>(0,$.jsxs)(s,{onValueChange:e.onChange,value:e.value??``,children:[(0,$.jsx)(o,{"aria-invalid":!!p.formState.errors.forwardTargetGroupArn,className:`w-full`,id:`rule-forward-tg`,children:(0,$.jsx)(r,{placeholder:`Select target group`})}),(0,$.jsx)(a,{children:l.map(e=>(0,$.jsxs)(i,{value:e.TargetGroupArn??``,children:[e.TargetGroupName,` · `,e.Protocol,`:`,e.Port]},e.TargetGroupArn))})]})}),(0,$.jsx)(I,{errors:[p.formState.errors.forwardTargetGroupArn]})]})]}),(0,$.jsxs)(H,{children:[(0,$.jsx)(U,{disabled:c,children:`Cancel`}),(0,$.jsx)(V,{disabled:c,onClick:_,children:De(u,c)})]})]})})}function Se(e){let t=(0,Q.c)(54),{form:n,index:l,onRemove:u}=e,d,f,p,m,h,g,_,v,y,b;if(t[0]!==n||t[1]!==l||t[2]!==u){let e=n.watch(`conditions.${l}.field`);p=n.formState.errors.conditions?.[l],v=`space-y-2 rounded-md border p-3`;let x=`rule-cond-${l}-field`,S;t[13]===x?S=t[14]:(S=(0,$.jsx)(F,{children:(0,$.jsx)(`label`,{htmlFor:x,children:`Type`})}),t[13]=x,t[14]=S);let C=`conditions.${l}.field`,w;t[15]===l?w=t[16]:(w=e=>{let{field:t}=e;return(0,$.jsxs)(s,{onValueChange:t.onChange,value:t.value,children:[(0,$.jsx)(o,{className:`w-full`,id:`rule-cond-${l}-field`,children:(0,$.jsx)(r,{})}),(0,$.jsxs)(a,{children:[(0,$.jsx)(i,{value:`path-pattern`,children:`Path pattern`}),(0,$.jsx)(i,{value:`host-header`,children:`Host header`}),(0,$.jsx)(i,{value:`http-header`,children:`HTTP header`}),(0,$.jsx)(i,{value:`http-request-method`,children:`HTTP method`}),(0,$.jsx)(i,{value:`source-ip`,children:`Source IP CIDR`}),(0,$.jsx)(i,{value:`query-string`,children:`Query string`})]})]})},t[15]=l,t[16]=w);let D;t[17]!==n.control||t[18]!==C||t[19]!==w?(D=(0,$.jsx)(N,{control:n.control,name:C,render:w}),t[17]=n.control,t[18]=C,t[19]=w,t[20]=D):D=t[20];let O;t[21]!==D||t[22]!==S?(O=(0,$.jsxs)(`div`,{className:`flex-1`,children:[S,D]}),t[21]=D,t[22]=S,t[23]=O):O=t[23];let k;t[24]!==l||t[25]!==u?(k=u&&(0,$.jsx)(T,{"aria-label":`Remove condition ${l+1}`,className:`self-end`,onClick:u,size:`sm`,type:`button`,variant:`ghost`,children:(0,$.jsx)(c,{className:`size-4`})}),t[24]=l,t[25]=u,t[26]=k):k=t[26],t[27]!==O||t[28]!==k?(y=(0,$.jsxs)(`div`,{className:`flex gap-2`,children:[O,k]}),t[27]=O,t[28]=k,t[29]=y):y=t[29],b=e===`http-header`&&(0,$.jsxs)(L,{children:[(0,$.jsx)(F,{children:(0,$.jsx)(`label`,{htmlFor:`rule-cond-${l}-hdr-name`,children:`Header name`})}),(0,$.jsx)(E,{"aria-invalid":!!p?.httpHeaderName,id:`rule-cond-${l}-hdr-name`,placeholder:`X-Tenant`,...n.register(`conditions.${l}.httpHeaderName`)}),(0,$.jsx)(I,{errors:[p?.httpHeaderName]})]}),f=L;let A=`rule-cond-${l}-values`,j;t[30]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,$.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`(comma or newline separated, max 5)`}),t[30]=j):j=t[30],t[31]===A?_=t[32]:(_=(0,$.jsx)(F,{children:(0,$.jsxs)(`label`,{htmlFor:A,children:[`Values`,` `,j]})}),t[31]=A,t[32]=_),d=E,m=!!p?.rawValues,h=`rule-cond-${l}-values`,g=Ce(e),t[0]=n,t[1]=l,t[2]=u,t[3]=d,t[4]=f,t[5]=p,t[6]=m,t[7]=h,t[8]=g,t[9]=_,t[10]=v,t[11]=y,t[12]=b}else d=t[3],f=t[4],p=t[5],m=t[6],h=t[7],g=t[8],_=t[9],v=t[10],y=t[11],b=t[12];let x;t[33]!==n||t[34]!==l?(x=n.register(`conditions.${l}.rawValues`),t[33]=n,t[34]=l,t[35]=x):x=t[35];let S;t[36]!==d||t[37]!==m||t[38]!==h||t[39]!==g||t[40]!==x?(S=(0,$.jsx)(d,{"aria-invalid":m,id:h,placeholder:g,...x}),t[36]=d,t[37]=m,t[38]=h,t[39]=g,t[40]=x,t[41]=S):S=t[41];let C=p?.rawValues,w;t[42]===C?w=t[43]:(w=(0,$.jsx)(I,{errors:[C]}),t[42]=C,t[43]=w);let D;t[44]!==f||t[45]!==w||t[46]!==_||t[47]!==S?(D=(0,$.jsxs)(f,{children:[_,S,w]}),t[44]=f,t[45]=w,t[46]=_,t[47]=S,t[48]=D):D=t[48];let O;return t[49]!==D||t[50]!==v||t[51]!==y||t[52]!==b?(O=(0,$.jsxs)(`div`,{className:v,children:[y,b,D]}),t[49]=D,t[50]=v,t[51]=y,t[52]=b,t[53]=O):O=t[53],O}function Ce(e){switch(e){case`path-pattern`:return`/api/*`;case`host-header`:return`*.example.com`;case`http-header`:return`acme`;case`http-request-method`:return`GET, POST`;case`source-ip`:return`10.0.0.0/8`;case`query-string`:return`key=value`;default:return``}}var we=new Set([`host-header`,`path-pattern`,`http-header`,`http-request-method`,`source-ip`,`query-string`]);function Te(e){return e!==void 0&&we.has(e)}function Ee(e){let t=ve(e);return{field:Te(e.Field)?e.Field:`path-pattern`,httpHeaderName:e.HttpHeaderConfig?.HttpHeaderName,rawValues:t.join(`, `)}}function De(e,t){return t?e===`add`?`Adding…`:`Saving…`:e===`add`?`Add rule`:`Save changes`}function Oe({loadBalancerArn:n,vpcId:r}){let{data:i}=t(j(n)),{data:a}=t(b),o=h(),s=l(),u=g(),[d,f]=(0,Z.useState)(!1),[p,m]=(0,Z.useState)(),[_,v]=(0,Z.useState)(),[y,x]=(0,Z.useState)(),S=i.Listeners??[],C=a.TargetGroups??[],w=C.filter(e=>e.VpcId===r),E=new Map;for(let e of C)e.TargetGroupArn&&e.TargetGroupName&&E.set(e.TargetGroupArn,e.TargetGroupName);let D=e=>{let t=e.DefaultActions?.[0];if(!t)return`—`;if(t.Type===`forward`&&t.TargetGroupArn){let e=E.get(t.TargetGroupArn);return e?`forward → ${e}`:`forward → ${t.TargetGroupArn}`}return t.Type??`—`};return(0,$.jsxs)(`div`,{className:`space-y-3`,children:[o.error&&(0,$.jsx)(e,{error:o.error,msg:`Failed to create listener`}),u.error&&(0,$.jsx)(e,{error:u.error,msg:`Failed to modify listener`}),s.error&&(0,$.jsx)(e,{error:s.error,msg:`Failed to delete listener`}),(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[S.length,` listener`,S.length===1?``:`s`]}),(0,$.jsx)(T,{onClick:()=>f(!0),size:`sm`,children:`Add listener`})]}),S.length===0?(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`No listeners configured.`}):(0,$.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,$.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Protocol`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Port`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Default action`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Listener ARN`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:(0,$.jsx)(`span`,{className:`sr-only`,children:`Actions`})})]})}),(0,$.jsx)(`tbody`,{children:S.map(e=>{let t=e.ListenerArn??``,n=y===t;return(0,$.jsxs)(Z.Fragment,{children:[(0,$.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,$.jsx)(`td`,{className:`px-4 py-2`,children:e.Protocol}),(0,$.jsx)(`td`,{className:`px-4 py-2`,children:e.Port}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:D(e)}),(0,$.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:t}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-right`,children:(0,$.jsxs)(`div`,{className:`flex justify-end gap-1`,children:[(0,$.jsx)(T,{"aria-label":`${n?`Hide`:`Show`} rules for listener ${e.Protocol}:${e.Port}`,onClick:()=>x(n?void 0:t),size:`sm`,variant:`ghost`,children:n?`Hide rules`:`Rules`}),(0,$.jsx)(T,{"aria-label":`Edit listener ${e.Protocol}:${e.Port}`,onClick:()=>m(e),size:`sm`,variant:`ghost`,children:(0,$.jsx)(fe,{className:`size-4`})}),(0,$.jsx)(T,{"aria-label":`Delete listener ${e.Protocol}:${e.Port}`,onClick:()=>v(e),size:`sm`,variant:`ghost`,children:(0,$.jsx)(c,{className:`size-4`})})]})})]}),n&&t&&(0,$.jsx)(`tr`,{className:`border-b bg-muted/30 last:border-0`,children:(0,$.jsx)(`td`,{className:`px-4 py-3`,colSpan:5,children:(0,$.jsx)(ge,{listenerArn:t,targetGroups:w})})})]},t)})})]})}),(0,$.jsx)(ke,{isPending:o.isPending,onOpenChange:f,onSubmit:async e=>{try{await o.mutateAsync({loadBalancerArn:n,protocol:e.protocol,port:e.port,defaultTargetGroupArn:e.defaultTargetGroupArn}),f(!1)}catch{}},open:d,targetGroups:w}),(0,$.jsx)(Ae,{isPending:u.isPending,listener:p,onOpenChange:e=>!e&&m(void 0),onSubmit:async e=>{if(p?.ListenerArn)try{await u.mutateAsync({listenerArn:p.ListenerArn,loadBalancerArn:n,protocol:e.protocol,port:e.port,defaultTargetGroupArn:e.defaultTargetGroupArn}),m(void 0)}catch{}},targetGroups:w}),(0,$.jsx)(K,{description:_?`Delete listener ${_.Protocol}:${_.Port}? This cannot be undone.`:``,isPending:s.isPending,onConfirm:async()=>{if(_?.ListenerArn)try{await s.mutateAsync(_.ListenerArn)}finally{v(void 0)}},onOpenChange:e=>!e&&v(void 0),open:_!==void 0,title:`Delete listener`})]})}function ke(e){let t=(0,Q.c)(34),{open:n,onOpenChange:r,targetGroups:i,isPending:a,onSubmit:o}=e,s;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(s={resolver:P(se),defaultValues:{protocol:`HTTP`,port:80,defaultTargetGroupArn:``}},t[0]=s):s=t[0];let c=M(s),l,u;t[1]!==c||t[2]!==n?(l=()=>{n||c.reset({protocol:`HTTP`,port:80,defaultTargetGroupArn:``})},u=[n,c],t[1]=c,t[2]=n,t[3]=l,t[4]=u):(l=t[3],u=t[4]),(0,Z.useEffect)(l,u);let d;t[5]!==c||t[6]!==o?(d=c.handleSubmit(o),t[5]=c,t[6]=o,t[7]=d):d=t[7];let f=d,p;t[8]===Symbol.for(`react.memo_cache_sentinel`)?(p={maxWidth:`32rem`,width:`calc(100vw - 2rem)`},t[8]=p):p=t[8];let m;t[9]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,$.jsxs)(W,{children:[(0,$.jsx)(B,{children:`Add listener`}),(0,$.jsx)(R,{children:`Listeners forward incoming traffic to a default target group.`})]}),t[9]=m):m=t[9];let h;t[10]===f?h=t[11]:(h=e=>{e.preventDefault(),f()},t[10]=f,t[11]=h);let g;t[12]!==c||t[13]!==i?(g=(0,$.jsx)(pe,{form:c,targetGroups:i}),t[12]=c,t[13]=i,t[14]=g):g=t[14];let _;t[15]!==h||t[16]!==g?(_=(0,$.jsx)(`form`,{className:`min-w-0 space-y-4`,onSubmit:h,children:g}),t[15]=h,t[16]=g,t[17]=_):_=t[17];let v;t[18]===a?v=t[19]:(v=(0,$.jsx)(U,{disabled:a,children:`Cancel`}),t[18]=a,t[19]=v);let y=a?`Adding…`:`Add listener`,b;t[20]!==f||t[21]!==a||t[22]!==y?(b=(0,$.jsx)(V,{disabled:a,onClick:f,children:y}),t[20]=f,t[21]=a,t[22]=y,t[23]=b):b=t[23];let x;t[24]!==v||t[25]!==b?(x=(0,$.jsxs)(H,{children:[v,b]}),t[24]=v,t[25]=b,t[26]=x):x=t[26];let S;t[27]!==x||t[28]!==_?(S=(0,$.jsxs)(z,{className:`grid-cols-[minmax(0,1fr)]`,style:p,children:[m,_,x]}),t[27]=x,t[28]=_,t[29]=S):S=t[29];let C;return t[30]!==r||t[31]!==n||t[32]!==S?(C=(0,$.jsx)(G,{onOpenChange:r,open:n,children:S}),t[30]=r,t[31]=n,t[32]=S,t[33]=C):C=t[33],C}function Ae(e){let t=(0,Q.c)(34),{listener:n,onOpenChange:r,targetGroups:i,isPending:a,onSubmit:o}=e,s=n!==void 0,c;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(c={resolver:P(se),defaultValues:{protocol:`HTTP`,port:80,defaultTargetGroupArn:``}},t[0]=c):c=t[0];let l=M(c),u,d;t[1]!==l||t[2]!==n?(u=()=>{n&&l.reset({protocol:`HTTP`,port:n.Port??80,defaultTargetGroupArn:n.DefaultActions?.[0]?.TargetGroupArn??``})},d=[n,l],t[1]=l,t[2]=n,t[3]=u,t[4]=d):(u=t[3],d=t[4]),(0,Z.useEffect)(u,d);let f;t[5]!==l||t[6]!==o?(f=l.handleSubmit(o),t[5]=l,t[6]=o,t[7]=f):f=t[7];let p=f,m;t[8]===Symbol.for(`react.memo_cache_sentinel`)?(m={maxWidth:`32rem`,width:`calc(100vw - 2rem)`},t[8]=m):m=t[8];let h;t[9]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,$.jsxs)(W,{children:[(0,$.jsx)(B,{children:`Edit listener`}),(0,$.jsx)(R,{children:`Update port, protocol, or default target group. Changes apply without dropping the listener.`})]}),t[9]=h):h=t[9];let g;t[10]===p?g=t[11]:(g=e=>{e.preventDefault(),p()},t[10]=p,t[11]=g);let _;t[12]!==l||t[13]!==i?(_=(0,$.jsx)(pe,{form:l,targetGroups:i}),t[12]=l,t[13]=i,t[14]=_):_=t[14];let v;t[15]!==g||t[16]!==_?(v=(0,$.jsx)(`form`,{className:`min-w-0 space-y-4`,onSubmit:g,children:_}),t[15]=g,t[16]=_,t[17]=v):v=t[17];let y;t[18]===a?y=t[19]:(y=(0,$.jsx)(U,{disabled:a,children:`Cancel`}),t[18]=a,t[19]=y);let b=a?`Saving…`:`Save changes`,x;t[20]!==p||t[21]!==a||t[22]!==b?(x=(0,$.jsx)(V,{disabled:a,onClick:p,children:b}),t[20]=p,t[21]=a,t[22]=b,t[23]=x):x=t[23];let S;t[24]!==y||t[25]!==x?(S=(0,$.jsxs)(H,{children:[y,x]}),t[24]=y,t[25]=x,t[26]=S):S=t[26];let C;t[27]!==S||t[28]!==v?(C=(0,$.jsxs)(z,{className:`grid-cols-[minmax(0,1fr)]`,style:m,children:[h,v,S]}),t[27]=S,t[28]=v,t[29]=C):C=t[29];let w;return t[30]!==r||t[31]!==s||t[32]!==C?(w=(0,$.jsx)(G,{onOpenChange:r,open:s,children:C}),t[30]=r,t[31]=s,t[32]=C,t[33]=w):w=t[33],w}function je({arn:r}){let i=C(),{data:a}=t(A(r)),{data:o}=t(_(r)),{data:s}=t(x([r])),{data:l}=t(k),d=f(),p=u(),[m,h]=(0,Z.useState)(!1),g=a.LoadBalancers?.[0];if(!g?.LoadBalancerArn)return(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(n,{to:`/ec2/describe-load-balancers`,children:`Back to load balancers`}),(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`Load balancer not found.`})]});let v=async()=>{try{await d.mutateAsync(r),i({to:`/ec2/describe-load-balancers`})}finally{h(!1)}},y=l.Subnets??[],b=e=>{let t=y.find(t=>t.SubnetId===e);if(!t)return e;let n=D(t.Tags),r=t.CidrBlock?` (${t.CidrBlock})`:``;return n?`${e} — ${n}${r}`:`${e}${r}`},S=o.Attributes??[],w=s?.TagDescriptions?.find(e=>e.ResourceArn===r)?.Tags??[];return(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(n,{to:`/ec2/describe-load-balancers`,children:`Back to load balancers`}),d.error&&(0,$.jsx)(e,{error:d.error,msg:`Failed to delete load balancer`}),(0,$.jsxs)(`div`,{className:`space-y-6`,children:[(0,$.jsx)(ie,{actions:(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsxs)(T,{onClick:()=>h(!0),size:`sm`,variant:`destructive`,children:[(0,$.jsx)(c,{className:`size-4`}),`Delete`]}),(0,$.jsx)(ae,{state:g.State?.Code})]}),subtitle:`Load Balancer Details`,title:g.LoadBalancerName??g.LoadBalancerArn}),(0,$.jsxs)(de,{defaultValue:`overview`,children:[(0,$.jsxs)(le,{children:[(0,$.jsx)(Y,{value:`overview`,children:`Overview`}),(0,$.jsx)(Y,{value:`listeners`,children:`Listeners`}),(0,$.jsx)(Y,{value:`attributes`,children:`Attributes`}),(0,$.jsx)(Y,{value:`tags`,children:`Tags`})]}),(0,$.jsxs)(X,{value:`overview`,children:[(0,$.jsxs)(q,{children:[(0,$.jsx)(q.Header,{children:`Load balancer`}),(0,$.jsxs)(q.Content,{children:[(0,$.jsx)(J,{label:`ARN`,value:g.LoadBalancerArn}),(0,$.jsx)(J,{label:`DNS name`,value:g.DNSName}),(0,$.jsx)(J,{label:`State`,value:g.State?.Reason?`${g.State.Code} — ${g.State.Reason}`:g.State?.Code}),(0,$.jsx)(J,{label:`Type`,value:g.Type}),(0,$.jsx)(J,{label:`Scheme`,value:g.Scheme}),(0,$.jsx)(J,{label:`IP address type`,value:g.IpAddressType}),(0,$.jsx)(J,{label:`VPC`,value:g.VpcId}),(0,$.jsx)(J,{label:`Created at`,value:ee(g.CreatedTime)})]})]}),(0,$.jsxs)(q,{children:[(0,$.jsx)(q.Header,{children:`Availability zones`}),(0,$.jsx)(q.Content,{children:(g.AvailabilityZones??[]).map(e=>(0,$.jsx)(J,{label:e.ZoneName??`—`,value:e.SubnetId?b(e.SubnetId):void 0},`${e.ZoneName}-${e.SubnetId}`))})]}),(g.SecurityGroups??[]).length>0&&(0,$.jsxs)(q,{children:[(0,$.jsx)(q.Header,{children:`Security groups`}),(0,$.jsx)(q.Content,{children:(g.SecurityGroups??[]).map(e=>(0,$.jsx)(J,{label:`Security group`,value:e},e))})]})]}),(0,$.jsx)(X,{value:`listeners`,children:(0,$.jsx)(Oe,{loadBalancerArn:r,vpcId:g.VpcId})}),(0,$.jsx)(X,{value:`attributes`,children:(0,$.jsx)(ce,{attributes:S,error:p.error,isPending:p.isPending,isSuccess:p.isSuccess,onSubmit:e=>{p.mutate({loadBalancerArn:r,attributes:e})},specs:ue})}),(0,$.jsx)(X,{value:`tags`,children:w.length>0?(0,$.jsxs)(q,{children:[(0,$.jsx)(q.Header,{children:`Tags`}),(0,$.jsx)(q.Content,{children:w.map(e=>(0,$.jsx)(J,{label:e.Key??``,value:e.Value},e.Key??``))})]}):(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`No tags.`})})]})]}),(0,$.jsx)(K,{description:`Are you sure you want to delete load balancer "${g.LoadBalancerName??g.LoadBalancerArn}"? This action cannot be undone.`,isPending:d.isPending,onConfirm:v,onOpenChange:h,open:m,title:`Delete load balancer`})]})}function Me(){let e=(0,Q.c)(2),{id:t}=te.useParams(),n=decodeURIComponent(t),r;return e[0]===n?r=e[1]:(r=(0,$.jsx)(je,{arn:n}),e[0]=n,e[1]=r),r}export{Me as component}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{t as a}from"./useSuspenseQuery.js";import{A as o,D as s}from"./SignatureV4.js";import{t as c}from"./back-link.js";import{c as l,i as u,n as d,s as f,t as p}from"./select.js";import{t as m}from"./trash-2.js";import{t as h}from"./button.js";import{t as g}from"./certificate-import-dialog.js";import{_,a as v,d as y,f as b,g as x,h as S,o as C,r as w,s as T,t as E,u as D}from"./elbv2.js";import{B as O,F as k,G as A,I as j,Ir as M,L as N,Nr as P,Nt as F,P as I,Rn as ee,U as te,kt as ne,o as L,z as R}from"./index.js";import{a as re,i as z,n as ie,r as B,t as V}from"./zod.js";import{t as H}from"./page-heading.js";import{a as U,r as ae,t as W}from"./field.js";import{a as G,i as K,l as q,n as J,o as Y,r as oe,s as se,t as ce}from"./alert-dialog.js";import{t as le}from"./delete-confirmation-dialog.js";import{n as X,t as Z}from"./detail-row.js";import{t as ue}from"./state-badge.js";import{c as de,i as fe,n as pe,o as me,s as he,t as ge}from"./elbv22.js";import{n as _e,r as ve,t as ye}from"./tags-editor.js";import{i as be,n as xe,r as Se,t as Ce}from"./tabs.js";var we=P(`pencil`,[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]),Te=r(),Q=t(n(),1),$=e(),Ee=80,De=443;function Oe(e){let t=e.CertificateArn?.split(`/`).pop()??e.CertificateArn??``;return e.DomainName?`${e.DomainName} · ${t}`:t}function ke(e){let t=(0,Te.c)(59),{form:n,protocols:r,targetGroups:i,certificates:a,sslPolicies:o}=e,s;t[0]===a?s=t[1]:(s=a===void 0?[]:a,t[0]=a,t[1]=s);let c=s,u;t[2]===o?u=t[3]:(u=o===void 0?[]:o,t[2]=o,t[3]=u);let m=u,{control:_,register:v,getValues:y,setValue:b,formState:x}=n,{errors:S}=x,[C,w]=(0,Q.useState)(!1),T;t[4]===_?T=t[5]:(T={control:_,name:`protocol`},t[4]=_,t[5]=T);let E=re(T),D=E===`HTTPS`,O;t[6]===E?O=t[7]:(O=he(E),t[6]=E,t[7]=O);let k=O,A;t[8]!==y||t[9]!==b?(A=e=>{if(!e)return;let t=y(`port`);he(e)?(t===Ee&&b(`port`,De),e===`HTTPS`?y(`sslPolicy`)||b(`sslPolicy`,ge):b(`sslPolicy`,void 0)):(t===De&&b(`port`,Ee),b(`certificateArn`,void 0),b(`sslPolicy`,void 0))},t[8]=y,t[9]=b,t[10]=A):A=t[10];let j=A,M;t[11]===Symbol.for(`react.memo_cache_sentinel`)?(M=(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:`listener-protocol`,children:`Protocol`})}),t[11]=M):M=t[11];let N;t[12]!==j||t[13]!==r?(N=e=>{let{field:t}=e;return(0,$.jsxs)(p,{onValueChange:e=>{t.onChange(e),j(e)},value:t.value,children:[(0,$.jsx)(f,{className:`w-full`,id:`listener-protocol`,children:(0,$.jsx)(l,{})}),(0,$.jsx)(d,{children:r.map(Ne)})]})},t[12]=j,t[13]=r,t[14]=N):N=t[14];let P;t[15]!==_||t[16]!==N?(P=(0,$.jsxs)(W,{children:[M,(0,$.jsx)(ie,{control:_,name:`protocol`,render:N})]}),t[15]=_,t[16]=N,t[17]=P):P=t[17];let F;t[18]===Symbol.for(`react.memo_cache_sentinel`)?(F=(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:`listener-port`,children:`Port`})}),t[18]=F):F=t[18];let I=!!S.port,te=k?`443`:`80`,ne;t[19]===v?ne=t[20]:(ne=v(`port`,{valueAsNumber:!0}),t[19]=v,t[20]=ne);let L;t[21]!==I||t[22]!==te||t[23]!==ne?(L=(0,$.jsx)(ee,{"aria-invalid":I,id:`listener-port`,inputMode:`numeric`,placeholder:te,type:`number`,...ne}),t[21]=I,t[22]=te,t[23]=ne,t[24]=L):L=t[24];let R;t[25]===S.port?R=t[26]:(R=(0,$.jsx)(ae,{errors:[S.port]}),t[25]=S.port,t[26]=R);let z;t[27]!==L||t[28]!==R?(z=(0,$.jsxs)(W,{children:[F,L,R]}),t[27]=L,t[28]=R,t[29]=z):z=t[29];let B;t[30]!==c||t[31]!==_||t[32]!==S.certificateArn||t[33]!==C||t[34]!==D||t[35]!==k||t[36]!==b||t[37]!==m?(B=k&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(W,{children:[(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:`listener-certificate`,children:`Certificate`})}),(0,$.jsx)(ie,{control:_,name:`certificateArn`,render:e=>{let{field:t}=e;return(0,$.jsxs)(p,{onValueChange:t.onChange,value:t.value??``,children:[(0,$.jsx)(f,{"aria-invalid":!!S.certificateArn,className:`w-full`,id:`listener-certificate`,children:(0,$.jsx)(l,{placeholder:`Select certificate`})}),(0,$.jsx)(d,{children:c.map(Me)})]})}}),(0,$.jsx)(ae,{errors:[S.certificateArn]}),(0,$.jsxs)(`div`,{className:`mt-1 flex items-center justify-between`,children:[c.length===0&&(0,$.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No certificates imported yet.`}),(0,$.jsx)(h,{className:`ml-auto`,onClick:()=>w(!0),size:`sm`,type:`button`,variant:`ghost`,children:`Import certificate…`})]})]}),D&&(0,$.jsxs)(W,{children:[(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:`listener-ssl-policy`,children:`Security policy`})}),(0,$.jsx)(ie,{control:_,name:`sslPolicy`,render:e=>{let{field:t}=e;return(0,$.jsxs)(p,{onValueChange:t.onChange,value:t.value??`ELBSecurityPolicy-2016-08`,children:[(0,$.jsx)(f,{className:`w-full`,id:`listener-ssl-policy`,children:(0,$.jsx)(l,{})}),(0,$.jsx)(d,{children:m.map(je)})]})}})]}),(0,$.jsx)(g,{onImported:e=>b(`certificateArn`,e,{shouldValidate:!0}),onOpenChange:w,open:C})]}),t[30]=c,t[31]=_,t[32]=S.certificateArn,t[33]=C,t[34]=D,t[35]=k,t[36]=b,t[37]=m,t[38]=B):B=t[38];let V;t[39]===Symbol.for(`react.memo_cache_sentinel`)?(V=(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:`listener-default-tg`,children:`Default target group`})}),t[39]=V):V=t[39];let H;t[40]!==S.defaultTargetGroupArn||t[41]!==i?(H=e=>{let{field:t}=e;return(0,$.jsxs)(p,{onValueChange:t.onChange,value:t.value??``,children:[(0,$.jsx)(f,{"aria-invalid":!!S.defaultTargetGroupArn,className:`w-full`,id:`listener-default-tg`,children:(0,$.jsx)(l,{placeholder:`Select target group`})}),(0,$.jsx)(d,{children:i.map(Ae)})]})},t[40]=S.defaultTargetGroupArn,t[41]=i,t[42]=H):H=t[42];let G;t[43]!==_||t[44]!==H?(G=(0,$.jsx)(ie,{control:_,name:`defaultTargetGroupArn`,render:H}),t[43]=_,t[44]=H,t[45]=G):G=t[45];let K;t[46]===S.defaultTargetGroupArn?K=t[47]:(K=(0,$.jsx)(ae,{errors:[S.defaultTargetGroupArn]}),t[46]=S.defaultTargetGroupArn,t[47]=K);let q;t[48]===i.length?q=t[49]:(q=i.length===0&&(0,$.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`No target groups available in this VPC. Create one first.`}),t[48]=i.length,t[49]=q);let J;t[50]!==G||t[51]!==K||t[52]!==q?(J=(0,$.jsxs)(W,{children:[V,G,K,q]}),t[50]=G,t[51]=K,t[52]=q,t[53]=J):J=t[53];let Y;return t[54]!==P||t[55]!==z||t[56]!==B||t[57]!==J?(Y=(0,$.jsxs)($.Fragment,{children:[P,z,B,J]}),t[54]=P,t[55]=z,t[56]=B,t[57]=J,t[58]=Y):Y=t[58],Y}function Ae(e){return(0,$.jsxs)(u,{value:e.TargetGroupArn??``,children:[e.TargetGroupName,` · `,e.Protocol,`:`,e.Port]},e.TargetGroupArn)}function je(e){return(0,$.jsx)(u,{value:e.Name??``,children:e.Name},e.Name)}function Me(e){return(0,$.jsx)(u,{value:e.CertificateArn??``,children:Oe(e)},e.CertificateArn)}function Ne(e){return(0,$.jsx)(u,{value:e,children:e},e)}function Pe({listenerArn:e,targetGroups:t}){let{data:n}=a(I(e)),r=w(),o=b(),s=T(),[c,l]=(0,Q.useState)(!1),[u,d]=(0,Q.useState)(),[f,p]=(0,Q.useState)(),g=n.Rules??[],_=new Map;for(let e of t)e.TargetGroupArn&&e.TargetGroupName&&_.set(e.TargetGroupArn,e.TargetGroupName);return(0,$.jsxs)(`div`,{className:`space-y-3`,children:[r.error&&(0,$.jsx)(i,{error:r.error,msg:`Failed to create rule`}),o.error&&(0,$.jsx)(i,{error:o.error,msg:`Failed to modify rule`}),s.error&&(0,$.jsx)(i,{error:s.error,msg:`Failed to delete rule`}),(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[g.length,` rule`,g.length===1?``:`s`,` (including default)`]}),(0,$.jsx)(h,{onClick:()=>l(!0),size:`sm`,children:`Add rule`})]}),(0,$.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,$.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Priority`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Conditions`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Forward to`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:(0,$.jsx)(`span`,{className:`sr-only`,children:`Actions`})})]})}),(0,$.jsx)(`tbody`,{children:g.map(e=>(0,$.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,$.jsx)(`td`,{className:`px-4 py-2`,children:e.Priority}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:Fe(e.Conditions,e.IsDefault===!0)}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:Le(e,_)}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-right`,children:e.IsDefault===!0?(0,$.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`Edit via listener`}):(0,$.jsxs)(`div`,{className:`flex justify-end gap-1`,children:[(0,$.jsx)(h,{"aria-label":`Edit rule ${e.Priority}`,onClick:()=>d(e),size:`sm`,variant:`ghost`,children:(0,$.jsx)(we,{className:`size-4`})}),(0,$.jsx)(h,{"aria-label":`Delete rule ${e.Priority}`,onClick:()=>p(e),size:`sm`,variant:`ghost`,children:(0,$.jsx)(m,{className:`size-4`})})]})})]},e.RuleArn??e.Priority??`default`))})]})}),(0,$.jsx)(ze,{isPending:r.isPending,mode:`add`,onOpenChange:l,onSubmit:async t=>{try{await r.mutateAsync({listenerArn:e,priority:t.priority,conditions:t.conditions.map(Re),forwardTargetGroupArn:t.forwardTargetGroupArn}),l(!1)}catch{}},open:c,targetGroups:t}),(0,$.jsx)(ze,{initialRule:u,isPending:o.isPending,mode:`edit`,onOpenChange:e=>!e&&d(void 0),onSubmit:async t=>{if(u?.RuleArn)try{await o.mutateAsync({ruleArn:u.RuleArn,listenerArn:e,conditions:t.conditions.map(Re),forwardTargetGroupArn:t.forwardTargetGroupArn}),d(void 0)}catch{}},open:u!==void 0,targetGroups:t}),(0,$.jsx)(le,{description:f?`Delete rule with priority ${f.Priority}? This cannot be undone.`:``,isPending:s.isPending,onConfirm:async()=>{if(f?.RuleArn)try{await s.mutateAsync({ruleArn:f.RuleArn,listenerArn:e})}finally{p(void 0)}},onOpenChange:e=>!e&&p(void 0),open:f!==void 0,title:`Delete rule`})]})}function Fe(e,t){return t?`default`:!e||e.length===0?`—`:e.map(e=>{let t=Ie(e);return`${e.Field}: ${t.join(`, `)}`}).join(` AND `)}function Ie(e){if(e.Field===void 0)return e.Values??[];switch(e.Field){case`host-header`:return e.HostHeaderConfig?.Values??e.Values??[];case`path-pattern`:return e.PathPatternConfig?.Values??e.Values??[];case`http-header`:{let t=e.HttpHeaderConfig?.HttpHeaderName??`?`;return(e.HttpHeaderConfig?.Values??[]).map(e=>`${t}=${e}`)}case`http-request-method`:return e.HttpRequestMethodConfig?.Values??[];case`source-ip`:return e.SourceIpConfig?.Values??[];case`query-string`:return(e.QueryStringConfig?.Values??[]).map(e=>e.Key?`${e.Key}=${e.Value??``}`:e.Value??``);default:return e.Values??[]}}function Le(e,t){let n=e.Actions?.[0];return n?n.Type===`forward`&&n.TargetGroupArn?t.get(n.TargetGroupArn)??n.TargetGroupArn:n.Type??`—`:`—`}function Re(e){let t=e.rawValues.split(/[\n,]/).map(e=>e.trim()).filter(e=>e.length>0);return e.field===`query-string`?{field:e.field,values:[],queryStringPairs:t.map(e=>{let t=e.indexOf(`=`);return t===-1?{value:e}:{key:e.slice(0,t),value:e.slice(t+1)}})}:{field:e.field,values:t,httpHeaderName:e.httpHeaderName}}function ze({open:e,onOpenChange:t,onSubmit:n,isPending:r,targetGroups:i,mode:a,initialRule:o}){let s=[{field:`path-pattern`,rawValues:``}],c=z({resolver:V(fe),defaultValues:{priority:1,conditions:s,forwardTargetGroupArn:``}}),{fields:m,append:g,remove:_}=B({control:c.control,name:`conditions`});(0,Q.useEffect)(()=>{if(a===`add`){e||c.reset({priority:1,conditions:s,forwardTargetGroupArn:``});return}if(!o)return;let t=Number(o.Priority??1),n=o.Conditions?.map(e=>We(e))??s;c.reset({priority:Number.isFinite(t)?t:1,conditions:n.length>0?n:s,forwardTargetGroupArn:o.Actions?.[0]?.TargetGroupArn??``})},[o,e,a]);let v=c.handleSubmit(n);return(0,$.jsx)(ce,{onOpenChange:t,open:e,children:(0,$.jsxs)(K,{className:`grid-cols-[minmax(0,1fr)]`,style:{maxWidth:`40rem`,width:`calc(100vw - 2rem)`},children:[(0,$.jsxs)(se,{children:[(0,$.jsx)(q,{children:a===`add`?`Add rule`:`Edit rule`}),(0,$.jsx)(G,{children:`Routing rules match incoming requests and forward them to a target group. Rules evaluate in priority order; the listener default applies when no rule matches.`})]}),(0,$.jsxs)(`form`,{className:`min-w-0 space-y-4`,onSubmit:e=>{e.preventDefault(),v()},children:[(0,$.jsxs)(W,{children:[(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:`rule-priority`,children:`Priority`})}),(0,$.jsx)(ee,{"aria-invalid":!!c.formState.errors.priority,id:`rule-priority`,inputMode:`numeric`,type:`number`,...c.register(`priority`,{valueAsNumber:!0})}),(0,$.jsx)(ae,{errors:[c.formState.errors.priority]})]}),(0,$.jsxs)(`div`,{className:`space-y-3`,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsx)(U,{children:`Conditions`}),(0,$.jsx)(h,{disabled:m.length>=5,onClick:()=>g({field:`path-pattern`,rawValues:``}),size:`sm`,type:`button`,variant:`outline`,children:`Add condition`})]}),m.map((e,t)=>(0,$.jsx)(Be,{form:c,index:t,onRemove:m.length>1?()=>_(t):void 0},e.id))]}),(0,$.jsxs)(W,{children:[(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:`rule-forward-tg`,children:`Forward to target group`})}),(0,$.jsx)(ie,{control:c.control,name:`forwardTargetGroupArn`,render:({field:e})=>(0,$.jsxs)(p,{onValueChange:e.onChange,value:e.value??``,children:[(0,$.jsx)(f,{"aria-invalid":!!c.formState.errors.forwardTargetGroupArn,className:`w-full`,id:`rule-forward-tg`,children:(0,$.jsx)(l,{placeholder:`Select target group`})}),(0,$.jsx)(d,{children:i.map(e=>(0,$.jsxs)(u,{value:e.TargetGroupArn??``,children:[e.TargetGroupName,` · `,e.Protocol,`:`,e.Port]},e.TargetGroupArn))})]})}),(0,$.jsx)(ae,{errors:[c.formState.errors.forwardTargetGroupArn]})]})]}),(0,$.jsxs)(Y,{children:[(0,$.jsx)(oe,{disabled:r,children:`Cancel`}),(0,$.jsx)(J,{disabled:r,onClick:v,children:Ge(a,r)})]})]})})}function Be(e){let t=(0,Te.c)(54),{form:n,index:r,onRemove:i}=e,a,o,s,c,g,_,v,y,b,x;if(t[0]!==n||t[1]!==r||t[2]!==i){let e=n.watch(`conditions.${r}.field`);s=n.formState.errors.conditions?.[r],y=`space-y-2 rounded-md border p-3`;let S=`rule-cond-${r}-field`,C;t[13]===S?C=t[14]:(C=(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:S,children:`Type`})}),t[13]=S,t[14]=C);let w=`conditions.${r}.field`,T;t[15]===r?T=t[16]:(T=e=>{let{field:t}=e;return(0,$.jsxs)(p,{onValueChange:t.onChange,value:t.value,children:[(0,$.jsx)(f,{className:`w-full`,id:`rule-cond-${r}-field`,children:(0,$.jsx)(l,{})}),(0,$.jsxs)(d,{children:[(0,$.jsx)(u,{value:`path-pattern`,children:`Path pattern`}),(0,$.jsx)(u,{value:`host-header`,children:`Host header`}),(0,$.jsx)(u,{value:`http-header`,children:`HTTP header`}),(0,$.jsx)(u,{value:`http-request-method`,children:`HTTP method`}),(0,$.jsx)(u,{value:`source-ip`,children:`Source IP CIDR`}),(0,$.jsx)(u,{value:`query-string`,children:`Query string`})]})]})},t[15]=r,t[16]=T);let E;t[17]!==n.control||t[18]!==w||t[19]!==T?(E=(0,$.jsx)(ie,{control:n.control,name:w,render:T}),t[17]=n.control,t[18]=w,t[19]=T,t[20]=E):E=t[20];let D;t[21]!==E||t[22]!==C?(D=(0,$.jsxs)(`div`,{className:`flex-1`,children:[C,E]}),t[21]=E,t[22]=C,t[23]=D):D=t[23];let O;t[24]!==r||t[25]!==i?(O=i&&(0,$.jsx)(h,{"aria-label":`Remove condition ${r+1}`,className:`self-end`,onClick:i,size:`sm`,type:`button`,variant:`ghost`,children:(0,$.jsx)(m,{className:`size-4`})}),t[24]=r,t[25]=i,t[26]=O):O=t[26],t[27]!==D||t[28]!==O?(b=(0,$.jsxs)(`div`,{className:`flex gap-2`,children:[D,O]}),t[27]=D,t[28]=O,t[29]=b):b=t[29],x=e===`http-header`&&(0,$.jsxs)(W,{children:[(0,$.jsx)(U,{children:(0,$.jsx)(`label`,{htmlFor:`rule-cond-${r}-hdr-name`,children:`Header name`})}),(0,$.jsx)(ee,{"aria-invalid":!!s?.httpHeaderName,id:`rule-cond-${r}-hdr-name`,placeholder:`X-Tenant`,...n.register(`conditions.${r}.httpHeaderName`)}),(0,$.jsx)(ae,{errors:[s?.httpHeaderName]})]}),o=W;let k=`rule-cond-${r}-values`,A;t[30]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,$.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`(comma or newline separated, max 5)`}),t[30]=A):A=t[30],t[31]===k?v=t[32]:(v=(0,$.jsx)(U,{children:(0,$.jsxs)(`label`,{htmlFor:k,children:[`Values`,` `,A]})}),t[31]=k,t[32]=v),a=ee,c=!!s?.rawValues,g=`rule-cond-${r}-values`,_=Ve(e),t[0]=n,t[1]=r,t[2]=i,t[3]=a,t[4]=o,t[5]=s,t[6]=c,t[7]=g,t[8]=_,t[9]=v,t[10]=y,t[11]=b,t[12]=x}else a=t[3],o=t[4],s=t[5],c=t[6],g=t[7],_=t[8],v=t[9],y=t[10],b=t[11],x=t[12];let S;t[33]!==n||t[34]!==r?(S=n.register(`conditions.${r}.rawValues`),t[33]=n,t[34]=r,t[35]=S):S=t[35];let C;t[36]!==a||t[37]!==c||t[38]!==g||t[39]!==_||t[40]!==S?(C=(0,$.jsx)(a,{"aria-invalid":c,id:g,placeholder:_,...S}),t[36]=a,t[37]=c,t[38]=g,t[39]=_,t[40]=S,t[41]=C):C=t[41];let w=s?.rawValues,T;t[42]===w?T=t[43]:(T=(0,$.jsx)(ae,{errors:[w]}),t[42]=w,t[43]=T);let E;t[44]!==o||t[45]!==T||t[46]!==v||t[47]!==C?(E=(0,$.jsxs)(o,{children:[v,C,T]}),t[44]=o,t[45]=T,t[46]=v,t[47]=C,t[48]=E):E=t[48];let D;return t[49]!==E||t[50]!==y||t[51]!==b||t[52]!==x?(D=(0,$.jsxs)(`div`,{className:y,children:[b,x,E]}),t[49]=E,t[50]=y,t[51]=b,t[52]=x,t[53]=D):D=t[53],D}function Ve(e){switch(e){case`path-pattern`:return`/api/*`;case`host-header`:return`*.example.com`;case`http-header`:return`acme`;case`http-request-method`:return`GET, POST`;case`source-ip`:return`10.0.0.0/8`;case`query-string`:return`key=value`;default:return``}}var He=new Set([`host-header`,`path-pattern`,`http-header`,`http-request-method`,`source-ip`,`query-string`]);function Ue(e){return e!==void 0&&He.has(e)}function We(e){let t=Ie(e);return{field:Ue(e.Field)?e.Field:`path-pattern`,httpHeaderName:e.HttpHeaderConfig?.HttpHeaderName,rawValues:t.join(`, `)}}function Ge(e,t){return t?e===`add`?`Adding…`:`Saving…`:e===`add`?`Add rule`:`Save changes`}function Ke(e){return{protocol:me(e),port:80,defaultTargetGroupArn:``}}function qe(e){if(e.Protocol!==`HTTPS`)return`—`;let t=e.Certificates?.[0]?.CertificateArn?.split(`/`).pop(),n=e.SslPolicy;return t&&n?`${n} · ${t}`:n??t??`—`}function Je({loadBalancerArn:e,vpcId:t,lbType:n}){let r=de(n),{data:o}=a(k(e)),{data:s}=a(te),{data:c}=a(A),{data:l}=a(R),u=E(),d=v(),f=D(),[p,g]=(0,Q.useState)(!1),[_,y]=(0,Q.useState)(),[b,x]=(0,Q.useState)(),[S,C]=(0,Q.useState)(),w=o.Listeners??[],T=s.TargetGroups??[],O=T.filter(e=>e.VpcId===t),j=c.CertificateSummaryList??[],M=l.SslPolicies??[],N=new Map;for(let e of T)e.TargetGroupArn&&e.TargetGroupName&&N.set(e.TargetGroupArn,e.TargetGroupName);let P=e=>{let t=e.DefaultActions?.[0];if(!t)return`—`;if(t.Type===`forward`&&t.TargetGroupArn){let e=N.get(t.TargetGroupArn);return e?`forward → ${e}`:`forward → ${t.TargetGroupArn}`}return t.Type??`—`};return(0,$.jsxs)(`div`,{className:`space-y-3`,children:[u.error&&(0,$.jsx)(i,{error:u.error,msg:`Failed to create listener`}),f.error&&(0,$.jsx)(i,{error:f.error,msg:`Failed to modify listener`}),d.error&&(0,$.jsx)(i,{error:d.error,msg:`Failed to delete listener`}),(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[w.length,` listener`,w.length===1?``:`s`]}),(0,$.jsx)(h,{onClick:()=>g(!0),size:`sm`,children:`Add listener`})]}),w.length===0?(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`No listeners configured.`}):(0,$.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,$.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Protocol`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Port`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`TLS`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Default action`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Listener ARN`}),(0,$.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:(0,$.jsx)(`span`,{className:`sr-only`,children:`Actions`})})]})}),(0,$.jsx)(`tbody`,{children:w.map(e=>{let t=e.ListenerArn??``,n=S===t;return(0,$.jsxs)(Q.Fragment,{children:[(0,$.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,$.jsx)(`td`,{className:`px-4 py-2`,children:e.Protocol}),(0,$.jsx)(`td`,{className:`px-4 py-2`,children:e.Port}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:qe(e)}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:P(e)}),(0,$.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:t}),(0,$.jsx)(`td`,{className:`px-4 py-2 text-right`,children:(0,$.jsxs)(`div`,{className:`flex justify-end gap-1`,children:[(0,$.jsx)(h,{"aria-label":`${n?`Hide`:`Show`} rules for listener ${e.Protocol}:${e.Port}`,onClick:()=>C(n?void 0:t),size:`sm`,variant:`ghost`,children:n?`Hide rules`:`Rules`}),(0,$.jsx)(h,{"aria-label":`Edit listener ${e.Protocol}:${e.Port}`,onClick:()=>y(e),size:`sm`,variant:`ghost`,children:(0,$.jsx)(we,{className:`size-4`})}),(0,$.jsx)(h,{"aria-label":`Delete listener ${e.Protocol}:${e.Port}`,onClick:()=>x(e),size:`sm`,variant:`ghost`,children:(0,$.jsx)(m,{className:`size-4`})})]})})]}),n&&t&&(0,$.jsx)(`tr`,{className:`border-b bg-muted/30 last:border-0`,children:(0,$.jsx)(`td`,{className:`px-4 py-3`,colSpan:6,children:(0,$.jsx)(Pe,{listenerArn:t,targetGroups:O})})})]},t)})})]})}),(0,$.jsx)(Ye,{certificates:j,isPending:u.isPending,lbType:n,onOpenChange:g,onSubmit:async t=>{try{await u.mutateAsync({loadBalancerArn:e,protocol:t.protocol,port:t.port,defaultTargetGroupArn:t.defaultTargetGroupArn,certificateArn:t.certificateArn,sslPolicy:t.sslPolicy}),g(!1)}catch{}},open:p,protocols:r,sslPolicies:M,targetGroups:O}),(0,$.jsx)(Xe,{certificates:j,isPending:f.isPending,listener:_,onOpenChange:e=>!e&&y(void 0),onSubmit:async t=>{if(_?.ListenerArn)try{await f.mutateAsync({listenerArn:_.ListenerArn,loadBalancerArn:e,protocol:t.protocol,port:t.port,defaultTargetGroupArn:t.defaultTargetGroupArn,certificateArn:t.certificateArn,sslPolicy:t.sslPolicy}),y(void 0)}catch{}},protocols:r,sslPolicies:M,targetGroups:O}),(0,$.jsx)(le,{description:b?`Delete listener ${b.Protocol}:${b.Port}? This cannot be undone.`:``,isPending:d.isPending,onConfirm:async()=>{if(b?.ListenerArn)try{await d.mutateAsync(b.ListenerArn)}finally{x(void 0)}},onOpenChange:e=>!e&&x(void 0),open:b!==void 0,title:`Delete listener`})]})}function Ye(e){let t=(0,Te.c)(42),{open:n,onOpenChange:r,lbType:i,protocols:a,targetGroups:o,certificates:s,sslPolicies:c,isPending:l,onSubmit:u}=e,d;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(d=V(pe),t[0]=d):d=t[0];let f;t[1]===i?f=t[2]:(f=Ke(i),t[1]=i,t[2]=f);let p;t[3]===f?p=t[4]:(p={resolver:d,defaultValues:f},t[3]=f,t[4]=p);let m=z(p),h,g;t[5]!==m||t[6]!==i||t[7]!==n?(h=()=>{n||m.reset(Ke(i))},g=[n,m,i],t[5]=m,t[6]=i,t[7]=n,t[8]=h,t[9]=g):(h=t[8],g=t[9]),(0,Q.useEffect)(h,g);let _;t[10]!==m||t[11]!==u?(_=m.handleSubmit(u),t[10]=m,t[11]=u,t[12]=_):_=t[12];let v=_,y;t[13]===Symbol.for(`react.memo_cache_sentinel`)?(y={maxWidth:`32rem`,width:`calc(100vw - 2rem)`},t[13]=y):y=t[13];let b;t[14]===Symbol.for(`react.memo_cache_sentinel`)?(b=(0,$.jsxs)(se,{children:[(0,$.jsx)(q,{children:`Add listener`}),(0,$.jsx)(G,{children:`Listeners forward incoming traffic to a default target group.`})]}),t[14]=b):b=t[14];let x;t[15]===v?x=t[16]:(x=e=>{e.preventDefault(),v()},t[15]=v,t[16]=x);let S;t[17]!==s||t[18]!==m||t[19]!==a||t[20]!==c||t[21]!==o?(S=(0,$.jsx)(ke,{certificates:s,form:m,protocols:a,sslPolicies:c,targetGroups:o}),t[17]=s,t[18]=m,t[19]=a,t[20]=c,t[21]=o,t[22]=S):S=t[22];let C;t[23]!==S||t[24]!==x?(C=(0,$.jsx)(`form`,{className:`min-w-0 space-y-4`,onSubmit:x,children:S}),t[23]=S,t[24]=x,t[25]=C):C=t[25];let w;t[26]===l?w=t[27]:(w=(0,$.jsx)(oe,{disabled:l,children:`Cancel`}),t[26]=l,t[27]=w);let T=l?`Adding…`:`Add listener`,E;t[28]!==v||t[29]!==l||t[30]!==T?(E=(0,$.jsx)(J,{disabled:l,onClick:v,children:T}),t[28]=v,t[29]=l,t[30]=T,t[31]=E):E=t[31];let D;t[32]!==w||t[33]!==E?(D=(0,$.jsxs)(Y,{children:[w,E]}),t[32]=w,t[33]=E,t[34]=D):D=t[34];let O;t[35]!==C||t[36]!==D?(O=(0,$.jsxs)(K,{className:`grid-cols-[minmax(0,1fr)]`,style:y,children:[b,C,D]}),t[35]=C,t[36]=D,t[37]=O):O=t[37];let k;return t[38]!==r||t[39]!==n||t[40]!==O?(k=(0,$.jsx)(ce,{onOpenChange:r,open:n,children:O}),t[38]=r,t[39]=n,t[40]=O,t[41]=k):k=t[41],k}function Xe(e){let t=(0,Te.c)(38),{listener:n,onOpenChange:r,protocols:i,targetGroups:a,certificates:o,sslPolicies:s,isPending:c,onSubmit:l}=e,u=n!==void 0,d;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(d={resolver:V(pe),defaultValues:{protocol:`HTTP`,port:80,defaultTargetGroupArn:``}},t[0]=d):d=t[0];let f=z(d),p,m;t[1]!==f||t[2]!==n||t[3]!==i?(p=()=>{if(!n)return;let e=i.find(e=>e===n.Protocol)??i[0]??`HTTP`,t=he(e);f.reset({protocol:e,port:n.Port??80,defaultTargetGroupArn:n.DefaultActions?.[0]?.TargetGroupArn??``,certificateArn:t?n.Certificates?.[0]?.CertificateArn:void 0,sslPolicy:e===`HTTPS`?n.SslPolicy:void 0})},m=[n,f,i],t[1]=f,t[2]=n,t[3]=i,t[4]=p,t[5]=m):(p=t[4],m=t[5]),(0,Q.useEffect)(p,m);let h;t[6]!==f||t[7]!==l?(h=f.handleSubmit(l),t[6]=f,t[7]=l,t[8]=h):h=t[8];let g=h,_;t[9]===Symbol.for(`react.memo_cache_sentinel`)?(_={maxWidth:`32rem`,width:`calc(100vw - 2rem)`},t[9]=_):_=t[9];let v;t[10]===Symbol.for(`react.memo_cache_sentinel`)?(v=(0,$.jsxs)(se,{children:[(0,$.jsx)(q,{children:`Edit listener`}),(0,$.jsx)(G,{children:`Update port, protocol, or default target group. Changes apply without dropping the listener.`})]}),t[10]=v):v=t[10];let y;t[11]===g?y=t[12]:(y=e=>{e.preventDefault(),g()},t[11]=g,t[12]=y);let b;t[13]!==o||t[14]!==f||t[15]!==i||t[16]!==s||t[17]!==a?(b=(0,$.jsx)(ke,{certificates:o,form:f,protocols:i,sslPolicies:s,targetGroups:a}),t[13]=o,t[14]=f,t[15]=i,t[16]=s,t[17]=a,t[18]=b):b=t[18];let x;t[19]!==y||t[20]!==b?(x=(0,$.jsx)(`form`,{className:`min-w-0 space-y-4`,onSubmit:y,children:b}),t[19]=y,t[20]=b,t[21]=x):x=t[21];let S;t[22]===c?S=t[23]:(S=(0,$.jsx)(oe,{disabled:c,children:`Cancel`}),t[22]=c,t[23]=S);let C=c?`Saving…`:`Save changes`,w;t[24]!==g||t[25]!==c||t[26]!==C?(w=(0,$.jsx)(J,{disabled:c,onClick:g,children:C}),t[24]=g,t[25]=c,t[26]=C,t[27]=w):w=t[27];let T;t[28]!==S||t[29]!==w?(T=(0,$.jsxs)(Y,{children:[S,w]}),t[28]=S,t[29]=w,t[30]=T):T=t[30];let E;t[31]!==T||t[32]!==x?(E=(0,$.jsxs)(K,{className:`grid-cols-[minmax(0,1fr)]`,style:_,children:[v,x,T]}),t[31]=T,t[32]=x,t[33]=E):E=t[33];let D;return t[34]!==r||t[35]!==u||t[36]!==E?(D=(0,$.jsx)(ce,{onOpenChange:r,open:u,children:E}),t[34]=r,t[35]=u,t[36]=E,t[37]=D):D=t[37],D}function Ze(e,t){if(e.length!==t.length)return!1;let n=new Set(t);return e.every(e=>n.has(e))}function Qe(e){let t=(0,Te.c)(46),{available:n,current:r,onSubmit:i,isPending:a,error:o,isSuccess:s}=e,c;t[0]===r?c=t[1]:(c=[...r],t[0]=r,t[1]=c);let l=c,[u,d]=(0,Q.useState)(l),f;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(f=e=>{d(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},t[2]=f):f=t[2];let p=f,m;t[3]===l?m=t[4]:(m=()=>d(l),t[3]=l,t[4]=m);let g=m,_;t[5]!==l||t[6]!==u?(_=Ze(u,l),t[5]=l,t[6]=u,t[7]=_):_=t[7];let v=!_,y=u.length===0,b;t[8]!==v||t[9]!==y||t[10]!==i||t[11]!==u?(b=e=>{e.preventDefault(),!(!v||y)&&i(u)},t[8]=v,t[9]=y,t[10]=i,t[11]=u,t[12]=b):b=t[12];let x;t[13]===o?x=t[14]:(x=o&&(0,$.jsx)(`div`,{className:`mb-4 rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive`,role:`alert`,children:o.message||`Failed to update security groups`}),t[13]=o,t[14]=x);let S;t[15]!==v||t[16]!==s?(S=s&&!v&&(0,$.jsx)(`div`,{className:`mb-4 rounded-md border border-emerald-500 bg-emerald-500/10 p-3 text-sm text-emerald-700 dark:text-emerald-400`,role:`status`,children:`Security groups updated.`}),t[15]=v,t[16]=s,t[17]=S):S=t[17];let C;t[18]!==n||t[19]!==u?(C=n.length===0?(0,$.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`No security groups in this VPC.`}):n.map(e=>(0,$.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,$.jsx)(`input`,{"aria-label":`Security group ${e.GroupId} (${e.GroupName})`,checked:u.includes(e.GroupId??``),onChange:()=>p(e.GroupId??``),type:`checkbox`}),(0,$.jsxs)(`span`,{className:`font-mono`,children:[e.GroupId,` (`,e.GroupName,`)`]})]},e.GroupId)),t[18]=n,t[19]=u,t[20]=C):C=t[20];let w;t[21]===y?w=t[22]:(w=y&&(0,$.jsx)(`p`,{className:`text-xs text-destructive`,children:`At least one security group is required.`}),t[21]=y,t[22]=w);let T;t[23]!==C||t[24]!==w?(T=(0,$.jsxs)(`div`,{className:`flex flex-col gap-1 p-4`,children:[C,w]}),t[23]=C,t[24]=w,t[25]=T):T=t[25];let E=v?`Unsaved changes`:`No changes`,D;t[26]===E?D=t[27]:(D=(0,$.jsx)(`span`,{className:`mr-auto text-xs text-muted-foreground`,children:E}),t[26]=E,t[27]=D);let O=a||!v,k;t[28]!==g||t[29]!==O?(k=(0,$.jsx)(h,{disabled:O,onClick:g,size:`sm`,type:`button`,variant:`secondary`,children:`Reset`}),t[28]=g,t[29]=O,t[30]=k):k=t[30];let A=a||!v||y,j=a?`Saving…`:`Save changes`,M;t[31]!==A||t[32]!==j?(M=(0,$.jsx)(h,{disabled:A,size:`sm`,type:`submit`,children:j}),t[31]=A,t[32]=j,t[33]=M):M=t[33];let N;t[34]!==D||t[35]!==k||t[36]!==M?(N=(0,$.jsxs)(`div`,{className:`flex items-center justify-end gap-2 border-t p-4`,children:[D,k,M]}),t[34]=D,t[35]=k,t[36]=M,t[37]=N):N=t[37];let P;t[38]!==T||t[39]!==N?(P=(0,$.jsxs)(`div`,{className:`rounded-lg border bg-card`,children:[T,N]}),t[38]=T,t[39]=N,t[40]=P):P=t[40];let F;return t[41]!==P||t[42]!==b||t[43]!==x||t[44]!==S?(F=(0,$.jsxs)(`form`,{onSubmit:b,children:[x,S,P]}),t[41]=P,t[42]=b,t[43]=x,t[44]=S,t[45]=F):F=t[45],F}function $e(e,t){if(e.length!==t.length)return!1;let n=new Set(t);return e.every(e=>n.has(e))}function et(e){let t=o(e.Tags),n=e.AvailabilityZone?` · ${e.AvailabilityZone}`:``,r=e.CidrBlock?` (${e.CidrBlock})`:``;return`${e.SubnetId}${t?` — ${t}`:``}${r}${n}`}function tt(e){let t=(0,Te.c)(47),{available:n,current:r,onSubmit:i,isPending:a,error:o,isSuccess:s}=e,c;t[0]===r?c=t[1]:(c=[...r],t[0]=r,t[1]=c);let l=c,[u,d]=(0,Q.useState)(l),f;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(f=e=>{d(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},t[2]=f):f=t[2];let p=f,m;t[3]===l?m=t[4]:(m=()=>d(l),t[3]=l,t[4]=m);let g=m,_;t[5]!==l||t[6]!==u?(_=$e(u,l),t[5]=l,t[6]=u,t[7]=_):_=t[7];let v=!_,y=u.length===0,b;t[8]!==v||t[9]!==y||t[10]!==i||t[11]!==u?(b=e=>{e.preventDefault(),!(!v||y)&&i(u)},t[8]=v,t[9]=y,t[10]=i,t[11]=u,t[12]=b):b=t[12];let x;t[13]===Symbol.for(`react.memo_cache_sentinel`)?(x=(0,$.jsx)(`div`,{className:`mb-4 rounded-md border border-amber-500 bg-amber-500/10 p-3 text-sm text-amber-700 dark:text-amber-400`,role:`note`,children:`Applying a subnet change re-homes the load balancer by relaunching its underlying system VM. Expect a brief data-plane interruption while it restarts.`}),t[13]=x):x=t[13];let S;t[14]===o?S=t[15]:(S=o&&(0,$.jsx)(`div`,{className:`mb-4 rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive`,role:`alert`,children:o.message||`Failed to update subnets`}),t[14]=o,t[15]=S);let C;t[16]!==v||t[17]!==s?(C=s&&!v&&(0,$.jsx)(`div`,{className:`mb-4 rounded-md border border-emerald-500 bg-emerald-500/10 p-3 text-sm text-emerald-700 dark:text-emerald-400`,role:`status`,children:`Subnets updated.`}),t[16]=v,t[17]=s,t[18]=C):C=t[18];let w;t[19]!==n||t[20]!==u?(w=n.length===0?(0,$.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`No subnets in this VPC.`}):n.map(e=>(0,$.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,$.jsx)(`input`,{"aria-label":`Subnet ${e.SubnetId}`,checked:u.includes(e.SubnetId??``),onChange:()=>p(e.SubnetId??``),type:`checkbox`}),(0,$.jsx)(`span`,{className:`font-mono`,children:et(e)})]},e.SubnetId)),t[19]=n,t[20]=u,t[21]=w):w=t[21];let T;t[22]===y?T=t[23]:(T=y&&(0,$.jsx)(`p`,{className:`text-xs text-destructive`,children:`At least one subnet is required.`}),t[22]=y,t[23]=T);let E;t[24]!==T||t[25]!==w?(E=(0,$.jsxs)(`div`,{className:`flex flex-col gap-1 p-4`,children:[w,T]}),t[24]=T,t[25]=w,t[26]=E):E=t[26];let D=v?`Unsaved changes`:`No changes`,O;t[27]===D?O=t[28]:(O=(0,$.jsx)(`span`,{className:`mr-auto text-xs text-muted-foreground`,children:D}),t[27]=D,t[28]=O);let k=a||!v,A;t[29]!==g||t[30]!==k?(A=(0,$.jsx)(h,{disabled:k,onClick:g,size:`sm`,type:`button`,variant:`secondary`,children:`Reset`}),t[29]=g,t[30]=k,t[31]=A):A=t[31];let j=a||!v||y,M=a?`Saving…`:`Save changes`,N;t[32]!==j||t[33]!==M?(N=(0,$.jsx)(h,{disabled:j,size:`sm`,type:`submit`,children:M}),t[32]=j,t[33]=M,t[34]=N):N=t[34];let P;t[35]!==O||t[36]!==A||t[37]!==N?(P=(0,$.jsxs)(`div`,{className:`flex items-center justify-end gap-2 border-t p-4`,children:[O,A,N]}),t[35]=O,t[36]=A,t[37]=N,t[38]=P):P=t[38];let F;t[39]!==E||t[40]!==P?(F=(0,$.jsxs)(`div`,{className:`rounded-lg border bg-card`,children:[E,P]}),t[39]=E,t[40]=P,t[41]=F):F=t[41];let I;return t[42]!==F||t[43]!==b||t[44]!==S||t[45]!==C?(I=(0,$.jsxs)(`form`,{onSubmit:b,children:[x,S,C,F]}),t[42]=F,t[43]=b,t[44]=S,t[45]=C,t[46]=I):I=t[46],I}function nt({arn:e}){let t=M(),{data:n}=a(N(e)),{data:r}=a(j(e)),{data:l}=a(O([e])),{data:u}=a(F),{data:d}=a(ne),f=C(),p=y(),g=_(),v=S(),b=x(),[w,T]=(0,Q.useState)(!1),E=n.LoadBalancers?.[0];if(!E?.LoadBalancerArn)return(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(c,{to:`/ec2/describe-load-balancers`,children:`Back to load balancers`}),(0,$.jsx)(`p`,{className:`text-muted-foreground`,children:`Load balancer not found.`})]});let D=async()=>{try{await f.mutateAsync(e),t({to:`/ec2/describe-load-balancers`})}finally{T(!1)}},k=u.Subnets??[],A=e=>{let t=k.find(t=>t.SubnetId===e);if(!t)return e;let n=o(t.Tags),r=t.CidrBlock?` (${t.CidrBlock})`:``;return n?`${e} — ${n}${r}`:`${e}${r}`},P=r.Attributes??[],I=l?.TagDescriptions?.find(t=>t.ResourceArn===e)?.Tags??[],ee=(E.SecurityGroups??[]).filter(e=>e!==void 0),te=(d.SecurityGroups??[]).filter(e=>e.VpcId===E.VpcId),L=k.filter(e=>e.VpcId===E.VpcId),R=(E.AvailabilityZones??[]).map(e=>e.SubnetId).filter(e=>e!==void 0),re=E.Type===`network`;return(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(c,{to:`/ec2/describe-load-balancers`,children:`Back to load balancers`}),f.error&&(0,$.jsx)(i,{error:f.error,msg:`Failed to delete load balancer`}),(0,$.jsxs)(`div`,{className:`space-y-6`,children:[(0,$.jsx)(H,{actions:(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsxs)(h,{onClick:()=>T(!0),size:`sm`,variant:`destructive`,children:[(0,$.jsx)(m,{className:`size-4`}),`Delete`]}),(0,$.jsx)(ue,{state:E.State?.Code})]}),subtitle:`Load Balancer Details`,title:E.LoadBalancerName??E.LoadBalancerArn}),(0,$.jsxs)(Ce,{defaultValue:`overview`,children:[(0,$.jsxs)(xe,{children:[(0,$.jsx)(be,{value:`overview`,children:`Overview`}),(0,$.jsx)(be,{value:`listeners`,children:`Listeners`}),(0,$.jsx)(be,{value:`subnets`,children:`Subnets`}),!re&&(0,$.jsx)(be,{value:`security-groups`,children:`Security groups`}),(0,$.jsx)(be,{value:`attributes`,children:`Attributes`}),(0,$.jsx)(be,{value:`tags`,children:`Tags`})]}),(0,$.jsxs)(Se,{value:`overview`,children:[(0,$.jsxs)(X,{children:[(0,$.jsx)(X.Header,{children:`Load balancer`}),(0,$.jsxs)(X.Content,{children:[(0,$.jsx)(Z,{label:`ARN`,value:E.LoadBalancerArn}),(0,$.jsx)(Z,{label:`DNS name`,value:E.DNSName}),(0,$.jsx)(Z,{label:`State`,value:E.State?.Reason?`${E.State.Code} — ${E.State.Reason}`:E.State?.Code}),(0,$.jsx)(Z,{label:`Type`,value:E.Type}),(0,$.jsx)(Z,{label:`Scheme`,value:E.Scheme}),(0,$.jsx)(Z,{label:`IP address type`,value:E.IpAddressType}),(0,$.jsx)(Z,{label:`VPC`,value:E.VpcId}),(0,$.jsx)(Z,{label:`Created at`,value:s(E.CreatedTime)})]})]}),(0,$.jsxs)(X,{children:[(0,$.jsx)(X.Header,{children:`Availability zones`}),(0,$.jsx)(X.Content,{children:(E.AvailabilityZones??[]).map(e=>(0,$.jsx)(Z,{label:e.ZoneName??`—`,value:e.SubnetId?A(e.SubnetId):void 0},`${e.ZoneName}-${e.SubnetId}`))})]}),(E.SecurityGroups??[]).length>0&&(0,$.jsxs)(X,{children:[(0,$.jsx)(X.Header,{children:`Security groups`}),(0,$.jsx)(X.Content,{children:(E.SecurityGroups??[]).map(e=>(0,$.jsx)(Z,{label:`Security group`,value:e},e))})]})]}),(0,$.jsx)(Se,{value:`listeners`,children:(0,$.jsx)(Je,{lbType:re?`network`:`application`,loadBalancerArn:e,vpcId:E.VpcId})}),(0,$.jsx)(Se,{value:`subnets`,children:(0,$.jsx)(tt,{available:L,current:R,error:b.error,isPending:b.isPending,isSuccess:b.isSuccess,onSubmit:t=>b.mutate({loadBalancerArn:e,subnetIds:t})})}),!re&&(0,$.jsx)(Se,{value:`security-groups`,children:(0,$.jsx)(Qe,{available:te,current:ee,error:v.error,isPending:v.isPending,isSuccess:v.isSuccess,onSubmit:t=>v.mutate({loadBalancerArn:e,securityGroupIds:t})})}),(0,$.jsx)(Se,{value:`attributes`,children:(0,$.jsx)(_e,{attributes:P,error:p.error,isPending:p.isPending,isSuccess:p.isSuccess,onSubmit:t=>{p.mutate({loadBalancerArn:e,attributes:t})},specs:ve})}),(0,$.jsx)(Se,{value:`tags`,children:(0,$.jsx)(ye,{error:g.error,isPending:g.isPending,isSuccess:g.isSuccess,onSubmit:t=>g.mutate({resourceArn:e,tags:t,initialKeys:I.map(e=>e.Key??``).filter(e=>e.length>0)}),tags:I})})]})]}),(0,$.jsx)(le,{description:`Are you sure you want to delete load balancer "${E.LoadBalancerName??E.LoadBalancerArn}"? This action cannot be undone.`,isPending:f.isPending,onConfirm:D,onOpenChange:T,open:w,title:`Delete load balancer`})]})}function rt(){let e=(0,Te.c)(2),{id:t}=L.useParams(),n=decodeURIComponent(t),r;return e[0]===n?r=e[1]:(r=(0,$.jsx)(nt,{arn:n}),e[0]=n,e[1]=r),r}export{rt as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id7.js b/spinifex/services/spinifexui/frontend/dist/assets/_id7.js index 8f18ad2b4..916123b00 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id7.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id7.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./trash-2.js";import{T as i}from"./ec2.js";import{$ as a,Ho as o,Lo as s,Ro as c,Uo as l,Xa as u,ao as d,cs as f,s as p}from"./index.js";import{t as m}from"./page-heading.js";import{t as h}from"./delete-confirmation-dialog.js";import{n as g,t as _}from"./detail-row.js";import{t as v}from"./state-badge.js";var y=f(l()),b=o();function x(){let{id:o}=p.useParams(),l=c(),{data:f}=t(a(o)),x=i(),[S,C]=(0,y.useState)(!1),w=f.NatGateways?.[0],T=async()=>{try{await x.mutateAsync(o),l({to:`/ec2/describe-nat-gateways`})}finally{C(!1)}};if(!w?.NatGatewayId)return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(n,{to:`/ec2/describe-nat-gateways`,children:`Back to NAT Gateways`}),(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`NAT Gateway not found.`})]});let E=d(w.Tags),D=w.NatGatewayAddresses?.[0];return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(n,{to:`/ec2/describe-nat-gateways`,children:`Back to NAT Gateways`}),x.error&&(0,b.jsx)(e,{error:x.error,msg:`Failed to delete NAT Gateway`}),(0,b.jsxs)(`div`,{className:`space-y-6`,children:[(0,b.jsx)(m,{actions:(0,b.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,b.jsxs)(u,{disabled:x.isPending,onClick:()=>C(!0),size:`sm`,variant:`destructive`,children:[(0,b.jsx)(r,{className:`size-4`}),`Delete`]}),(0,b.jsx)(v,{state:w.State})]}),subtitle:`NAT Gateway Details`,title:E?`${w.NatGatewayId} (${E})`:w.NatGatewayId}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`NAT Gateway Information`}),(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`NAT Gateway ID`,value:w.NatGatewayId}),(0,b.jsx)(_,{label:`State`,value:w.State}),(0,b.jsx)(_,{label:`Connectivity`,value:w.ConnectivityType}),(0,b.jsx)(_,{label:`Subnet ID`,value:w.SubnetId?(0,b.jsx)(s,{className:`text-primary hover:underline`,params:{id:w.SubnetId},to:`/ec2/describe-subnets/$id`,children:w.SubnetId}):`—`}),(0,b.jsx)(_,{label:`VPC ID`,value:w.VpcId?(0,b.jsx)(s,{className:`text-primary hover:underline`,params:{id:w.VpcId},to:`/ec2/describe-vpcs/$id`,children:w.VpcId}):`—`}),(0,b.jsx)(_,{label:`Public IP`,value:D?.PublicIp??`—`}),(0,b.jsx)(_,{label:`Allocation ID`,value:D?.AllocationId?(0,b.jsx)(s,{className:`text-primary hover:underline`,params:{id:D.AllocationId},to:`/ec2/describe-addresses/$id`,children:D.AllocationId}):`—`})]})]})]}),(0,b.jsx)(h,{description:`Are you sure you want to delete the NAT Gateway "${w.NatGatewayId}"? This action cannot be undone. The associated Elastic IP is not released.`,isPending:x.isPending,onConfirm:T,onOpenChange:C,open:S,title:`Delete NAT Gateway`})]})}export{x as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{A as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{t as c}from"./trash-2.js";import{t as l}from"./button.js";import{E as u}from"./ec2.js";import{Ir as d,s as f,xt as p}from"./index.js";import{t as m}from"./page-heading.js";import{t as h}from"./delete-confirmation-dialog.js";import{n as g,t as _}from"./detail-row.js";import{t as v}from"./state-badge.js";var y=t(n()),b=e();function x(){let{id:e}=f.useParams(),t=d(),{data:n}=i(p(e)),x=u(),[S,C]=(0,y.useState)(!1),w=n.NatGateways?.[0],T=async()=>{try{await x.mutateAsync(e),t({to:`/ec2/describe-nat-gateways`})}finally{C(!1)}};if(!w?.NatGatewayId)return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(s,{to:`/ec2/describe-nat-gateways`,children:`Back to NAT Gateways`}),(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`NAT Gateway not found.`})]});let E=o(w.Tags),D=w.NatGatewayAddresses?.[0];return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(s,{to:`/ec2/describe-nat-gateways`,children:`Back to NAT Gateways`}),x.error&&(0,b.jsx)(r,{error:x.error,msg:`Failed to delete NAT Gateway`}),(0,b.jsxs)(`div`,{className:`space-y-6`,children:[(0,b.jsx)(m,{actions:(0,b.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,b.jsxs)(l,{disabled:x.isPending,onClick:()=>C(!0),size:`sm`,variant:`destructive`,children:[(0,b.jsx)(c,{className:`size-4`}),`Delete`]}),(0,b.jsx)(v,{state:w.State})]}),subtitle:`NAT Gateway Details`,title:E?`${w.NatGatewayId} (${E})`:w.NatGatewayId}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`NAT Gateway Information`}),(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`NAT Gateway ID`,value:w.NatGatewayId}),(0,b.jsx)(_,{label:`State`,value:w.State}),(0,b.jsx)(_,{label:`Connectivity`,value:w.ConnectivityType}),(0,b.jsx)(_,{label:`Subnet ID`,value:w.SubnetId?(0,b.jsx)(a,{className:`text-primary hover:underline`,params:{id:w.SubnetId},to:`/ec2/describe-subnets/$id`,children:w.SubnetId}):`—`}),(0,b.jsx)(_,{label:`VPC ID`,value:w.VpcId?(0,b.jsx)(a,{className:`text-primary hover:underline`,params:{id:w.VpcId},to:`/ec2/describe-vpcs/$id`,children:w.VpcId}):`—`}),(0,b.jsx)(_,{label:`Public IP`,value:D?.PublicIp??`—`}),(0,b.jsx)(_,{label:`Allocation ID`,value:D?.AllocationId?(0,b.jsx)(a,{className:`text-primary hover:underline`,params:{id:D.AllocationId},to:`/ec2/describe-addresses/$id`,children:D.AllocationId}):`—`})]})]})]}),(0,b.jsx)(h,{description:`Are you sure you want to delete the NAT Gateway "${w.NatGatewayId}"? This action cannot be undone. The associated Elastic IP is not released.`,isPending:x.isPending,onConfirm:T,onOpenChange:C,open:S,title:`Delete NAT Gateway`})]})}export{x as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id8.js b/spinifex/services/spinifexui/frontend/dist/assets/_id8.js index 71b05262a..f8d94c25c 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id8.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id8.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./trash-2.js";import{E as i}from"./ec2.js";import{Ho as a,J as o,Lo as s,Ro as c,Uo as l,Xa as u,c as d,cs as f,tt as p}from"./index.js";import{t as m}from"./page-heading.js";import{t as h}from"./delete-confirmation-dialog.js";import{n as g,t as _}from"./detail-row.js";import{t as v}from"./state-badge.js";var y=f(l()),b=a();function x(){let{id:a}=d.useParams(),l=c(),{data:f}=t(p(a)),{data:x}=t(o),S=i(),[C,w]=(0,y.useState)(!1),T=f.PlacementGroups?.[0],E=x.Reservations?.flatMap(e=>e.Instances??[]).filter(e=>e.Placement?.GroupName===T?.GroupName)??[];return T?.GroupId?(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(n,{to:`/ec2/describe-placement-groups`,children:`Back to placement groups`}),S.error&&(0,b.jsx)(e,{error:S.error,msg:`Failed to delete placement group`}),(0,b.jsxs)(`div`,{className:`space-y-6`,children:[(0,b.jsx)(m,{actions:(0,b.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,b.jsxs)(u,{onClick:()=>w(!0),size:`sm`,variant:`destructive`,children:[(0,b.jsx)(r,{className:`size-4`}),`Delete`]}),(0,b.jsx)(v,{state:T.State})]}),subtitle:`Placement Group Details`,title:T.GroupName??T.GroupId}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`Placement Group Information`}),(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`Group ID`,value:T.GroupId}),(0,b.jsx)(_,{label:`Group Name`,value:T.GroupName}),(0,b.jsx)(_,{label:`Strategy`,value:T.Strategy}),(0,b.jsx)(_,{label:`State`,value:T.State}),(0,b.jsx)(_,{label:`Spread Level`,value:T.SpreadLevel})]})]}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`Instances`}),E.length>0?E.map(e=>(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`Instance ID`,value:e.InstanceId?(0,b.jsx)(s,{className:`text-primary hover:underline`,params:{id:e.InstanceId},to:`/ec2/describe-instances/$id`,children:e.InstanceId}):void 0}),(0,b.jsx)(_,{label:`State`,value:e.State?.Name}),(0,b.jsx)(_,{label:`Instance Type`,value:e.InstanceType}),(0,b.jsx)(_,{label:`Availability Zone`,value:e.Placement?.AvailabilityZone})]},e.InstanceId)):(0,b.jsx)(g.Content,{children:(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`No instances in this placement group.`})})]})]}),(0,b.jsx)(h,{description:`Are you sure you want to delete the placement group "${T.GroupName}"? The group must have no running instances.`,isPending:S.isPending,onConfirm:async()=>{if(T?.GroupName)try{await S.mutateAsync(T.GroupName),l({to:`/ec2/describe-placement-groups`})}finally{w(!1)}},onOpenChange:w,open:C,title:`Delete Placement Group`})]}):(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(n,{to:`/ec2/describe-placement-groups`,children:`Back to placement groups`}),(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`Placement group not found.`})]})}export{x as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{t as o}from"./back-link.js";import{t as s}from"./trash-2.js";import{t as c}from"./button.js";import{D as l}from"./ec2.js";import{Ct as u,Ir as d,c as f,gt as p}from"./index.js";import{t as m}from"./page-heading.js";import{t as h}from"./delete-confirmation-dialog.js";import{n as g,t as _}from"./detail-row.js";import{t as v}from"./state-badge.js";var y=t(n()),b=e();function x(){let{id:e}=f.useParams(),t=d(),{data:n}=i(u(e)),{data:x}=i(p),S=l(),[C,w]=(0,y.useState)(!1),T=n.PlacementGroups?.[0],E=x.Reservations?.flatMap(e=>e.Instances??[]).filter(e=>e.Placement?.GroupName===T?.GroupName)??[];return T?.GroupId?(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(o,{to:`/ec2/describe-placement-groups`,children:`Back to placement groups`}),S.error&&(0,b.jsx)(r,{error:S.error,msg:`Failed to delete placement group`}),(0,b.jsxs)(`div`,{className:`space-y-6`,children:[(0,b.jsx)(m,{actions:(0,b.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,b.jsxs)(c,{onClick:()=>w(!0),size:`sm`,variant:`destructive`,children:[(0,b.jsx)(s,{className:`size-4`}),`Delete`]}),(0,b.jsx)(v,{state:T.State})]}),subtitle:`Placement Group Details`,title:T.GroupName??T.GroupId}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`Placement Group Information`}),(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`Group ID`,value:T.GroupId}),(0,b.jsx)(_,{label:`Group Name`,value:T.GroupName}),(0,b.jsx)(_,{label:`Strategy`,value:T.Strategy}),(0,b.jsx)(_,{label:`State`,value:T.State}),(0,b.jsx)(_,{label:`Spread Level`,value:T.SpreadLevel})]})]}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`Instances`}),E.length>0?E.map(e=>(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`Instance ID`,value:e.InstanceId?(0,b.jsx)(a,{className:`text-primary hover:underline`,params:{id:e.InstanceId},to:`/ec2/describe-instances/$id`,children:e.InstanceId}):void 0}),(0,b.jsx)(_,{label:`State`,value:e.State?.Name}),(0,b.jsx)(_,{label:`Instance Type`,value:e.InstanceType}),(0,b.jsx)(_,{label:`Availability Zone`,value:e.Placement?.AvailabilityZone})]},e.InstanceId)):(0,b.jsx)(g.Content,{children:(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`No instances in this placement group.`})})]})]}),(0,b.jsx)(h,{description:`Are you sure you want to delete the placement group "${T.GroupName}"? The group must have no running instances.`,isPending:S.isPending,onConfirm:async()=>{if(T?.GroupName)try{await S.mutateAsync(T.GroupName),t({to:`/ec2/describe-placement-groups`})}finally{w(!1)}},onOpenChange:w,open:C,title:`Delete Placement Group`})]}):(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(o,{to:`/ec2/describe-placement-groups`,children:`Back to placement groups`}),(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`Placement group not found.`})]})}export{x as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_id9.js b/spinifex/services/spinifexui/frontend/dist/assets/_id9.js index a6f608ddc..0aca25526 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_id9.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_id9.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as a,s as o,t as s}from"./select.js";import{t as c}from"./trash-2.js";import{D as ee,L as te,O as ne,h as re,r as l}from"./ec2.js";import{Ho as u,Lo as d,Ro as f,Uo as p,X as m,Xa as h,Xi as g,ao as _,cs as v,dt as y,et as b,it as x,l as ie}from"./index.js";import{a as ae,i as S,n as C,t as w}from"./zod.js";import{t as T}from"./page-heading.js";import{a as E,r as D,t as O}from"./field.js";import{l as k,r as oe}from"./ec22.js";import{t as se}from"./delete-confirmation-dialog.js";import{n as A,t as j}from"./detail-row.js";import{t as M}from"./badge.js";var N=v(p()),P=u();function F(e){return e.GatewayId??e.NatGatewayId??e.NetworkInterfaceId??e.InstanceId??e.VpcPeeringConnectionId??`—`}function I(){let{id:u}=ie.useParams(),p=f(),{data:v}=t(x(u)),{data:I}=t(m),{data:L}=t(b),{data:R}=t(y),z=ne(),B=re(),V=ee(),H=l(),U=te(),[W,G]=(0,N.useState)(!1),K=v.RouteTables?.[0],q=S({resolver:w(k),defaultValues:{destinationCidrBlock:`0.0.0.0/0`,targetType:`igw`,targetId:``}}),ce=ae({control:q.control,name:`targetType`}),J=S({resolver:w(oe),defaultValues:{subnetId:``}});if(!K?.RouteTableId)return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(n,{to:`/ec2/describe-route-tables`,children:`Back to Route Tables`}),(0,P.jsx)(`p`,{className:`text-muted-foreground`,children:`Route Table not found.`})]});let Y=K.RouteTableId,X=_(K.Tags),Z=K.Associations?.some(e=>e.Main)??!1,Q=K.Routes??[],$=(K.Associations??[]).filter(e=>e.SubnetId),le=(I.InternetGateways??[]).filter(e=>e.Attachments?.some(e=>e.VpcId===K.VpcId)),ue=(L.NatGateways??[]).filter(e=>e.VpcId===K.VpcId&&e.State===`available`),de=new Set($.map(e=>e.SubnetId)),fe=(R.Subnets??[]).filter(e=>e.VpcId===K.VpcId&&!de.has(e.SubnetId)),pe=async()=>{try{await z.mutateAsync(Y),p({to:`/ec2/describe-route-tables`})}finally{G(!1)}},me=async e=>{await B.mutateAsync({routeTableId:Y,destinationCidrBlock:e.destinationCidrBlock,gatewayId:e.targetType===`igw`?e.targetId:void 0,natGatewayId:e.targetType===`nat`?e.targetId:void 0}),q.reset({destinationCidrBlock:`0.0.0.0/0`,targetType:`igw`,targetId:``})},he=async e=>{await H.mutateAsync({routeTableId:Y,subnetId:e.subnetId}),J.reset({subnetId:``})};return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(n,{to:`/ec2/describe-route-tables`,children:`Back to Route Tables`}),z.error&&(0,P.jsx)(e,{error:z.error,msg:`Failed to delete Route Table`}),(0,P.jsxs)(`div`,{className:`space-y-6`,children:[(0,P.jsx)(T,{actions:(0,P.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,P.jsxs)(h,{disabled:Z||z.isPending,onClick:()=>G(!0),size:`sm`,variant:`destructive`,children:[(0,P.jsx)(c,{className:`size-4`}),`Delete`]}),Z&&(0,P.jsx)(M,{variant:`secondary`,children:`Main`})]}),subtitle:`Route Table Details`,title:X?`${K.RouteTableId} (${X})`:K.RouteTableId}),(0,P.jsxs)(A,{children:[(0,P.jsx)(A.Header,{children:`Route Table Information`}),(0,P.jsxs)(A.Content,{children:[(0,P.jsx)(j,{label:`Route Table ID`,value:K.RouteTableId}),(0,P.jsx)(j,{label:`Main`,value:Z?`Yes`:`No`}),(0,P.jsx)(j,{label:`VPC ID`,value:K.VpcId?(0,P.jsx)(d,{className:`text-primary hover:underline`,params:{id:K.VpcId},to:`/ec2/describe-vpcs/$id`,children:K.VpcId}):`—`}),(0,P.jsx)(j,{label:`Owner ID`,value:K.OwnerId??`—`})]})]}),(0,P.jsxs)(A,{children:[(0,P.jsx)(A.Header,{children:`Routes`}),(0,P.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,P.jsx)(`div`,{className:`overflow-x-auto rounded-md border`,children:(0,P.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,P.jsx)(`thead`,{className:`bg-muted/50 text-left text-muted-foreground`,children:(0,P.jsxs)(`tr`,{children:[(0,P.jsx)(`th`,{className:`p-2 font-medium`,children:`Destination`}),(0,P.jsx)(`th`,{className:`p-2 font-medium`,children:`Target`}),(0,P.jsx)(`th`,{className:`p-2 font-medium`,children:`Status`}),(0,P.jsx)(`th`,{className:`p-2`,children:(0,P.jsx)(`span`,{className:`sr-only`,children:`Actions`})})]})}),(0,P.jsx)(`tbody`,{children:Q.length>0?Q.map(e=>{let t=e.DestinationCidrBlock??e.DestinationPrefixListId??`—`,n=e.GatewayId===`local`,r=e.DestinationCidrBlock;return(0,P.jsxs)(`tr`,{className:`border-t`,children:[(0,P.jsx)(`td`,{className:`p-2 font-mono`,children:t}),(0,P.jsx)(`td`,{className:`p-2 font-mono`,children:F(e)}),(0,P.jsx)(`td`,{className:`p-2`,children:e.State??`—`}),(0,P.jsx)(`td`,{className:`p-2 text-right`,children:!n&&r&&(0,P.jsx)(h,{disabled:V.isPending,onClick:async()=>await V.mutateAsync({routeTableId:Y,destinationCidrBlock:r}),size:`sm`,variant:`ghost`,children:(0,P.jsx)(c,{className:`size-4`})})})]},t)}):(0,P.jsx)(`tr`,{children:(0,P.jsx)(`td`,{className:`p-2 text-muted-foreground`,colSpan:4,children:`No routes.`})})})]})}),(B.error??V.error)&&(0,P.jsx)(e,{error:B.error??V.error??void 0,msg:`Failed to update route`}),(0,P.jsxs)(`form`,{className:`grid items-end gap-3 sm:grid-cols-[1fr_1fr_1fr_auto]`,onSubmit:q.handleSubmit(me),children:[(0,P.jsxs)(O,{children:[(0,P.jsx)(E,{children:(0,P.jsx)(`label`,{htmlFor:`destinationCidrBlock`,children:`Destination`})}),(0,P.jsx)(g,{id:`destinationCidrBlock`,placeholder:`0.0.0.0/0`,...q.register(`destinationCidrBlock`)}),(0,P.jsx)(D,{errors:[q.formState.errors.destinationCidrBlock]})]}),(0,P.jsxs)(O,{children:[(0,P.jsx)(E,{children:(0,P.jsx)(`label`,{htmlFor:`targetType`,children:`Target type`})}),(0,P.jsx)(C,{control:q.control,name:`targetType`,render:({field:e})=>(0,P.jsxs)(s,{onValueChange:t=>{e.onChange(t),q.setValue(`targetId`,``)},value:e.value,children:[(0,P.jsx)(o,{className:`w-full`,id:`targetType`,children:(0,P.jsx)(r,{})}),(0,P.jsxs)(a,{children:[(0,P.jsx)(i,{value:`igw`,children:`Internet Gateway`}),(0,P.jsx)(i,{value:`nat`,children:`NAT Gateway`})]})]})})]}),(0,P.jsxs)(O,{children:[(0,P.jsx)(E,{children:(0,P.jsx)(`label`,{htmlFor:`targetId`,children:`Target`})}),(0,P.jsx)(C,{control:q.control,name:`targetId`,render:({field:e})=>(0,P.jsxs)(s,{onValueChange:t=>e.onChange(t),value:e.value??``,children:[(0,P.jsx)(o,{"aria-invalid":!!q.formState.errors.targetId,className:`w-full`,id:`targetId`,children:(0,P.jsx)(r,{placeholder:`Select target`})}),(0,P.jsx)(a,{children:ce===`igw`?le.map(e=>(0,P.jsx)(i,{value:e.InternetGatewayId??``,children:e.InternetGatewayId},e.InternetGatewayId)):ue.map(e=>(0,P.jsx)(i,{value:e.NatGatewayId??``,children:e.NatGatewayId},e.NatGatewayId))})]})}),(0,P.jsx)(D,{errors:[q.formState.errors.targetId]})]}),(0,P.jsx)(h,{disabled:B.isPending,size:`sm`,type:`submit`,children:`Add route`})]})]})]}),(0,P.jsxs)(A,{children:[(0,P.jsx)(A.Header,{children:`Subnet Associations`}),(0,P.jsxs)(`div`,{className:`space-y-4 p-4`,children:[$.length>0?(0,P.jsx)(`ul`,{className:`space-y-2`,children:$.map(e=>{let t=e.RouteTableAssociationId;return(0,P.jsxs)(`li`,{className:`flex items-center justify-between rounded-md border p-2 text-sm`,children:[(0,P.jsx)(d,{className:`font-mono text-primary hover:underline`,params:{id:e.SubnetId??``},to:`/ec2/describe-subnets/$id`,children:e.SubnetId}),(0,P.jsx)(h,{disabled:!t||U.isPending,onClick:async()=>{t&&await U.mutateAsync(t)},size:`sm`,variant:`ghost`,children:(0,P.jsx)(c,{className:`size-4`})})]},t)})}):(0,P.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`No subnet associations.`}),(H.error??U.error)&&(0,P.jsx)(e,{error:H.error??U.error??void 0,msg:`Failed to update association`}),(0,P.jsxs)(`form`,{className:`grid items-end gap-3 sm:grid-cols-[1fr_auto]`,onSubmit:J.handleSubmit(he),children:[(0,P.jsxs)(O,{children:[(0,P.jsx)(E,{children:(0,P.jsx)(`label`,{htmlFor:`subnetId`,children:`Associate subnet`})}),(0,P.jsx)(C,{control:J.control,name:`subnetId`,render:({field:e})=>(0,P.jsxs)(s,{onValueChange:t=>e.onChange(t),value:e.value??``,children:[(0,P.jsx)(o,{"aria-invalid":!!J.formState.errors.subnetId,className:`w-full`,id:`subnetId`,children:(0,P.jsx)(r,{placeholder:`Select a subnet`})}),(0,P.jsx)(a,{children:fe.map(e=>{let t=_(e.Tags);return(0,P.jsx)(i,{value:e.SubnetId??``,children:t?`${e.SubnetId} (${t})`:`${e.SubnetId} (${e.CidrBlock})`},e.SubnetId)})})]})}),(0,P.jsx)(D,{errors:[J.formState.errors.subnetId]})]}),(0,P.jsx)(h,{disabled:H.isPending,size:`sm`,type:`submit`,children:`Associate`})]})]})]})]}),(0,P.jsx)(se,{description:`Are you sure you want to delete the Route Table "${K.RouteTableId}"? This action cannot be undone.`,isPending:z.isPending,onConfirm:pe,onOpenChange:G,open:W,title:`Delete Route Table`})]})}export{I as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{A as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{c,i as l,n as u,s as d,t as f}from"./select.js";import{t as p}from"./trash-2.js";import{t as m}from"./button.js";import{O as h,g,i as ee,k as te,z as _}from"./ec2.js";import{Et as v,Ir as y,Nt as b,Rn as x,St as S,l as ne,vt as re}from"./index.js";import{a as ie,i as C,n as w,t as T}from"./zod.js";import{t as ae}from"./page-heading.js";import{a as E,r as D,t as O}from"./field.js";import{l as k,r as A}from"./ec22.js";import{t as oe}from"./delete-confirmation-dialog.js";import{n as j,t as M}from"./detail-row.js";import{t as se}from"./badge.js";var N=t(n()),P=e();function F(e){return e.GatewayId??e.NatGatewayId??e.NetworkInterfaceId??e.InstanceId??e.VpcPeeringConnectionId??`—`}function I(){let{id:e}=ne.useParams(),t=y(),{data:n}=i(v(e)),{data:I}=i(re),{data:L}=i(S),{data:R}=i(b),z=te(),B=g(),V=h(),H=ee(),U=_(),[W,G]=(0,N.useState)(!1),K=n.RouteTables?.[0],q=C({resolver:T(k),defaultValues:{destinationCidrBlock:`0.0.0.0/0`,targetType:`igw`,targetId:``}}),ce=ie({control:q.control,name:`targetType`}),J=C({resolver:T(A),defaultValues:{subnetId:``}});if(!K?.RouteTableId)return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(s,{to:`/ec2/describe-route-tables`,children:`Back to Route Tables`}),(0,P.jsx)(`p`,{className:`text-muted-foreground`,children:`Route Table not found.`})]});let Y=K.RouteTableId,X=o(K.Tags),Z=K.Associations?.some(e=>e.Main)??!1,Q=K.Routes??[],$=(K.Associations??[]).filter(e=>e.SubnetId),le=(I.InternetGateways??[]).filter(e=>e.Attachments?.some(e=>e.VpcId===K.VpcId)),ue=(L.NatGateways??[]).filter(e=>e.VpcId===K.VpcId&&e.State===`available`),de=new Set($.map(e=>e.SubnetId)),fe=(R.Subnets??[]).filter(e=>e.VpcId===K.VpcId&&!de.has(e.SubnetId)),pe=async()=>{try{await z.mutateAsync(Y),t({to:`/ec2/describe-route-tables`})}finally{G(!1)}},me=async e=>{await B.mutateAsync({routeTableId:Y,destinationCidrBlock:e.destinationCidrBlock,gatewayId:e.targetType===`igw`?e.targetId:void 0,natGatewayId:e.targetType===`nat`?e.targetId:void 0}),q.reset({destinationCidrBlock:`0.0.0.0/0`,targetType:`igw`,targetId:``})},he=async e=>{await H.mutateAsync({routeTableId:Y,subnetId:e.subnetId}),J.reset({subnetId:``})};return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(s,{to:`/ec2/describe-route-tables`,children:`Back to Route Tables`}),z.error&&(0,P.jsx)(r,{error:z.error,msg:`Failed to delete Route Table`}),(0,P.jsxs)(`div`,{className:`space-y-6`,children:[(0,P.jsx)(ae,{actions:(0,P.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,P.jsxs)(m,{disabled:Z||z.isPending,onClick:()=>G(!0),size:`sm`,variant:`destructive`,children:[(0,P.jsx)(p,{className:`size-4`}),`Delete`]}),Z&&(0,P.jsx)(se,{variant:`secondary`,children:`Main`})]}),subtitle:`Route Table Details`,title:X?`${K.RouteTableId} (${X})`:K.RouteTableId}),(0,P.jsxs)(j,{children:[(0,P.jsx)(j.Header,{children:`Route Table Information`}),(0,P.jsxs)(j.Content,{children:[(0,P.jsx)(M,{label:`Route Table ID`,value:K.RouteTableId}),(0,P.jsx)(M,{label:`Main`,value:Z?`Yes`:`No`}),(0,P.jsx)(M,{label:`VPC ID`,value:K.VpcId?(0,P.jsx)(a,{className:`text-primary hover:underline`,params:{id:K.VpcId},to:`/ec2/describe-vpcs/$id`,children:K.VpcId}):`—`}),(0,P.jsx)(M,{label:`Owner ID`,value:K.OwnerId??`—`})]})]}),(0,P.jsxs)(j,{children:[(0,P.jsx)(j.Header,{children:`Routes`}),(0,P.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,P.jsx)(`div`,{className:`overflow-x-auto rounded-md border`,children:(0,P.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,P.jsx)(`thead`,{className:`bg-muted/50 text-left text-muted-foreground`,children:(0,P.jsxs)(`tr`,{children:[(0,P.jsx)(`th`,{className:`p-2 font-medium`,children:`Destination`}),(0,P.jsx)(`th`,{className:`p-2 font-medium`,children:`Target`}),(0,P.jsx)(`th`,{className:`p-2 font-medium`,children:`Status`}),(0,P.jsx)(`th`,{className:`p-2`,children:(0,P.jsx)(`span`,{className:`sr-only`,children:`Actions`})})]})}),(0,P.jsx)(`tbody`,{children:Q.length>0?Q.map(e=>{let t=e.DestinationCidrBlock??e.DestinationPrefixListId??`—`,n=e.GatewayId===`local`,r=e.DestinationCidrBlock;return(0,P.jsxs)(`tr`,{className:`border-t`,children:[(0,P.jsx)(`td`,{className:`p-2 font-mono`,children:t}),(0,P.jsx)(`td`,{className:`p-2 font-mono`,children:F(e)}),(0,P.jsx)(`td`,{className:`p-2`,children:e.State??`—`}),(0,P.jsx)(`td`,{className:`p-2 text-right`,children:!n&&r&&(0,P.jsx)(m,{disabled:V.isPending,onClick:async()=>await V.mutateAsync({routeTableId:Y,destinationCidrBlock:r}),size:`sm`,variant:`ghost`,children:(0,P.jsx)(p,{className:`size-4`})})})]},t)}):(0,P.jsx)(`tr`,{children:(0,P.jsx)(`td`,{className:`p-2 text-muted-foreground`,colSpan:4,children:`No routes.`})})})]})}),(B.error??V.error)&&(0,P.jsx)(r,{error:B.error??V.error??void 0,msg:`Failed to update route`}),(0,P.jsxs)(`form`,{className:`grid items-end gap-3 sm:grid-cols-[1fr_1fr_1fr_auto]`,onSubmit:q.handleSubmit(me),children:[(0,P.jsxs)(O,{children:[(0,P.jsx)(E,{children:(0,P.jsx)(`label`,{htmlFor:`destinationCidrBlock`,children:`Destination`})}),(0,P.jsx)(x,{id:`destinationCidrBlock`,placeholder:`0.0.0.0/0`,...q.register(`destinationCidrBlock`)}),(0,P.jsx)(D,{errors:[q.formState.errors.destinationCidrBlock]})]}),(0,P.jsxs)(O,{children:[(0,P.jsx)(E,{children:(0,P.jsx)(`label`,{htmlFor:`targetType`,children:`Target type`})}),(0,P.jsx)(w,{control:q.control,name:`targetType`,render:({field:e})=>(0,P.jsxs)(f,{onValueChange:t=>{e.onChange(t),q.setValue(`targetId`,``)},value:e.value,children:[(0,P.jsx)(d,{className:`w-full`,id:`targetType`,children:(0,P.jsx)(c,{})}),(0,P.jsxs)(u,{children:[(0,P.jsx)(l,{value:`igw`,children:`Internet Gateway`}),(0,P.jsx)(l,{value:`nat`,children:`NAT Gateway`})]})]})})]}),(0,P.jsxs)(O,{children:[(0,P.jsx)(E,{children:(0,P.jsx)(`label`,{htmlFor:`targetId`,children:`Target`})}),(0,P.jsx)(w,{control:q.control,name:`targetId`,render:({field:e})=>(0,P.jsxs)(f,{onValueChange:t=>e.onChange(t),value:e.value??``,children:[(0,P.jsx)(d,{"aria-invalid":!!q.formState.errors.targetId,className:`w-full`,id:`targetId`,children:(0,P.jsx)(c,{placeholder:`Select target`})}),(0,P.jsx)(u,{children:ce===`igw`?le.map(e=>(0,P.jsx)(l,{value:e.InternetGatewayId??``,children:e.InternetGatewayId},e.InternetGatewayId)):ue.map(e=>(0,P.jsx)(l,{value:e.NatGatewayId??``,children:e.NatGatewayId},e.NatGatewayId))})]})}),(0,P.jsx)(D,{errors:[q.formState.errors.targetId]})]}),(0,P.jsx)(m,{disabled:B.isPending,size:`sm`,type:`submit`,children:`Add route`})]})]})]}),(0,P.jsxs)(j,{children:[(0,P.jsx)(j.Header,{children:`Subnet Associations`}),(0,P.jsxs)(`div`,{className:`space-y-4 p-4`,children:[$.length>0?(0,P.jsx)(`ul`,{className:`space-y-2`,children:$.map(e=>{let t=e.RouteTableAssociationId;return(0,P.jsxs)(`li`,{className:`flex items-center justify-between rounded-md border p-2 text-sm`,children:[(0,P.jsx)(a,{className:`font-mono text-primary hover:underline`,params:{id:e.SubnetId??``},to:`/ec2/describe-subnets/$id`,children:e.SubnetId}),(0,P.jsx)(m,{disabled:!t||U.isPending,onClick:async()=>{t&&await U.mutateAsync(t)},size:`sm`,variant:`ghost`,children:(0,P.jsx)(p,{className:`size-4`})})]},t)})}):(0,P.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`No subnet associations.`}),(H.error??U.error)&&(0,P.jsx)(r,{error:H.error??U.error??void 0,msg:`Failed to update association`}),(0,P.jsxs)(`form`,{className:`grid items-end gap-3 sm:grid-cols-[1fr_auto]`,onSubmit:J.handleSubmit(he),children:[(0,P.jsxs)(O,{children:[(0,P.jsx)(E,{children:(0,P.jsx)(`label`,{htmlFor:`subnetId`,children:`Associate subnet`})}),(0,P.jsx)(w,{control:J.control,name:`subnetId`,render:({field:e})=>(0,P.jsxs)(f,{onValueChange:t=>e.onChange(t),value:e.value??``,children:[(0,P.jsx)(d,{"aria-invalid":!!J.formState.errors.subnetId,className:`w-full`,id:`subnetId`,children:(0,P.jsx)(c,{placeholder:`Select a subnet`})}),(0,P.jsx)(u,{children:fe.map(e=>{let t=o(e.Tags);return(0,P.jsx)(l,{value:e.SubnetId??``,children:t?`${e.SubnetId} (${t})`:`${e.SubnetId} (${e.CidrBlock})`},e.SubnetId)})})]})}),(0,P.jsx)(D,{errors:[J.formState.errors.subnetId]})]}),(0,P.jsx)(m,{disabled:H.isPending,size:`sm`,type:`submit`,children:`Associate`})]})]})]})]}),(0,P.jsx)(oe,{description:`Are you sure you want to delete the Route Table "${K.RouteTableId}"? This action cannot be undone.`,isPending:z.isPending,onConfirm:pe,onOpenChange:G,open:W,title:`Delete Route Table`})]})}export{I as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_instanceProfileName.js b/spinifex/services/spinifexui/frontend/dist/assets/_instanceProfileName.js new file mode 100644 index 000000000..871703450 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/_instanceProfileName.js @@ -0,0 +1 @@ +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{D as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{t as c}from"./trash-2.js";import{t as l}from"./button.js";import{g as u,t as d,u as f}from"./iam.js";import{Ir as p,X as m,rt as h,v as g}from"./index.js";import{t as _}from"./page-heading.js";import{t as v}from"./delete-confirmation-dialog.js";import{n as y,t as b}from"./detail-row.js";var x=t(n()),S=e();function C(){let{instanceProfileName:e}=g.useParams(),t=p(),{data:n}=i(m(e)),{data:C}=i(h),w=f(),T=d(),E=u(),[D,O]=(0,x.useState)(!1),[k,A]=(0,x.useState)(!1),[j,M]=(0,x.useState)(null),N=n.InstanceProfile,P=N?.Roles??[],F=C.Roles??[],I=new Set(P.map(e=>e.RoleName)),L=F.filter(e=>e.RoleName&&!I.has(e.RoleName)),R=async()=>{try{await w.mutateAsync(e),t({to:`/iam/list-instance-profiles`})}finally{O(!1)}},z=async t=>{await T.mutateAsync({instanceProfileName:e,roleName:t}),A(!1)},B=async t=>{M(t);try{await E.mutateAsync({instanceProfileName:e,roleName:t})}finally{M(null)}};return N?(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(s,{to:`/iam/list-instance-profiles`,children:`Back to instance profiles`}),w.error&&(0,S.jsx)(r,{error:w.error,msg:`Failed to delete instance profile`}),T.error&&(0,S.jsx)(r,{error:T.error,msg:`Failed to add role`}),E.error&&(0,S.jsx)(r,{error:E.error,msg:`Failed to remove role`}),(0,S.jsxs)(`div`,{className:`space-y-6`,children:[(0,S.jsx)(_,{actions:(0,S.jsxs)(l,{onClick:()=>O(!0),size:`sm`,variant:`destructive`,children:[(0,S.jsx)(c,{className:`size-4`}),`Delete`]}),subtitle:`Instance Profile Details`,title:N.InstanceProfileName??``}),(0,S.jsxs)(y,{children:[(0,S.jsx)(y.Header,{children:`Instance Profile Information`}),(0,S.jsxs)(y.Content,{children:[(0,S.jsx)(b,{label:`Name`,value:N.InstanceProfileName}),(0,S.jsx)(b,{label:`ID`,value:N.InstanceProfileId}),(0,S.jsx)(b,{label:`ARN`,value:N.Arn}),(0,S.jsx)(b,{label:`Path`,value:N.Path}),(0,S.jsx)(b,{label:`Created`,value:o(N.CreateDate)})]})]}),(0,S.jsxs)(y,{children:[(0,S.jsx)(y.Header,{children:(0,S.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,S.jsx)(`span`,{children:`Roles`}),(0,S.jsx)(l,{onClick:()=>A(!k),size:`sm`,children:`Add Role`})]})}),(0,S.jsxs)(y.Content,{children:[k&&L.length>0&&(0,S.jsxs)(`div`,{className:`col-span-2 space-y-2 rounded-md border p-3`,children:[(0,S.jsx)(`p`,{className:`text-sm font-medium`,children:`Select a role to add:`}),(0,S.jsx)(`div`,{className:`space-y-1`,children:L.map(e=>(0,S.jsxs)(`button`,{className:`flex w-full items-center justify-between rounded-md p-2 text-left text-sm hover:bg-accent`,disabled:T.isPending,onClick:()=>{e.RoleName&&z(e.RoleName)},type:`button`,children:[(0,S.jsx)(`span`,{children:e.RoleName}),(0,S.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:e.Arn})]},e.RoleName))})]}),k&&L.length===0&&(0,S.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No roles available to add.`}),P.length>0?(0,S.jsx)(`div`,{className:`col-span-2 space-y-3`,children:P.map(e=>(0,S.jsxs)(`div`,{className:`flex items-center justify-between rounded-md border p-3`,children:[(0,S.jsx)(a,{className:`text-sm font-medium text-primary hover:underline`,params:{roleName:e.RoleName??``},to:`/iam/list-roles/$roleName`,children:e.RoleName}),(0,S.jsx)(l,{disabled:j===e.RoleName,onClick:()=>{e.RoleName&&B(e.RoleName)},size:`sm`,variant:`outline`,children:`Remove`})]},e.RoleName))}):(0,S.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No roles in this instance profile.`})]})]})]}),(0,S.jsx)(v,{description:`Are you sure you want to delete the instance profile "${e}"? This action cannot be undone. You must remove all roles first.`,isPending:w.isPending,onConfirm:R,onOpenChange:O,open:D,title:`Delete Instance Profile`})]}):(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(s,{to:`/iam/list-instance-profiles`,children:`Back to instance profiles`}),(0,S.jsx)(`p`,{className:`text-muted-foreground`,children:`Instance profile not found.`})]})}export{C as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_policyArn.js b/spinifex/services/spinifexui/frontend/dist/assets/_policyArn.js index 5335ea7f3..1d738a432 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_policyArn.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_policyArn.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./trash-2.js";import{o as i}from"./iam.js";import{C as a,Fo as o,Ho as s,Ro as c,Uo as l,Xa as u,_ as d,cs as f,no as p,w as m}from"./index.js";import{t as h}from"./page-heading.js";import{t as g}from"./delete-confirmation-dialog.js";import{n as _,t as v}from"./detail-row.js";var y=f(l()),b=o(),x=s();function S(e){let t=(0,b.c)(4),{document:n}=e,r;try{let e;t[0]===n?e=t[1]:(e=JSON.stringify(JSON.parse(n),null,2),t[0]=n,t[1]=e),r=e}catch{r=n}let i;return t[2]===r?i=t[3]:(i=(0,x.jsx)(`pre`,{className:`overflow-auto rounded-md border bg-muted p-4 font-mono text-sm`,children:r}),t[2]=r,t[3]=i),i}function C(){let{policyArn:o}=d.useParams(),s=decodeURIComponent(o),l=c(),{data:f}=t(a(s)),b=f.Policy,C=i(),[w,T]=(0,y.useState)(!1),{data:E}=t(m(s,b?.DefaultVersionId??`v1`)),D=E?.PolicyVersion?.Document?decodeURIComponent(E.PolicyVersion.Document):null;return b?(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(n,{to:`/iam/list-policies`,children:`Back to policies`}),C.error&&(0,x.jsx)(e,{error:C.error,msg:`Failed to delete policy`}),(0,x.jsxs)(`div`,{className:`space-y-6`,children:[(0,x.jsx)(h,{actions:(0,x.jsxs)(u,{onClick:()=>T(!0),size:`sm`,variant:`destructive`,children:[(0,x.jsx)(r,{className:`size-4`}),`Delete`]}),subtitle:`Policy Details`,title:b.PolicyName??``}),(0,x.jsxs)(_,{children:[(0,x.jsx)(_.Header,{children:`Policy Information`}),(0,x.jsxs)(_.Content,{children:[(0,x.jsx)(v,{label:`Policy Name`,value:b.PolicyName}),(0,x.jsx)(v,{label:`Policy ID`,value:b.PolicyId}),(0,x.jsx)(v,{label:`ARN`,value:b.Arn}),(0,x.jsx)(v,{label:`Path`,value:b.Path}),(0,x.jsx)(v,{label:`Description`,value:b.Description??`-`}),(0,x.jsx)(v,{label:`Created`,value:p(b.CreateDate)})]})]}),D&&(0,x.jsxs)(_,{children:[(0,x.jsx)(_.Header,{children:`Policy Document`}),(0,x.jsx)(`div`,{className:`p-4`,children:(0,x.jsx)(S,{document:D})})]})]}),(0,x.jsx)(g,{description:`Are you sure you want to delete the policy "${b.PolicyName}"? This action cannot be undone.`,isPending:C.isPending,onConfirm:async()=>{try{await C.mutateAsync(s),l({to:`/iam/list-policies`})}finally{T(!1)}},onOpenChange:T,open:w,title:`Delete Policy`})]}):(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(n,{to:`/iam/list-policies`,children:`Back to policies`}),(0,x.jsx)(`p`,{className:`text-muted-foreground`,children:`Policy not found.`})]})}export{C as component}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{D as a}from"./SignatureV4.js";import{t as o}from"./back-link.js";import{t as s}from"./trash-2.js";import{t as c}from"./button.js";import{d as l}from"./iam.js";import{Ir as u,et as d,tt as f,y as p}from"./index.js";import{t as m}from"./page-heading.js";import{t as h}from"./delete-confirmation-dialog.js";import{n as g,t as _}from"./detail-row.js";import{t as v}from"./policy-document-viewer.js";var y=t(n()),b=e();function x(){let{policyArn:e}=p.useParams(),t=decodeURIComponent(e),n=u(),{data:x}=i(d(t)),S=x.Policy,C=l(),[w,T]=(0,y.useState)(!1),{data:E}=i(f(t,S?.DefaultVersionId??`v1`)),D=E?.PolicyVersion?.Document?decodeURIComponent(E.PolicyVersion.Document):null;return S?(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(o,{to:`/iam/list-policies`,children:`Back to policies`}),C.error&&(0,b.jsx)(r,{error:C.error,msg:`Failed to delete policy`}),(0,b.jsxs)(`div`,{className:`space-y-6`,children:[(0,b.jsx)(m,{actions:(0,b.jsxs)(c,{onClick:()=>T(!0),size:`sm`,variant:`destructive`,children:[(0,b.jsx)(s,{className:`size-4`}),`Delete`]}),subtitle:`Policy Details`,title:S.PolicyName??``}),(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`Policy Information`}),(0,b.jsxs)(g.Content,{children:[(0,b.jsx)(_,{label:`Policy Name`,value:S.PolicyName}),(0,b.jsx)(_,{label:`Policy ID`,value:S.PolicyId}),(0,b.jsx)(_,{label:`ARN`,value:S.Arn}),(0,b.jsx)(_,{label:`Path`,value:S.Path}),(0,b.jsx)(_,{label:`Description`,value:S.Description??`-`}),(0,b.jsx)(_,{label:`Created`,value:a(S.CreateDate)})]})]}),D&&(0,b.jsxs)(g,{children:[(0,b.jsx)(g.Header,{children:`Policy Document`}),(0,b.jsx)(`div`,{className:`p-4`,children:(0,b.jsx)(v,{document:D})})]})]}),(0,b.jsx)(h,{description:`Are you sure you want to delete the policy "${S.PolicyName}"? This action cannot be undone.`,isPending:C.isPending,onConfirm:async()=>{try{await C.mutateAsync(t),n({to:`/iam/list-policies`})}finally{T(!1)}},onOpenChange:T,open:w,title:`Delete Policy`})]}):(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(o,{to:`/iam/list-policies`,children:`Back to policies`}),(0,b.jsx)(`p`,{className:`text-muted-foreground`,children:`Policy not found.`})]})}export{x as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_roleName.js b/spinifex/services/spinifexui/frontend/dist/assets/_roleName.js new file mode 100644 index 000000000..8b7c25244 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/_roleName.js @@ -0,0 +1 @@ +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{Or as a}from"./awsClient.js";import{D as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{t as c}from"./trash-2.js";import{t as l}from"./button.js";import{f as u,m as d,n as f}from"./iam.js";import{$ as p,Ir as m,J as h,Z as g,b as _,nt as v}from"./index.js";import{t as y}from"./page-heading.js";import{t as b}from"./delete-confirmation-dialog.js";import{n as x,t as S}from"./detail-row.js";import{t as C}from"./policy-document-viewer.js";var w=t(n()),T=e();function E(){let{roleName:e}=_.useParams(),t=m(),{data:n}=i(v(e)),{data:E}=i(h(e)),{data:D}=i(g(e)),{data:O}=i(p),k=u(),A=f(),j=d(),[M,N]=(0,w.useState)(!1),[P,F]=(0,w.useState)(!1),[I,L]=(0,w.useState)(null),R=n.Role,z=E.AttachedPolicies??[],B=D.InstanceProfiles??[],V=O.Policies??[],H=new Set(z.map(e=>e.PolicyArn)),U=V.filter(e=>e.Arn&&!H.has(e.Arn)),W=R?.AssumeRolePolicyDocument?decodeURIComponent(R.AssumeRolePolicyDocument):null,G=async()=>{try{await k.mutateAsync(e),t({to:`/iam/list-roles`})}finally{N(!1)}},K=async t=>{await A.mutateAsync({roleName:e,policyArn:t}),F(!1)},q=async t=>{L(t);try{await j.mutateAsync({roleName:e,policyArn:t})}finally{L(null)}};return R?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(s,{to:`/iam/list-roles`,children:`Back to roles`}),k.error&&(0,T.jsx)(r,{error:k.error,msg:`Failed to delete role`}),A.error&&(0,T.jsx)(r,{error:A.error,msg:`Failed to attach policy`}),j.error&&(0,T.jsx)(r,{error:j.error,msg:`Failed to detach policy`}),(0,T.jsxs)(`div`,{className:`space-y-6`,children:[(0,T.jsx)(y,{actions:(0,T.jsxs)(l,{onClick:()=>N(!0),size:`sm`,variant:`destructive`,children:[(0,T.jsx)(c,{className:`size-4`}),`Delete`]}),subtitle:`Role Details`,title:R.RoleName??``}),(0,T.jsxs)(x,{children:[(0,T.jsx)(x.Header,{children:`Role Information`}),(0,T.jsxs)(x.Content,{children:[(0,T.jsx)(S,{label:`Role Name`,value:R.RoleName}),(0,T.jsx)(S,{label:`Role ID`,value:R.RoleId}),(0,T.jsx)(S,{label:`ARN`,value:R.Arn}),(0,T.jsx)(S,{label:`Path`,value:R.Path}),(0,T.jsx)(S,{label:`Description`,value:R.Description??`-`}),(0,T.jsx)(S,{label:`Created`,value:o(R.CreateDate)})]})]}),W&&(0,T.jsxs)(x,{children:[(0,T.jsx)(x.Header,{children:`Trust Policy`}),(0,T.jsx)(`div`,{className:`p-4`,children:(0,T.jsx)(C,{document:W})})]}),(0,T.jsxs)(x,{children:[(0,T.jsx)(x.Header,{children:(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsx)(`span`,{children:`Attached Policies`}),(0,T.jsx)(l,{onClick:()=>F(!P),size:`sm`,children:`Attach Policy`})]})}),(0,T.jsxs)(x.Content,{children:[P&&U.length>0&&(0,T.jsxs)(`div`,{className:`col-span-2 space-y-2 rounded-md border p-3`,children:[(0,T.jsx)(`p`,{className:`text-sm font-medium`,children:`Select a policy to attach:`}),(0,T.jsx)(`div`,{className:`space-y-1`,children:U.map(e=>(0,T.jsxs)(`button`,{className:`flex w-full items-center justify-between rounded-md p-2 text-left text-sm hover:bg-accent`,disabled:A.isPending,onClick:()=>{e.Arn&&K(e.Arn)},type:`button`,children:[(0,T.jsx)(`span`,{children:e.PolicyName}),(0,T.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:e.Arn})]},e.Arn))})]}),P&&U.length===0&&(0,T.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No policies available to attach.`}),z.length>0?(0,T.jsx)(`div`,{className:`col-span-2 space-y-3`,children:z.map(e=>(0,T.jsxs)(`div`,{className:`flex items-center justify-between rounded-md border p-3`,children:[(0,T.jsxs)(`div`,{className:`space-y-1`,children:[(0,T.jsx)(`p`,{className:`text-sm font-medium`,children:e.PolicyName}),(0,T.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e.PolicyArn})]}),(0,T.jsx)(l,{disabled:I===e.PolicyArn,onClick:()=>{e.PolicyArn&&q(e.PolicyArn)},size:`sm`,variant:`outline`,children:`Detach`})]},e.PolicyArn))}):(0,T.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No attached policies.`})]})]}),(0,T.jsxs)(x,{children:[(0,T.jsx)(x.Header,{children:`Instance Profiles`}),(0,T.jsx)(x.Content,{children:B.length>0?(0,T.jsx)(`div`,{className:`col-span-2 space-y-3`,children:B.map(e=>(0,T.jsxs)(a,{className:`flex items-center justify-between rounded-md border p-3 transition-colors hover:bg-accent`,params:{instanceProfileName:e.InstanceProfileName??``},to:`/iam/list-instance-profiles/$instanceProfileName`,children:[(0,T.jsx)(`p`,{className:`text-sm font-medium`,children:e.InstanceProfileName}),(0,T.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:e.Arn})]},e.InstanceProfileName))}):(0,T.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`This role is not in any instance profile.`})})]})]}),(0,T.jsx)(b,{description:`Are you sure you want to delete the role "${e}"? This action cannot be undone. You must detach all policies and remove it from all instance profiles first.`,isPending:k.isPending,onConfirm:G,onOpenChange:N,open:M,title:`Delete Role`})]}):(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(s,{to:`/iam/list-roles`,children:`Back to roles`}),(0,T.jsx)(`p`,{className:`text-muted-foreground`,children:`Role not found.`})]})}export{E as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/_userName.js b/spinifex/services/spinifexui/frontend/dist/assets/_userName.js index 2bc34f847..3e997449a 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/_userName.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/_userName.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./check.js";import{t as i}from"./trash-2.js";import{t as a}from"./triangle-alert.js";import{a as o,c as s,l as c,n as l,s as u,t as d}from"./iam.js";import{Fo as f,Ho as p,Ro as m,S as h,T as g,Uo as _,Xa as v,b as y,cs as b,no as x,v as S,x as C}from"./index.js";import{t as w}from"./use-copy-to-clipboard.js";import{t as T}from"./page-heading.js";import{a as E,c as D,i as O,l as k,o as A,r as j,s as M,t as N}from"./alert-dialog.js";import{t as P}from"./delete-confirmation-dialog.js";import{n as F,t as I}from"./detail-row.js";import{t as L}from"./state-badge.js";var R=b(_()),z=f(),B=p();function V(e){let t=(0,z.c)(27),{onClose:n,accessKeyId:i,secretAccessKey:o}=e,{copied:s,copy:c}=w(),l;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,B.jsxs)(M,{children:[(0,B.jsx)(D,{children:(0,B.jsx)(a,{className:`text-destructive`})}),(0,B.jsx)(k,{children:`Save Your Access Key`}),(0,B.jsx)(E,{children:`This is your only chance to view the secret access key. Please copy it now. You won't be able to retrieve it again.`})]}),t[0]=l):l=t[0];let u;t[1]===Symbol.for(`react.memo_cache_sentinel`)?(u=(0,B.jsx)(`span`,{className:`text-muted-foreground`,children:`Access Key ID: `}),t[1]=u):u=t[1];let d;t[2]===i?d=t[3]:(d=(0,B.jsxs)(`div`,{children:[u,i]}),t[2]=i,t[3]=d);let f;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(f=(0,B.jsx)(`span`,{className:`text-muted-foreground`,children:`Secret Access Key: `}),t[4]=f):f=t[4];let p;t[5]===o?p=t[6]:(p=(0,B.jsxs)(`div`,{children:[f,o]}),t[5]=o,t[6]=p);let m;t[7]!==d||t[8]!==p?(m=(0,B.jsxs)(`div`,{className:`space-y-2 rounded-md border p-4 font-mono text-sm`,children:[d,p]}),t[7]=d,t[8]=p,t[9]=m):m=t[9];let h;t[10]!==i||t[11]!==c||t[12]!==o?(h=async()=>await c(`Access Key ID: ${i}\nSecret Access Key: ${o}`),t[10]=i,t[11]=c,t[12]=o,t[13]=h):h=t[13];let g;t[14]===s?g=t[15]:(g=s?(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(r,{className:`mr-2 size-4`}),`Copied!`]}):`Copy to Clipboard`,t[14]=s,t[15]=g);let _;t[16]!==h||t[17]!==g?(_=(0,B.jsx)(v,{className:`w-full`,onClick:h,type:`button`,variant:`outline`,children:g}),t[16]=h,t[17]=g,t[18]=_):_=t[18];let y;t[19]!==m||t[20]!==_?(y=(0,B.jsxs)(`div`,{className:`space-y-3`,children:[m,_]}),t[19]=m,t[20]=_,t[21]=y):y=t[21];let b;t[22]===n?b=t[23]:(b=(0,B.jsx)(A,{children:(0,B.jsx)(j,{onClick:n,children:`Close and Continue`})}),t[22]=n,t[23]=b);let x;return t[24]!==y||t[25]!==b?(x=(0,B.jsx)(N,{open:!0,children:(0,B.jsxs)(O,{className:`max-w-2xl`,children:[l,y,b]})}),t[24]=y,t[25]=b,t[26]=x):x=t[26],x}function H(){let{userName:r}=S.useParams(),a=m(),{data:f}=t(g(r)),{data:p}=t(y(r)),{data:_}=t(C(r)),{data:b}=t(h),w=u(),E=l(),D=o(),O=c(),k=d(),A=s(),[j,M]=(0,R.useState)(!1),[N,z]=(0,R.useState)(null),[H,U]=(0,R.useState)(!1),[W,G]=(0,R.useState)(null),[K,q]=(0,R.useState)(null),J=f.User,Y=p.AccessKeyMetadata??[],X=_.AttachedPolicies??[],Z=b.Policies??[],Q=new Set(X.map(e=>e.PolicyArn)),$=Z.filter(e=>e.Arn&&!Q.has(e.Arn)),ee=async()=>{try{await w.mutateAsync(r),a({to:`/iam/list-users`})}finally{M(!1)}},te=async()=>{let e=await E.mutateAsync(r);e.AccessKey&&z({accessKeyId:e.AccessKey.AccessKeyId??``,secretAccessKey:e.AccessKey.SecretAccessKey??``})},ne=async e=>{if(e.AccessKeyId){G(e.AccessKeyId);try{await O.mutateAsync({userName:r,accessKeyId:e.AccessKeyId,status:e.Status===`Active`?`Inactive`:`Active`})}finally{G(null)}}},re=async e=>{G(e);try{await D.mutateAsync({userName:r,accessKeyId:e})}finally{G(null)}},ie=async e=>{await k.mutateAsync({userName:r,policyArn:e}),U(!1)},ae=async e=>{q(e);try{await A.mutateAsync({userName:r,policyArn:e})}finally{q(null)}};return J?(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(n,{to:`/iam/list-users`,children:`Back to users`}),w.error&&(0,B.jsx)(e,{error:w.error,msg:`Failed to delete user`}),E.error&&(0,B.jsx)(e,{error:E.error,msg:`Failed to create access key`}),D.error&&(0,B.jsx)(e,{error:D.error,msg:`Failed to delete access key`}),k.error&&(0,B.jsx)(e,{error:k.error,msg:`Failed to attach policy`}),A.error&&(0,B.jsx)(e,{error:A.error,msg:`Failed to detach policy`}),(0,B.jsxs)(`div`,{className:`space-y-6`,children:[(0,B.jsx)(T,{actions:(0,B.jsxs)(v,{onClick:()=>M(!0),size:`sm`,variant:`destructive`,children:[(0,B.jsx)(i,{className:`size-4`}),`Delete`]}),subtitle:`User Details`,title:J.UserName??``}),(0,B.jsxs)(F,{children:[(0,B.jsx)(F.Header,{children:`User Information`}),(0,B.jsxs)(F.Content,{children:[(0,B.jsx)(I,{label:`User Name`,value:J.UserName}),(0,B.jsx)(I,{label:`User ID`,value:J.UserId}),(0,B.jsx)(I,{label:`ARN`,value:J.Arn}),(0,B.jsx)(I,{label:`Path`,value:J.Path}),(0,B.jsx)(I,{label:`Created`,value:x(J.CreateDate)})]})]}),(0,B.jsxs)(F,{children:[(0,B.jsx)(F.Header,{children:(0,B.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,B.jsx)(`span`,{children:`Access Keys`}),(0,B.jsx)(v,{disabled:E.isPending,onClick:te,size:`sm`,children:E.isPending?`Creating...`:`Create Access Key`})]})}),(0,B.jsx)(F.Content,{children:Y.length>0?(0,B.jsx)(`div`,{className:`col-span-2 space-y-3`,children:Y.map(e=>(0,B.jsxs)(`div`,{className:`flex items-center justify-between rounded-md border p-3`,children:[(0,B.jsxs)(`div`,{className:`space-y-1`,children:[(0,B.jsx)(`p`,{className:`font-mono text-sm`,children:e.AccessKeyId}),(0,B.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Created `,x(e.CreateDate)]})]}),(0,B.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,B.jsx)(L,{state:e.Status}),(0,B.jsx)(v,{disabled:W===e.AccessKeyId,onClick:async()=>await ne(e),size:`sm`,variant:`outline`,children:e.Status===`Active`?`Deactivate`:`Activate`}),(0,B.jsx)(v,{disabled:W===e.AccessKeyId,onClick:()=>{e.AccessKeyId&&re(e.AccessKeyId)},size:`sm`,variant:`destructive`,children:`Delete`})]})]},e.AccessKeyId))}):(0,B.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No access keys.`})})]}),(0,B.jsxs)(F,{children:[(0,B.jsx)(F.Header,{children:(0,B.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,B.jsx)(`span`,{children:`Attached Policies`}),(0,B.jsx)(v,{onClick:()=>U(!H),size:`sm`,children:`Attach Policy`})]})}),(0,B.jsxs)(F.Content,{children:[H&&$.length>0&&(0,B.jsxs)(`div`,{className:`col-span-2 space-y-2 rounded-md border p-3`,children:[(0,B.jsx)(`p`,{className:`text-sm font-medium`,children:`Select a policy to attach:`}),(0,B.jsx)(`div`,{className:`space-y-1`,children:$.map(e=>(0,B.jsxs)(`button`,{className:`flex w-full items-center justify-between rounded-md p-2 text-left text-sm hover:bg-accent`,disabled:k.isPending,onClick:()=>{e.Arn&&ie(e.Arn)},type:`button`,children:[(0,B.jsx)(`span`,{children:e.PolicyName}),(0,B.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:e.Arn})]},e.Arn))})]}),H&&$.length===0&&(0,B.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No policies available to attach.`}),X.length>0?(0,B.jsx)(`div`,{className:`col-span-2 space-y-3`,children:X.map(e=>(0,B.jsxs)(`div`,{className:`flex items-center justify-between rounded-md border p-3`,children:[(0,B.jsxs)(`div`,{className:`space-y-1`,children:[(0,B.jsx)(`p`,{className:`text-sm font-medium`,children:e.PolicyName}),(0,B.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e.PolicyArn})]}),(0,B.jsx)(v,{disabled:K===e.PolicyArn,onClick:()=>{e.PolicyArn&&ae(e.PolicyArn)},size:`sm`,variant:`outline`,children:`Detach`})]},e.PolicyArn))}):(0,B.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No attached policies.`})]})]})]}),(0,B.jsx)(P,{description:`Are you sure you want to delete the user "${r}"? This action cannot be undone. You must detach all policies and delete all access keys first.`,isPending:w.isPending,onConfirm:ee,onOpenChange:M,open:j,title:`Delete User`}),N&&(0,B.jsx)(V,{accessKeyId:N.accessKeyId,onClose:()=>z(null),secretAccessKey:N.secretAccessKey})]}):(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(n,{to:`/iam/list-users`,children:`Back to users`}),(0,B.jsx)(`p`,{className:`text-muted-foreground`,children:`User not found.`})]})}export{H as component}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{t as a}from"./useSuspenseQuery.js";import{D as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{t as c}from"./check.js";import{t as l}from"./trash-2.js";import{t as u}from"./triangle-alert.js";import{t as d}from"./button.js";import{_ as f,h as p,i as m,l as h,p as g,r as _}from"./iam.js";import{$ as v,Ir as y,Y as b,it as x,q as S,x as C}from"./index.js";import{t as w}from"./use-copy-to-clipboard.js";import{t as T}from"./page-heading.js";import{a as E,c as D,i as O,l as k,o as A,r as j,s as M,t as N}from"./alert-dialog.js";import{t as P}from"./delete-confirmation-dialog.js";import{n as F,t as I}from"./detail-row.js";import{t as L}from"./state-badge.js";var R=t(n()),z=r(),B=e();function V(e){let t=(0,z.c)(27),{onClose:n,accessKeyId:r,secretAccessKey:i}=e,{copied:a,copy:o}=w(),s;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,B.jsxs)(M,{children:[(0,B.jsx)(D,{children:(0,B.jsx)(u,{className:`text-destructive`})}),(0,B.jsx)(k,{children:`Save Your Access Key`}),(0,B.jsx)(E,{children:`This is your only chance to view the secret access key. Please copy it now. You won't be able to retrieve it again.`})]}),t[0]=s):s=t[0];let l;t[1]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,B.jsx)(`span`,{className:`text-muted-foreground`,children:`Access Key ID: `}),t[1]=l):l=t[1];let f;t[2]===r?f=t[3]:(f=(0,B.jsxs)(`div`,{children:[l,r]}),t[2]=r,t[3]=f);let p;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(p=(0,B.jsx)(`span`,{className:`text-muted-foreground`,children:`Secret Access Key: `}),t[4]=p):p=t[4];let m;t[5]===i?m=t[6]:(m=(0,B.jsxs)(`div`,{children:[p,i]}),t[5]=i,t[6]=m);let h;t[7]!==f||t[8]!==m?(h=(0,B.jsxs)(`div`,{className:`space-y-2 rounded-md border p-4 font-mono text-sm`,children:[f,m]}),t[7]=f,t[8]=m,t[9]=h):h=t[9];let g;t[10]!==r||t[11]!==o||t[12]!==i?(g=async()=>await o(`Access Key ID: ${r}\nSecret Access Key: ${i}`),t[10]=r,t[11]=o,t[12]=i,t[13]=g):g=t[13];let _;t[14]===a?_=t[15]:(_=a?(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(c,{className:`mr-2 size-4`}),`Copied!`]}):`Copy to Clipboard`,t[14]=a,t[15]=_);let v;t[16]!==g||t[17]!==_?(v=(0,B.jsx)(d,{className:`w-full`,onClick:g,type:`button`,variant:`outline`,children:_}),t[16]=g,t[17]=_,t[18]=v):v=t[18];let y;t[19]!==h||t[20]!==v?(y=(0,B.jsxs)(`div`,{className:`space-y-3`,children:[h,v]}),t[19]=h,t[20]=v,t[21]=y):y=t[21];let b;t[22]===n?b=t[23]:(b=(0,B.jsx)(A,{children:(0,B.jsx)(j,{onClick:n,children:`Close and Continue`})}),t[22]=n,t[23]=b);let x;return t[24]!==y||t[25]!==b?(x=(0,B.jsx)(N,{open:!0,children:(0,B.jsxs)(O,{className:`max-w-2xl`,children:[s,y,b]})}),t[24]=y,t[25]=b,t[26]=x):x=t[26],x}function H(){let{userName:e}=C.useParams(),t=y(),{data:n}=a(x(e)),{data:r}=a(S(e)),{data:c}=a(b(e)),{data:u}=a(v),w=g(),E=m(),D=h(),O=f(),k=_(),A=p(),[j,M]=(0,R.useState)(!1),[N,z]=(0,R.useState)(null),[H,U]=(0,R.useState)(!1),[W,G]=(0,R.useState)(null),[K,q]=(0,R.useState)(null),J=n.User,Y=r.AccessKeyMetadata??[],X=c.AttachedPolicies??[],Z=u.Policies??[],Q=new Set(X.map(e=>e.PolicyArn)),$=Z.filter(e=>e.Arn&&!Q.has(e.Arn)),ee=async()=>{try{await w.mutateAsync(e),t({to:`/iam/list-users`})}finally{M(!1)}},te=async()=>{let t=await E.mutateAsync(e);t.AccessKey&&z({accessKeyId:t.AccessKey.AccessKeyId??``,secretAccessKey:t.AccessKey.SecretAccessKey??``})},ne=async t=>{if(t.AccessKeyId){G(t.AccessKeyId);try{await O.mutateAsync({userName:e,accessKeyId:t.AccessKeyId,status:t.Status===`Active`?`Inactive`:`Active`})}finally{G(null)}}},re=async t=>{G(t);try{await D.mutateAsync({userName:e,accessKeyId:t})}finally{G(null)}},ie=async t=>{await k.mutateAsync({userName:e,policyArn:t}),U(!1)},ae=async t=>{q(t);try{await A.mutateAsync({userName:e,policyArn:t})}finally{q(null)}};return J?(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(s,{to:`/iam/list-users`,children:`Back to users`}),w.error&&(0,B.jsx)(i,{error:w.error,msg:`Failed to delete user`}),E.error&&(0,B.jsx)(i,{error:E.error,msg:`Failed to create access key`}),D.error&&(0,B.jsx)(i,{error:D.error,msg:`Failed to delete access key`}),k.error&&(0,B.jsx)(i,{error:k.error,msg:`Failed to attach policy`}),A.error&&(0,B.jsx)(i,{error:A.error,msg:`Failed to detach policy`}),(0,B.jsxs)(`div`,{className:`space-y-6`,children:[(0,B.jsx)(T,{actions:(0,B.jsxs)(d,{onClick:()=>M(!0),size:`sm`,variant:`destructive`,children:[(0,B.jsx)(l,{className:`size-4`}),`Delete`]}),subtitle:`User Details`,title:J.UserName??``}),(0,B.jsxs)(F,{children:[(0,B.jsx)(F.Header,{children:`User Information`}),(0,B.jsxs)(F.Content,{children:[(0,B.jsx)(I,{label:`User Name`,value:J.UserName}),(0,B.jsx)(I,{label:`User ID`,value:J.UserId}),(0,B.jsx)(I,{label:`ARN`,value:J.Arn}),(0,B.jsx)(I,{label:`Path`,value:J.Path}),(0,B.jsx)(I,{label:`Created`,value:o(J.CreateDate)})]})]}),(0,B.jsxs)(F,{children:[(0,B.jsx)(F.Header,{children:(0,B.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,B.jsx)(`span`,{children:`Access Keys`}),(0,B.jsx)(d,{disabled:E.isPending,onClick:te,size:`sm`,children:E.isPending?`Creating...`:`Create Access Key`})]})}),(0,B.jsx)(F.Content,{children:Y.length>0?(0,B.jsx)(`div`,{className:`col-span-2 space-y-3`,children:Y.map(e=>(0,B.jsxs)(`div`,{className:`flex items-center justify-between rounded-md border p-3`,children:[(0,B.jsxs)(`div`,{className:`space-y-1`,children:[(0,B.jsx)(`p`,{className:`font-mono text-sm`,children:e.AccessKeyId}),(0,B.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Created `,o(e.CreateDate)]})]}),(0,B.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,B.jsx)(L,{state:e.Status}),(0,B.jsx)(d,{disabled:W===e.AccessKeyId,onClick:async()=>await ne(e),size:`sm`,variant:`outline`,children:e.Status===`Active`?`Deactivate`:`Activate`}),(0,B.jsx)(d,{disabled:W===e.AccessKeyId,onClick:()=>{e.AccessKeyId&&re(e.AccessKeyId)},size:`sm`,variant:`destructive`,children:`Delete`})]})]},e.AccessKeyId))}):(0,B.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No access keys.`})})]}),(0,B.jsxs)(F,{children:[(0,B.jsx)(F.Header,{children:(0,B.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,B.jsx)(`span`,{children:`Attached Policies`}),(0,B.jsx)(d,{onClick:()=>U(!H),size:`sm`,children:`Attach Policy`})]})}),(0,B.jsxs)(F.Content,{children:[H&&$.length>0&&(0,B.jsxs)(`div`,{className:`col-span-2 space-y-2 rounded-md border p-3`,children:[(0,B.jsx)(`p`,{className:`text-sm font-medium`,children:`Select a policy to attach:`}),(0,B.jsx)(`div`,{className:`space-y-1`,children:$.map(e=>(0,B.jsxs)(`button`,{className:`flex w-full items-center justify-between rounded-md p-2 text-left text-sm hover:bg-accent`,disabled:k.isPending,onClick:()=>{e.Arn&&ie(e.Arn)},type:`button`,children:[(0,B.jsx)(`span`,{children:e.PolicyName}),(0,B.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:e.Arn})]},e.Arn))})]}),H&&$.length===0&&(0,B.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No policies available to attach.`}),X.length>0?(0,B.jsx)(`div`,{className:`col-span-2 space-y-3`,children:X.map(e=>(0,B.jsxs)(`div`,{className:`flex items-center justify-between rounded-md border p-3`,children:[(0,B.jsxs)(`div`,{className:`space-y-1`,children:[(0,B.jsx)(`p`,{className:`text-sm font-medium`,children:e.PolicyName}),(0,B.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e.PolicyArn})]}),(0,B.jsx)(d,{disabled:K===e.PolicyArn,onClick:()=>{e.PolicyArn&&ae(e.PolicyArn)},size:`sm`,variant:`outline`,children:`Detach`})]},e.PolicyArn))}):(0,B.jsx)(`p`,{className:`col-span-2 text-sm text-muted-foreground`,children:`No attached policies.`})]})]})]}),(0,B.jsx)(P,{description:`Are you sure you want to delete the user "${e}"? This action cannot be undone. You must detach all policies and delete all access keys first.`,isPending:w.isPending,onConfirm:ee,onOpenChange:M,open:j,title:`Delete User`}),N&&(0,B.jsx)(V,{accessKeyId:N.accessKeyId,onClose:()=>z(null),secretAccessKey:N.secretAccessKey})]}):(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(s,{to:`/iam/list-users`,children:`Back to users`}),(0,B.jsx)(`p`,{className:`text-muted-foreground`,children:`User not found.`})]})}export{H as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/admin.js b/spinifex/services/spinifexui/frontend/dist/assets/admin.js index 12843e390..7d675c28f 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/admin.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/admin.js @@ -1 +1 @@ -import{Bo as e,Pr as t,_r as n}from"./index.js";var r=e({queryKey:[`admin`,`nodes`],queryFn:async()=>{let e=t();if(!e)throw Error(`Not authenticated`);return await n({action:`GetNodes`,credentials:e})},staleTime:1e4}),i=e({queryKey:[`admin`,`vms`],queryFn:async()=>{let e=t();if(!e)throw Error(`Not authenticated`);return await n({action:`GetVMs`,credentials:e})},staleTime:1e4}),a=e({queryKey:[`admin`,`storageStatus`],queryFn:async()=>{let e=t();if(!e)throw Error(`Not authenticated`);return await n({action:`GetStorageStatus`,credentials:e})},staleTime:1e4});export{a as n,i as r,r as t}; \ No newline at end of file +import{t as e}from"./queryOptions.js";import{u as t}from"./SignatureV4.js";import{Vt as n}from"./index.js";var r=e({queryKey:[`admin`,`nodes`],queryFn:async()=>{let e=t();if(!e)throw Error(`Not authenticated`);return await n({action:`GetNodes`,credentials:e})},staleTime:1e4}),i=e({queryKey:[`admin`,`vms`],queryFn:async()=>{let e=t();if(!e)throw Error(`Not authenticated`);return await n({action:`GetVMs`,credentials:e})},staleTime:1e4}),a=e({queryKey:[`admin`,`storageStatus`],queryFn:async()=>{let e=t();if(!e)throw Error(`Not authenticated`);return await n({action:`GetStorageStatus`,credentials:e})},staleTime:1e4});export{a as n,i as r,r as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/alert-dialog.js b/spinifex/services/spinifexui/frontend/dist/assets/alert-dialog.js index b9d7c5110..ff99ecfa3 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/alert-dialog.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/alert-dialog.js @@ -1 +1 @@ -import{$a as e,$r as t,Fo as n,Hi as r,Ho as i,Ji as a,Qr as o,Ui as s,Uo as c,Vi as l,Xa as u,Zr as d,ai as f,ci as p,cs as m,ei as h,qi as g}from"./index.js";var _=m(c()),v=i();function y(e){let{children:n,open:r,defaultOpen:i=!1,onOpenChange:s,onOpenChangeComplete:c,actionsRef:l,handle:u,triggerId:d,defaultTriggerId:f=null}=e,p=a(!0),m=!!p,y=t.useStore(u?.store,{open:i,openProp:r,activeTriggerId:f,triggerIdProp:d,modal:!0,disablePointerDismissal:!0,nested:m,role:`alertdialog`});y.useControlledProp(`openProp`,r),y.useControlledProp(`triggerIdProp`,d),y.useSyncedValue(`nested`,m),y.useContextCallback(`onOpenChange`,s),y.useContextCallback(`onOpenChangeComplete`,c);let b=y.useState(`payload`);h({store:y,actionsRef:l,parentContext:p?.store.context,isDrawer:!1,onOpenChange:s,triggerIdProp:d});let x=_.useMemo(()=>({store:y}),[y]);return(0,v.jsx)(o.Provider,{value:!1,children:(0,v.jsx)(g.Provider,{value:x,children:typeof n==`function`?n({payload:b}):n})})}var b=n();function x(e){let t=(0,b.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,v.jsx)(y,{"data-slot":`alert-dialog`,...n}),t[2]=n,t[3]=r),r}function S(e){let t=(0,b.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,v.jsx)(f,{"data-slot":`alert-dialog-portal`,...n}),t[2]=n,t[3]=r),r}function C(t){let n=(0,b.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let a;n[3]===r?a=n[4]:(a=e(`data-closed:fade-out-0 data-open:fade-in-0 data-closed:animate-out data-open:animate-in fixed inset-0 isolate z-50 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs`,r),n[3]=r,n[4]=a);let o;return n[5]!==i||n[6]!==a?(o=(0,v.jsx)(s,{className:a,"data-slot":`alert-dialog-overlay`,...i}),n[5]=i,n[6]=a,n[7]=o):o=n[7],o}function w(t){let n=(0,b.c)(11),r,i,a;n[0]===t?(r=n[1],i=n[2],a=n[3]):({className:r,size:a,...i}=t,n[0]=t,n[1]=r,n[2]=i,n[3]=a);let o=a===void 0?`default`:a,s;n[4]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,v.jsx)(C,{}),n[4]=s):s=n[4];let c;n[5]===r?c=n[6]:(c=e(`data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 group/alert-dialog-content data-closed:animate-out data-open:animate-in fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-3 rounded-xl bg-background p-4 ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-64 data-[size=default]:sm:max-w-sm`,r),n[5]=r,n[6]=c);let l;return n[7]!==i||n[8]!==o||n[9]!==c?(l=(0,v.jsxs)(S,{children:[s,(0,v.jsx)(p,{className:c,"data-size":o,"data-slot":`alert-dialog-content`,...i})]}),n[7]=i,n[8]=o,n[9]=c,n[10]=l):l=n[10],l}function T(t){let n=(0,b.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let a;n[3]===r?a=n[4]:(a=e(`grid grid-rows-[auto_1fr] place-items-center gap-1 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]`,r),n[3]=r,n[4]=a);let o;return n[5]!==i||n[6]!==a?(o=(0,v.jsx)(`div`,{className:a,"data-slot":`alert-dialog-header`,...i}),n[5]=i,n[6]=a,n[7]=o):o=n[7],o}function E(t){let n=(0,b.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let a;n[3]===r?a=n[4]:(a=e(`flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end`,r),n[3]=r,n[4]=a);let o;return n[5]!==i||n[6]!==a?(o=(0,v.jsx)(`div`,{className:a,"data-slot":`alert-dialog-footer`,...i}),n[5]=i,n[6]=a,n[7]=o):o=n[7],o}function D(t){let n=(0,b.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let a;n[3]===r?a=n[4]:(a=e(`mb-2 inline-flex size-8 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-4`,r),n[3]=r,n[4]=a);let o;return n[5]!==i||n[6]!==a?(o=(0,v.jsx)(`div`,{className:a,"data-slot":`alert-dialog-media`,...i}),n[5]=i,n[6]=a,n[7]=o):o=n[7],o}function O(t){let n=(0,b.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let a;n[3]===r?a=n[4]:(a=e(`text-sm font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2`,r),n[3]=r,n[4]=a);let o;return n[5]!==i||n[6]!==a?(o=(0,v.jsx)(d,{className:a,"data-slot":`alert-dialog-title`,...i}),n[5]=i,n[6]=a,n[7]=o):o=n[7],o}function k(t){let n=(0,b.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let a;n[3]===r?a=n[4]:(a=e(`text-xs/relaxed text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground`,r),n[3]=r,n[4]=a);let o;return n[5]!==i||n[6]!==a?(o=(0,v.jsx)(l,{className:a,"data-slot":`alert-dialog-description`,...i}),n[5]=i,n[6]=a,n[7]=o):o=n[7],o}function A(t){let n=(0,b.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let a;n[3]===r?a=n[4]:(a=e(r),n[3]=r,n[4]=a);let o;return n[5]!==i||n[6]!==a?(o=(0,v.jsx)(u,{className:a,"data-slot":`alert-dialog-action`,...i}),n[5]=i,n[6]=a,n[7]=o):o=n[7],o}function j(t){let n=(0,b.c)(14),i,a,o,s;n[0]===t?(i=n[1],a=n[2],o=n[3],s=n[4]):({className:i,variant:o,size:s,...a}=t,n[0]=t,n[1]=i,n[2]=a,n[3]=o,n[4]=s);let c=o===void 0?`outline`:o,l=s===void 0?`default`:s,d;n[5]===i?d=n[6]:(d=e(i),n[5]=i,n[6]=d);let f;n[7]!==l||n[8]!==c?(f=(0,v.jsx)(u,{size:l,variant:c}),n[7]=l,n[8]=c,n[9]=f):f=n[9];let p;return n[10]!==a||n[11]!==d||n[12]!==f?(p=(0,v.jsx)(r,{className:d,"data-slot":`alert-dialog-cancel`,render:f,...a}),n[10]=a,n[11]=d,n[12]=f,n[13]=p):p=n[13],p}export{k as a,D as c,w as i,O as l,A as n,E as o,j as r,T as s,x as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{w as n}from"./SignatureV4.js";import{t as r}from"./button.js";import{$t as i,Kt as a,Mn as o,Nn as s,Xt as c,jn as l,qt as u}from"./index.js";function d(e){return u(e,`alert-dialog`)}var f=t(),p=e();function m(e){let t=(0,f.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,p.jsx)(d,{"data-slot":`alert-dialog`,...n}),t[2]=n,t[3]=r),r}function h(e){let t=(0,f.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,p.jsx)(c,{"data-slot":`alert-dialog-portal`,...n}),t[2]=n,t[3]=r),r}function g(e){let t=(0,f.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`data-closed:fade-out-0 data-open:fade-in-0 data-closed:animate-out data-open:animate-in fixed inset-0 isolate z-50 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,p.jsx)(s,{className:a,"data-slot":`alert-dialog-overlay`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function _(e){let t=(0,f.c)(11),r,a,o;t[0]===e?(r=t[1],a=t[2],o=t[3]):({className:r,size:o,...a}=e,t[0]=e,t[1]=r,t[2]=a,t[3]=o);let s=o===void 0?`default`:o,c;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,p.jsx)(g,{}),t[4]=c):c=t[4];let l;t[5]===r?l=t[6]:(l=n(`data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 group/alert-dialog-content data-closed:animate-out data-open:animate-in fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-3 rounded-xl bg-background p-4 ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-64 data-[size=default]:sm:max-w-sm`,r),t[5]=r,t[6]=l);let u;return t[7]!==a||t[8]!==s||t[9]!==l?(u=(0,p.jsxs)(h,{children:[c,(0,p.jsx)(i,{className:l,"data-size":s,"data-slot":`alert-dialog-content`,...a})]}),t[7]=a,t[8]=s,t[9]=l,t[10]=u):u=t[10],u}function v(e){let t=(0,f.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`grid grid-rows-[auto_1fr] place-items-center gap-1 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,p.jsx)(`div`,{className:a,"data-slot":`alert-dialog-header`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function y(e){let t=(0,f.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,p.jsx)(`div`,{className:a,"data-slot":`alert-dialog-footer`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function b(e){let t=(0,f.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`mb-2 inline-flex size-8 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-4`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,p.jsx)(`div`,{className:a,"data-slot":`alert-dialog-media`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function x(e){let t=(0,f.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let o;t[3]===r?o=t[4]:(o=n(`text-sm font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2`,r),t[3]=r,t[4]=o);let s;return t[5]!==i||t[6]!==o?(s=(0,p.jsx)(a,{className:o,"data-slot":`alert-dialog-title`,...i}),t[5]=i,t[6]=o,t[7]=s):s=t[7],s}function S(e){let t=(0,f.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`text-xs/relaxed text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,p.jsx)(l,{className:a,"data-slot":`alert-dialog-description`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function C(e){let t=(0,f.c)(8),i,a;t[0]===e?(i=t[1],a=t[2]):({className:i,...a}=e,t[0]=e,t[1]=i,t[2]=a);let o;t[3]===i?o=t[4]:(o=n(i),t[3]=i,t[4]=o);let s;return t[5]!==a||t[6]!==o?(s=(0,p.jsx)(r,{className:o,"data-slot":`alert-dialog-action`,...a}),t[5]=a,t[6]=o,t[7]=s):s=t[7],s}function w(e){let t=(0,f.c)(14),i,a,s,c;t[0]===e?(i=t[1],a=t[2],s=t[3],c=t[4]):({className:i,variant:s,size:c,...a}=e,t[0]=e,t[1]=i,t[2]=a,t[3]=s,t[4]=c);let l=s===void 0?`outline`:s,u=c===void 0?`default`:c,d;t[5]===i?d=t[6]:(d=n(i),t[5]=i,t[6]=d);let m;t[7]!==u||t[8]!==l?(m=(0,p.jsx)(r,{size:u,variant:l}),t[7]=u,t[8]=l,t[9]=m):m=t[9];let h;return t[10]!==a||t[11]!==d||t[12]!==m?(h=(0,p.jsx)(o,{className:d,"data-slot":`alert-dialog-cancel`,render:m,...a}),t[10]=a,t[11]=d,t[12]=m,t[13]=h):h=t[13],h}export{S as a,b as c,_ as i,x as l,C as n,y as o,w as r,v as s,m as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/allocate-address.js b/spinifex/services/spinifexui/frontend/dist/assets/allocate-address.js index 6e43d1416..bda64ac1c 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/allocate-address.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/allocate-address.js @@ -1,3 +1,3 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{t as n}from"./cli-command-panel.js";import{t as r}from"./ec2.js";import{Fo as i,Ho as a,Ro as o,Xi as s}from"./index.js";import{a as c,i as l,t as u}from"./zod.js";import{t as d}from"./form-actions.js";import{t as f}from"./page-heading.js";import{a as p,t as m}from"./field.js";import{n as h}from"./ec22.js";var g=i(),_=a();function v(){let i=(0,g.c)(36),a=o(),v=r(),b;i[0]===Symbol.for(`react.memo_cache_sentinel`)?(b={resolver:u(h),defaultValues:{name:``}},i[0]=b):b=i[0];let{control:x,handleSubmit:S,register:C,formState:w}=l(b),{isSubmitting:T}=w,E;i[1]===x?E=i[2]:(E={control:x},i[1]=x,i[2]=E);let D=c(E),O;i[3]!==v||i[4]!==a?(O=async e=>{let t=(await v.mutateAsync({name:e.name?.trim()??void 0})).AllocationId;a(t?{to:`/ec2/describe-addresses/$id`,params:{id:t}}:{to:`/ec2/describe-addresses`})},i[3]=v,i[4]=a,i[5]=O):O=i[5];let k=O,A,j;i[6]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,_.jsx)(t,{to:`/ec2/describe-addresses`,children:`Back to Elastic IPs`}),j=(0,_.jsx)(f,{title:`Allocate Elastic IP`}),i[6]=A,i[7]=j):(A=i[6],j=i[7]);let M;i[8]===v.error?M=i[9]:(M=v.error&&(0,_.jsx)(e,{error:v.error,msg:`Failed to allocate Elastic IP`}),i[8]=v.error,i[9]=M);let N;i[10]!==S||i[11]!==k?(N=S(k),i[10]=S,i[11]=k,i[12]=N):N=i[12];let P;i[13]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,_.jsx)(p,{children:(0,_.jsx)(`label`,{htmlFor:`name`,children:`Name (optional)`})}),i[13]=P):P=i[13];let F;i[14]===C?F=i[15]:(F=C(`name`),i[14]=C,i[15]=F);let I;i[16]===F?I=i[17]:(I=(0,_.jsxs)(m,{children:[P,(0,_.jsx)(s,{id:`name`,placeholder:`my-elastic-ip`,...F})]}),i[16]=F,i[17]=I);let L=D.name??``,R;i[18]===L?R=i[19]:(R=y(L),i[18]=L,i[19]=R);let z;i[20]===R?z=i[21]:(z=(0,_.jsx)(n,{commands:R}),i[20]=R,i[21]=z);let B;i[22]===a?B=i[23]:(B=async()=>await a({to:`/ec2/describe-addresses`}),i[22]=a,i[23]=B);let V;i[24]!==v.isPending||i[25]!==T||i[26]!==B?(V=(0,_.jsx)(d,{isPending:v.isPending,isSubmitting:T,onCancel:B,pendingLabel:`Allocating…`,submitLabel:`Allocate`}),i[24]=v.isPending,i[25]=T,i[26]=B,i[27]=V):V=i[27];let H;i[28]!==I||i[29]!==z||i[30]!==V||i[31]!==N?(H=(0,_.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:N,children:[I,z,V]}),i[28]=I,i[29]=z,i[30]=V,i[31]=N,i[32]=H):H=i[32];let U;return i[33]!==H||i[34]!==M?(U=(0,_.jsxs)(_.Fragment,{children:[A,j,M,H]}),i[33]=H,i[34]=M,i[35]=U):U=i[35],U}function y(e){let t=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 allocate-address`},{type:`flag`,value:` \\ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{t as a}from"./ec2.js";import{Ir as o,Rn as s}from"./index.js";import{a as c,i as l,t as u}from"./zod.js";import{t as d}from"./form-actions.js";import{t as f}from"./page-heading.js";import{a as p,t as m}from"./field.js";import{n as h}from"./ec22.js";var g=t(),_=e();function v(){let e=(0,g.c)(36),t=o(),v=a(),b;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(b={resolver:u(h),defaultValues:{name:``}},e[0]=b):b=e[0];let{control:x,handleSubmit:S,register:C,formState:w}=l(b),{isSubmitting:T}=w,E;e[1]===x?E=e[2]:(E={control:x},e[1]=x,e[2]=E);let D=c(E),O;e[3]!==v||e[4]!==t?(O=async e=>{let n=(await v.mutateAsync({name:e.name?.trim()??void 0})).AllocationId;t(n?{to:`/ec2/describe-addresses/$id`,params:{id:n}}:{to:`/ec2/describe-addresses`})},e[3]=v,e[4]=t,e[5]=O):O=e[5];let k=O,A,j;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,_.jsx)(r,{to:`/ec2/describe-addresses`,children:`Back to Elastic IPs`}),j=(0,_.jsx)(f,{title:`Allocate Elastic IP`}),e[6]=A,e[7]=j):(A=e[6],j=e[7]);let M;e[8]===v.error?M=e[9]:(M=v.error&&(0,_.jsx)(n,{error:v.error,msg:`Failed to allocate Elastic IP`}),e[8]=v.error,e[9]=M);let N;e[10]!==S||e[11]!==k?(N=S(k),e[10]=S,e[11]=k,e[12]=N):N=e[12];let P;e[13]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,_.jsx)(p,{children:(0,_.jsx)(`label`,{htmlFor:`name`,children:`Name (optional)`})}),e[13]=P):P=e[13];let F;e[14]===C?F=e[15]:(F=C(`name`),e[14]=C,e[15]=F);let I;e[16]===F?I=e[17]:(I=(0,_.jsxs)(m,{children:[P,(0,_.jsx)(s,{id:`name`,placeholder:`my-elastic-ip`,...F})]}),e[16]=F,e[17]=I);let L=D.name??``,R;e[18]===L?R=e[19]:(R=y(L),e[18]=L,e[19]=R);let z;e[20]===R?z=e[21]:(z=(0,_.jsx)(i,{commands:R}),e[20]=R,e[21]=z);let B;e[22]===t?B=e[23]:(B=async()=>await t({to:`/ec2/describe-addresses`}),e[22]=t,e[23]=B);let V;e[24]!==v.isPending||e[25]!==T||e[26]!==B?(V=(0,_.jsx)(d,{isPending:v.isPending,isSubmitting:T,onCancel:B,pendingLabel:`Allocating…`,submitLabel:`Allocate`}),e[24]=v.isPending,e[25]=T,e[26]=B,e[27]=V):V=e[27];let H;e[28]!==I||e[29]!==z||e[30]!==V||e[31]!==N?(H=(0,_.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:N,children:[I,z,V]}),e[28]=I,e[29]=z,e[30]=V,e[31]=N,e[32]=H):H=e[32];let U;return e[33]!==H||e[34]!==M?(U=(0,_.jsxs)(_.Fragment,{children:[A,j,M,H]}),e[33]=H,e[34]=M,e[35]=U):U=e[35],U}function y(e){let t=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 allocate-address`},{type:`flag`,value:` \\ --domain`},{type:`value`,value:` vpc`}];return e.trim()&&t.push({type:`flag`,value:` \\ --tag-specifications`},{type:`value`,value:` 'ResourceType=elastic-ip,Tags=[{Key=Name,Value=${e.trim()}}]'`}),[{label:`Allocate Elastic IP`,parts:t}]}export{v as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/ami-details.js b/spinifex/services/spinifexui/frontend/dist/assets/ami-details.js index 51365732a..479ca80e0 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/ami-details.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/ami-details.js @@ -1 +1 @@ -import{Fo as e,Ho as t,Lo as n,no as r}from"./index.js";import{n as i,t as a}from"./detail-row.js";var o=e(),s=t();function c(e){let t=(0,o.c)(58),{image:c,showExtendedDetails:u}=e,d=u===void 0?!1:u,f;t[0]===c.CreationDate?f=t[1]:(f=r(c.CreationDate),t[0]=c.CreationDate,t[1]=f);let p=f,m=d?`AMI Information`:`AMI Details`,h;t[2]===m?h=t[3]:(h=(0,s.jsx)(i.Header,{children:m}),t[2]=m,t[3]=h);let g;t[4]!==c.ImageId||t[5]!==d?(g=c.ImageId&&!d?(0,s.jsx)(n,{className:`text-primary hover:underline`,params:{id:c.ImageId},to:`/ec2/describe-images/$id`,children:c.ImageId}):c.ImageId,t[4]=c.ImageId,t[5]=d,t[6]=g):g=t[6];let _;t[7]===g?_=t[8]:(_=(0,s.jsx)(a,{label:`AMI ID`,value:g}),t[7]=g,t[8]=_);let v;t[9]===c.Name?v=t[10]:(v=(0,s.jsx)(a,{label:`AMI Name`,value:c.Name}),t[9]=c.Name,t[10]=v);let y;t[11]===c.Description?y=t[12]:(y=(0,s.jsx)(a,{label:`Description`,value:c.Description}),t[11]=c.Description,t[12]=y);let b;t[13]===c.Architecture?b=t[14]:(b=(0,s.jsx)(a,{label:`Architecture`,value:c.Architecture}),t[13]=c.Architecture,t[14]=b);let x;t[15]===c.VirtualizationType?x=t[16]:(x=(0,s.jsx)(a,{label:`Virtualization Type`,value:c.VirtualizationType}),t[15]=c.VirtualizationType,t[16]=x);let S=c.Platform??`Linux`,C;t[17]===S?C=t[18]:(C=(0,s.jsx)(a,{label:`Platform`,value:S}),t[17]=S,t[18]=C);let w;t[19]===c.PlatformDetails?w=t[20]:(w=(0,s.jsx)(a,{label:`Platform Details`,value:c.PlatformDetails}),t[19]=c.PlatformDetails,t[20]=w);let T;t[21]===c.ImageType?T=t[22]:(T=(0,s.jsx)(a,{label:`Image Type`,value:c.ImageType}),t[21]=c.ImageType,t[22]=T);let E;t[23]===c.RootDeviceType?E=t[24]:(E=(0,s.jsx)(a,{label:`Root Device Type`,value:c.RootDeviceType}),t[23]=c.RootDeviceType,t[24]=E);let D;t[25]===c.RootDeviceName?D=t[26]:(D=(0,s.jsx)(a,{label:`Root Device Name`,value:c.RootDeviceName}),t[25]=c.RootDeviceName,t[26]=D);let O;t[27]===c.OwnerId?O=t[28]:(O=(0,s.jsx)(a,{label:`Owner`,value:c.OwnerId}),t[27]=c.OwnerId,t[28]=O);let k;t[29]===c.State?k=t[30]:(k=(0,s.jsx)(a,{label:`State`,value:c.State}),t[29]=c.State,t[30]=k);let A;t[31]!==p||t[32]!==c.Public||t[33]!==d?(A=d&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a,{label:`Creation Date`,value:p}),(0,s.jsx)(a,{label:`Public`,value:c.Public?`Yes`:`No`})]}),t[31]=p,t[32]=c.Public,t[33]=d,t[34]=A):A=t[34];let j;t[35]!==x||t[36]!==C||t[37]!==w||t[38]!==T||t[39]!==E||t[40]!==D||t[41]!==O||t[42]!==k||t[43]!==A||t[44]!==_||t[45]!==v||t[46]!==y||t[47]!==b?(j=(0,s.jsxs)(i.Content,{children:[_,v,y,b,x,C,w,T,E,D,O,k,A]}),t[35]=x,t[36]=C,t[37]=w,t[38]=T,t[39]=E,t[40]=D,t[41]=O,t[42]=k,t[43]=A,t[44]=_,t[45]=v,t[46]=y,t[47]=b,t[48]=j):j=t[48];let M;t[49]!==j||t[50]!==h?(M=(0,s.jsxs)(i,{children:[h,j]}),t[49]=j,t[50]=h,t[51]=M):M=t[51];let N;t[52]!==c.BlockDeviceMappings||t[53]!==d?(N=d&&c.BlockDeviceMappings&&c.BlockDeviceMappings.length>0&&c.BlockDeviceMappings.map(l),t[52]=c.BlockDeviceMappings,t[53]=d,t[54]=N):N=t[54];let P;return t[55]!==M||t[56]!==N?(P=(0,s.jsxs)(s.Fragment,{children:[M,N]}),t[55]=M,t[56]=N,t[57]=P):P=t[57],P}function l(e){return(0,s.jsxs)(i,{children:[(0,s.jsxs)(i.Header,{children:[`Block Device: `,e.DeviceName]}),(0,s.jsxs)(i.Content,{children:[(0,s.jsx)(a,{label:`Device Name`,value:e.DeviceName}),e.Ebs&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a,{label:`Volume Size`,value:e.Ebs.VolumeSize?`${e.Ebs.VolumeSize} GB`:void 0}),(0,s.jsx)(a,{label:`Volume Type`,value:e.Ebs.VolumeType}),(0,s.jsx)(a,{label:`Delete On Termination`,value:e.Ebs.DeleteOnTermination?`Yes`:`No`}),(0,s.jsx)(a,{label:`Encrypted`,value:e.Ebs.Encrypted?`Yes`:`No`}),e.Ebs.SnapshotId&&(0,s.jsx)(a,{label:`Snapshot ID`,value:(0,s.jsx)(n,{className:`text-primary hover:underline`,params:{id:e.Ebs.SnapshotId},to:`/ec2/describe-snapshots/$id`,children:e.Ebs.SnapshotId})})]}),e.VirtualName&&(0,s.jsx)(a,{label:`Virtual Name`,value:e.VirtualName})]})]},e.DeviceName)}export{c as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{Or as n}from"./awsClient.js";import{D as r}from"./SignatureV4.js";import{n as i,t as a}from"./detail-row.js";var o=t(),s=e();function c(e){let t=(0,o.c)(58),{image:c,showExtendedDetails:u}=e,d=u===void 0?!1:u,f;t[0]===c.CreationDate?f=t[1]:(f=r(c.CreationDate),t[0]=c.CreationDate,t[1]=f);let p=f,m=d?`AMI Information`:`AMI Details`,h;t[2]===m?h=t[3]:(h=(0,s.jsx)(i.Header,{children:m}),t[2]=m,t[3]=h);let g;t[4]!==c.ImageId||t[5]!==d?(g=c.ImageId&&!d?(0,s.jsx)(n,{className:`text-primary hover:underline`,params:{id:c.ImageId},to:`/ec2/describe-images/$id`,children:c.ImageId}):c.ImageId,t[4]=c.ImageId,t[5]=d,t[6]=g):g=t[6];let _;t[7]===g?_=t[8]:(_=(0,s.jsx)(a,{label:`AMI ID`,value:g}),t[7]=g,t[8]=_);let v;t[9]===c.Name?v=t[10]:(v=(0,s.jsx)(a,{label:`AMI Name`,value:c.Name}),t[9]=c.Name,t[10]=v);let y;t[11]===c.Description?y=t[12]:(y=(0,s.jsx)(a,{label:`Description`,value:c.Description}),t[11]=c.Description,t[12]=y);let b;t[13]===c.Architecture?b=t[14]:(b=(0,s.jsx)(a,{label:`Architecture`,value:c.Architecture}),t[13]=c.Architecture,t[14]=b);let x;t[15]===c.VirtualizationType?x=t[16]:(x=(0,s.jsx)(a,{label:`Virtualization Type`,value:c.VirtualizationType}),t[15]=c.VirtualizationType,t[16]=x);let S=c.Platform??`Linux`,C;t[17]===S?C=t[18]:(C=(0,s.jsx)(a,{label:`Platform`,value:S}),t[17]=S,t[18]=C);let w;t[19]===c.PlatformDetails?w=t[20]:(w=(0,s.jsx)(a,{label:`Platform Details`,value:c.PlatformDetails}),t[19]=c.PlatformDetails,t[20]=w);let T;t[21]===c.ImageType?T=t[22]:(T=(0,s.jsx)(a,{label:`Image Type`,value:c.ImageType}),t[21]=c.ImageType,t[22]=T);let E;t[23]===c.RootDeviceType?E=t[24]:(E=(0,s.jsx)(a,{label:`Root Device Type`,value:c.RootDeviceType}),t[23]=c.RootDeviceType,t[24]=E);let D;t[25]===c.RootDeviceName?D=t[26]:(D=(0,s.jsx)(a,{label:`Root Device Name`,value:c.RootDeviceName}),t[25]=c.RootDeviceName,t[26]=D);let O;t[27]===c.OwnerId?O=t[28]:(O=(0,s.jsx)(a,{label:`Owner`,value:c.OwnerId}),t[27]=c.OwnerId,t[28]=O);let k;t[29]===c.State?k=t[30]:(k=(0,s.jsx)(a,{label:`State`,value:c.State}),t[29]=c.State,t[30]=k);let A;t[31]!==p||t[32]!==c.Public||t[33]!==d?(A=d&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a,{label:`Creation Date`,value:p}),(0,s.jsx)(a,{label:`Public`,value:c.Public?`Yes`:`No`})]}),t[31]=p,t[32]=c.Public,t[33]=d,t[34]=A):A=t[34];let j;t[35]!==x||t[36]!==C||t[37]!==w||t[38]!==T||t[39]!==E||t[40]!==D||t[41]!==O||t[42]!==k||t[43]!==A||t[44]!==_||t[45]!==v||t[46]!==y||t[47]!==b?(j=(0,s.jsxs)(i.Content,{children:[_,v,y,b,x,C,w,T,E,D,O,k,A]}),t[35]=x,t[36]=C,t[37]=w,t[38]=T,t[39]=E,t[40]=D,t[41]=O,t[42]=k,t[43]=A,t[44]=_,t[45]=v,t[46]=y,t[47]=b,t[48]=j):j=t[48];let M;t[49]!==j||t[50]!==h?(M=(0,s.jsxs)(i,{children:[h,j]}),t[49]=j,t[50]=h,t[51]=M):M=t[51];let N;t[52]!==c.BlockDeviceMappings||t[53]!==d?(N=d&&c.BlockDeviceMappings&&c.BlockDeviceMappings.length>0&&c.BlockDeviceMappings.map(l),t[52]=c.BlockDeviceMappings,t[53]=d,t[54]=N):N=t[54];let P;return t[55]!==M||t[56]!==N?(P=(0,s.jsxs)(s.Fragment,{children:[M,N]}),t[55]=M,t[56]=N,t[57]=P):P=t[57],P}function l(e){return(0,s.jsxs)(i,{children:[(0,s.jsxs)(i.Header,{children:[`Block Device: `,e.DeviceName]}),(0,s.jsxs)(i.Content,{children:[(0,s.jsx)(a,{label:`Device Name`,value:e.DeviceName}),e.Ebs&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a,{label:`Volume Size`,value:e.Ebs.VolumeSize?`${e.Ebs.VolumeSize} GB`:void 0}),(0,s.jsx)(a,{label:`Volume Type`,value:e.Ebs.VolumeType}),(0,s.jsx)(a,{label:`Delete On Termination`,value:e.Ebs.DeleteOnTermination?`Yes`:`No`}),(0,s.jsx)(a,{label:`Encrypted`,value:e.Ebs.Encrypted?`Yes`:`No`}),e.Ebs.SnapshotId&&(0,s.jsx)(a,{label:`Snapshot ID`,value:(0,s.jsx)(n,{className:`text-primary hover:underline`,params:{id:e.Ebs.SnapshotId},to:`/ec2/describe-snapshots/$id`,children:e.Ebs.SnapshotId})})]}),e.VirtualName&&(0,s.jsx)(a,{label:`Virtual Name`,value:e.VirtualName})]})]},e.DeviceName)}export{c as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/awsClient.js b/spinifex/services/spinifexui/frontend/dist/assets/awsClient.js new file mode 100644 index 000000000..37f34a35e --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/awsClient.js @@ -0,0 +1,5 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index2.browser.js","assets/compiler-runtime.js","assets/index.browser.js"])))=>i.map(i=>d[i]); +import{i as e,n as t,o as n,r}from"./compiler-runtime.js";import{A as i,F as a,H as o,N as s,U as c,i as l,p as u,s as d,t as f,u as p}from"./useStore.js";import{a as ee,i as m,n as te,o as ne,r as re,s as ie,t as ae,u as oe}from"./SignatureV4.js";import{C as se,D as ce,E as le,F as ue,I as de,O as fe,S as pe,T as me,_ as he,a as ge,b as _e,g as ve,h as ye,i as be,m as xe,n as Se,p as Ce,r as we,t as Te,u as Ee,v as De,w as Oe,x as ke,y as Ae}from"./index.browser.js";import{n as eee,r as je}from"./index2.browser.js";var Me=`Error preloading route! ☝️`,Ne=e((e=>{var t=r();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=Ne()})),Fe=n(r(),1);t();var Ie=n(Pe(),1);function Le(e,t){let n=l(),r=o(t),{activeProps:ee,inactiveProps:m,activeOptions:te,to:ne,preload:re,preloadDelay:ie,preloadIntentProximity:ae,hashScrollIntoView:oe,replace:se,startTransition:ce,resetScroll:le,viewTransition:ue,children:de,target:fe,disabled:pe,style:me,className:he,onClick:ge,onBlur:_e,onFocus:ve,onMouseEnter:ye,onMouseLeave:be,onTouchStart:xe,ignoreBlocker:Se,params:Ce,search:we,hash:Te,state:Ee,mask:De,reloadDocument:Oe,unsafeRelative:ke,from:Ae,_fromLocation:eee,...je}=e,Ne=d(),Pe=Fe.useMemo(()=>e,[n,e.from,e._fromLocation,e.hash,e.to,e.search,e.params,e.state,e.mask,e.unsafeRelative]),Le=f(n.stores.location,e=>e,(e,t)=>e.href===t.href),qe=Fe.useMemo(()=>{let e={_fromLocation:Le,...Pe};return n.buildLocation(e)},[n,Le,Pe]),Je=qe.maskedLocation?qe.maskedLocation.publicHref:qe.publicHref,Ye=qe.maskedLocation?qe.maskedLocation.external:qe.external,Xe=Fe.useMemo(()=>Ke(Je,Ye,n.history,pe),[pe,Ye,Je,n.history]),Ze=Fe.useMemo(()=>{if(Xe?.external)return a(Xe.href,n.protocolAllowlist)?void 0:Xe.href;if(!tee(ne)&&!(typeof ne!=`string`||ne.indexOf(`:`)===-1))try{return new URL(ne),a(ne,n.protocolAllowlist)?void 0:ne}catch{}},[ne,Xe,n.protocolAllowlist]),Qe=Fe.useMemo(()=>{if(Ze)return!1;if(te?.exact){if(!p(Le.pathname,qe.pathname,n.basepath))return!1}else{let e=u(Le.pathname,n.basepath),t=u(qe.pathname,n.basepath);if(!(e.startsWith(t)&&(e.length===t.length||e[t.length]===`/`)))return!1}return(te?.includeSearch??!0)&&!i(Le.search,qe.search,{partial:!te?.exact,ignoreUndefined:!te?.explicitUndefined})?!1:te?.includeHash?Ne&&Le.hash===qe.hash:!0},[te?.exact,te?.explicitUndefined,te?.includeHash,te?.includeSearch,Le,Ze,Ne,qe.hash,qe.pathname,qe.search,n.basepath]),$e=Qe?s(ee,{})??ze:Re,et=Qe?Re:s(m,{})??Re,tt=[he,$e.className,et.className].filter(Boolean).join(` `),nt=(me||$e.style||et.style)&&{...me,...$e.style,...et.style},[rt,it]=Fe.useState(!1),at=Fe.useRef(!1),ot=e.reloadDocument||Ze?!1:re??n.options.defaultPreload,st=ie??n.options.defaultPreloadDelay??0,ct=Fe.useCallback(()=>{n.preloadRoute({...Pe,_builtLocation:qe}).catch(e=>{console.warn(e),console.warn(Me)})},[n,Pe,qe]);c(r,Fe.useCallback(e=>{e?.isIntersecting&&ct()},[ct]),We,{disabled:!!pe||ot!==`viewport`}),Fe.useEffect(()=>{at.current||!pe&&ot===`render`&&(ct(),at.current=!0)},[pe,ct,ot]);let lt=e=>{let t=e.currentTarget.getAttribute(`target`),r=fe===void 0?t:fe;if(!pe&&!nee(e)&&!e.defaultPrevented&&(!r||r===`_self`)&&e.button===0){e.preventDefault(),(0,Ie.flushSync)(()=>{it(!0)});let t=n.subscribe(`onResolved`,()=>{t(),it(!1)});n.navigate({...Pe,replace:se,resetScroll:le,hashScrollIntoView:oe,startTransition:ce,viewTransition:ue,ignoreBlocker:Se})}};if(Ze)return{...je,ref:r,href:Ze,...de&&{children:de},...fe&&{target:fe},...pe&&{disabled:pe},...me&&{style:me},...he&&{className:he},...ge&&{onClick:ge},..._e&&{onBlur:_e},...ve&&{onFocus:ve},...ye&&{onMouseEnter:ye},...be&&{onMouseLeave:be},...xe&&{onTouchStart:xe}};let ut=e=>{if(pe||ot!==`intent`)return;if(!st){ct();return}let t=e.currentTarget;if(Ue.has(t))return;let n=setTimeout(()=>{Ue.delete(t),ct()},st);Ue.set(t,n)},dt=e=>{pe||ot!==`intent`||ct()},ft=e=>{if(pe||!ot||!st)return;let t=e.currentTarget,n=Ue.get(t);n&&(clearTimeout(n),Ue.delete(t))};return{...je,...$e,...et,href:Xe?.href,ref:r,onClick:Ge([ge,lt]),onBlur:Ge([_e,ft]),onFocus:Ge([ve,ut]),onMouseEnter:Ge([ye,ut]),onMouseLeave:Ge([be,ft]),onTouchStart:Ge([xe,dt]),disabled:!!pe,target:fe,...nt&&{style:nt},...tt&&{className:tt},...pe&&Be,...Qe&&Ve,...Ne&&rt&&He}}var Re={},ze={className:`active`},Be={role:`link`,"aria-disabled":!0},Ve={"data-status":`active`,"aria-current":`page`},He={"data-transitioning":`transitioning`},Ue=new WeakMap,We={rootMargin:`100px`},Ge=e=>t=>{for(let n of e)if(n){if(t.defaultPrevented)return;n(t)}};function Ke(e,t,n,r){if(!r)return t?{href:e,external:!0}:{href:n.createHref(e)||`/`,external:!1}}function tee(e){if(typeof e!=`string`)return!1;let t=e.charCodeAt(0);return t===47?e.charCodeAt(1)!==47:t===46}var qe=Fe.forwardRef((e,t)=>{let{_asChild:n,...r}=e,{type:i,...a}=Le(r,t),o=typeof r.children==`function`?r.children({isActive:a[`data-status`]===`active`}):r.children;if(!n){let{disabled:e,...t}=a;return Fe.createElement(`a`,t,o)}return Fe.createElement(n,a,o)});function nee(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}var Je;(function(e){e.HTTP=`http`,e.HTTPS=`https`})(Je||={});var Ye;(function(e){e.MD5=`md5`,e.CRC32=`crc32`,e.CRC32C=`crc32c`,e.SHA1=`sha1`,e.SHA256=`sha256`})(Ye||={});var Xe=`__smithy_context`,Ze=e=>e.__smithy_context||={},Qe=class{statusCode;reason;headers;body;constructor(e){this.statusCode=e.statusCode,this.reason=e.reason,this.headers=e.headers||{},this.body=e.body}static isInstance(e){if(!e)return!1;let t=e;return typeof t.statusCode==`number`&&typeof t.headers==`object`}},$e=RegExp(`^(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}$`),et=(e,t=!1)=>{if(!t)return $e.test(e);let n=e.split(`.`);for(let e of n)if(!et(e))return!1;return!0};function tt(e){let t={};if(e=e.replace(/^\?/,``),e)for(let n of e.split(`&`)){let[e,r=null]=n.split(`=`);e=decodeURIComponent(e),r&&=decodeURIComponent(r),e in t?Array.isArray(t[e])?t[e].push(r):t[e]=[t[e],r]:t[e]=r}return t}var nt=e=>{if(typeof e==`string`)return nt(new URL(e));let{hostname:t,pathname:n,port:r,protocol:i,search:a}=e,o;return a&&(o=tt(a)),{hostname:t,port:r?parseInt(r):void 0,protocol:i,path:n,query:o}},rt=e=>{if(typeof e==`object`){if(`url`in e){let t=nt(e.url);if(e.headers){t.headers={};for(let n in e.headers)t.headers[n.toLowerCase()]=e.headers[n].join(`, `)}return t}return e}return nt(e)},it=async e=>void 0,at=async e=>{let t=e?.Bucket||``;if(typeof e.Bucket==`string`&&(e.Bucket=t.replace(/#/g,`%23`).replace(/\?/g,`%3F`)),ut(t)){if(e.ForcePathStyle===!0)throw Error(`Path-style addressing cannot be used with ARN buckets`)}else (!lt(t)||t.indexOf(`.`)!==-1&&!String(e.Endpoint).startsWith(`http:`)||t.toLowerCase()!==t||t.length<3)&&(e.ForcePathStyle=!0);return e.DisableMultiRegionAccessPoints&&(e.disableMultiRegionAccessPoints=!0,e.DisableMRAP=!0),e},ot=/^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$/,st=/(\d+\.){3}\d+/,ct=/\.\./,lt=e=>ot.test(e)&&!st.test(e)&&!ct.test(e),ut=e=>{let[t,n,r,,,i]=e.split(`:`),a=t===`arn`&&e.split(`:`).length>=6,o=!!(a&&n&&r&&i);if(a&&!o)throw Error(`Invalid ARN: ${e} was an invalid ARN.`);return o},dt=(e,t,n,r=!1)=>{let i=async()=>{let i;return i=r?n.clientContextParams?.[e]??n[e]??n[t]:n[e]??n[t],typeof i==`function`?i():i};return e===`credentialScope`||t===`CredentialScope`?async()=>{let e=typeof n.credentials==`function`?await n.credentials():n.credentials;return e?.credentialScope??e?.CredentialScope}:e===`accountId`||t===`AccountId`?async()=>{let e=typeof n.credentials==`function`?await n.credentials():n.credentials;return e?.accountId??e?.AccountId}:e===`endpoint`||t===`endpoint`?async()=>{if(n.isCustomEndpoint===!1)return;let e=await i();if(e&&typeof e==`object`){if(`url`in e)return e.url.href;if(`hostname`in e){let{protocol:t,hostname:n,port:r,path:i}=e;return`${t}//${n}${r?`:`+r:``}${i}`}}return e}:i};function ft(e){return async(t,n,r,i)=>{if(!r.isCustomEndpoint){let t;t=r.serviceConfiguredEndpoint?await r.serviceConfiguredEndpoint():await e(r.serviceId),t&&(r.endpoint=()=>Promise.resolve(rt(t)),r.isCustomEndpoint=!0)}let a=await pt(t,n,r);if(typeof r.endpointProvider!=`function`)throw Error(`config.endpointProvider is not set.`);let o=r.endpointProvider(a,i);if(r.isCustomEndpoint&&r.endpoint){let e=await r.endpoint();if(e?.headers){o.headers??={};for(let[t,n]of Object.entries(e.headers))o.headers[t]=Array.isArray(n)?n:[n]}}return o}}var pt=async(e,t,n)=>{let r={},i=t?.getEndpointParameterInstructions?.()||{};for(let[t,a]of Object.entries(i))switch(a.type){case`staticContextParams`:r[t]=a.value;break;case`contextParams`:r[t]=e[a.name];break;case`clientContextParams`:case`builtInParams`:r[t]=await dt(a.name,t,n,a.type!==`builtInParams`)();break;case`operationContextParams`:r[t]=a.get(e);break;default:throw Error(`Unrecognized endpoint parameter instruction: `+JSON.stringify(a))}return Object.keys(i).length===0&&Object.assign(r,n),String(n.serviceId).toLowerCase()===`s3`&&await at(r),r},mt=(e,t)=>{let n=[];if(e&&n.push(e),t)for(let e of t)n.push(e);return n},ht=(e,t)=>`${e||`anonymous`}${t&&t.length>0?` (a.k.a. ${t.join(`,`)})`:``}`,gt=()=>{let e=[],t=[],n=!1,r=new Set,i=e=>e.sort((e,t)=>_t[t.step]-_t[e.step]||vt[t.priority||`normal`]-vt[e.priority||`normal`]),a=n=>{let i=!1,a=e=>{let t=mt(e.name,e.aliases);if(t.includes(n)){i=!0;for(let e of t)r.delete(e);return!1}return!0};return e=e.filter(a),t=t.filter(a),i},o=n=>{let i=!1,a=e=>{if(e.middleware===n){i=!0;for(let t of mt(e.name,e.aliases))r.delete(t);return!1}return!0};return e=e.filter(a),t=t.filter(a),i},s=n=>(e.forEach(e=>{n.add(e.middleware,{...e})}),t.forEach(e=>{n.addRelativeTo(e.middleware,{...e})}),n.identifyOnResolve?.(u.identifyOnResolve()),n),c=e=>{let t=[];return e.before.forEach(e=>{e.before.length===0&&e.after.length===0?t.push(e):t.push(...c(e))}),t.push(e),e.after.reverse().forEach(e=>{e.before.length===0&&e.after.length===0?t.push(e):t.push(...c(e))}),t},l=(n=!1)=>{let r=[],a=[],o={};return e.forEach(e=>{let t={...e,before:[],after:[]};for(let e of mt(t.name,t.aliases))o[e]=t;r.push(t)}),t.forEach(e=>{let t={...e,before:[],after:[]};for(let e of mt(t.name,t.aliases))o[e]=t;a.push(t)}),a.forEach(e=>{if(e.toMiddleware){let t=o[e.toMiddleware];if(t===void 0){if(n)return;throw Error(`${e.toMiddleware} is not found when adding ${ht(e.name,e.aliases)} middleware ${e.relation} ${e.toMiddleware}`)}e.relation===`after`&&t.after.push(e),e.relation===`before`&&t.before.push(e)}}),i(r).map(c).reduce((e,t)=>(e.push(...t),e),[])},u={add:(t,n={})=>{let{name:i,override:a,aliases:o}=n,s={step:`initialize`,priority:`normal`,middleware:t,...n},c=mt(i,o);if(c.length>0){if(c.some(e=>r.has(e))){if(!a)throw Error(`Duplicate middleware name '${ht(i,o)}'`);for(let t of c){let n=e.findIndex(e=>e.name===t||e.aliases?.some(e=>e===t));if(n===-1)continue;let r=e[n];if(r.step!==s.step||s.priority!==r.priority)throw Error(`"${ht(r.name,r.aliases)}" middleware with ${r.priority} priority in ${r.step} step cannot be overridden by "${ht(i,o)}" middleware with ${s.priority} priority in ${s.step} step.`);e.splice(n,1)}}for(let e of c)r.add(e)}e.push(s)},addRelativeTo:(e,n)=>{let{name:i,override:a,aliases:o}=n,s={middleware:e,...n},c=mt(i,o);if(c.length>0){if(c.some(e=>r.has(e))){if(!a)throw Error(`Duplicate middleware name '${ht(i,o)}'`);for(let e of c){let n=t.findIndex(t=>t.name===e||t.aliases?.some(t=>t===e));if(n===-1)continue;let r=t[n];if(r.toMiddleware!==s.toMiddleware||r.relation!==s.relation)throw Error(`"${ht(r.name,r.aliases)}" middleware ${r.relation} "${r.toMiddleware}" middleware cannot be overridden by "${ht(i,o)}" middleware ${s.relation} "${s.toMiddleware}" middleware.`);t.splice(n,1)}}for(let e of c)r.add(e)}t.push(s)},clone:()=>s(gt()),use:e=>{e.applyToStack(u)},remove:e=>typeof e==`string`?a(e):o(e),removeByTag:n=>{let i=!1,a=e=>{let{tags:t,name:a,aliases:o}=e;if(t&&t.includes(n)){let e=mt(a,o);for(let t of e)r.delete(t);return i=!0,!1}return!0};return e=e.filter(a),t=t.filter(a),i},concat:e=>{let t=s(gt());return t.use(e),t.identifyOnResolve(n||t.identifyOnResolve()||(e.identifyOnResolve?.()??!1)),t},applyToStack:s,identify:()=>l(!0).map(e=>{let t=e.step??e.relation+` `+e.toMiddleware;return ht(e.name,e.aliases)+` - `+t}),identifyOnResolve(e){return typeof e==`boolean`&&(n=e),n},resolve:(e,t)=>{for(let n of l().map(e=>e.middleware).reverse())e=n(e,t);return n&&console.log(u.identify()),e}};return u},_t={initialize:5,serialize:4,build:3,finalizeRequest:2,deserialize:1},vt={high:3,normal:2,low:1},yt=e=>()=>Promise.reject(e),bt=class{config;middlewareStack=gt();initConfig;handlers;constructor(e){this.config=e;let{protocol:t,protocolSettings:n}=e;n&&typeof t==`function`&&(e.protocol=new t(n))}send(e,t,n){let r=typeof t==`function`?void 0:t,i=typeof t==`function`?t:n,a=r===void 0&&this.config.cacheMiddleware===!0,o;if(a){this.handlers||=new WeakMap;let t=this.handlers;t.has(e.constructor)?o=t.get(e.constructor):(o=e.resolveMiddleware(this.middlewareStack,this.config,r),t.set(e.constructor,o))}else delete this.handlers,o=e.resolveMiddleware(this.middlewareStack,this.config,r);if(i)o(e).then(e=>i(null,e.output),e=>i(e)).catch(()=>{});else return o(e).then(e=>e.output)}destroy(){this.config?.requestHandler?.destroy?.(),delete this.handlers}},xt=e=>typeof e==`function`?e():e,St=(e,t,n,r,i)=>({name:t,namespace:e,traits:n,input:r,output:i}),Ct=e=>(t,n)=>async r=>{let{response:i}=await t(r),{operationSchema:a}=Ze(n),[,o,s,c,l,u]=a??[];try{return{response:i,output:await e.protocol.deserializeResponse(St(o,s,c,l,u),{...e,...n},i)}}catch(e){if(Object.defineProperty(e,"$response",{value:i,enumerable:!1,writable:!1,configurable:!1}),!(`$metadata`in e)){let t=`Deserialization error: to see the raw response, inspect the hidden field {error}.$response on this object.`;try{e.message+=` + Deserialization error: to see the raw response, inspect the hidden field {error}.$response on this object.`}catch{!n.logger||n.logger?.constructor?.name===`NoOpLogger`?console.warn(t):n.logger?.warn?.(t)}e.$responseBodyText!==void 0&&e.$response&&(e.$response.body=e.$responseBodyText);try{if(Qe.isInstance(i)){let{headers:t={},statusCode:n}=i,r=Object.entries(t);e.$metadata={httpStatusCode:n,requestId:wt(/^x-[\w-]+-request-?id$/,r),extendedRequestId:wt(/^x-[\w-]+-id-2$/,r),cfId:wt(/^x-[\w-]+-cf-id$/,r)}}}catch{}}throw e}},wt=(e,t)=>(t.find(([t])=>t.match(e))||[void 0,void 0])[1],Tt=e=>(t,n)=>async r=>{let{operationSchema:i}=Ze(n),[,a,o,s,c,l]=i??[],u=n.endpointV2?async()=>rt(n.endpointV2):e.endpoint,d=await e.protocol.serializeRequest(St(a,o,s,c,l),r.input,{...e,...n,endpoint:u});return t({...r,request:d})},Et={name:`deserializerMiddleware`,step:`deserialize`,tags:[`DESERIALIZER`],override:!0},Dt={name:`serializerMiddleware`,step:`serialize`,tags:[`SERIALIZER`],override:!0};function Ot(e){return{applyToStack:t=>{t.add(Tt(e),Dt),t.add(Ct(e),Et),e.protocol.setSerdeContext(e)}}}var kt=[];function At(e){if(typeof e==`object`)return e;if(e|=0,kt[e])return kt[e];let t={},n=0;for(let r of[`httpLabel`,`idempotent`,`idempotencyToken`,`sensitive`,`httpPayload`,`httpResponseCode`,`httpQueryParams`])(e>>n++&1)==1&&(t[r]=1);return kt[e]=t}var jt={it:Symbol.for(`@smithy/nor-struct-it`),ns:Symbol.for(`@smithy/ns`)},Mt=[],Nt={},h=class e{ref;memberName;static symbol=Symbol.for(`@smithy/nor`);symbol=e.symbol;name;schema;_isMemberSchema;traits;memberTraits;normalizedTraits;constructor(t,n){this.ref=t,this.memberName=n;let r=[],i=t,a=t;for(this._isMemberSchema=!1;Ft(i);)r.push(i[1]),i=i[0],a=xt(i),this._isMemberSchema=!0;if(r.length>0){this.memberTraits={};for(let e=r.length-1;e>=0;--e){let t=r[e];Object.assign(this.memberTraits,At(t))}}else this.memberTraits=0;if(a instanceof e){let e=this.memberTraits;Object.assign(this,a),this.memberTraits=Object.assign({},e,a.getMemberTraits(),this.getMemberTraits()),this.normalizedTraits=void 0,this.memberName=n??a.memberName;return}if(this.schema=xt(a),It(this.schema)?(this.name=`${this.schema[1]}#${this.schema[2]}`,this.traits=this.schema[3]):(this.name=this.memberName??String(a),this.traits=0),this._isMemberSchema&&!n)throw Error(`@smithy/core/schema - NormalizedSchema member init ${this.getName(!0)} missing member name.`)}static[Symbol.hasInstance](e){let t=this.prototype.isPrototypeOf(e);return!t&&typeof e==`object`&&e?e.symbol===this.symbol:t}static of(t){let n=typeof t==`function`||typeof t==`object`&&!!t;if(typeof t==`number`){if(Mt[t])return Mt[t]}else if(typeof t==`string`){if(Nt[t])return Nt[t]}else if(n&&t[jt.ns])return t[jt.ns];let r=xt(t);if(r instanceof e)return r;if(Ft(r)){let[n,i]=r;if(n instanceof e)return Object.assign(n.getMergedTraits(),At(i)),n;throw Error(`@smithy/core/schema - may not init unwrapped member schema=${JSON.stringify(t,null,2)}.`)}let i=new e(r);return n?t[jt.ns]=i:typeof r==`string`?Nt[r]=i:typeof r==`number`?Mt[r]=i:i}getSchema(){let e=this.schema;return Array.isArray(e)&&e[0]===0?e[4]:e}getName(e=!1){let{name:t}=this;return!e&&t&&t.includes(`#`)?t.split(`#`)[1]:t||void 0}getMemberName(){return this.memberName}isMemberSchema(){return this._isMemberSchema}isListSchema(){let e=this.getSchema();return typeof e==`number`?e>=64&&e<128:e[0]===1}isMapSchema(){let e=this.getSchema();return typeof e==`number`?e>=128&&e<=255:e[0]===2}isStructSchema(){let e=this.getSchema();if(typeof e!=`object`)return!1;let t=e[0];return t===3||t===-3||t===4}isUnionSchema(){let e=this.getSchema();return typeof e==`object`?e[0]===4:!1}isBlobSchema(){let e=this.getSchema();return e===21||e===42}isTimestampSchema(){let e=this.getSchema();return typeof e==`number`&&e>=4&&e<=7}isUnitSchema(){return this.getSchema()===`unit`}isDocumentSchema(){return this.getSchema()===15}isStringSchema(){return this.getSchema()===0}isBooleanSchema(){return this.getSchema()===2}isNumericSchema(){return this.getSchema()===1}isBigIntegerSchema(){return this.getSchema()===17}isBigDecimalSchema(){return this.getSchema()===19}isStreaming(){let{streaming:e}=this.getMergedTraits();return!!e||this.getSchema()===42}isIdempotencyToken(){return!!this.getMergedTraits().idempotencyToken}getMergedTraits(){return this.normalizedTraits??={...this.getOwnTraits(),...this.getMemberTraits()}}getMemberTraits(){return At(this.memberTraits)}getOwnTraits(){return At(this.traits)}getKeySchema(){let[e,t]=[this.isDocumentSchema(),this.isMapSchema()];if(!e&&!t)throw Error(`@smithy/core/schema - cannot get key for non-map: ${this.getName(!0)}`);let n=this.getSchema();return Pt([e?15:n[4]??0,0],`key`)}getValueSchema(){let e=this.getSchema(),[t,n,r]=[this.isDocumentSchema(),this.isMapSchema(),this.isListSchema()],i=typeof e==`number`?63&e:e&&typeof e==`object`&&(n||r)?e[3+e[0]]:t?15:void 0;if(i!=null)return Pt([i,0],n?`value`:`member`);throw Error(`@smithy/core/schema - ${this.getName(!0)} has no value member.`)}getMemberSchema(e){let t=this.getSchema();if(this.isStructSchema()&&t[4].includes(e)){let n=t[4].indexOf(e),r=t[5][n];return Pt(Ft(r)?r:[r,0],e)}if(this.isDocumentSchema())return Pt([15,0],e);throw Error(`@smithy/core/schema - ${this.getName(!0)} has no member=${e}.`)}getMemberSchemas(){let e={};try{for(let[t,n]of this.structIterator())e[t]=n}catch{}return e}getEventStreamMember(){if(this.isStructSchema()){for(let[e,t]of this.structIterator())if(t.isStreaming()&&t.isStructSchema())return e}return``}*structIterator(){if(this.isUnitSchema())return;if(!this.isStructSchema())throw Error(`@smithy/core/schema - cannot iterate non-struct schema.`);let e=this.getSchema(),t=e[4].length,n=e[jt.it];if(n&&t===n.length){yield*n;return}n=Array(t);for(let r=0;rArray.isArray(e)&&e.length===2,It=e=>Array.isArray(e)&&e.length>=5,Lt=class e{namespace;schemas;exceptions;static registries=new Map;constructor(e,t=new Map,n=new Map){this.namespace=e,this.schemas=t,this.exceptions=n}static for(t){return e.registries.has(t)||e.registries.set(t,new e(t)),e.registries.get(t)}copyFrom(e){let{schemas:t,exceptions:n}=this;for(let[n,r]of e.schemas)t.has(n)||t.set(n,r);for(let[t,r]of e.exceptions)n.has(t)||n.set(t,r)}register(t,n){let r=this.normalizeShapeId(t);for(let t of[this,e.for(r.split(`#`)[0])])t.schemas.set(r,n)}getSchema(e){let t=this.normalizeShapeId(e);if(!this.schemas.has(t)){if(!e.includes(`#`)){let t=`#`+e,n=[];for(let[e,r]of this.schemas.entries())e.endsWith(t)&&n.push(r);if(n.length===1)return n[0]}throw Error(`@smithy/core/schema - schema not found for ${t}`)}return this.schemas.get(t)}registerError(t,n){let r=t,i=r[1];for(let t of[this,e.for(i)])t.schemas.set(i+`#`+r[2],r),t.exceptions.set(r,n)}getErrorCtor(t){let n=t;return this.exceptions.has(n)?this.exceptions.get(n):e.for(n[1]).exceptions.get(n)}getBaseException(){for(let e of this.exceptions.keys())if(Array.isArray(e)){let[,t,n]=e,r=t+`#`+n;if(r.startsWith(`smithy.ts.sdk.synthetic.`)&&r.endsWith(`ServiceException`))return e}}find(e){for(let t of this.schemas.values())if(e(t))return t}clear(){this.schemas.clear(),this.exceptions.clear()}normalizeShapeId(e){return e.includes(`#`)?e:this.namespace+`#`+e}},Rt=`***SensitiveInformation***`;function zt(e,t){if(t==null)return t;let n=h.of(e);if(n.getMergedTraits().sensitive)return Rt;if(n.isListSchema()){if(n.getValueSchema().getMergedTraits().sensitive)return Rt}else if(n.isMapSchema()){if(n.getKeySchema().getMergedTraits().sensitive||n.getValueSchema().getMergedTraits().sensitive)return Rt}else if(n.isStructSchema()&&typeof t==`object`){let e=t,r={};for(let[t,i]of n.structIterator())e[t]!=null&&(r[t]=zt(i,e[t]));return r}return t}var Bt=class{middlewareStack=gt();schema;static classBuilder(){return new Vt}resolveMiddlewareWithContext(e,t,n,{middlewareFn:r,clientName:i,commandName:a,inputFilterSensitiveLog:o,outputFilterSensitiveLog:s,smithyContext:c,additionalContext:l,CommandCtor:u}){for(let i of r.bind(this)(u,e,t,n))this.middlewareStack.use(i);let d=e.concat(this.middlewareStack),{logger:f}=t,p={logger:f,clientName:i,commandName:a,inputFilterSensitiveLog:o,outputFilterSensitiveLog:s,[Xe]:{commandInstance:this,...c},...l},{requestHandler:ee}=t,m=n??{};return c.eventStream&&(m={isEventStream:!0,...m}),d.resolve(e=>ee.handle(e.request,m),p)}},Vt=class{_init=()=>{};_ep={};_middlewareFn=()=>[];_commandName=``;_clientName=``;_additionalContext={};_smithyContext={};_inputFilterSensitiveLog=void 0;_outputFilterSensitiveLog=void 0;_serializer=null;_deserializer=null;_operationSchema;init(e){this._init=e}ep(e){return this._ep=e,this}m(e){return this._middlewareFn=e,this}s(e,t,n={}){return this._smithyContext={service:e,operation:t,...n},this}c(e={}){return this._additionalContext=e,this}n(e,t){return this._clientName=e,this._commandName=t,this}f(e=e=>e,t=e=>e){return this._inputFilterSensitiveLog=e,this._outputFilterSensitiveLog=t,this}ser(e){return this._serializer=e,this}de(e){return this._deserializer=e,this}sc(e){return this._operationSchema=e,this._smithyContext.operationSchema=e,this}build(){let e=this,t;return t=class extends Bt{input;static getEndpointParameterInstructions(){return e._ep}constructor(...[t]){super(),this.input=t??{},e._init(this),this.schema=e._operationSchema}resolveMiddleware(n,r,i){let a=e._operationSchema,o=a?.[4]??a?.input,s=a?.[5]??a?.output;return this.resolveMiddlewareWithContext(n,r,i,{CommandCtor:t,middlewareFn:e._middlewareFn,clientName:e._clientName,commandName:e._commandName,inputFilterSensitiveLog:e._inputFilterSensitiveLog??(a?zt.bind(null,o):e=>e),outputFilterSensitiveLog:e._outputFilterSensitiveLog??(a?zt.bind(null,s):e=>e),smithyContext:e._smithyContext,additionalContext:e._additionalContext})}serialize=e._serializer;deserialize=e._deserializer}}},Ht=class e extends Error{$fault;$response;$retryable;$metadata;constructor(e){super(e.message),Object.setPrototypeOf(this,Object.getPrototypeOf(this).constructor.prototype),this.name=e.name,this.$fault=e.$fault,this.$metadata=e.$metadata}static isInstance(t){if(!t)return!1;let n=t;return e.prototype.isPrototypeOf(n)||!!n.$fault&&!!n.$metadata&&(n.$fault===`client`||n.$fault===`server`)}static[Symbol.hasInstance](t){if(!t)return!1;let n=t;return this===e?e.isInstance(t):e.isInstance(t)?n.name&&this.name?this.prototype.isPrototypeOf(t)||n.name===this.name:this.prototype.isPrototypeOf(t):!1}},Ut=(e,t={})=>(Object.entries(t).filter(([,e])=>e!==void 0).forEach(([t,n])=>{(e[t]==null||e[t]===``)&&(e[t]=n)}),e.message=e.message||e.Message||`UnknownError`,delete e.Message,e),Wt=e=>{switch(e){case`standard`:return{retryMode:`standard`,connectionTimeout:3100};case`in-region`:return{retryMode:`standard`,connectionTimeout:1100};case`cross-region`:return{retryMode:`standard`,connectionTimeout:3100};case`mobile`:return{retryMode:`standard`,connectionTimeout:3e4};default:return{}}},Gt=Object.values(Ye),Kt=e=>{let t=[];for(let n in Ye){let r=Ye[n];e[r]!==void 0&&t.push({algorithmId:()=>r,checksumConstructor:()=>e[r]})}for(let[n,r]of Object.entries(e.checksumAlgorithms??{}))t.push({algorithmId:()=>n,checksumConstructor:()=>r});return{addChecksumAlgorithm(n){e.checksumAlgorithms=e.checksumAlgorithms??{};let r=n.algorithmId(),i=n.checksumConstructor();Gt.includes(r)?e.checksumAlgorithms[r.toUpperCase()]=i:e.checksumAlgorithms[r]=i,t.push(n)},checksumAlgorithms(){return t}}},qt=e=>{let t={};return e.checksumAlgorithms().forEach(e=>{let n=e.algorithmId();Gt.includes(n)&&(t[n]=e.checksumConstructor())}),t},Jt=e=>({setRetryStrategy(t){e.retryStrategy=t},retryStrategy(){return e.retryStrategy}}),Yt=e=>{let t={};return t.retryStrategy=e.retryStrategy(),t},Xt=e=>Object.assign(Kt(e),Jt(e)),Zt=e=>Object.assign(qt(e),Yt(e)),Qt=e=>{let t=`#text`;for(let n in e)e.hasOwnProperty(n)&&e[n][t]!==void 0?e[n]=e[n][t]:typeof e[n]==`object`&&e[n]!==null&&(e[n]=Qt(e[n]));return e},$t=class{trace(){}debug(){}info(){}warn(){}error(){}};function en(e,t,n){e.__smithy_context?e.__smithy_context.features||(e.__smithy_context.features={}):e.__smithy_context={features:{}},e.__smithy_context.features[t]=n}function tn(e){let t=ft(e);return({config:e,instructions:n})=>(r,i)=>async a=>{e.isCustomEndpoint&&en(i,`ENDPOINT_OVERRIDE`,`N`);let o=await t(a.input,{getEndpointParameterInstructions(){return n}},{...e},i);i.endpointV2=o,i.authSchemes=o.properties?.authSchemes;let s=i.authSchemes?.[0];if(s){i.signing_region=s.signingRegion,i.signing_service=s.signingName;let e=Ze(i)?.selectedHttpAuthScheme?.httpAuthOption;e&&(e.signingProperties=Object.assign(e.signingProperties||{},{signing_region:s.signingRegion,signingRegion:s.signingRegion,signing_service:s.signingName,signingName:s.signingName,signingRegionSet:s.signingRegionSet},s.properties))}return r({...a})}}var nn={step:`serialize`,tags:[`ENDPOINT_PARAMETERS`,`ENDPOINT_V2`,`ENDPOINT`],name:`endpointV2Middleware`,override:!0,relation:`before`,toMiddleware:{name:`serializerMiddleware`,step:`serialize`,tags:[`SERIALIZER`],override:!0}.name};function rn(e){let t=tn(e);return(e,n)=>({applyToStack:r=>{r.addRelativeTo(t({config:e,instructions:n}),nn)}})}function an(e){return t=>{let n=t.tls??!0,{endpoint:r,useDualstackEndpoint:i,useFipsEndpoint:a}=t,o=Object.assign(t,{endpoint:r==null?void 0:async()=>rt(await re(r)()),tls:n,isCustomEndpoint:!!r,useDualstackEndpoint:re(i??!1),useFipsEndpoint:re(a??!1)}),s;return o.serviceConfiguredEndpoint=async()=>(t.serviceId&&!s&&(s=e(t.serviceId)),s),o}}var on=class e{nodes;root;conditions;results;constructor(e,t,n,r){this.nodes=e,this.root=t,this.conditions=n,this.results=r}static from(t,n,r,i){return new e(t,n,r,i)}},sn=class{capacity;data=new Map;parameters=[];constructor({size:e,params:t}){this.capacity=e??50,t&&(this.parameters=t)}get(e,t){let n=this.hash(e);if(n===!1)return t();if(!this.data.has(n)){if(this.data.size>this.capacity+10){let e=this.data.keys(),t=0;for(;;){let{value:n,done:r}=e.next();if(this.data.delete(n),r||++t>10)break}}this.data.set(n,t())}return this.data.get(n)}size(){return this.data.size}hash(e){let t=``,{parameters:n}=this;if(n.length===0)return!1;for(let r of n){let n=String(e[r]??``);if(n.includes(`|;`))return!1;t+=n+`|;`}return t}},cn=class extends Error{constructor(e){super(e),this.name=`EndpointError`}},ln=`endpoints`;function un(e){return typeof e!=`object`||!e?e:`ref`in e?`$${un(e.ref)}`:`fn`in e?`${e.fn}(${(e.argv||[]).map(un).join(`, `)})`:JSON.stringify(e,null,2)}var dn={},fn=(e,t)=>e===t;function pn(...e){for(let t of e)if(t!=null)return t}var mn=e=>{let t=e.split(`.`),n=[];for(let r of t){let t=r.indexOf(`[`);if(t!==-1){if(r.indexOf(`]`)!==r.length-1)throw new cn(`Path: '${e}' does not end with ']'`);let i=r.slice(t+1,-1);if(Number.isNaN(parseInt(i)))throw new cn(`Invalid array index: '${i}' in path: '${e}'`);t!==0&&n.push(r.slice(0,t)),n.push(i)}else n.push(r)}return n},hn=(e,t)=>mn(t).reduce((n,r)=>{if(typeof n!=`object`)throw new cn(`Index '${r}' in '${t}' not found in '${JSON.stringify(e)}'`);if(Array.isArray(n)){let e=parseInt(r);return n[e<0?n.length+e:e]}return n[r]},e),gn=e=>e!=null;function _n(e,t,n){return e?t:n}var ree=e=>!e,iee=RegExp(`^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$`),vn=e=>iee.test(e)||e.startsWith(`[`)&&e.endsWith(`]`),yn={[Je.HTTP]:80,[Je.HTTPS]:443},aee=e=>{let t=(()=>{try{if(e instanceof URL)return e;if(typeof e==`object`&&`hostname`in e){let{hostname:t,port:n,protocol:r=``,path:i=``,query:a={}}=e,o=new URL(`${r}//${t}${n?`:${n}`:``}${i}`);return o.search=Object.entries(a).map(([e,t])=>`${e}=${t}`).join(`&`),o}return new URL(e)}catch{return null}})();if(!t)return console.error(`Unable to parse ${JSON.stringify(e)} as a whatwg URL.`),null;let n=t.href,{host:r,hostname:i,pathname:a,protocol:o,search:s}=t;if(s)return null;let c=o.slice(0,-1);if(!Object.values(Je).includes(c))return null;let l=vn(i);return{scheme:c,authority:`${r}${n.includes(`${r}:${yn[c]}`)||typeof e==`string`&&e.includes(`${r}:${yn[c]}`)?`:${yn[c]}`:``}`,path:a,normalizedPath:a.endsWith(`/`)?a:`${a}/`,isIp:l}};function oee(e,t,n){if(n===1)return[e];if(e===``)return[``];let r=e.split(t);return n===0?r:r.slice(0,n-1).concat(r.slice(1).join(t))}var see={booleanEquals:fn,coalesce:pn,getAttr:hn,isSet:gn,isValidHostLabel:et,ite:_n,not:ree,parseURL:aee,split:oee,stringEquals:(e,t)=>e===t,substring:(e,t,n,r)=>e==null||t>=n||e.lengthencodeURIComponent(e).replace(/[!*'()]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`)},bn=(e,t)=>{let n=[],{referenceRecord:r,endpointParams:i}=t,a=0;for(;at.referenceRecord[e]??t.endpointParams[e],xn=(e,t,n)=>{if(typeof e==`string`)return bn(e,n);if(e.fn)return Cn.callFunction(e,n);if(e.ref)return cee(e,n);throw new cn(`'${t}': ${String(e)} is not a string, function or reference.`)},Sn=({fn:e,argv:t},n)=>{let r=Array(t.length);for(let e=0;e{let{assign:n}=e;if(n&&n in t.referenceRecord)throw new cn(`'${n}' is already defined in Reference Record.`);let r=Sn(e,t);t.logger?.debug?.(`${ln} evaluateCondition: ${un(e)} = ${un(r)}`);let i=r===``?!0:!!r;return n==null?{result:i}:{result:i,toAssign:{name:n,value:r}}},Tn=(e,t)=>Object.entries(e??{}).reduce((e,[n,r])=>(e[n]=r.map(e=>{let r=xn(e,`Header value entry`,t);if(typeof r!=`string`)throw new cn(`Header '${n}' value '${r}' is not a string`);return r}),e),{}),En=(e,t)=>Object.entries(e).reduce((e,[n,r])=>(e[n]=On.getEndpointProperty(r,t),e),{}),Dn=(e,t)=>{if(Array.isArray(e))return e.map(e=>Dn(e,t));switch(typeof e){case`string`:return bn(e,t);case`object`:if(e===null)throw new cn(`Unexpected endpoint property: ${e}`);return On.getEndpointProperties(e,t);case`boolean`:return e;default:throw new cn(`Unexpected endpoint property type: ${typeof e}`)}},On={getEndpointProperty:Dn,getEndpointProperties:En},kn=(e,t)=>{let n=xn(e,`Endpoint URL`,t);if(typeof n==`string`)try{return new URL(n)}catch(e){throw console.error(`Failed to construct URL with ${n}`,e),e}throw new cn(`Endpoint URL must be a string, got ${typeof n}`)},An=1e8,jn=(e,t)=>{let{nodes:n,root:r,results:i,conditions:a}=e,o=r,s={},c={referenceRecord:s,endpointParams:t.endpointParams,logger:t.logger};for(;o!==1&&o!==-1&&o=0===f.result?r:i}if(o>=An){let e=i[o-An];if(e[0]===-1){let[,t]=e;throw new cn(xn(t,`Error`,c))}let[t,n,r]=e;return{url:kn(t,c),properties:En(n,c),headers:Tn(r??{},c)}}throw new cn(`No matching endpoint.`)},Mn=ft(it),Nn=an(it);tn(it);var Pn=rn(it),Fn=async(e=new Uint8Array,t)=>{if(e instanceof Uint8Array)return Te.mutate(e);if(!e)return Te.mutate(new Uint8Array);let n=t.streamCollector(e);return Te.mutate(await n)};function In(e){return encodeURIComponent(e).replace(/[!'()*]/g,function(e){return`%`+e.charCodeAt(0).toString(16).toUpperCase()})}var Ln=class{serdeContext;setSerdeContext(e){this.serdeContext=e}},Rn=`modulepreload`,zn=function(e){return`/`+e},Bn={},Vn=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=zn(t,n),t in Bn)return;Bn[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:Rn,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},Hn=class extends Ln{options;compositeErrorRegistry;constructor(e){super(),this.options=e,this.compositeErrorRegistry=Lt.for(e.defaultNamespace);for(let t of e.errorTypeRegistries??[])this.compositeErrorRegistry.copyFrom(t)}getRequestType(){return m}getResponseType(){return Qe}setSerdeContext(e){this.serdeContext=e,this.serializer.setSerdeContext(e),this.deserializer.setSerdeContext(e),this.getPayloadCodec()&&this.getPayloadCodec().setSerdeContext(e)}updateServiceEndpoint(e,t){if(`url`in t){e.protocol=t.url.protocol,e.hostname=t.url.hostname,e.port=t.url.port?Number(t.url.port):void 0,e.path=t.url.pathname,e.fragment=t.url.hash||void 0,e.username=t.url.username||void 0,e.password=t.url.password||void 0,e.query||={};for(let[n,r]of t.url.searchParams.entries())e.query[n]=r;if(t.headers)for(let n in t.headers)e.headers[n]=t.headers[n].join(`, `);return e}else{if(e.protocol=t.protocol,e.hostname=t.hostname,e.port=t.port?Number(t.port):void 0,e.path=t.path,e.query={...t.query},t.headers)for(let n in t.headers)e.headers[n]=t.headers[n];return e}}setHostPrefix(e,t,n){if(this.serdeContext?.disableHostPrefix)return;let r=h.of(t.input),i=At(t.traits??{});if(i.endpoint){let t=i.endpoint?.[0];if(typeof t==`string`){for(let[e,i]of r.structIterator()){if(!i.getMergedTraits().hostLabel)continue;let r=n[e];if(typeof r!=`string`)throw Error(`@smithy/core/schema - ${e} in input must be a string as hostLabel.`);t=t.replace(`{${e}}`,r)}e.hostname=t+e.hostname}}}deserializeMetadata(e){return{httpStatusCode:e.statusCode,requestId:e.headers[`x-amzn-requestid`]??e.headers[`x-amzn-request-id`]??e.headers[`x-amz-request-id`],extendedRequestId:e.headers[`x-amz-id-2`],cfId:e.headers[`x-amz-cf-id`]}}async serializeEventStream({eventStream:e,requestSchema:t,initialRequest:n}){return(await this.loadEventStreamCapability()).serializeEventStream({eventStream:e,requestSchema:t,initialRequest:n})}async deserializeEventStream({response:e,responseSchema:t,initialResponseContainer:n}){return(await this.loadEventStreamCapability()).deserializeEventStream({response:e,responseSchema:t,initialResponseContainer:n})}async loadEventStreamCapability(){let{EventStreamSerde:e}=await Vn(async()=>{let{EventStreamSerde:e}=await import(`./index2.browser.js`).then(e=>e.t);return{EventStreamSerde:e}},__vite__mapDeps([0,1,2]));return new e({marshaller:this.getEventStreamMarshaller(),serializer:this.serializer,deserializer:this.deserializer,serdeContext:this.serdeContext,defaultContentType:this.getDefaultContentType()})}getDefaultContentType(){throw Error(`@smithy/core/protocols - ${this.constructor.name} getDefaultContentType() implementation missing.`)}async deserializeHttpMessage(e,t,n,r,i){return[]}getEventStreamMarshaller(){let e=this.serdeContext;if(!e.eventStreamMarshaller)throw Error(`@smithy/core - HttpProtocol: eventStreamMarshaller missing in serdeContext.`);return e.eventStreamMarshaller}},Un=class extends Hn{async serializeRequest(e,t,n){let r=t&&typeof t==`object`?t:{},i=this.serializer,a={},o={},s=await n.endpoint(),c=h.of(e?.input),l=[],u=[],d=!1,f,p=new m({protocol:``,hostname:``,port:void 0,path:``,fragment:void 0,query:a,headers:o,body:void 0});if(s){this.updateServiceEndpoint(p,s),this.setHostPrefix(p,e,r);let t=At(e.traits);if(t.http){p.method=t.http[0];let[e,n]=t.http[1].split(`?`);p.path==`/`?p.path=e:p.path+=e;let r=new URLSearchParams(n??``);for(let[e,t]of r)a[e]=t}}for(let[e,t]of c.structIterator()){let n=t.getMergedTraits()??{},s=r[e];if(s==null&&!t.isIdempotencyToken()){if(n.httpLabel&&(p.path.includes(`{${e}+}`)||p.path.includes(`{${e}}`)))throw Error(`No value provided for input HTTP label: ${e}.`);continue}if(n.httpPayload)t.isStreaming()?t.isStructSchema()?r[e]&&(f=await this.serializeEventStream({eventStream:r[e],requestSchema:c})):f=s:(i.write(t,s),f=i.flush());else if(n.httpLabel){i.write(t,s);let n=i.flush();p.path.includes(`{${e}+}`)?p.path=p.path.replace(`{${e}+}`,n.split(`/`).map(In).join(`/`)):p.path.includes(`{${e}}`)&&(p.path=p.path.replace(`{${e}}`,In(n)))}else if(n.httpHeader)i.write(t,s),o[n.httpHeader.toLowerCase()]=String(i.flush());else if(typeof n.httpPrefixHeaders==`string`)for(let e in s){let r=s[e],a=n.httpPrefixHeaders+e;i.write([t.getValueSchema(),{httpHeader:a}],r),o[a.toLowerCase()]=i.flush()}else n.httpQuery||n.httpQueryParams?this.serializeQuery(t,s,a):(d=!0,l.push(e),u.push(t))}if(d&&r){let[e,t]=(c.getName(!0)??`#Unknown`).split(`#`),n=c.getSchema()[6],a=[3,e,t,c.getMergedTraits(),l,u,void 0];n?a[6]=n:a.pop(),i.write(a,r),f=i.flush()}return p.headers=o,p.query=a,p.body=f,p}serializeQuery(e,t,n){let r=this.serializer,i=e.getMergedTraits();if(i.httpQueryParams){for(let r in t)if(!(r in n)){let a=t[r],o=e.getValueSchema();Object.assign(o.getMergedTraits(),{...i,httpQuery:r,httpQueryParams:void 0}),this.serializeQuery(o,a,n)}return}if(e.isListSchema()){let a=!!e.getMergedTraits().sparse,o=[];for(let n of t){r.write([e.getValueSchema(),i],n);let t=r.flush();(a||t!==void 0)&&o.push(t)}n[i.httpQuery]=o}else r.write([e,i],t),n[i.httpQuery]=r.flush()}async deserializeResponse(e,t,n){let r=this.deserializer,i=h.of(e.output),a={};if(n.statusCode>=300){let i=await Fn(n.body,t);throw i.byteLength>0&&Object.assign(a,await r.read(15,i)),await this.handleError(e,t,n,a,this.deserializeMetadata(n)),Error(`@smithy/core/protocols - HTTP Protocol error handler failed to throw.`)}for(let e in n.headers){let t=n.headers[e];delete n.headers[e],n.headers[e.toLowerCase()]=t}let o=await this.deserializeHttpMessage(i,t,n,a);if(o.length){let e=await Fn(n.body,t);if(e.byteLength>0){let t=await r.read(i,e);for(let e of o)t[e]!=null&&(a[e]=t[e])}}else o.discardResponseBody&&await Fn(n.body,t);return a.$metadata=this.deserializeMetadata(n),a}async deserializeHttpMessage(e,t,n,r,i){let a;a=r instanceof Set?i:r;let o=!0,s=this.deserializer,c=h.of(e),l=[];for(let[e,r]of c.structIterator()){let i=r.getMemberTraits();if(i.httpPayload){if(o=!1,r.isStreaming())r.isStructSchema()?a[e]=await this.deserializeEventStream({response:n,responseSchema:c}):a[e]=be(n.body);else if(n.body){let i=await Fn(n.body,t);i.byteLength>0&&(a[e]=await s.read(r,i))}}else if(i.httpHeader){let t=String(i.httpHeader).toLowerCase(),o=n.headers[t];if(o!=null)if(r.isListSchema()){let n=r.getValueSchema();n.getMergedTraits().httpHeader=t;let i;i=n.isTimestampSchema()&&n.getSchema()===4?ye(o,`,`,2):xe(o);let c=[];for(let e of i)c.push(await s.read(n,e.trim()));a[e]=c}else a[e]=await s.read(r,o)}else if(i.httpPrefixHeaders!==void 0){a[e]={};for(let t in n.headers)if(t.startsWith(i.httpPrefixHeaders)){let o=n.headers[t],c=r.getValueSchema();c.getMergedTraits().httpHeader=t,a[e][t.slice(i.httpPrefixHeaders.length)]=await s.read(c,o)}}else i.httpResponseCode?a[e]=n.statusCode:l.push(e)}return l.discardResponseBody=o,l}},Wn=class extends Hn{async serializeRequest(e,t,n){let r=this.serializer,i={},a={},o=await n.endpoint(),s=h.of(e?.input),c=s.getSchema(),l,u=t&&typeof t==`object`?t:{},d=new m({protocol:``,hostname:``,port:void 0,path:`/`,fragment:void 0,query:i,headers:a,body:void 0});if(o&&(this.updateServiceEndpoint(d,o),this.setHostPrefix(d,e,u)),u){let e=s.getEventStreamMember();if(e){if(u[e]){let t={};for(let[n,i]of s.structIterator())n!==e&&u[n]&&(r.write(i,u[n]),t[n]=r.flush());l=await this.serializeEventStream({eventStream:u[e],requestSchema:s,initialRequest:t})}}else r.write(c,u),l=r.flush()}return d.headers=Object.assign(d.headers,a),d.query=i,d.body=l,d.method=`POST`,d}async deserializeResponse(e,t,n){let r=this.deserializer,i=h.of(e.output),a={};if(n.statusCode>=300){let i=await Fn(n.body,t);throw i.byteLength>0&&Object.assign(a,await r.read(15,i)),await this.handleError(e,t,n,a,this.deserializeMetadata(n)),Error(`@smithy/core/protocols - RPC Protocol error handler failed to throw.`)}for(let e in n.headers){let t=n.headers[e];delete n.headers[e],n.headers[e.toLowerCase()]=t}let o=i.getEventStreamMember();if(o)a[o]=await this.deserializeEventStream({response:n,responseSchema:i,initialResponseContainer:a});else{let e=await Fn(n.body,t);e.byteLength>0&&Object.assign(a,await r.read(i,e))}return a.$metadata=this.deserializeMetadata(n),a}};function Gn(e,t){if(t.timestampFormat.useTrait&&e.isTimestampSchema()&&(e.getSchema()===5||e.getSchema()===6||e.getSchema()===7))return e.getSchema();let{httpLabel:n,httpPrefixHeaders:r,httpHeader:i,httpQuery:a}=e.getMergedTraits();return(t.httpBindings?typeof r==`string`||i?6:a||n?5:void 0:void 0)??t.timestampFormat.default}var Kn=class extends Ln{settings;constructor(e){super(),this.settings=e}read(e,t){let n=h.of(e);if(n.isListSchema())return xe(t).map(e=>this.read(n.getValueSchema(),e));if(n.isBlobSchema())return(this.serdeContext?.base64Decoder??fe)(t);if(n.isTimestampSchema())switch(Gn(n,this.settings)){case 5:return he(t);case 6:return De(t);case 7:return ve(t);default:return console.warn(`Missing timestamp format, parsing value with Date constructor:`,t),new Date(t)}if(n.isStringSchema()){let e=n.getMergedTraits().mediaType,r=t;if(e)return n.getMergedTraits().httpHeader&&(r=this.base64ToUtf8(r)),(e===`application/json`||e.endsWith(`+json`))&&(r=_e.from(r)),r}return n.isNumericSchema()?Number(t):n.isBigIntegerSchema()?BigInt(t):n.isBigDecimalSchema()?new Ce(t,`bigDecimal`):n.isBooleanSchema()?String(t).toLowerCase()===`true`:t}base64ToUtf8(e){return(this.serdeContext?.utf8Encoder??me)((this.serdeContext?.base64Decoder??fe)(e))}},qn=class extends Ln{codecDeserializer;stringDeserializer;constructor(e,t){super(),this.codecDeserializer=e,this.stringDeserializer=new Kn(t)}setSerdeContext(e){this.stringDeserializer.setSerdeContext(e),this.codecDeserializer.setSerdeContext(e),this.serdeContext=e}read(e,t){let n=h.of(e),r=n.getMergedTraits(),i=this.serdeContext?.utf8Encoder??me;if(r.httpHeader||r.httpResponseCode)return this.stringDeserializer.read(n,i(t));if(r.httpPayload){if(n.isBlobSchema()){let e=this.serdeContext?.utf8Decoder??ce;return typeof t==`string`?e(t):t}else if(n.isStringSchema())return`byteLength`in t?i(t):t}return this.codecDeserializer.read(n,t)}},Jn=class extends Ln{settings;stringBuffer=``;constructor(e){super(),this.settings=e}write(e,t){let n=h.of(e);switch(typeof t){case`object`:if(t===null){this.stringBuffer=`null`;return}if(n.isTimestampSchema()){if(!(t instanceof Date))throw Error(`@smithy/core/protocols - received non-Date value ${t} when schema expected Date in ${n.getName(!0)}`);switch(Gn(n,this.settings)){case 5:this.stringBuffer=t.toISOString().replace(`.000Z`,`Z`);break;case 6:this.stringBuffer=ke(t);break;case 7:this.stringBuffer=String(t.getTime()/1e3);break;default:console.warn(`Missing timestamp format, using epoch seconds`,t),this.stringBuffer=String(t.getTime()/1e3)}return}if(n.isBlobSchema()&&`byteLength`in t){this.stringBuffer=(this.serdeContext?.base64Encoder??le)(t);return}if(n.isListSchema()&&Array.isArray(t)){let e=``;for(let r of t){this.write([n.getValueSchema(),n.getMergedTraits()],r);let t=this.flush(),i=n.getValueSchema().isTimestampSchema()?t:Ae(t);e!==``&&(e+=`, `),e+=i}this.stringBuffer=e;return}this.stringBuffer=JSON.stringify(t,null,2);break;case`string`:let e=n.getMergedTraits().mediaType,r=t;if(e&&((e===`application/json`||e.endsWith(`+json`))&&(r=_e.from(r)),n.getMergedTraits().httpHeader)){this.stringBuffer=(this.serdeContext?.base64Encoder??le)(r.toString());return}this.stringBuffer=t;break;default:n.isIdempotencyToken()?this.stringBuffer=Se():this.stringBuffer=String(t)}}flush(){let e=this.stringBuffer;return this.stringBuffer=``,e}},Yn=class{codecSerializer;stringSerializer;buffer;constructor(e,t,n=new Jn(t)){this.codecSerializer=e,this.stringSerializer=n}setSerdeContext(e){this.codecSerializer.setSerdeContext(e),this.stringSerializer.setSerdeContext(e)}write(e,t){let n=h.of(e),r=n.getMergedTraits();if(r.httpHeader||r.httpLabel||r.httpQuery){this.stringSerializer.write(n,t),this.buffer=this.stringSerializer.flush();return}return this.codecSerializer.write(n,t)}flush(){if(this.buffer!==void 0){let e=this.buffer;return this.buffer=void 0,e}return this.codecSerializer.flush()}},Xn=e=>({setHttpHandler(t){e.httpHandler=t},httpHandler(){return e.httpHandler},updateHttpClientConfig(t,n){e.httpHandler?.updateHttpClientConfig(t,n)},httpHandlerConfigs(){return e.httpHandler.httpHandlerConfigs()}}),Zn=e=>({httpHandler:e.httpHandler()}),Qn=`content-length`;function $n(e){return t=>async n=>{let r=n.request;if(m.isInstance(r)){let{body:t,headers:n}=r;if(t&&Object.keys(n).map(e=>e.toLowerCase()).indexOf(Qn)===-1)try{let n=e(t);r.headers={...r.headers,[Qn]:String(n)}}catch{}}return t({...n,request:r})}}var er={step:`build`,tags:[`SET_CONTENT_LENGTH`,`CONTENT_LENGTH`],name:`contentLengthMiddleware`,override:!0},tr=e=>({applyToStack:t=>{t.add($n(e.bodyLengthChecker),er)}});function nr(e){let t=[];for(let n of Object.keys(e).sort()){let r=e[n];if(n=te(n),Array.isArray(r))for(let e=0,i=r.length;ee?.body instanceof ReadableStream,or=[`BandwidthLimitExceeded`,`EC2ThrottledException`,`LimitExceededException`,`PriorRequestNotComplete`,`ProvisionedThroughputExceededException`,`RequestLimitExceeded`,`RequestThrottled`,`RequestThrottledException`,`SlowDown`,`ThrottledException`,`Throttling`,`ThrottlingException`,`TooManyRequestsException`,`TransactionInProgressException`],sr=[`TimeoutError`,`RequestTimeout`,`RequestTimeoutException`],cr=[500,502,503,504],lr=[`ECONNRESET`,`ECONNREFUSED`,`EPIPE`,`ETIMEDOUT`],ur=[`EHOSTUNREACH`,`ENETUNREACH`,`ENOTFOUND`,`EAI_AGAIN`],dr=e=>e?.$retryable!==void 0,fr=e=>e.$metadata?.clockSkewCorrected,pr=e=>{let t=new Set([`Failed to fetch`,`NetworkError when attempting to fetch resource`,`The Internet connection appears to be offline`,`Load failed`,`Network request failed`]);return e&&e instanceof TypeError?t.has(e.message):!1},mr=e=>e.$metadata?.httpStatusCode===429||or.includes(e.name)||e.$retryable?.throttling==1,hr=(e,t=0)=>dr(e)||fr(e)||e.name===`InvalidSignatureException`&&e.message?.includes(`Signature expired`)||sr.includes(e.name)||lr.includes(e?.code||``)||ur.includes(e?.code||``)||cr.includes(e.$metadata?.httpStatusCode||0)||pr(e)||_r(e)||e.cause!==void 0&&t<=10&&hr(e.cause,t+1),gr=e=>{if(e.$metadata?.httpStatusCode!==void 0){let t=e.$metadata.httpStatusCode;return 500<=t&&t<=599&&!hr(e)}return!1};function _r(e){return e.code===`ERR_HTTP2_STREAM_ERROR`&&e.message.includes(`NGHTTP2_REFUSED_STREAM`)}var vr=20*1e3,yr=`amz-sdk-invocation-id`,br=`amz-sdk-request`;function xr(e,t){if(Qe.isInstance(e))for(let n of Object.keys(e.headers)){let r=n.toLowerCase();if(r===`retry-after`){let r=e.headers[n],i=NaN;if(r.endsWith(`GMT`))try{i=(Oe(r).getTime()-Date.now())/1e3}catch(e){t?.trace?.(`Failed to parse retry-after header`),t?.trace?.(e)}else r.match(/ GMT, ((\d+)|(\d+\.\d+))$/)?i=Number(r.match(/ GMT, ([\d.]+)$/)?.[1]):r.match(/^((\d+)|(\d+\.\d+))$/)?i=Number(r):Date.parse(r)>=Date.now()&&(i=(Date.parse(r)-Date.now())/1e3);return isNaN(i)?void 0:new Date(Date.now()+i*1e3)}else if(r===`x-amz-retry-after`){let r=e.headers[n],i=Number(r);if(isNaN(i)){t?.trace?.(`Failed to parse x-amz-retry-after=${r}`);return}return new Date(Date.now()+i)}}}var Sr=e=>e instanceof Error?e:e instanceof Object?Object.assign(Error(),e):Error(typeof e==`string`?e:`AWS SDK error wrapper for ${e}`);function Cr(e){return t=>(n,r)=>async i=>{let a=await t.retryStrategy(),o=await t.maxAttempts();if(Tr(a)){a=a;let s=await a.acquireInitialRetryToken((r.partition_id??``)+(r.__retryLongPoll?`:longpoll`:``)),c=Error(),l=0,u=0,{request:d}=i,f=m.isInstance(d);for(f&&(d.headers[yr]=we());;)try{f&&(d.headers[br]=`attempt=${l+1}; max=${o}`);let{response:e,output:t}=await n(i);return a.recordSuccess(s),t.$metadata.attempts=l+1,t.$metadata.totalRetryDelay=u,{response:e,output:t}}catch(n){let i=Er(n,t.logger);if(c=Sr(n),f&&e(d))throw(r.logger instanceof $t?console:r.logger)?.warn(`An error was encountered in a non-retryable streaming request.`),c;try{s=await a.refreshRetryTokenForRetry(s,i)}catch{throw c.$metadata||={},c.$metadata.attempts=l+1,c.$metadata.totalRetryDelay=u,c}l=s.getRetryCount();let o=s.getRetryDelay();u+=(s?.$retryLog?.acquisitionDelay??0)+o,o>0&&await wr(o)}}else return a=a,a?.mode&&(r.userAgent=[...r.userAgent||[],[`cfg/retry-mode`,a.mode]]),a.retry(n,i)}}var wr=e=>new Promise(t=>setTimeout(t,e)),Tr=e=>e.acquireInitialRetryToken!==void 0&&e.refreshRetryTokenForRetry!==void 0&&e.recordSuccess!==void 0,Er=(e,t)=>{let n={error:e,errorType:Dr(e)},r=xr(e.$response,t);return r&&(n.retryAfterHint=r),n},Dr=e=>mr(e)?`THROTTLING`:hr(e)?`TRANSIENT`:gr(e)?`SERVER_ERROR`:`CLIENT_ERROR`,Or={name:`retryMiddleware`,tags:[`RETRY`],step:`finalizeRequest`,priority:`high`,override:!0};function kr(e){let t=Cr(e);return e=>({applyToStack:n=>{n.add(t(e),Or)}})}var lee=class e{static setTimeoutFn=setTimeout;beta;minCapacity;minFillRate;scaleConstant;smooth;enabled=!1;availableTokens=0;lastMaxRate=0;measuredTxRate=0;requestCount=0;fillRate;lastThrottleTime;lastTimestamp=0;lastTxRateBucket;maxCapacity;timeWindow=0;constructor(e){this.beta=e?.beta??.7,this.minCapacity=e?.minCapacity??1,this.minFillRate=e?.minFillRate??.5,this.scaleConstant=e?.scaleConstant??.4,this.smooth=e?.smooth??.8,this.lastThrottleTime=this.getCurrentTimeInSeconds(),this.lastTxRateBucket=Math.floor(this.getCurrentTimeInSeconds()),this.fillRate=this.minFillRate,this.maxCapacity=this.minCapacity}async getSendToken(){return this.acquireTokenBucket(1)}updateClientSendingRate(e){let t;this.updateMeasuredRate();let n=e;if(n?.errorType===`THROTTLING`||mr(n?.error??e)){let e=this.enabled?Math.min(this.measuredTxRate,this.fillRate):this.measuredTxRate;this.lastMaxRate=e,this.calculateTimeWindow(),this.lastThrottleTime=this.getCurrentTimeInSeconds(),t=this.cubicThrottle(e),this.enableTokenBucket()}else this.calculateTimeWindow(),t=this.cubicSuccess(this.getCurrentTimeInSeconds());let r=Math.min(t,2*this.measuredTxRate);this.updateTokenBucketRate(r)}getCurrentTimeInSeconds(){return Date.now()/1e3}async acquireTokenBucket(t){if(this.enabled){for(this.refillTokenBucket();t>this.availableTokens;){let n=(t-this.availableTokens)/this.fillRate*1e3;await new Promise(t=>e.setTimeoutFn(t,n)),this.refillTokenBucket()}this.availableTokens-=t}}refillTokenBucket(){let e=this.getCurrentTimeInSeconds();if(!this.lastTimestamp){this.lastTimestamp=e;return}let t=(e-this.lastTimestamp)*this.fillRate;this.availableTokens=Math.min(this.maxCapacity,this.availableTokens+t),this.lastTimestamp=e}calculateTimeWindow(){this.timeWindow=this.getPrecise((this.lastMaxRate*(1-this.beta)/this.scaleConstant)**(1/3))}cubicThrottle(e){return this.getPrecise(e*this.beta)}cubicSuccess(e){return this.getPrecise(this.scaleConstant*(e-this.lastThrottleTime-this.timeWindow)**3+this.lastMaxRate)}enableTokenBucket(){this.enabled=!0}updateTokenBucketRate(e){this.refillTokenBucket(),this.fillRate=Math.max(e,this.minFillRate),this.maxCapacity=Math.max(e,this.minCapacity),this.availableTokens=Math.min(this.availableTokens,this.maxCapacity)}updateMeasuredRate(){let e=this.getCurrentTimeInSeconds(),t=Math.floor(e*2)/2;if(this.requestCount++,t>this.lastTxRateBucket){let e=this.requestCount/(t-this.lastTxRateBucket);this.measuredTxRate=this.getPrecise(e*this.smooth+this.measuredTxRate*(1-this.smooth)),this.requestCount=0,this.lastTxRateBucket=t}}getPrecise(e){return parseFloat(e.toFixed(8))}},Ar=class e{static v2026=typeof process<`u`&&{}.SMITHY_NEW_RETRIES_2026===`true`;static delay(){return e.v2026?50:100}static throttlingDelay(){return e.v2026?1e3:500}static cost(){return e.v2026?14:5}static throttlingCost(){return e.v2026?5:10}static modifiedCostType(){return e.v2026?`THROTTLING`:`TRANSIENT`}},uee=class{x=Ar.delay();computeNextBackoffDelay(e){let t=Math.random()*Math.min(this.x*2**e,vr);return Math.floor(t)}setDelayBase(e){this.x=e}},jr=class{delay;count;cost;longPoll;$retryLog={acquisitionDelay:0};constructor(e,t,n,r){this.delay=e,this.count=t,this.cost=n,this.longPoll=r}getRetryCount(){return this.count}getRetryDelay(){return Math.min(vr,this.delay)}getRetryCost(){return this.cost}isLongPoll(){return this.longPoll}},Mr;(function(e){e.STANDARD=`standard`,e.ADAPTIVE=`adaptive`})(Mr||={});var Nr=Mr.STANDARD,Pr={incompatible:1,attempts:2,capacity:3},Fr=class{mode=Mr.STANDARD;retryBackoffStrategy;capacity=500;maxAttemptsProvider;baseDelay;constructor(e){typeof e==`number`?this.maxAttemptsProvider=async()=>e:typeof e==`function`?this.maxAttemptsProvider=e:e&&typeof e==`object`&&(this.maxAttemptsProvider=async()=>e.maxAttempts,this.baseDelay=e.baseDelay,this.retryBackoffStrategy=e.backoff),this.maxAttemptsProvider??=async()=>3,this.baseDelay??=Ar.delay(),this.retryBackoffStrategy??=new uee}async acquireInitialRetryToken(e){return new jr(Ar.delay(),0,void 0,Ar.v2026&&e.includes(`:longpoll`))}async refreshRetryTokenForRetry(e,t){let n=await this.getMaxAttempts(),r=this.retryCode(e,t,n),i=r===0,a=e.isLongPoll?.();if(i||a){let n=t.errorType;this.retryBackoffStrategy.setDelayBase(n===`THROTTLING`?Ar.throttlingDelay():this.baseDelay);let o=this.retryBackoffStrategy.computeNextBackoffDelay(e.getRetryCount()),s=o;if(t.retryAfterHint instanceof Date&&(s=Math.max(o,Math.min(t.retryAfterHint.getTime()-Date.now(),o+5e3))),i){let t=this.getCapacityCost(n);this.capacity-=t;let r=new jr(0,e.getRetryCount()+1,t,e.isLongPoll?.()??!1);return await new Promise(e=>setTimeout(e,s)),r.$retryLog.acquisitionDelay=s,r}else{let e=Ar.v2026&&r===Pr.capacity&&a?s:0;e>0&&await new Promise(t=>setTimeout(t,e))}}throw Error(`No retry token available`)}recordSuccess(e){this.capacity=Math.min(500,this.capacity+(e.getRetryCost()??1))}getCapacity(){return this.capacity}async maxAttempts(){return this.maxAttemptsProvider()}async getMaxAttempts(){try{return await this.maxAttemptsProvider()}catch{return console.warn(`Max attempts provider could not resolve. Using default of 3`),3}}retryCode(e,t,n){let r=e.getRetryCount()+1,i=this.isRetryableError(t.errorType)?0:Pr.incompatible,a=r=this.getCapacityCost(t.errorType)?0:Pr.capacity;return i||a||o}getCapacityCost(e){return e===Ar.modifiedCostType()?Ar.throttlingCost():Ar.cost()}isRetryableError(e){return e===`THROTTLING`||e===`TRANSIENT`}},dee=class{mode=Mr.ADAPTIVE;rateLimiter;standardRetryStrategy;constructor(e,t){let{rateLimiter:n}=t??{};this.rateLimiter=n??new lee,this.standardRetryStrategy=t?new Fr({maxAttempts:typeof e==`number`?e:3,...t}):new Fr(e)}async acquireInitialRetryToken(e){let t=await this.standardRetryStrategy.acquireInitialRetryToken(e);return await this.rateLimiter.getSendToken(),t}async refreshRetryTokenForRetry(e,t){this.rateLimiter.updateClientSendingRate(t);let n=await this.standardRetryStrategy.refreshRetryTokenForRetry(e,t);return await this.rateLimiter.getSendToken(),n}recordSuccess(e){this.rateLimiter.updateClientSendingRate({}),this.standardRetryStrategy.recordSuccess(e)}async maxAttemptsProvider(){return this.standardRetryStrategy.maxAttempts()}},Ir=(e,t)=>{let{retryStrategy:n,retryMode:r}=e,{defaultMaxAttempts:i=3,defaultBaseDelay:a=Ar.delay()}=t??{},o=re(e.maxAttempts??i),s=n?Promise.resolve(n):void 0,c=async()=>{let e=await o();return await re(r)()===Mr.ADAPTIVE?new dee(o,{maxAttempts:e,baseDelay:a}):new Fr({maxAttempts:e,baseDelay:a})};return Object.assign(e,{maxAttempts:o,retryStrategy:()=>s??=c()})},Lr=kr(ar);Ar.v2026||=typeof process==`object`&&{}.AWS_NEW_RETRIES_2026===`true`;function Rr(e,t,n){e.__aws_sdk_context?e.__aws_sdk_context.features||(e.__aws_sdk_context.features={}):e.__aws_sdk_context={features:{}},e.__aws_sdk_context.features[t]=n}function zr(e){return e}var Br=e=>t=>async n=>{if(!m.isInstance(n.request))return t(n);let{request:r}=n,{handlerProtocol:i=``}=e.requestHandler.metadata||{};if(i.indexOf(`h2`)>=0&&!r.headers[`:authority`])delete r.headers.host,r.headers[`:authority`]=r.hostname+(r.port?`:`+r.port:``);else if(!r.headers.host){let e=r.hostname;r.port!=null&&(e+=`:${r.port}`),r.headers.host=e}return t(n)},Vr={name:`hostHeaderMiddleware`,step:`build`,priority:`low`,tags:[`HOST`],override:!0},Hr=e=>({applyToStack:t=>{t.add(Br(e),Vr)}}),Ur=()=>(e,t)=>async n=>{try{let r=await e(n),{clientName:i,commandName:a,logger:o,dynamoDbDocumentClientOptions:s={}}=t,{overrideInputFilterSensitiveLog:c,overrideOutputFilterSensitiveLog:l}=s,u=c??t.inputFilterSensitiveLog,d=l??t.outputFilterSensitiveLog,{$metadata:f,...p}=r.output;return o?.info?.({clientName:i,commandName:a,input:u(n.input),output:d(p),metadata:f}),r}catch(e){let{clientName:r,commandName:i,logger:a,dynamoDbDocumentClientOptions:o={}}=t,{overrideInputFilterSensitiveLog:s}=o,c=s??t.inputFilterSensitiveLog;throw a?.error?.({clientName:r,commandName:i,input:c(n.input),error:e,metadata:e.$metadata}),e}},Wr={name:`loggerMiddleware`,tags:[`LOGGER`],step:`initialize`,override:!0},Gr=e=>({applyToStack:e=>{e.add(Ur(),Wr)}}),Kr=e=>({applyToStack:e=>{}}),qr=(e,t)=>{if(!t||t.length===0)return e;let n=[];for(let r of t)for(let t of e)t.schemeId.split(`#`)[1]===r&&n.push(t);for(let t of e)n.find(({schemeId:e})=>e===t.schemeId)||n.push(t);return n};function Jr(e){let t=new Map;for(let n of e)t.set(n.schemeId,n);return t}var Yr=(e,t)=>(n,r)=>async i=>{let a=qr(e.httpAuthSchemeProvider(await t.httpAuthSchemeParametersProvider(e,r,i.input)),e.authSchemePreference?await e.authSchemePreference():[]),o=Jr(e.httpAuthSchemes),s=Ze(r),c=[];for(let n of a){let i=o.get(n.schemeId);if(!i){c.push(`HttpAuthScheme \`${n.schemeId}\` was not enabled for this service.`);continue}let a=i.identityProvider(await t.identityProviderConfigProvider(e));if(!a){c.push(`HttpAuthScheme \`${n.schemeId}\` did not have an IdentityProvider configured.`);continue}let{identityProperties:l={},signingProperties:u={}}=n.propertiesExtractor?.(e,r)||{};n.identityProperties=Object.assign(n.identityProperties||{},l),n.signingProperties=Object.assign(n.signingProperties||{},u),s.selectedHttpAuthScheme={httpAuthOption:n,identity:await a(n.identityProperties),signer:i.signer};break}if(!s.selectedHttpAuthScheme)throw Error(c.join(` +`));return n(i)},Xr={step:`serialize`,tags:[`HTTP_AUTH_SCHEME`],name:`httpAuthSchemeMiddleware`,override:!0,relation:`before`,toMiddleware:`endpointV2Middleware`},Zr=(e,{httpAuthSchemeParametersProvider:t,identityProviderConfigProvider:n})=>({applyToStack:r=>{r.addRelativeTo(Yr(e,{httpAuthSchemeParametersProvider:t,identityProviderConfigProvider:n}),Xr)}}),Qr=e=>e=>{throw e},$r=(e,t)=>{},ei=e=>(e,t)=>async n=>{if(!m.isInstance(n.request))return e(n);let r=Ze(t).selectedHttpAuthScheme;if(!r)throw Error(`No HttpAuthScheme was selected: unable to sign request`);let{httpAuthOption:{signingProperties:i={}},identity:a,signer:o}=r,s=await e({...n,request:await o.sign(n.request,a,i)}).catch((o.errorHandler||Qr)(i));return(o.successHandler||$r)(s.response,i),s},ti={step:`finalizeRequest`,tags:[`HTTP_SIGNING`],name:`httpSigningMiddleware`,aliases:[`apiKeyMiddleware`,`tokenMiddleware`,`awsAuthMiddleware`],override:!0,relation:`after`,toMiddleware:`retryMiddleware`},ni=e=>({applyToStack:t=>{t.addRelativeTo(ei(e),ti)}}),ri=e=>{if(typeof e==`function`)return e;let t=Promise.resolve(e);return()=>t},ii=class{authSchemes=new Map;constructor(e){for(let t in e){let n=e[t];n!==void 0&&this.authSchemes.set(t,n)}}getIdentityProvider(e){return this.authSchemes.get(e)}},ai=(e=>function(t){return oi(t)&&t.expiration.getTime()-Date.now()e.expiration!==void 0,si=(e,t,n)=>{if(e===void 0)return;let r=typeof e==`function`?e:async()=>Promise.resolve(e),i,a,o,s=!1,c=async e=>{a||=r(e);try{i=await a,o=!0,s=!1}finally{a=void 0}return i};return t===void 0?async e=>((!o||e?.forceRefresh)&&(i=await c(e)),i):async e=>((!o||e?.forceRefresh)&&(i=await c(e)),s?i:n(i)?(t(i)&&await c(e),i):(s=!0,i))};function ci(e){return e===void 0?!0:typeof e==`string`&&e.length<=50}function li(e){let t=ri(e.userAgentAppId??void 0),{customUserAgent:n}=e;return Object.assign(e,{customUserAgent:typeof n==`string`?[[n]]:n,userAgentAppId:async()=>{let n=await t();if(!ci(n)){let t=e.logger?.constructor?.name===`NoOpLogger`||!e.logger?console:e.logger;typeof n==`string`?n.length>50&&t?.warn(`The provided userAgentAppId exceeds the maximum length of 50 characters.`):t?.warn(`userAgentAppId must be a string or undefined.`)}return n}})}var ui={partitions:[{id:`aws`,outputs:{dnsSuffix:`amazonaws.com`,dualStackDnsSuffix:`api.aws`,implicitGlobalRegion:`us-east-1`,name:`aws`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^(us|eu|ap|sa|ca|me|af|il|mx)\\-\\w+\\-\\d+$`,regions:{"af-south-1":{description:`Africa (Cape Town)`},"ap-east-1":{description:`Asia Pacific (Hong Kong)`},"ap-east-2":{description:`Asia Pacific (Taipei)`},"ap-northeast-1":{description:`Asia Pacific (Tokyo)`},"ap-northeast-2":{description:`Asia Pacific (Seoul)`},"ap-northeast-3":{description:`Asia Pacific (Osaka)`},"ap-south-1":{description:`Asia Pacific (Mumbai)`},"ap-south-2":{description:`Asia Pacific (Hyderabad)`},"ap-southeast-1":{description:`Asia Pacific (Singapore)`},"ap-southeast-2":{description:`Asia Pacific (Sydney)`},"ap-southeast-3":{description:`Asia Pacific (Jakarta)`},"ap-southeast-4":{description:`Asia Pacific (Melbourne)`},"ap-southeast-5":{description:`Asia Pacific (Malaysia)`},"ap-southeast-6":{description:`Asia Pacific (New Zealand)`},"ap-southeast-7":{description:`Asia Pacific (Thailand)`},"aws-global":{description:`aws global region`},"ca-central-1":{description:`Canada (Central)`},"ca-west-1":{description:`Canada West (Calgary)`},"eu-central-1":{description:`Europe (Frankfurt)`},"eu-central-2":{description:`Europe (Zurich)`},"eu-north-1":{description:`Europe (Stockholm)`},"eu-south-1":{description:`Europe (Milan)`},"eu-south-2":{description:`Europe (Spain)`},"eu-west-1":{description:`Europe (Ireland)`},"eu-west-2":{description:`Europe (London)`},"eu-west-3":{description:`Europe (Paris)`},"il-central-1":{description:`Israel (Tel Aviv)`},"me-central-1":{description:`Middle East (UAE)`},"me-south-1":{description:`Middle East (Bahrain)`},"mx-central-1":{description:`Mexico (Central)`},"sa-east-1":{description:`South America (Sao Paulo)`},"us-east-1":{description:`US East (N. Virginia)`},"us-east-2":{description:`US East (Ohio)`},"us-west-1":{description:`US West (N. California)`},"us-west-2":{description:`US West (Oregon)`}}},{id:`aws-cn`,outputs:{dnsSuffix:`amazonaws.com.cn`,dualStackDnsSuffix:`api.amazonwebservices.com.cn`,implicitGlobalRegion:`cn-northwest-1`,name:`aws-cn`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^cn\\-\\w+\\-\\d+$`,regions:{"aws-cn-global":{description:`aws-cn global region`},"cn-north-1":{description:`China (Beijing)`},"cn-northwest-1":{description:`China (Ningxia)`}}},{id:`aws-eusc`,outputs:{dnsSuffix:`amazonaws.eu`,dualStackDnsSuffix:`api.amazonwebservices.eu`,implicitGlobalRegion:`eusc-de-east-1`,name:`aws-eusc`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^eusc\\-(de)\\-\\w+\\-\\d+$`,regions:{"eusc-de-east-1":{description:`AWS European Sovereign Cloud (Germany)`}}},{id:`aws-iso`,outputs:{dnsSuffix:`c2s.ic.gov`,dualStackDnsSuffix:`api.aws.ic.gov`,implicitGlobalRegion:`us-iso-east-1`,name:`aws-iso`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^us\\-iso\\-\\w+\\-\\d+$`,regions:{"aws-iso-global":{description:`aws-iso global region`},"us-iso-east-1":{description:`US ISO East`},"us-iso-west-1":{description:`US ISO WEST`}}},{id:`aws-iso-b`,outputs:{dnsSuffix:`sc2s.sgov.gov`,dualStackDnsSuffix:`api.aws.scloud`,implicitGlobalRegion:`us-isob-east-1`,name:`aws-iso-b`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^us\\-isob\\-\\w+\\-\\d+$`,regions:{"aws-iso-b-global":{description:`aws-iso-b global region`},"us-isob-east-1":{description:`US ISOB East (Ohio)`},"us-isob-west-1":{description:`US ISOB West`}}},{id:`aws-iso-e`,outputs:{dnsSuffix:`cloud.adc-e.uk`,dualStackDnsSuffix:`api.cloud-aws.adc-e.uk`,implicitGlobalRegion:`eu-isoe-west-1`,name:`aws-iso-e`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^eu\\-isoe\\-\\w+\\-\\d+$`,regions:{"aws-iso-e-global":{description:`aws-iso-e global region`},"eu-isoe-west-1":{description:`EU ISOE West`}}},{id:`aws-iso-f`,outputs:{dnsSuffix:`csp.hci.ic.gov`,dualStackDnsSuffix:`api.aws.hci.ic.gov`,implicitGlobalRegion:`us-isof-south-1`,name:`aws-iso-f`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^us\\-isof\\-\\w+\\-\\d+$`,regions:{"aws-iso-f-global":{description:`aws-iso-f global region`},"us-isof-east-1":{description:`US ISOF EAST`},"us-isof-south-1":{description:`US ISOF SOUTH`}}},{id:`aws-us-gov`,outputs:{dnsSuffix:`amazonaws.com`,dualStackDnsSuffix:`api.aws`,implicitGlobalRegion:`us-gov-west-1`,name:`aws-us-gov`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^us\\-gov\\-\\w+\\-\\d+$`,regions:{"aws-us-gov-global":{description:`aws-us-gov global region`},"us-gov-east-1":{description:`AWS GovCloud (US-East)`},"us-gov-west-1":{description:`AWS GovCloud (US-West)`}}}],version:`1.1`},di=``,fi=e=>{let{partitions:t}=ui;for(let n of t){let{regions:t,outputs:r}=n;for(let[n,i]of Object.entries(t))if(n===e)return{...r,...i}}for(let n of t){let{regionRegex:t,outputs:r}=n;if(new RegExp(t).test(e))return{...r}}let n=t.find(e=>e.id===`aws`);if(!n)throw Error(`Provided region was not found in the partition array or regex, and default partition with id 'aws' doesn't exist.`);return{...n.outputs}},pi=()=>di,mi=/\d{12}\.ddb/;async function hi(e,t,n){if(n.request?.headers?.[`smithy-protocol`]===`rpc-v2-cbor`&&Rr(e,`PROTOCOL_RPC_V2_CBOR`,`M`),typeof t.retryStrategy==`function`){let n=await t.retryStrategy();if(typeof n.mode==`string`)switch(n.mode){case Mr.ADAPTIVE:Rr(e,`RETRY_MODE_ADAPTIVE`,`F`);break;case Mr.STANDARD:Rr(e,`RETRY_MODE_STANDARD`,`E`);break}}if(typeof t.accountIdEndpointMode==`function`){let n=e.endpointV2;switch(String(n?.url?.hostname).match(mi)&&Rr(e,`ACCOUNT_ID_ENDPOINT`,`O`),await t.accountIdEndpointMode?.()){case`disabled`:Rr(e,`ACCOUNT_ID_MODE_DISABLED`,`Q`);break;case`preferred`:Rr(e,`ACCOUNT_ID_MODE_PREFERRED`,`P`);break;case`required`:Rr(e,`ACCOUNT_ID_MODE_REQUIRED`,`R`);break}}let r=e.__smithy_context?.selectedHttpAuthScheme?.identity;if(r?.$source){let t=r;t.accountId&&Rr(e,`RESOLVED_ACCOUNT_ID`,`T`);for(let[n,r]of Object.entries(t.$source??{}))Rr(e,n,r)}}var gi=`user-agent`,_i=`x-amz-user-agent`,vi=/[^!$%&'*+\-.^_`|~\w]/g,yi=/[^!$%&'*+\-.^_`|~\w#]/g,bi=1024;function xi(e){let t=``;for(let n in e){let r=e[n];if(t.length+r.length+1<=bi){t.length?t+=`,`+r:t+=r;continue}break}return t}var Si=e=>(t,n)=>async r=>{let{request:i}=r;if(!m.isInstance(i))return t(r);let{headers:a}=i,o=n?.userAgent?.map(Ci)||[],s=(await e.defaultUserAgentProvider()).map(Ci);await hi(n,e,r);let c=n;s.push(`m/${xi(Object.assign({},n.__smithy_context?.features,c.__aws_sdk_context?.features))}`);let l=e?.customUserAgent?.map(Ci)||[],u=await e.userAgentAppId();u&&s.push(Ci([`app`,`${u}`]));let d=pi(),f=(d?[d]:[]).concat([...s,...o,...l]).join(` `),p=[...s.filter(e=>e.startsWith(`aws-sdk-`)),...l].join(` `);return e.runtime===`browser`?a[_i]=f:(p&&(a[_i]=a[`x-amz-user-agent`]?`${a[gi]} ${p}`:p),a[gi]=f),t({...r,request:i})},Ci=e=>{let t=e[0].split(`/`).map(e=>e.replace(vi,`-`)).join(`/`),n=e[1]?.replace(yi,`-`),r=t.indexOf(`/`),i=t.substring(0,r),a=t.substring(r+1);return i===`api`&&(a=a.toLowerCase()),[i,a,n].filter(e=>e&&e.length>0).reduce((e,t,n)=>{switch(n){case 0:return t;case 1:return`${e}/${t}`;default:return`${e}#${t}`}},``)},wi={name:`getUserAgentMiddleware`,step:`build`,priority:`low`,tags:[`SET_USER_AGENT`,`USER_AGENT`],override:!0},Ti=e=>({applyToStack:t=>{t.add(Si(e),wi)}}),Ei=({serviceId:e,clientVersion:t})=>async n=>{let r=typeof window<`u`?window.navigator:void 0,i=r?.userAgent??``,a=r?.userAgentData?.platform??Di.os(i)??`other`,o=r?.userAgentData?.brands??[],s=o[o.length-1],c=s?.brand??Di.browser(i)??`unknown`,l=s?.version??`unknown`,u=[[`aws-sdk-js`,t],[`ua`,`2.1`],[`os/${a}`,void 0],[`lang/js`],[`md/browser`,`${c}_${l}`]];e&&u.push([`api/${e}`,t]);let d=await n?.userAgentAppId?.();return d&&u.push([`app/${d}`]),u},Di={os(e){if(/iPhone|iPad|iPod/.test(e))return`iOS`;if(/Macintosh|Mac OS X/.test(e))return`macOS`;if(/Windows NT/.test(e))return`Windows`;if(/Android/.test(e))return`Android`;if(/Linux/.test(e))return`Linux`},browser(e){if(/EdgiOS|EdgA|Edg\//.test(e))return`Microsoft Edge`;if(/Firefox\//.test(e))return`Firefox`;if(/Chrome\//.test(e))return`Chrome`;if(/Safari\//.test(e))return`Safari`}},Oi=(e,t=!1)=>{if(t){for(let t of e.split(`.`))if(!Oi(t))return!1;return!0}return!(!et(e)||e.length<3||e.length>63||e!==e.toLowerCase()||vn(e))},ki=`:`,Ai=`/`,ji={isVirtualHostableS3Bucket:Oi,parseArn:e=>{let t=e.split(ki);if(t.length<6)return null;let[n,r,i,a,o,...s]=t;return n!==`arn`||r===``||i===``||s.join(ki)===``?null:{partition:r,service:i,region:a,accountId:o,resourceId:s.map(e=>e.split(Ai)).flat()}},partition:fi};dn.aws=ji;var Mi=(e,t,n)=>{let r,i,a,o=!1,s=async()=>{i||=e();try{r=await i,a=!0,o=!1}finally{i=void 0}return r};return t===void 0?async e=>((!a||e?.forceRefresh)&&(r=await s()),r):async e=>((!a||e?.forceRefresh)&&(r=await s()),o?r:n&&!n(r)?(o=!0,r):(t(r)&&await s(),r))},Ni=new Set,Pi=(e,t=et)=>{if(!Ni.has(e)&&!t(e))if(e===`*`)console.warn(`@smithy/config-resolver WARN - Please use the caller region instead of "*". See "sigv4a" in https://github.com/aws/aws-sdk-js-v3/blob/main/supplemental-docs/CLIENTS.md.`);else throw Error(`Region not accepted: region="${e}" is not a valid hostname component.`);else Ni.add(e)},Fi=e=>typeof e==`string`&&(e.startsWith(`fips-`)||e.endsWith(`-fips`)),Ii=e=>Fi(e)?[`fips-aws-global`,`aws-fips`].includes(e)?`us-east-1`:e.replace(/fips-(dkr-|prod-)?|-fips/,``):e,Li=e=>{let{region:t,useFipsEndpoint:n}=e;if(!t)throw Error(`Region is missing`);return Object.assign(e,{region:async()=>{let e=Ii(typeof t==`function`?await t():t);return Pi(e),e},useFipsEndpoint:async()=>Fi(typeof t==`string`?t:await t())?!0:typeof n==`function`?n():Promise.resolve(!!n)})},Ri=[`in-region`,`cross-region`,`mobile`,`standard`,`legacy`],zi=({defaultsMode:e}={})=>Mi(async()=>{let t=typeof e==`function`?await e():e;switch(t?.toLowerCase()){case`auto`:return Promise.resolve(Bi()?`mobile`:`standard`);case`mobile`:case`in-region`:case`cross-region`:case`standard`:case`legacy`:return Promise.resolve(t?.toLocaleLowerCase());case void 0:return Promise.resolve(`legacy`);default:throw Error(`Invalid parameter for "defaultsMode", expect ${Ri.join(`, `)}, got ${t}`)}}),Bi=()=>{let e=window?.navigator;if(e?.connection){let{effectiveType:t,rtt:n,downlink:r}=e?.connection;if(typeof t==`string`&&t!==`4g`||Number(n)>100||Number(r)<10)return!0}return e?.userAgentData?.mobile||typeof e?.maxTouchPoints==`number`&&e?.maxTouchPoints>1},Vi=e=>({setRegion(t){e.region=t},region(){return e.region}}),Hi=e=>({region:e.region()}),Ui=e=>Qe.isInstance(e)?e.headers?.date??e.headers?.Date:void 0,Wi=e=>new Date(Date.now()+e),Gi=(e,t)=>Math.abs(Wi(t).getTime()-e)>=3e5,Ki=(e,t)=>{let n=Date.parse(e);return Gi(n,t)?n-Date.now():t},qi=(e,t)=>{if(!t)throw Error(`Property \`${e}\` is not resolved for AWS SDK SigV4Auth`);return t},Ji=async e=>{let t=qi(`context`,e.context),n=qi(`config`,e.config),r=t.endpointV2?.properties?.authSchemes?.[0];return{config:n,signer:await qi(`signer`,n.signer)(r),signingRegion:e?.signingRegion,signingRegionSet:e?.signingRegionSet,signingName:e?.signingName}},Yi=class{async sign(e,t,n){if(!m.isInstance(e))throw Error("The request is not an instance of `HttpRequest` and cannot be signed");let r=await Ji(n),{config:i,signer:a}=r,{signingRegion:o,signingName:s}=r,c=n.context;if(c?.authSchemes?.length??!1){let[e,t]=c.authSchemes;e?.name===`sigv4a`&&t?.name===`sigv4`&&(o=t?.signingRegion??o,s=t?.signingName??s)}return n._preRequestSystemClockOffset=i.systemClockOffset,await a.sign(e,{signingDate:Wi(i.systemClockOffset),signingRegion:o,signingService:s})}errorHandler(e){return t=>{let n=t,r=n.ServerTime??Ui(n.$response);if(r){let t=qi(`config`,e.config),i=e._preRequestSystemClockOffset,a=Ki(r,t.systemClockOffset);(a!==t.systemClockOffset||i!==void 0&&i!==a)&&n.$metadata&&(t.systemClockOffset=a,n.$metadata.clockSkewCorrected=!0)}throw t}}successHandler(e,t){let n=Ui(e);if(n){let e=qi(`config`,t.config);e.systemClockOffset=Ki(n,e.systemClockOffset)}}},Xi=class extends Yi{async sign(e,t,n){if(!m.isInstance(e))throw Error("The request is not an instance of `HttpRequest` and cannot be signed");let{config:r,signer:i,signingRegion:a,signingRegionSet:o,signingName:s}=await Ji(n),c=(await r.sigv4aSigningRegionSet?.()??o??[a]).join(`,`);return n._preRequestSystemClockOffset=r.systemClockOffset,await i.sign(e,{signingDate:Wi(r.systemClockOffset),signingRegion:c,signingService:s})}},Zi=e=>(e.sigv4aSigningRegionSet=ri(e.sigv4aSigningRegionSet),e),Qi=e=>{let t=e.credentials,n=!!e.credentials,r;Object.defineProperty(e,"credentials",{set(i){i&&i!==t&&i!==r&&(n=!0),t=i;let a=ea(e,$i(e,{credentials:t,credentialDefaultProvider:e.credentialDefaultProvider}));if(n&&!a.attributed){let e=typeof t==`object`&&!!t;r=async t=>{let n=await a(t);return e&&(!n.$source||Object.keys(n.$source).length===0)?ir(n,`CREDENTIALS_CODE`,`e`):n},r.memoized=a.memoized,r.configBound=a.configBound,r.attributed=!0}else r=a},get(){return r},enumerable:!0,configurable:!0}),e.credentials=t;let{signingEscapePath:i=!0,systemClockOffset:a=e.systemClockOffset||0,sha256:o}=e,s;return s=e.signer?ri(e.signer):e.regionInfoProvider?()=>ri(e.region)().then(async t=>[await e.regionInfoProvider(t,{useFipsEndpoint:await e.useFipsEndpoint(),useDualstackEndpoint:await e.useDualstackEndpoint()})||{},t]).then(([t,n])=>{let{signingRegion:r,signingService:a}=t;e.signingRegion=e.signingRegion||r||n,e.signingName=e.signingName||a||e.serviceId;let s={...e,credentials:e.credentials,region:e.signingRegion,service:e.signingName,sha256:o,uriEscapePath:i};return new(e.signerConstructor||ae)(s)}):async t=>{t=Object.assign({},{name:`sigv4`,signingName:e.signingName||e.defaultSigningName,signingRegion:await ri(e.region)(),properties:{}},t);let n=t.signingRegion,r=t.signingName;e.signingRegion=e.signingRegion||n,e.signingName=e.signingName||r||e.serviceId;let a={...e,credentials:e.credentials,region:e.signingRegion,service:e.signingName,sha256:o,uriEscapePath:i};return new(e.signerConstructor||ae)(a)},Object.assign(e,{systemClockOffset:a,signingEscapePath:i,signer:s})};function $i(e,{credentials:t,credentialDefaultProvider:n}){let r;return r=t?t?.memoized?t:si(t,ai,oi):n?ri(n(Object.assign({},e,{parentClientConfig:e}))):async()=>{throw Error("@aws-sdk/core::resolveAwsSdkSigV4Config - `credentials` not provided and no credentialDefaultProvider was configured.")},r.memoized=!0,r}function ea(e,t){if(t.configBound)return t;let n=async n=>t({...n,callerClientConfig:e});return n.memoized=t.memoized,n.configBound=!0,n}var ta=async(e,t,n)=>({operation:Ze(t).operation,region:await re(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()});function na(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`acm`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var ra=e=>{let t=[];switch(e.operation){default:t.push(na(e))}return t},ia=e=>{let t=Qi(e);return Object.assign(t,{authSchemePreference:re(e.authSchemePreference??[])})},aa=e=>Object.assign(e,{useDualstackEndpoint:e.useDualstackEndpoint??!1,useFipsEndpoint:e.useFipsEndpoint??!1,defaultSigningName:`acm`}),oa={UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},sa={name:`@aws-sdk/client-acm`,description:`AWS SDK for JavaScript Acm Client for Node.js, Browser and React Native`,version:`3.1063.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-acm`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo acm`,"test:e2e":`yarn g:vitest run -c vitest.config.e2e.mts`,"test:e2e:watch":`yarn g:vitest watch -c vitest.config.e2e.mts`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.18`,"@aws-sdk/credential-provider-node":`^3.972.52`,"@aws-sdk/types":`^3.973.11`,"@smithy/core":`^3.24.6`,"@smithy/fetch-http-handler":`^5.4.6`,"@smithy/node-http-handler":`^4.7.6`,"@smithy/types":`^4.14.3`,tslib:`^2.6.2`},devDependencies:{"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/sdk-for-javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-acm`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-acm`}};function ca(e,t){return new Request(e,t)}function la(e=0){return new Promise((t,n)=>{e&&setTimeout(()=>{let t=Error(`Request did not complete within ${e} ms`);t.name=`TimeoutError`,n(t)},e)})}var ua={supported:void 0},da=class e{config;configProvider;static create(t){return typeof t?.handle==`function`?t:new e(t)}constructor(e){typeof e==`function`?this.configProvider=e().then(e=>e||{}):(this.config=e??{},this.configProvider=Promise.resolve(this.config)),ua.supported===void 0&&(ua.supported=typeof Request<`u`&&`keepalive`in ca(`https://[::1]`))}destroy(){}async handle(e,{abortSignal:t,requestTimeout:n}={}){this.config||=await this.configProvider;let r=n??this.config.requestTimeout,i=this.config.keepAlive===!0,a=this.config.credentials;if(t?.aborted){let e=fa(t);return Promise.reject(e)}let o=e.path,s=nr(e.query||{});s&&(o+=`?${s}`),e.fragment&&(o+=`#${e.fragment}`);let c=``;(e.username!=null||e.password!=null)&&(c=`${e.username??``}:${e.password??``}@`);let{port:l,method:u}=e,d=`${e.protocol}//${c}${e.hostname}${l?`:${l}`:``}${o}`,f=u===`GET`||u===`HEAD`?void 0:e.body,p={body:f,headers:new Headers(e.headers),method:u,credentials:a};this.config?.cache&&(p.cache=this.config.cache),f&&(p.duplex=`half`),typeof AbortController<`u`&&(p.signal=t),ua.supported&&(p.keepalive=i),typeof this.config.requestInit==`function`&&Object.assign(p,this.config.requestInit(e));let ee=()=>{},m=ca(d,p),te=[fetch(m).then(e=>{let t=e.headers,n={};for(let e of t.entries())n[e[0]]=e[1];return e.body==null?e.blob().then(t=>({response:new Qe({headers:n,reason:e.statusText,statusCode:e.status,body:t})})):{response:new Qe({headers:n,reason:e.statusText,statusCode:e.status,body:e.body})}}),la(r)];return t&&te.push(new Promise((e,n)=>{let r=()=>{n(fa(t))};if(typeof t.addEventListener==`function`){let e=t;e.addEventListener(`abort`,r,{once:!0}),ee=()=>e.removeEventListener(`abort`,r)}else t.onabort=r})),Promise.race(te).finally(ee)}updateHttpClientConfig(e,t){this.config=void 0,this.configProvider=this.configProvider.then(n=>(n[e]=t,n))}httpHandlerConfigs(){return this.config??{}}};function fa(e){let t=e&&typeof e==`object`&&`reason`in e?e.reason:void 0;if(t){if(t instanceof Error){let e=Error(`Request aborted`);return e.name=`AbortError`,e.cause=t,e}let e=Error(String(t));return e.name=`AbortError`,e}let n=Error(`Request aborted`);return n.name=`AbortError`,n}var pa=async e=>typeof Blob==`function`&&e instanceof Blob||e.constructor?.name===`Blob`?Blob.prototype.arrayBuffer===void 0?ma(e):new Uint8Array(await e.arrayBuffer()):ha(e);async function ma(e){let t=fe(await ga(e));return new Uint8Array(t)}async function ha(e){let t=[],n=e.getReader(),r=!1,i=0;for(;!r;){let{done:e,value:a}=await n.read();a&&(t.push(a),i+=a.length),r=e}let a=new Uint8Array(i),o=0;for(let e of t)a.set(e,o),o+=e.length;return a}function ga(e){return new Promise((t,n)=>{let r=new FileReader;r.onloadend=()=>{if(r.readyState!==2)return n(Error(`Reader aborted too early`));let e=r.result??``,i=e.indexOf(`,`),a=i>-1?i+1:e.length;t(e.substring(a))},r.onabort=()=>n(Error(`Read aborted`)),r.onerror=()=>n(r.error),r.readAsDataURL(e)})}var _a=class{queryCompat;errorRegistry;constructor(e=!1){this.queryCompat=e}resolveRestContentType(e,t){let n=t.getMemberSchemas(),r=Object.values(n).find(e=>!!e.getMergedTraits().httpPayload);if(r)return r.getMergedTraits().mediaType||(r.isStringSchema()?`text/plain`:r.isBlobSchema()?`application/octet-stream`:e);if(!t.isUnitSchema()&&Object.values(n).find(e=>{let{httpQuery:t,httpQueryParams:n,httpHeader:r,httpLabel:i,httpPrefixHeaders:a}=e.getMergedTraits();return!t&&!n&&!r&&!i&&a===void 0}))return e}async getErrorSchemaOrThrowBaseException(e,t,n,r,i,a){let o=e;e.includes(`#`)&&([,o]=e.split(`#`));let s={$metadata:i,$fault:n.statusCode<500?`client`:`server`};if(!this.errorRegistry)throw Error(`@aws-sdk/core/protocols - error handler not initialized.`);try{return{errorSchema:a?.(this.errorRegistry,o)??this.errorRegistry.getSchema(e),errorMetadata:s}}catch{r.message=r.message??r.Message??`UnknownError`;let e=this.errorRegistry,t=e.getBaseException();if(t){let n=e.getErrorCtor(t)??Error;throw this.decorateServiceException(Object.assign(new n({name:o}),s),r)}let n=r,i=n?.message??n?.Message??n?.Error?.Message??n?.Error?.message;throw this.decorateServiceException(Object.assign(Error(i),{name:o},s),r)}}compose(e,t,n){let r=n;t.includes(`#`)&&([r]=t.split(`#`));let i=Lt.for(r),a=Lt.for(`smithy.ts.sdk.synthetic.`+n);e.copyFrom(i),e.copyFrom(a),this.errorRegistry=e}decorateServiceException(e,t={}){if(this.queryCompat){let n=e.Message??t.Message,r=Ut(e,t);n&&(r.message=n);let i=r.Error??{};i.Type=r.Error?.Type,i.Code=r.Error?.Code,i.Message=r.Error?.message??r.Error?.Message??n,r.Error=i;let a=r.$metadata.requestId;return a&&(r.RequestId=a),r}return Ut(e,t)}setQueryCompatError(e,t){let n=t.headers?.[`x-amzn-query-error`];if(e!==void 0&&n!=null){let[t,r]=n.split(`;`),i=Object.keys(e),a={Code:t,Type:r};e.Code=t,e.Type=r;for(let t=0;th.of(e).getMergedTraits().awsQueryError?.[0]===t)}}},va=class{serdeContext;setSerdeContext(e){this.serdeContext=e}},ya=class{from;to;keys;constructor(e,t){this.from=e,this.to=t;let n=Object.keys(this.from),r=new Set(n);r.delete(`__type`),this.keys=r}mark(e){this.keys.delete(e)}hasUnknown(){return this.keys.size===1&&Object.keys(this.to).length===0}writeUnknown(){if(this.hasUnknown()){let e=this.keys.values().next().value,t=this.from[e];this.to.$unknown=[e,t]}}};function ba(e,t,n){if(n?.source){let e=n.source;if(typeof t==`number`&&(t>2**53-1||t<-(2**53-1)||e!==String(t)))return e.includes(`.`)?new Ce(e,`bigDecimal`):BigInt(e)}return t}var xa=(e,t)=>Fn(e,t).then(e=>(t?.utf8Encoder??me)(e)),Sa=(e,t)=>xa(e,t).then(e=>{if(e.length)try{return JSON.parse(e)}catch(t){throw t?.name===`SyntaxError`&&Object.defineProperty(t,"$responseBodyText",{value:e}),t}return{}}),Ca=(e,t)=>Object.keys(e).find(e=>e.toLowerCase()===t.toLowerCase()),wa=e=>{let t=e;return typeof t==`number`&&(t=t.toString()),t.indexOf(`,`)>=0&&(t=t.split(`,`)[0]),t.indexOf(`:`)>=0&&(t=t.split(`:`)[0]),t.indexOf(`#`)>=0&&(t=t.split(`#`)[1]),t},Ta=(e,t)=>Da(e,t,[`header`,`code`,`type`]),Ea=(e,t,n=!1)=>Da(e,t,n?[`code`,`header`,`type`]:[`type`,`code`,`header`]),Da=({headers:e},t,n)=>{for(;n.length>0;)switch(n.shift()){case`header`:let n=Ca(e??{},`x-amzn-errortype`);if(n!==void 0)return wa(e[n]);break;case`code`:let r=Ca(t??{},`code`);if(r&&t[r]!==void 0)return wa(t[r]);break;case`type`:if(t?.__type!==void 0)return wa(t.__type);break}},Oa=class extends va{settings;constructor(e){super(),this.settings=e}async read(e,t){return this._read(e,typeof t==`string`?JSON.parse(t,ba):await Sa(t,this.serdeContext))}readObject(e,t){return this._read(e,t)}_read(e,t){let n=typeof t==`object`&&!!t,r=h.of(e);if(n){if(r.isStructSchema()){let e=t,n=r.isUnionSchema(),i={},a,{jsonName:o}=this.settings;o&&(a={});let s;n&&(s=new ya(e,i));for(let[t,c]of r.structIterator()){let r=t;o&&(r=c.getMergedTraits().jsonName??r,a[r]=t),n&&s.mark(r),e[r]!=null&&(i[t]=this._read(c,e[r]))}if(n)s.writeUnknown();else if(typeof e.__type==`string`)for(let t in e){let n=e[t],r=o?a[t]??t:t;r in i||(i[r]=n)}return i}if(Array.isArray(t)&&r.isListSchema()){let e=r.getValueSchema(),n=[];for(let r of t)n.push(this._read(e,r));return n}if(r.isMapSchema()){let e=r.getValueSchema(),n={};for(let r in t)n[r]=this._read(e,t[r]);return n}}if(r.isBlobSchema()&&typeof t==`string`)return fe(t);let i=r.getMergedTraits().mediaType;if(r.isStringSchema()&&typeof t==`string`&&i)return i===`application/json`||i.endsWith(`+json`)?_e.from(t):t;if(r.isTimestampSchema()&&t!=null)switch(Gn(r,this.settings)){case 5:return se(t);case 6:return Oe(t);case 7:return pe(t);default:return console.warn(`Missing timestamp format, parsing value with Date constructor:`,t),new Date(t)}if(r.isBigIntegerSchema()&&(typeof t==`number`||typeof t==`string`))return BigInt(t);if(r.isBigDecimalSchema()&&t!=null){if(t instanceof Ce)return t;let e=t;return e.type===`bigDecimal`&&`string`in e?new Ce(e.string,e.type):new Ce(String(t),`bigDecimal`)}if(r.isNumericSchema()&&typeof t==`string`){switch(t){case`Infinity`:return 1/0;case`-Infinity`:return-1/0;case`NaN`:return NaN}return t}if(r.isDocumentSchema())if(n){let e=Array.isArray(t)?[]:{};for(let n in t){let i=t[n];i instanceof Ce?e[n]=i:e[n]=this._read(r,i)}return e}else return structuredClone(t);return t}},ka=class{values=new Map;counter=0;stage=0;createReplacer(){if(this.stage===1)throw Error(`@aws-sdk/core/protocols - JsonReplacer already created.`);if(this.stage===2)throw Error(`@aws-sdk/core/protocols - JsonReplacer exhausted.`);return this.stage=1,(e,t)=>{if(t instanceof Ce){let e=`${`Νnv`+ this.counter++}_`+t.string;return this.values.set(`"${e}"`,t.string),e}if(typeof t==`bigint`){let e=t.toString(),n=`${`Νb`+ this.counter++}_`+e;return this.values.set(`"${n}"`,e),n}return t}}replaceInJson(e){if(this.stage===0)throw Error(`@aws-sdk/core/protocols - JsonReplacer not created yet.`);if(this.stage===2)throw Error(`@aws-sdk/core/protocols - JsonReplacer exhausted.`);if(this.stage=2,this.counter===0)return e;for(let[t,n]of this.values)e=e.replace(t,n);return e}},Aa=class extends va{settings;buffer;useReplacer=!1;rootSchema;constructor(e){super(),this.settings=e}write(e,t){this.rootSchema=h.of(e),this.buffer=this._write(this.rootSchema,t)}flush(){let{rootSchema:e,useReplacer:t}=this;if(this.rootSchema=void 0,this.useReplacer=!1,e?.isStructSchema()||e?.isDocumentSchema()){if(!t)return JSON.stringify(this.buffer);let e=new ka;return e.replaceInJson(JSON.stringify(this.buffer,e.createReplacer(),0))}return this.buffer}writeDiscriminatedDocument(e,t){this.write(e,t),typeof this.buffer==`object`&&(this.buffer.__type=h.of(e).getName(!0))}_write(e,t,n){let r=typeof t==`object`&&!!t,i=h.of(e);if(r){if(i.isStructSchema()){let e=t,n={},{jsonName:r}=this.settings,a;r&&(a={});let o=0;for(let[t,s]of i.structIterator()){let c=this._write(s,e[t],i);if(c!==void 0){let e=t;r&&(e=s.getMergedTraits().jsonName??t,a[t]=e),n[e]=c,o++}}if(i.isUnionSchema()&&o===0){let{$unknown:t}=e;if(Array.isArray(t)){let[e,r]=t;n[e]=this._write(15,r)}}else if(typeof e.__type==`string`)for(let t in e){let i=e[t],o=r?a[t]??t:t;o in n||(n[o]=this._write(15,i))}return n}if(Array.isArray(t)&&i.isListSchema()){let e=i.getValueSchema(),n=[],r=!!i.getMergedTraits().sparse;for(let i of t)(r||i!=null)&&n.push(this._write(e,i));return n}if(i.isMapSchema()){let e=i.getValueSchema(),n={},r=!!i.getMergedTraits().sparse;for(let i in t){let a=t[i];(r||a!=null)&&(n[i]=this._write(e,a))}return n}if(t instanceof Uint8Array&&(i.isBlobSchema()||i.isDocumentSchema()))return i===this.rootSchema?t:(this.serdeContext?.base64Encoder??le)(t);if(t instanceof Date&&(i.isTimestampSchema()||i.isDocumentSchema()))switch(Gn(i,this.settings)){case 5:return t.toISOString().replace(`.000Z`,`Z`);case 6:return ke(t);case 7:return t.getTime()/1e3;default:return console.warn(`Missing timestamp format, using epoch seconds`,t),t.getTime()/1e3}t instanceof Ce&&(this.useReplacer=!0)}if(!(t===null&&n?.isStructSchema())){if(i.isStringSchema()){if(t===void 0&&i.isIdempotencyToken())return Se();let e=i.getMergedTraits().mediaType;return t!=null&&e&&(e===`application/json`||e.endsWith(`+json`))?_e.from(t):t}if(typeof t==`number`&&i.isNumericSchema())return Math.abs(t)===1/0||isNaN(t)?String(t):t;if(typeof t==`string`&&i.isBlobSchema())return i===this.rootSchema?t:(this.serdeContext?.base64Encoder??le)(t);if(typeof t==`bigint`&&(this.useReplacer=!0),i.isDocumentSchema())if(r){let e=Array.isArray(t)?[]:{};for(let n in t){let r=t[n];r instanceof Ce?(this.useReplacer=!0,e[n]=r):e[n]=this._write(i,r)}return e}else return structuredClone(t);return t}}},ja=class extends va{settings;constructor(e){super(),this.settings=e}createSerializer(){let e=new Aa(this.settings);return e.setSerdeContext(this.serdeContext),e}createDeserializer(){let e=new Oa(this.settings);return e.setSerdeContext(this.serdeContext),e}},Ma=class extends Wn{serializer;deserializer;serviceTarget;codec;mixin;awsQueryCompatible;constructor({defaultNamespace:e,errorTypeRegistries:t,serviceTarget:n,awsQueryCompatible:r,jsonCodec:i}){super({defaultNamespace:e,errorTypeRegistries:t}),this.serviceTarget=n,this.codec=i??new ja({timestampFormat:{useTrait:!0,default:7},jsonName:!1}),this.serializer=this.codec.createSerializer(),this.deserializer=this.codec.createDeserializer(),this.awsQueryCompatible=!!r,this.mixin=new _a(this.awsQueryCompatible)}async serializeRequest(e,t,n){let r=await super.serializeRequest(e,t,n);return r.path.endsWith(`/`)||(r.path+=`/`),r.headers[`content-type`]=`application/x-amz-json-${this.getJsonRpcVersion()}`,r.headers[`x-amz-target`]=`${this.serviceTarget}.${e.name}`,this.awsQueryCompatible&&(r.headers[`x-amzn-query-mode`]=`true`),(xt(e.input)===`unit`||!r.body)&&(r.body=`{}`),r}getPayloadCodec(){return this.codec}async handleError(e,t,n,r,i){let{awsQueryCompatible:a}=this;a&&this.mixin.setQueryCompatError(r,n);let o=Ea(n,r,a)??`Unknown`;this.mixin.compose(this.compositeErrorRegistry,o,this.options.defaultNamespace);let{errorSchema:s,errorMetadata:c}=await this.mixin.getErrorSchemaOrThrowBaseException(o,this.options.defaultNamespace,n,r,i,a?this.mixin.findQueryCompatibleError:void 0),l=h.of(s),u=r.message??r.Message??`UnknownError`,d=new((this.compositeErrorRegistry.getErrorCtor(s))??Error)({}),f={},p=this.codec.createDeserializer();for(let[e,t]of l.structIterator())r[e]!=null&&(f[e]=p.readObject(t,r[e]));throw a&&this.mixin.queryCompatOutput(r,f),this.mixin.decorateServiceException(Object.assign(d,c,{$fault:l.getMergedTraits().error,message:u},f),r)}},Na=class extends Ma{constructor({defaultNamespace:e,errorTypeRegistries:t,serviceTarget:n,awsQueryCompatible:r,jsonCodec:i}){super({defaultNamespace:e,errorTypeRegistries:t,serviceTarget:n,awsQueryCompatible:r,jsonCodec:i})}getShapeId(){return`aws.protocols#awsJson1_1`}getJsonRpcVersion(){return`1.1`}getDefaultContentType(){return`application/x-amz-json-1.1`}},Pa=class extends Un{serializer;deserializer;codec;mixin=new _a;constructor({defaultNamespace:e,errorTypeRegistries:t}){super({defaultNamespace:e,errorTypeRegistries:t});let n={timestampFormat:{useTrait:!0,default:7},httpBindings:!0,jsonName:!0};this.codec=new ja(n),this.serializer=new Yn(this.codec.createSerializer(),n),this.deserializer=new qn(this.codec.createDeserializer(),n)}getShapeId(){return`aws.protocols#restJson1`}getPayloadCodec(){return this.codec}setSerdeContext(e){this.codec.setSerdeContext(e),super.setSerdeContext(e)}async serializeRequest(e,t,n){let r=await super.serializeRequest(e,t,n),i=h.of(e.input);if(!r.headers[`content-type`]){let e=this.mixin.resolveRestContentType(this.getDefaultContentType(),i);e&&(r.headers[`content-type`]=e)}return r.body==null&&r.headers[`content-type`]===this.getDefaultContentType()&&(r.body=`{}`),r}async deserializeResponse(e,t,n){let r=await super.deserializeResponse(e,t,n),i=h.of(e.output);for(let[e,t]of i.structIterator())t.getMemberTraits().httpPayload&&!(e in r)&&(r[e]=null);return r}async handleError(e,t,n,r,i){let a=Ta(n,r)??`Unknown`;this.mixin.compose(this.compositeErrorRegistry,a,this.options.defaultNamespace);let{errorSchema:o,errorMetadata:s}=await this.mixin.getErrorSchemaOrThrowBaseException(a,this.options.defaultNamespace,n,r,i),c=h.of(o),l=r.message??r.Message??`UnknownError`,u=new((this.compositeErrorRegistry.getErrorCtor(o))??Error)({});await this.deserializeHttpMessage(o,t,n,r);let d={},f=this.codec.createDeserializer();for(let[e,t]of c.structIterator()){let n=t.getMergedTraits().jsonName??e;d[e]=f.readObject(t,r[n])}throw this.mixin.decorateServiceException(Object.assign(u,s,{$fault:c.getMergedTraits().error,message:l},d),r)}getDefaultContentType(){return`application/json`}},Fa=/[&<>"]/g,Ia={"&":`&`,"<":`<`,">":`>`,'"':`"`};function La(e){return e.replace(Fa,e=>Ia[e])}var Ra=/[&"'<>\r\n\u0085\u2028]/g,za={"&":`&`,'"':`"`,"'":`'`,"<":`<`,">":`>`,"\r":` `,"\n":` `,"…":`…`,"\u2028":`
`};function Ba(e){return e.replace(Ra,e=>za[e])}var Va=class{value;constructor(e){this.value=e}toString(){return Ba(``+this.value)}},Ha=class e{name;children;attributes={};static of(t,n,r){let i=new e(t);return n!==void 0&&i.addChildNode(new Va(n)),r!==void 0&&i.withName(r),i}constructor(e,t=[]){this.name=e,this.children=t}withName(e){return this.name=e,this}addAttribute(e,t){return this.attributes[e]=t,this}addChildNode(e){return this.children.push(e),this}removeAttribute(e){return delete this.attributes[e],this}n(e){return this.name=e,this}c(e){return this.children.push(e),this}a(e,t){return t!=null&&(this.attributes[e]=t),this}cc(t,n,r=n){if(t[n]!=null){let i=e.of(n,t[n]).withName(r);this.c(i)}}l(e,t,n,r){e[t]!=null&&r().map(e=>{e.withName(n),this.c(e)})}lc(t,n,r,i){if(t[n]!=null){let t=i(),n=new e(r);t.map(e=>{n.c(e)}),this.c(n)}}toString(){let e=!!this.children.length,t=`<${this.name}`,n=this.attributes;for(let e of Object.keys(n)){let r=n[e];r!=null&&(t+=` ${e}="${La(``+r)}"`)}return t+=e?`>${this.children.map(e=>e.toString()).join(``)}`:`/>`}},Ua;function Wa(e){Ua||=new DOMParser;let t=Ua.parseFromString(e,`application/xml`);if(t.getElementsByTagName(`parsererror`).length>0)throw Error(`DOMParser XML parsing error.`);let n=e=>{if(e.nodeType===Node.TEXT_NODE&&e.textContent?.trim())return e.textContent;if(e.nodeType===Node.ELEMENT_NODE){let t=e;if(t.attributes.length===0&&t.childNodes.length===0)return``;let r={},i=Array.from(t.attributes);for(let e of i)r[`${e.name}`]=e.value;let a=Array.from(t.childNodes);for(let e of a){let o=n(e);if(o!=null){let t=e.nodeName;if(a.length===1&&i.length===0&&t===`#text`)return o;r[t]?Array.isArray(r[t])?r[t].push(o):r[t]=[r[t],o]:r[t]=o}else if(a.length===1&&i.length===0)return t.textContent}return r}return null};return{[t.documentElement.nodeName]:n(t.documentElement)}}var Ga=class extends va{settings;stringDeserializer;constructor(e){super(),this.settings=e,this.stringDeserializer=new Kn(e)}setSerdeContext(e){this.serdeContext=e,this.stringDeserializer.setSerdeContext(e)}read(e,t,n){let r=h.of(e),i=r.getMemberSchemas();if(r.isStructSchema()&&r.isMemberSchema()&&Object.values(i).find(e=>!!e.getMemberTraits().eventPayload)){let e={},n=Object.keys(i)[0];return i[n].isBlobSchema()?e[n]=t:e[n]=this.read(i[n],t),e}let a=(this.serdeContext?.utf8Encoder??me)(t),o=this.parseXml(a);return this.readSchema(e,n?o[n]:o)}readSchema(e,t){let n=h.of(e);if(n.isUnitSchema())return;let r=n.getMergedTraits();if(n.isListSchema()&&!Array.isArray(t))return this.readSchema(n,[t]);if(t==null)return t;if(typeof t==`object`){let e=!!r.xmlFlattened;if(n.isListSchema()){let r=n.getValueSchema(),i=[],a=r.getMergedTraits().xmlName??`member`,o=e?t:(t[0]??t)[a];if(o==null)return i;let s=Array.isArray(o)?o:[o];for(let e of s)i.push(this.readSchema(r,e));return i}let i={};if(n.isMapSchema()){let r=n.getKeySchema(),a=n.getValueSchema(),o;o=e?Array.isArray(t)?t:[t]:Array.isArray(t.entry)?t.entry:[t.entry];let s=r.getMergedTraits().xmlName??`key`,c=a.getMergedTraits().xmlName??`value`;for(let e of o){let t=e[s],n=e[c];i[t]=this.readSchema(a,n)}return i}if(n.isStructSchema()){let e=n.isUnionSchema(),r;e&&(r=new ya(t,i));for(let[a,o]of n.structIterator()){let n=o.getMergedTraits(),s=n.httpPayload?n.xmlName??o.getName():o.getMemberTraits().xmlName??a;e&&r.mark(s),t[s]!=null&&(i[a]=this.readSchema(o,t[s]))}return e&&r.writeUnknown(),i}if(n.isDocumentSchema())return t;throw Error(`@aws-sdk/core/protocols - xml deserializer unhandled schema type for ${n.getName(!0)}`)}return n.isListSchema()?[]:n.isMapSchema()||n.isStructSchema()?{}:this.stringDeserializer.read(n,t)}parseXml(e){if(e.length){let t;try{t=Wa(e)}catch(t){throw t&&typeof t==`object`&&Object.defineProperty(t,"$responseBodyText",{value:e}),t}let n=`#text`,r=Object.keys(t)[0],i=t[r];return i[n]&&(i[r]=i[n],delete i[n]),Qt(i)}return{}}},Ka=class extends va{settings;buffer;constructor(e){super(),this.settings=e}write(e,t,n=``){this.buffer===void 0&&(this.buffer=``);let r=h.of(e);if(n&&!n.endsWith(`.`)&&(n+=`.`),r.isBlobSchema())(typeof t==`string`||t instanceof Uint8Array)&&(this.writeKey(n),this.writeValue((this.serdeContext?.base64Encoder??le)(t)));else if(r.isBooleanSchema()||r.isNumericSchema()||r.isStringSchema())t==null?r.isIdempotencyToken()&&(this.writeKey(n),this.writeValue(Se())):(this.writeKey(n),this.writeValue(String(t)));else if(r.isBigIntegerSchema())t!=null&&(this.writeKey(n),this.writeValue(String(t)));else if(r.isBigDecimalSchema())t!=null&&(this.writeKey(n),this.writeValue(t instanceof Ce?t.string:String(t)));else if(r.isTimestampSchema()){if(t instanceof Date)switch(this.writeKey(n),Gn(r,this.settings)){case 5:this.writeValue(t.toISOString().replace(`.000Z`,`Z`));break;case 6:this.writeValue(ke(t));break;case 7:this.writeValue(String(t.getTime()/1e3));break}}else if(r.isDocumentSchema())Array.isArray(t)?this.write(79,t,n):t instanceof Date?this.write(4,t,n):t instanceof Uint8Array?this.write(21,t,n):t&&typeof t==`object`?this.write(143,t,n):(this.writeKey(n),this.writeValue(String(t)));else if(r.isListSchema()){if(Array.isArray(t))if(t.length===0)this.settings.serializeEmptyLists&&(this.writeKey(n),this.writeValue(``));else{let e=r.getValueSchema(),i=this.settings.flattenLists||r.getMergedTraits().xmlFlattened,a=1;for(let r of t){if(r==null)continue;let t=e.getMergedTraits(),o=this.getKey(`member`,t.xmlName,t.ec2QueryName),s=i?`${n}${a}`:`${n}${o}.${a}`;this.write(e,r,s),++a}}}else if(r.isMapSchema()){if(t&&typeof t==`object`){let e=r.getKeySchema(),i=r.getValueSchema(),a=r.getMergedTraits().xmlFlattened,o=1;for(let r in t){let s=t[r];if(s==null)continue;let c=e.getMergedTraits(),l=this.getKey(`key`,c.xmlName,c.ec2QueryName),u=a?`${n}${o}.${l}`:`${n}entry.${o}.${l}`,d=i.getMergedTraits(),f=this.getKey(`value`,d.xmlName,d.ec2QueryName),p=a?`${n}${o}.${f}`:`${n}entry.${o}.${f}`;this.write(e,r,u),this.write(i,s,p),++o}}}else if(r.isStructSchema()){if(t&&typeof t==`object`){let e=!1;for(let[i,a]of r.structIterator()){if(t[i]==null&&!a.isIdempotencyToken())continue;let r=a.getMergedTraits(),o=this.getKey(i,r.xmlName,r.ec2QueryName,`struct`),s=`${n}${o}`;this.write(a,t[i],s),e=!0}if(!e&&r.isUnionSchema()){let{$unknown:e}=t;if(Array.isArray(e)){let[t,r]=e,i=`${n}${t}`;this.write(15,r,i)}}}}else if(!r.isUnitSchema())throw Error(`@aws-sdk/core/protocols - QuerySerializer unrecognized schema type ${r.getName(!0)}`)}flush(){if(this.buffer===void 0)throw Error(`@aws-sdk/core/protocols - QuerySerializer cannot flush with nothing written to buffer.`);let e=this.buffer;return delete this.buffer,e}getKey(e,t,n,r){let{ec2:i,capitalizeKeys:a}=this.settings;if(i&&n)return n;let o=t??e;return a&&r===`struct`?o[0].toUpperCase()+o.slice(1):o}writeKey(e){e.endsWith(`.`)&&(e=e.slice(0,e.length-1)),this.buffer+=`&${In(e)}=`}writeValue(e){this.buffer+=In(e)}},qa=class extends Wn{options;serializer;deserializer;mixin=new _a;constructor(e){super({defaultNamespace:e.defaultNamespace,errorTypeRegistries:e.errorTypeRegistries}),this.options=e;let t={timestampFormat:{useTrait:!0,default:5},httpBindings:!1,xmlNamespace:e.xmlNamespace,serviceNamespace:e.defaultNamespace,serializeEmptyLists:!0};this.serializer=new Ka(t),this.deserializer=new Ga(t)}getShapeId(){return`aws.protocols#awsQuery`}setSerdeContext(e){this.serializer.setSerdeContext(e),this.deserializer.setSerdeContext(e)}getPayloadCodec(){throw Error(`AWSQuery protocol has no payload codec.`)}async serializeRequest(e,t,n){let r=await super.serializeRequest(e,t,n);return r.path.endsWith(`/`)||(r.path+=`/`),r.headers[`content-type`]=`application/x-www-form-urlencoded`,(xt(e.input)===`unit`||!r.body)&&(r.body=``),r.body=`Action=${e.name.split(`#`)[1]??e.name}&Version=${this.options.version}`+r.body,r.body.endsWith(`&`)&&(r.body=r.body.slice(-1)),r}async deserializeResponse(e,t,n){let r=this.deserializer,i=h.of(e.output),a={};if(n.statusCode>=300){let i=await Fn(n.body,t);i.byteLength>0&&Object.assign(a,await r.read(15,i)),await this.handleError(e,t,n,a,this.deserializeMetadata(n))}for(let e in n.headers){let t=n.headers[e];delete n.headers[e],n.headers[e.toLowerCase()]=t}let o=e.name.split(`#`)[1]??e.name,s=i.isStructSchema()&&this.useNestedResult()?o+`Result`:void 0,c=await Fn(n.body,t);return c.byteLength>0&&Object.assign(a,await r.read(i,c,s)),a.$metadata=this.deserializeMetadata(n),a}useNestedResult(){return!0}async handleError(e,t,n,r,i){let a=this.loadQueryErrorCode(n,r)??`Unknown`;this.mixin.compose(this.compositeErrorRegistry,a,this.options.defaultNamespace);let o=this.loadQueryError(r)??{},s=this.loadQueryErrorMessage(r);o.message=s,o.Error={Type:o.Type,Code:o.Code,Message:s};let{errorSchema:c,errorMetadata:l}=await this.mixin.getErrorSchemaOrThrowBaseException(a,this.options.defaultNamespace,n,o,i,this.mixin.findQueryCompatibleError),u=h.of(c),d=new((this.compositeErrorRegistry.getErrorCtor(c))??Error)({}),f={Type:o.Error.Type,Code:o.Error.Code,Error:o.Error};for(let[e,t]of u.structIterator()){let n=t.getMergedTraits().xmlName??e,i=o[n]??r[n];f[e]=this.deserializer.readSchema(t,i)}throw this.mixin.decorateServiceException(Object.assign(d,l,{$fault:u.getMergedTraits().error,message:s},f),r)}loadQueryErrorCode(e,t){let n=(t.Errors?.[0]?.Error??t.Errors?.Error??t.Error)?.Code;if(n!==void 0)return n;if(e.statusCode==404)return`NotFound`}loadQueryError(e){return e.Errors?.[0]?.Error??e.Errors?.Error??e.Error}loadQueryErrorMessage(e){let t=this.loadQueryError(e);return t?.message??t?.Message??e.message??e.Message??`Unknown`}getDefaultContentType(){return`application/x-www-form-urlencoded`}},Ja=class extends qa{options;constructor(e){super(e),this.options=e,Object.assign(this.serializer.settings,{capitalizeKeys:!0,flattenLists:!0,serializeEmptyLists:!1,ec2:!0})}getShapeId(){return`aws.protocols#ec2Query`}useNestedResult(){return!1}},Ya=(e,t)=>{if(t?.Error?.Code!==void 0)return t.Error.Code;if(t?.Code!==void 0)return t.Code;if(e.statusCode==404)return`NotFound`},Xa=class extends va{settings;stringBuffer;byteBuffer;buffer;constructor(e){super(),this.settings=e}write(e,t){let n=h.of(e);if(n.isStringSchema()&&typeof t==`string`)this.stringBuffer=t;else if(n.isBlobSchema())this.byteBuffer=`byteLength`in t?t:(this.serdeContext?.base64Decoder??fe)(t);else{this.buffer=this.writeStruct(n,t,void 0);let e=n.getMergedTraits();e.httpPayload&&!e.xmlName&&this.buffer.withName(n.getName())}}flush(){if(this.byteBuffer!==void 0){let e=this.byteBuffer;return delete this.byteBuffer,e}if(this.stringBuffer!==void 0){let e=this.stringBuffer;return delete this.stringBuffer,e}let e=this.buffer;return this.settings.xmlNamespace&&(e?.attributes?.xmlns||e.addAttribute(`xmlns`,this.settings.xmlNamespace)),delete this.buffer,e.toString()}writeStruct(e,t,n){let r=e.getMergedTraits(),i=e.isMemberSchema()&&!r.httpPayload?e.getMemberTraits().xmlName??e.getMemberName():r.xmlName??e.getName();if(!i||!e.isStructSchema())throw Error(`@aws-sdk/core/protocols - xml serializer, cannot write struct with empty name or non-struct, schema=${e.getName(!0)}.`);let a=Ha.of(i),[o,s]=this.getXmlnsAttribute(e,n);for(let[n,r]of e.structIterator()){let e=t[n];if(e!=null||r.isIdempotencyToken()){if(r.getMergedTraits().xmlAttribute){a.addAttribute(r.getMergedTraits().xmlName??n,this.writeSimple(r,e));continue}if(r.isListSchema())this.writeList(r,e,a,s);else if(r.isMapSchema())this.writeMap(r,e,a,s);else if(r.isStructSchema())a.addChildNode(this.writeStruct(r,e,s));else{let t=Ha.of(r.getMergedTraits().xmlName??r.getMemberName());this.writeSimpleInto(r,e,t,s),a.addChildNode(t)}}}let{$unknown:c}=t;if(c&&e.isUnionSchema()&&Array.isArray(c)&&Object.keys(t).length===1){let[e,n]=c,r=Ha.of(e);if(typeof n!=`string`)if(t instanceof Ha||t instanceof Va)a.addChildNode(t);else throw Error(`@aws-sdk - $unknown union member in XML requires value of type string, @aws-sdk/xml-builder::XmlNode or XmlText.`);this.writeSimpleInto(0,n,r,s),a.addChildNode(r)}return s&&a.addAttribute(o,s),a}writeList(e,t,n,r){if(!e.isMemberSchema())throw Error(`@aws-sdk/core/protocols - xml serializer, cannot write non-member list: ${e.getName(!0)}`);let i=e.getMergedTraits(),a=e.getValueSchema(),o=a.getMergedTraits(),s=!!o.sparse,c=!!i.xmlFlattened,[l,u]=this.getXmlnsAttribute(e,r),d=(t,n)=>{if(a.isListSchema())this.writeList(a,Array.isArray(n)?n:[n],t,u);else if(a.isMapSchema())this.writeMap(a,n,t,u);else if(a.isStructSchema()){let r=this.writeStruct(a,n,u);t.addChildNode(r.withName(c?i.xmlName??e.getMemberName():o.xmlName??`member`))}else{let r=Ha.of(c?i.xmlName??e.getMemberName():o.xmlName??`member`);this.writeSimpleInto(a,n,r,u),t.addChildNode(r)}};if(c)for(let e of t)(s||e!=null)&&d(n,e);else{let r=Ha.of(i.xmlName??e.getMemberName());u&&r.addAttribute(l,u);for(let e of t)(s||e!=null)&&d(r,e);n.addChildNode(r)}}writeMap(e,t,n,r,i=!1){if(!e.isMemberSchema())throw Error(`@aws-sdk/core/protocols - xml serializer, cannot write non-member map: ${e.getName(!0)}`);let a=e.getMergedTraits(),o=e.getKeySchema(),s=o.getMergedTraits().xmlName??`key`,c=e.getValueSchema(),l=c.getMergedTraits(),u=l.xmlName??`value`,d=!!l.sparse,f=!!a.xmlFlattened,[p,ee]=this.getXmlnsAttribute(e,r),m=(e,t,n)=>{let r=Ha.of(s,t),[i,a]=this.getXmlnsAttribute(o,ee);a&&r.addAttribute(i,a),e.addChildNode(r);let l=Ha.of(u);c.isListSchema()?this.writeList(c,n,l,ee):c.isMapSchema()?this.writeMap(c,n,l,ee,!0):c.isStructSchema()?l=this.writeStruct(c,n,ee):this.writeSimpleInto(c,n,l,ee),e.addChildNode(l)};if(f)for(let r in t){let i=t[r];if(d||i!=null){let t=Ha.of(a.xmlName??e.getMemberName());m(t,r,i),n.addChildNode(t)}}else{let r;i||(r=Ha.of(a.xmlName??e.getMemberName()),ee&&r.addAttribute(p,ee),n.addChildNode(r));for(let e in t){let a=t[e];if(d||a!=null){let t=Ha.of(`entry`);m(t,e,a),(i?n:r).addChildNode(t)}}}}writeSimple(e,t){if(t===null)throw Error(`@aws-sdk/core/protocols - (XML serializer) cannot write null value.`);let n=h.of(e),r=null;if(t&&typeof t==`object`)if(n.isBlobSchema())r=(this.serdeContext?.base64Encoder??le)(t);else if(n.isTimestampSchema()&&t instanceof Date)switch(Gn(n,this.settings)){case 5:r=t.toISOString().replace(`.000Z`,`Z`);break;case 6:r=ke(t);break;case 7:r=String(t.getTime()/1e3);break;default:console.warn(`Missing timestamp format, using http date`,t),r=ke(t);break}else if(n.isBigDecimalSchema()&&t)return t instanceof Ce?t.string:String(t);else if(n.isMapSchema()||n.isListSchema())throw Error(`@aws-sdk/core/protocols - xml serializer, cannot call _write() on List/Map schema, call writeList or writeMap() instead.`);else throw Error(`@aws-sdk/core/protocols - xml serializer, unhandled schema type for object value and schema: ${n.getName(!0)}`);if((n.isBooleanSchema()||n.isNumericSchema()||n.isBigIntegerSchema()||n.isBigDecimalSchema())&&(r=String(t)),n.isStringSchema()&&(r=t===void 0&&n.isIdempotencyToken()?Se():String(t)),r===null)throw Error(`Unhandled schema-value pair ${n.getName(!0)}=${t}`);return r}writeSimpleInto(e,t,n,r){let i=this.writeSimple(e,t),a=h.of(e),o=new Va(i),[s,c]=this.getXmlnsAttribute(a,r);c&&n.addAttribute(s,c),n.addChildNode(o)}getXmlnsAttribute(e,t){let[n,r]=e.getMergedTraits().xmlNamespace??[];return r&&r!==t?[n?`xmlns:${n}`:`xmlns`,r]:[void 0,void 0]}},Za=class extends va{settings;constructor(e){super(),this.settings=e}createSerializer(){let e=new Xa(this.settings);return e.setSerdeContext(this.serdeContext),e}createDeserializer(){let e=new Ga(this.settings);return e.setSerdeContext(this.serdeContext),e}},Qa=class extends Un{codec;serializer;deserializer;mixin=new _a;constructor(e){super(e);let t={timestampFormat:{useTrait:!0,default:5},httpBindings:!0,xmlNamespace:e.xmlNamespace,serviceNamespace:e.defaultNamespace};this.codec=new Za(t),this.serializer=new Yn(this.codec.createSerializer(),t),this.deserializer=new qn(this.codec.createDeserializer(),t)}getPayloadCodec(){return this.codec}getShapeId(){return`aws.protocols#restXml`}async serializeRequest(e,t,n){let r=await super.serializeRequest(e,t,n),i=h.of(e.input);if(!r.headers[`content-type`]){let e=this.mixin.resolveRestContentType(this.getDefaultContentType(),i);e&&(r.headers[`content-type`]=e)}return typeof r.body==`string`&&r.headers[`content-type`]===this.getDefaultContentType()&&!r.body.startsWith(``+r.body),r}async deserializeResponse(e,t,n){return super.deserializeResponse(e,t,n)}async handleError(e,t,n,r,i){let a=Ya(n,r)??`Unknown`;if(this.mixin.compose(this.compositeErrorRegistry,a,this.options.defaultNamespace),r.Error&&typeof r.Error==`object`)for(let e of Object.keys(r.Error))r[e]=r.Error[e],e.toLowerCase()===`message`&&(r.message=r.Error[e]);r.RequestId&&!i.requestId&&(i.requestId=r.RequestId);let{errorSchema:o,errorMetadata:s}=await this.mixin.getErrorSchemaOrThrowBaseException(a,this.options.defaultNamespace,n,r,i),c=h.of(o),l=r.Error?.message??r.Error?.Message??r.message??r.Message??`UnknownError`,u=new((this.compositeErrorRegistry.getErrorCtor(o))??Error)({});await this.deserializeHttpMessage(o,t,n,r);let d={},f=this.codec.createDeserializer();for(let[e,t]of c.structIterator()){let n=t.getMergedTraits().xmlName??e,i=r.Error?.[n]??r[n];d[e]=f.readSchema(t,i)}throw this.mixin.decorateServiceException(Object.assign(u,s,{$fault:c.getMergedTraits().error,message:l},d),r)}getDefaultContentType(){return`application/xml`}hasUnstructuredPayloadBinding(e){for(let[,t]of e.structIterator())if(t.getMergedTraits().httpPayload)return!(t.isStructSchema()||t.isMapSchema()||t.isListSchema());return!1}},$a=`ref`,eo=-1,to=!0,no=`isSet`,ro=`PartitionResult`,io=`booleanEquals`,ao=`getAttr`,oo={[$a]:`Endpoint`},so={[$a]:ro},co={},lo=[{[$a]:`Region`}],uo={conditions:[[no,[oo]],[no,lo],[`aws.partition`,lo,ro],[io,[{[$a]:`UseFIPS`},to]],[io,[{[$a]:`UseDualStack`},to]],[io,[{fn:ao,argv:[so,`supportsDualStack`]},to]],[io,[{fn:ao,argv:[so,`supportsFIPS`]},to]],[`stringEquals`,[{fn:ao,argv:[so,`name`]},`aws-us-gov`]]],results:[[eo],[eo,`Invalid Configuration: FIPS and custom endpoint are not supported`],[eo,`Invalid Configuration: Dualstack and custom endpoint are not supported`],[oo,co],[`https://acm-fips.{Region}.{PartitionResult#dualStackDnsSuffix}`,co],[eo,`FIPS and DualStack are enabled, but this partition does not support one or both`],[`https://acm.{Region}.amazonaws.com`,co],[`https://acm-fips.{Region}.{PartitionResult#dnsSuffix}`,co],[eo,`FIPS is enabled but this partition does not support FIPS`],[`https://acm.{Region}.{PartitionResult#dualStackDnsSuffix}`,co],[eo,`DualStack is enabled but this partition does not support DualStack`],[`https://acm.{Region}.{PartitionResult#dnsSuffix}`,co],[eo,`Invalid Configuration: Missing Region`]]},fo=2,po=new Int32Array([-1,1,-1,0,13,3,1,4,100000012,2,5,100000012,3,8,6,4,7,100000011,5,100000009,100000010,4,11,9,6,10,100000008,7,100000006,100000007,5,12,100000005,6,100000004,100000005,3,100000001,14,4,100000002,100000003]),mo=on.from(po,fo,uo.conditions,uo.results),ho=new sn({size:50,params:[`Endpoint`,`Region`,`UseDualStack`,`UseFIPS`]}),go=(e,t={})=>ho.get(e,()=>jn(mo,{endpointParams:e,logger:t.logger}));dn.aws=ji;var _o=class e extends Ht{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},vo=class e extends _o{name=`AccessDeniedException`;$fault=`client`;Message;constructor(t){super({name:`AccessDeniedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},yo=class e extends _o{name=`InvalidArnException`;$fault=`client`;constructor(t){super({name:`InvalidArnException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},bo=class e extends _o{name=`InvalidParameterException`;$fault=`client`;constructor(t){super({name:`InvalidParameterException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},xo=class e extends _o{name=`InvalidTagException`;$fault=`client`;constructor(t){super({name:`InvalidTagException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},fee=class e extends _o{name=`ResourceNotFoundException`;$fault=`client`;constructor(t){super({name:`ResourceNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},pee=class e extends _o{name=`TagPolicyException`;$fault=`client`;constructor(t){super({name:`TagPolicyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},mee=class e extends _o{name=`ThrottlingException`;$fault=`client`;throttlingReasons;constructor(t){super({name:`ThrottlingException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.throttlingReasons=t.throttlingReasons}},hee=class e extends _o{name=`TooManyTagsException`;$fault=`client`;constructor(t){super({name:`TooManyTagsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},gee=class e extends _o{name=`ConflictException`;$fault=`client`;constructor(t){super({name:`ConflictException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},_ee=class e extends _o{name=`ResourceInUseException`;$fault=`client`;constructor(t){super({name:`ResourceInUseException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},vee=class e extends _o{name=`RequestInProgressException`;$fault=`client`;constructor(t){super({name:`RequestInProgressException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},yee=class e extends _o{name=`LimitExceededException`;$fault=`client`;constructor(t){super({name:`LimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},bee=class e extends _o{name=`InvalidArgsException`;$fault=`client`;constructor(t){super({name:`InvalidArgsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},xee=class e extends _o{name=`ValidationException`;$fault=`client`;constructor(t){super({name:`ValidationException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},See=class e extends _o{name=`InvalidDomainValidationOptionsException`;$fault=`client`;constructor(t){super({name:`InvalidDomainValidationOptionsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Cee=class e extends _o{name=`InvalidStateException`;$fault=`client`;constructor(t){super({name:`InvalidStateException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},wee=`AccessDeniedException`,Tee=`Certificate`,Eee=`CreatedAt`,So=`CertificateArn`,Dee=`CertificateChain`,Oee=`ConflictException`,kee=`CertificateSummary`,Co=`CertificateSummaryList`,Aee=`CertificateStatuses`,jee=`DomainName`,Mee=`Exported`,Nee=`ExtendedKeyUsages`,Pee=`ExportOption`,Fee=`Filters`,Iee=`HasAdditionalSubjectAlternativeNames`,Lee=`ImportedAt`,Ree=`InvalidArgsException`,zee=`InvalidArnException`,Bee=`IssuedAt`,Vee=`ImportCertificate`,Hee=`ImportCertificateRequest`,Uee=`ImportCertificateResponse`,Wee=`InvalidDomainValidationOptionsException`,Gee=`InvalidParameterException`,Kee=`InvalidStateException`,qee=`InvalidTagException`,Jee=`InUse`,Yee=`Includes`,Xee=`Key`,Zee=`KeyAlgorithm`,Qee=`KeyUsages`,$ee=`ListCertificates`,ete=`ListCertificatesRequest`,tte=`ListCertificatesResponse`,nte=`LimitExceededException`,rte=`Message`,ite=`ManagedBy`,ate=`MaxItems`,ote=`NotAfter`,ste=`NotBefore`,wo=`NextToken`,cte=`PrivateKey`,lte=`PrivateKeyBlob`,ute=`RevokedAt`,dte=`RenewalEligibility`,fte=`RequestInProgressException`,pte=`ResourceInUseException`,mte=`ResourceNotFoundException`,hte=`Status`,gte=`SubjectAlternativeNameSummaries`,_te=`SortBy`,vte=`SortOrder`,yte=`Type`,bte=`ThrottlingException`,xte=`TagList`,Ste=`TooManyTagsException`,Cte=`TagPolicyException`,wte=`ThrottlingReason`,Tte=`ThrottlingReasonList`,Ete=`Tags`,Dte=`Tag`,Ote=`Value`,kte=`ValidationException`,To=`awsQueryError`,Eo=`client`,Do=`error`,Ate=`extendedKeyUsage`,jte=`exportOption`,Oo=`httpError`,Mte=`keyTypes`,Nte=`keyUsage`,ko=`message`,Pte=`managedBy`,Fte=`reason`,Ite=`resource`,Ao=`smithy.ts.sdk.synthetic.com.amazonaws.acm`,Lte=`throttlingReasons`,g=`com.amazonaws.acm`,jo=Lt.for(Ao),Rte=[-3,Ao,`ACMServiceException`,0,[],[]];jo.registerError(Rte,_o);var Mo=Lt.for(g),zte=[-3,g,wee,{[To]:[`AccessDenied`,403],[Do]:Eo,[Oo]:403},[rte],[0]];Mo.registerError(zte,vo);var Bte=[-3,g,Oee,{[Do]:Eo},[ko],[0]];Mo.registerError(Bte,gee);var Vte=[-3,g,Ree,{[Do]:Eo},[ko],[0]];Mo.registerError(Vte,bee);var Hte=[-3,g,zee,{[Do]:Eo},[ko],[0]];Mo.registerError(Hte,yo);var Ute=[-3,g,Wee,{[Do]:Eo},[ko],[0]];Mo.registerError(Ute,See);var Wte=[-3,g,Gee,{[Do]:Eo},[ko],[0]];Mo.registerError(Wte,bo);var Gte=[-3,g,Kee,{[Do]:Eo},[ko],[0]];Mo.registerError(Gte,Cee);var Kte=[-3,g,qee,{[Do]:Eo},[ko],[0]];Mo.registerError(Kte,xo);var qte=[-3,g,nte,{[Do]:Eo},[ko],[0]];Mo.registerError(qte,yee);var Jte=[-3,g,fte,{[Do]:Eo},[ko],[0]];Mo.registerError(Jte,vee);var Yte=[-3,g,pte,{[Do]:Eo},[ko],[0]];Mo.registerError(Yte,_ee);var Xte=[-3,g,mte,{[Do]:Eo},[ko],[0]];Mo.registerError(Xte,fee);var Zte=[-3,g,Cte,{[Do]:Eo},[ko],[0]];Mo.registerError(Zte,pee);var Qte=[-3,g,bte,{[To]:[`Throttling`,400],[Do]:Eo,[Oo]:400},[ko,Lte],[0,()=>Ko]];Mo.registerError(Qte,mee);var $te=[-3,g,Ste,{[Do]:Eo},[ko],[0]];Mo.registerError($te,hee);var No=[-3,g,kte,{[To]:[`ValidationError`,400],[Do]:Eo,[Oo]:400},[ko],[0]];Mo.registerError(No,xee);var Po=[jo,Mo],Fo=[0,g,lte,8,21],Io=[3,g,kee,0,[So,jee,gte,Iee,hte,yte,Zee,Qee,Nee,Pee,Jee,Mee,dte,ste,ote,Eee,Bee,Lee,ute,ite],[0,0,64,2,0,0,0,64,64,0,2,2,0,4,4,4,4,4,4,0]],Lo=[3,g,Fee,0,[Ate,Nte,Mte,jte,Pte],[64,64,64,0,0]],Ro=[3,g,Hee,0,[Tee,cte,So,Dee,Ete],[21,[()=>Fo,0],0,21,()=>Go],2],zo=[3,g,Uee,0,[So],[0]],Bo=[3,g,ete,0,[Aee,Yee,wo,ate,_te,vte],[64,()=>Lo,0,1,0,0]],Vo=[3,g,tte,0,[wo,Co],[0,()=>Wo]],Ho=[3,g,Dte,0,[Xee,Ote],[0,0],1],Uo=[3,g,wte,0,[Fte,Ite],[0,0]],Wo=[1,g,Co,0,()=>Io],Go=[1,g,xte,0,()=>Ho],Ko=[1,g,Tte,0,()=>Uo],qo=[9,g,Vee,0,()=>Ro,()=>zo],Jo=[9,g,$ee,0,()=>Bo,()=>Vo],Yo=e=>({apiVersion:`2015-12-08`,base64Decoder:e?.base64Decoder??fe,base64Encoder:e?.base64Encoder??le,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??go,extensions:e?.extensions??[],httpAuthSchemeProvider:e?.httpAuthSchemeProvider??ra,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new Yi}],logger:e?.logger??new $t,protocol:e?.protocol??Na,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.acm`,errorTypeRegistries:Po,version:`2015-12-08`,serviceTarget:`CertificateManager`},serviceId:e?.serviceId??`ACM`,urlParser:e?.urlParser??nt,utf8Decoder:e?.utf8Decoder??ce,utf8Encoder:e?.utf8Encoder??me}),Xo=e=>{let t=zi(e),n=()=>t().then(Wt),r=Yo(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??Ee,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??Ei({serviceId:r.serviceId,clientVersion:sa.version}),maxAttempts:e?.maxAttempts??3,region:e?.region??yt(`Region is missing`),requestHandler:da.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||Nr),sha256:e?.sha256??ee,streamCollector:e?.streamCollector??pa,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},Zo=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},Qo=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),$o=(e,t)=>{let n=Object.assign(Vi(e),Xt(e),Xn(e),Zo(e));return t.forEach(e=>e.configure(n)),Object.assign(e,Hi(n),Zt(n),Zn(n),Qo(n))},es=class extends bt{config;constructor(...[e]){let t=Xo(e||{});super(t),this.initConfig=t;let n=$o(ia(Nn(zr(Li(Ir(li(aa(t))))))),e?.extensions||[]);this.config=n,this.middlewareStack.use(Ot(this.config)),this.middlewareStack.use(Ti(this.config)),this.middlewareStack.use(Lr(this.config)),this.middlewareStack.use(tr(this.config)),this.middlewareStack.use(Hr(this.config)),this.middlewareStack.use(Gr(this.config)),this.middlewareStack.use(Kr(this.config)),this.middlewareStack.use(Zr(this.config,{httpAuthSchemeParametersProvider:ta,identityProviderConfigProvider:async e=>new ii({"aws.auth#sigv4":e.credentials})})),this.middlewareStack.use(ni(this.config))}destroy(){super.destroy()}},ts=async(e,t,n)=>({operation:Ze(t).operation,region:await re(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()});function ns(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`ec2`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var rs=e=>{let t=[];switch(e.operation){default:t.push(ns(e))}return t},is=e=>{let t=Qi(e);return Object.assign(t,{authSchemePreference:re(e.authSchemePreference??[])})},as=e=>Object.assign(e,{useDualstackEndpoint:e.useDualstackEndpoint??!1,useFipsEndpoint:e.useFipsEndpoint??!1,defaultSigningName:`ec2`}),os={UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},ss={name:`@aws-sdk/client-ec2`,description:`AWS SDK for JavaScript Ec2 Client for Node.js, Browser and React Native`,version:`3.1063.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-ec2`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo ec2`,test:`yarn g:vitest run --passWithNoTests`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`,"test:e2e":`yarn g:vitest run -c vitest.config.e2e.mts --mode development`,"test:e2e:watch":`yarn g:vitest watch -c vitest.config.e2e.mts`,"test:integration":`yarn g:vitest run --passWithNoTests -c vitest.config.integ.mts`,"test:integration:watch":`yarn g:vitest run --passWithNoTests -c vitest.config.integ.mts`,"test:watch":`yarn g:vitest watch --passWithNoTests`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.18`,"@aws-sdk/credential-provider-node":`^3.972.52`,"@aws-sdk/middleware-sdk-ec2":`^3.972.32`,"@aws-sdk/types":`^3.973.11`,"@smithy/core":`^3.24.6`,"@smithy/fetch-http-handler":`^5.4.6`,"@smithy/node-http-handler":`^4.7.6`,"@smithy/types":`^4.14.3`,tslib:`^2.6.2`},devDependencies:{"@smithy/snapshot-testing":`^2.1.7`,"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`,vitest:`^4.0.17`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/sdk-for-javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-ec2`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-ec2`}},cs=`ref`,ls=-1,us=!0,ds=`isSet`,fs=`PartitionResult`,ps=`booleanEquals`,ms=`getAttr`,hs={[cs]:`Endpoint`},gs={[cs]:fs},_s={},vs=[{[cs]:`Region`}],ys={conditions:[[ds,[hs]],[ds,vs],[`aws.partition`,vs,fs],[ps,[{[cs]:`UseFIPS`},us]],[ps,[{[cs]:`UseDualStack`},us]],[ps,[{fn:ms,argv:[gs,`supportsDualStack`]},us]],[ps,[{fn:ms,argv:[gs,`supportsFIPS`]},us]],[`stringEquals`,[{fn:ms,argv:[gs,`name`]},`aws-us-gov`]]],results:[[ls],[ls,`Invalid Configuration: FIPS and custom endpoint are not supported`],[ls,`Invalid Configuration: Dualstack and custom endpoint are not supported`],[hs,_s],[`https://ec2-fips.{Region}.{PartitionResult#dualStackDnsSuffix}`,_s],[ls,`FIPS and DualStack are enabled, but this partition does not support one or both`],[`https://ec2.{Region}.amazonaws.com`,_s],[`https://ec2-fips.{Region}.{PartitionResult#dnsSuffix}`,_s],[ls,`FIPS is enabled but this partition does not support FIPS`],[`https://ec2.{Region}.{PartitionResult#dualStackDnsSuffix}`,_s],[ls,`DualStack is enabled but this partition does not support DualStack`],[`https://ec2.{Region}.{PartitionResult#dnsSuffix}`,_s],[ls,`Invalid Configuration: Missing Region`]]},bs=2,xs=new Int32Array([-1,1,-1,0,13,3,1,4,100000012,2,5,100000012,3,8,6,4,7,100000011,5,100000009,100000010,4,11,9,6,10,100000008,7,100000006,100000007,5,12,100000005,6,100000004,100000005,3,100000001,14,4,100000002,100000003]),Ss=on.from(xs,bs,ys.conditions,ys.results),Cs=new sn({size:50,params:[`Endpoint`,`Region`,`UseDualStack`,`UseFIPS`]}),ws=(e,t={})=>Cs.get(e,()=>jn(Ss,{endpointParams:e,logger:t.logger}));dn.aws=ji;var Ts=class e extends Ht{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},Es=`Address`,Ds=`AllocateAddressRequest`,Os=`AllocateAddressResult`,ks=`AssociateAddressRequest`,As=`AssociateAddressResult`,js=`AllAvailabilityZones`,Ms=`AllocateAddress`,Ns=`ApplianceArn`,Ps=`AssociateAddress`,Fs=`AttachedAppliances`,Is=`AttributeBooleanValue`,Ls=`AssociateCarrierIpAddress`,Rs=`AdditionalFlexibleNetworkInterfaces`,zs=`AvailableIpAddressCount`,Bs=`AssignIpv6AddressOnCreation`,Vs=`AttachInternetGateway`,Hs=`AttachInternetGatewayRequest`,Us=`AssociateIamInstanceProfile`,Ws=`AssociateIamInstanceProfileRequest`,Gs=`AssociateIamInstanceProfileResult`,Ks=`AllocationIdList`,qs=`AssociationIdList`,Js=`AdditionalInfo`,Ys=`AllocationId`,Xs=`AllocationIds`,Zs=`AssociationId`,Qs=`AssociationIds`,$s=`AttachmentId`,ec=`AmiLaunchIndex`,tc=`AttachmentLimitType`,nc=`AddressList`,rc=`AvailabilityMode`,ic=`AssociatePublicIpAddress`,ac=`AmazonProvidedIpv6CidrBlock`,oc=`AutoProvisionZones`,sc=`AutoRecoverySupported`,cc=`AssociateRouteTableRequest`,lc=`AssociateRouteTableResult`,uc=`AssociateRouteTable`,dc=`AllowReassociation`,fc=`AllRegions`,pc=`AssociatedResource`,mc=`AutoRecovery`,hc=`AssociationState`,gc=`AuthorizeSecurityGroupEgress`,_c=`AuthorizeSecurityGroupEgressRequest`,vc=`AuthorizeSecurityGroupEgressResult`,yc=`AuthorizeSecurityGroupIngressRequest`,bc=`AuthorizeSecurityGroupIngressResult`,xc=`AuthorizeSecurityGroupIngress`,Sc=`AutoScalingIps`,Cc=`AmdSevSnp`,wc=`AttachmentState`,Tc=`ArchitectureTypeList`,Ec=`AttachTime`,Dc=`AttachVolumeRequest`,Oc=`AttributeValue`,kc=`AttachVolume`,Ac=`AvailabilityZone`,jc=`AvailabilityZoneAddress`,Mc=`AvailabilityZoneAddresses`,Nc=`AvailabilityZoneGeography`,Pc=`AvailabilityZoneGeographyList`,Fc=`AvailabilityZoneId`,Ic=`AvailabilityZoneList`,Lc=`AvailabilityZoneMessage`,Rc=`AvailabilityZoneMessageList`,zc=`AvailabilityZoneSubGeography`,Bc=`AvailabilityZoneSubGeographyList`,Vc=`AvailabilityZones`,Hc=`Accelerators`,Uc=`Addresses`,Wc=`Affinity`,Gc=`Arn`,Kc=`Architecture`,qc=`Associations`,Jc=`Association`,Yc=`Attribute`,Xc=`Attachment`,Zc=`Attachments`,Qc=`BlobAttributeValue`,$c=`BaselineBandwidthInGbps`,el=`BaselineBandwidthInMbps`,tl=`BlockDeviceMapping`,nl=`BlockDeviceMappingList`,rl=`BlockDeviceMappingRequestList`,il=`BlockDeviceMappings`,al=`BlockDurationMinutes`,ol=`BaselineIops`,sl=`BootMode`,cl=`BootModeTypeList`,ll=`BareMetal`,ul=`BlockPublicAccessStates`,dl=`BurstablePerformanceSupported`,fl=`BaselineThroughputInMBps`,pl=`BandwidthWeighting`,ml=`BandwidthWeightingTypeList`,hl=`BandwidthWeightings`,gl=`CidrBlock`,_l=`CidrBlockAssociationSet`,vl=`CapacityBlockId`,yl=`CidrBlockState`,bl=`CoreCountList`,xl=`CoreCount`,Sl=`CpuCredits`,Cl=`CompletionDurationMinutes`,wl=`CreationDate`,Tl=`CarrierGatewayId`,El=`CurrentGeneration`,Dl=`CarrierIp`,Ol=`CurrentInstanceBootMode`,kl=`CreateInternetGateway`,Al=`CreateInternetGatewayRequest`,jl=`CreateInternetGatewayResult`,Ml=`CreateImageRequest`,Nl=`CreateImageResult`,Pl=`CidrIp`,Fl=`CidrIpv6`,Il=`CidrIpv4`,Ll=`CoreInfo`,Rl=`CreateImage`,zl=`CreateKeyPair`,Bl=`CreateKeyPairRequest`,Vl=`CoreNetworkArn`,Hl=`CreateNatGateway`,Ul=`CreateNatGatewayRequest`,Wl=`CreateNatGatewayResult`,Gl=`CpuOptions`,Kl=`CustomerOwnedIp`,ql=`CustomerOwnedIpv4Pool`,Jl=`CpuOptionsRequest`,Yl=`CreatePlacementGroup`,Xl=`CreatePlacementGroupRequest`,Zl=`CreatePlacementGroupResult`,Ql=`CapacityReservationId`,$l=`CapacityReservationPreference`,eu=`CreateRouteRequest`,tu=`CapacityReservationResourceGroupArn`,nu=`CreateRouteResult`,ru=`CapacityReservationSpecificationResponse`,iu=`CapacityReservationSpecification`,au=`CapacityReservationTarget`,ou=`CapacityReservationTargetResponse`,su=`CreateRouteTableRequest`,cu=`CreateRouteTableResult`,lu=`CreateRouteTable`,uu=`CreateRoute`,du=`CreateSecurityGroup`,fu=`CreateSecurityGroupRequest`,pu=`CreateSecurityGroupResult`,mu=`CopySnapshotRequest`,hu=`CopySnapshotRequestPSU`,gu=`CopySnapshotResult`,_u=`CreateSnapshotRequest`,vu=`CreateSubnetRequest`,yu=`CreateSubnetResult`,bu=`CreditSpecificationRequest`,xu=`CopySnapshot`,Su=`CreditSpecification`,Cu=`CreateSnapshot`,wu=`CreateSubnet`,Tu=`CurrentState`,Eu=`ClientToken`,Du=`ConnectionTrackingConfiguration`,Ou=`ConnectionTrackingSpecification`,ku=`ConnectionTrackingSpecificationRequest`,Au=`ConnectionTrackingSpecificationResponse`,ju=`CompletionTime`,Mu=`ConnectivityType`,Nu=`CreateTime`,Pu=`CreateVolumeRequest`,Fu=`CreateVpcRequest`,Iu=`CreateVpcResult`,Lu=`CreateVolume`,Ru=`CreateVpc`,zu=`Code`,Bu=`Configured`,Vu=`Count`,Hu=`DescribeAddressesRequest`,Uu=`DescribeAddressesResult`,Wu=`DisassociateAddressRequest`,Gu=`DisableApiStop`,Ku=`DisableApiTermination`,qu=`DestinationAvailabilityZone`,Ju=`DescribeAvailabilityZonesRequest`,Yu=`DescribeAvailabilityZonesResult`,Xu=`DescribeAvailabilityZones`,Zu=`DescribeAddresses`,Qu=`DisassociateAddress`,$u=`DestinationCidrBlock`,ed=`DefaultConnectionTrackingConfiguration`,td=`DefaultCores`,nd=`DataEncryptionKeyId`,rd=`DefaultEnaQueueCountPerInterface`,id=`DefaultForAz`,ad=`DedicatedHostsSupported`,od=`DeviceIndex`,sd=`DestinationIpv6CidrBlock`,cd=`DeleteInternetGateway`,ld=`DeleteInternetGatewayRequest`,ud=`DescribeInternetGatewaysRequest`,dd=`DescribeInternetGatewaysResult`,fd=`DetachInternetGatewayRequest`,pd=`DescribeInternetGateways`,md=`DetachInternetGateway`,hd=`DisassociateIamInstanceProfile`,gd=`DescribeIamInstanceProfileAssociations`,_d=`DescribeIamInstanceProfileAssociationsRequest`,vd=`DescribeIamInstanceProfileAssociationsResult`,yd=`DisassociateIamInstanceProfileRequest`,bd=`DisassociateIamInstanceProfileResult`,xd=`DiskInfoList`,Sd=`DescribeImagesRequest`,Cd=`DescribeImagesResult`,wd=`DescribeInstancesRequest`,Td=`DescribeInstancesResult`,Ed=`DescribeInstanceTypesRequest`,Dd=`DescribeInstanceTypesResult`,Od=`DescribeInstanceTypes`,kd=`DescribeImages`,Ad=`DescribeInstances`,jd=`DiskInfo`,Md=`DeleteKeyPair`,Nd=`DeleteKeyPairRequest`,Pd=`DeleteKeyPairResult`,Fd=`DescribeKeyPairsRequest`,Id=`DescribeKeyPairsResult`,Ld=`DescribeKeyPairs`,Rd=`DisableLniAtDeviceIndex`,zd=`DeviceName`,Bd=`DefaultNetworkCardIndex`,Vd=`DeleteNatGateway`,Hd=`DeleteNatGatewayRequest`,Ud=`DeleteNatGatewayResult`,Wd=`DescribeNatGatewaysRequest`,Gd=`DescribeNatGatewaysResult`,Kd=`DescribeNatGateways`,qd=`DestinationOutpostArn`,Jd=`DhcpOptionsId`,Yd=`DeleteOnTermination`,Xd=`DeletePlacementGroup`,Zd=`DeletePlacementGroupRequest`,Qd=`DescribePlacementGroupsRequest`,$d=`DescribePlacementGroupsResult`,ef=`DescribePlacementGroups`,tf=`DestinationPrefixListId`,nf=`DeregistrationProtection`,_=`DryRun`,rf=`DeleteRouteRequest`,af=`DescribeRegionsRequest`,of=`DescribeRegionsResult`,sf=`DeleteRouteTable`,cf=`DeleteRouteTableRequest`,lf=`DescribeRouteTablesRequest`,uf=`DescribeRouteTablesResult`,df=`DisassociateRouteTableRequest`,ff=`DescribeRouteTables`,pf=`DisassociateRouteTable`,mf=`DestinationRegion`,hf=`DeleteRoute`,gf=`DescribeRegions`,_f=`DeleteSecurityGroup`,vf=`DeleteSecurityGroupRequest`,yf=`DeleteSecurityGroupResult`,bf=`DescribeSecurityGroupsRequest`,xf=`DescribeSecurityGroupsResult`,Sf=`DescribeSecurityGroups`,Cf=`DeleteSnapshotRequest`,wf=`DeleteSubnetRequest`,Tf=`DescribeSnapshotsRequest`,Ef=`DescribeSnapshotsResult`,Df=`DescribeSubnetsRequest`,Of=`DescribeSubnetsResult`,kf=`DeleteSnapshot`,Af=`DeleteSubnet`,jf=`DescribeSnapshots`,Mf=`DescribeSubnets`,Nf=`DefaultTcpEstablishedTimeout`,Pf=`DefaultThreadsPerCore`,Ff=`DeprecationTime`,If=`DeleteTime`,Lf=`DefaultUdpStreamTimeout`,Rf=`DefaultUdpTimeout`,zf=`DefaultVCpus`,Bf=`DeleteVolumeRequest`,Vf=`DeleteVpcRequest`,Hf=`DescribeVolumesRequest`,Uf=`DescribeVolumesResult`,Wf=`DescribeVpcsRequest`,Gf=`DescribeVpcsResult`,Kf=`DetachVolumeRequest`,qf=`DeleteVolume`,Jf=`DeleteVpc`,Yf=`DescribeVolumes`,Xf=`DescribeVpcs`,Zf=`DetachVolume`,Qf=`Description`,$f=`Device`,ep=`Disks`,tp=`Domain`,np=`EbsBlockDevice`,rp=`ExecutableByStringList`,ip=`ExecutableBy`,ap=`EbsCardIndex`,op=`EbsCardInfoList`,sp=`EbsCardInfo`,cp=`EbsCards`,lp=`EncryptionControl`,up=`EnableDns64`,dp=`ElasticFileSystem`,fp=`ElasticFileSystemExclusion`,pp=`ElasticGpuAssociation`,mp=`ElasticGpuAssociationId`,hp=`ElasticGpuAssociationList`,gp=`ElasticGpuAssociationState`,_p=`ElasticGpuAssociationTime`,vp=`ElasticGpuAssociations`,yp=`ElasticGpuId`,bp=`ElasticGpuSpecification`,xp=`ElasticGpuSpecifications`,Sp=`ElasticInferenceAccelerator`,Cp=`ElasticInferenceAcceleratorAssociation`,wp=`ElasticInferenceAcceleratorAssociationId`,Tp=`ElasticInferenceAcceleratorAssociationList`,Ep=`ElasticInferenceAcceleratorAssociationState`,Dp=`ElasticInferenceAcceleratorAssociationTime`,Op=`ElasticInferenceAcceleratorArn`,kp=`ElasticInferenceAcceleratorAssociations`,Ap=`ElasticInferenceAccelerators`,jp=`EbsInstanceBlockDevice`,Mp=`EbsInstanceBlockDeviceSpecification`,Np=`EncryptionInTransitSupported`,Pp=`EbsInfo`,Fp=`EfaInfo`,Ip=`EnableLniAtDeviceIndex`,Lp=`EbsOptimized`,Rp=`EbsOptimizedInfo`,zp=`EgressOnlyInternetGateway`,Bp=`EgressOnlyInternetGatewayExclusion`,Vp=`EgressOnlyInternetGatewayId`,Hp=`EnclaveOptionsRequest`,Up=`EbsOptimizedSupport`,Wp=`EnclaveOptions`,Gp=`EnablePrimaryIpv6`,Kp=`EnaQueueCount`,qp=`EnableResourceNameDnsAAAARecord`,Jp=`EnableResourceNameDnsAAAARecordOnLaunch`,Yp=`EnableResourceNameDnsARecord`,Xp=`EnableResourceNameDnsARecordOnLaunch`,Zp=`EnaSrdEnabled`,Qp=`EnaSrdSpecification`,$p=`EnaSrdSpecificationRequest`,em=`EnaSrdSupported`,tm=`EnaSrdUdpEnabled`,nm=`EnaSrdUdpSpecification`,rm=`EnaSrdUdpSpecificationRequest`,im=`EfaSupported`,am=`EncryptionSupport`,om=`EnaSupport`,sm=`EndTime`,cm=`ExecutableUsers`,lm=`Ebs`,um=`Enabled`,dm=`Encrypted`,fm=`Endpoint`,pm=`Force`,mm=`FailureCode`,hm=`FpgaDeviceInfo`,gm=`FpgaDeviceInfoList`,_m=`FpgaDeviceMemoryInfo`,vm=`FlexibleEnaQueuesSupport`,ym=`FpgaInfo`,bm=`FilterList`,xm=`FailureMessage`,Sm=`FromPort`,Cm=`FastRestored`,wm=`FullSnapshotSizeInBytes`,Tm=`FreeTierEligible`,Em=`Filters`,Dm=`Filter`,Om=`Fpgas`,km=`Groups`,Am=`GroupArn`,jm=`GetConsoleOutput`,Mm=`GetConsoleOutputRequest`,Nm=`GetConsoleOutputResult`,Pm=`GroupDescription`,Fm=`GpuDeviceInfo`,Im=`GpuDeviceInfoList`,Lm=`GpuDeviceMemoryInfo`,Rm=`GatewayId`,zm=`GroupIdentifierList`,Bm=`GroupIdStringList`,Vm=`GpuInfo`,Hm=`GroupId`,Um=`GroupIds`,Wm=`GroupIdentifier`,Gm=`GroupLongName`,Km=`GroupName`,qm=`GroupNameStringList`,Jm=`GroupNames`,Ym=`GroupOwnerId`,Xm=`GpuPartitionSize`,Zm=`Geography`,Qm=`Gpus`,$m=`HiddenByDefault`,eh=`HttpEndpoint`,th=`HostId`,nh=`HibernationOptions`,rh=`HibernationOptionsRequest`,ih=`HttpProtocolIpv6`,ah=`HttpPutResponseHopLimit`,oh=`HostResourceGroupArn`,sh=`HibernationSupported`,ch=`HttpTokens`,lh=`HostnameType`,uh=`Hibernate`,dh=`Hypervisor`,fh=`Id`,ph=`Ipv6Addresses`,mh=`Ipv6AddressAttribute`,hh=`Ipv6AddressCount`,gh=`InstanceAttachmentEnaSrdSpecification`,_h=`InstanceAttachmentEnaSrdUdpSpecification`,vh=`InferenceAcceleratorInfo`,yh=`Ipv4AddressesPerInterface`,bh=`Ipv6AddressesPerInterface`,xh=`Ipv4AddressesPerSecondaryInterface`,Sh=`ImageAllowed`,Ch=`IpAddress`,wh=`Ipv6Address`,Th=`InstanceBlockDeviceMapping`,Eh=`InstanceBlockDeviceMappingList`,Dh=`InstanceBlockDeviceMappingSpecification`,Oh=`InstanceBlockDeviceMappingSpecificationList`,kh=`Ipv6CidrBlock`,Ah=`Ipv6CidrBlockAssociationSet`,jh=`Ipv6CidrBlockNetworkBorderGroup`,Mh=`Ipv6CidrBlockState`,Nh=`IncludeDeprecated`,Ph=`InferenceDeviceInfo`,Fh=`InferenceDeviceInfoList`,Ih=`InferenceDeviceMemoryInfo`,Lh=`IncludeDisabled`,Rh=`IsDefault`,zh=`IsEgress`,Bh=`InternetGateway`,Vh=`InternetGatewayAttachment`,Hh=`InternetGatewayAttachmentList`,Uh=`InternetGatewayBlockMode`,Wh=`InternetGatewayExclusion`,Gh=`InternetGatewayId`,Kh=`InternetGatewayIdList`,qh=`InternetGatewayIds`,Jh=`InternetGatewayList`,Yh=`InternetGateways`,v=`InstanceId`,Xh=`InstanceIpv6Address`,Zh=`InstanceIpv6AddressList`,Qh=`InstanceInterruptionBehavior`,$h=`IamInstanceProfile`,eg=`IamInstanceProfileAssociation`,tg=`IamInstanceProfileAssociationSet`,ng=`IamInstanceProfileAssociations`,rg=`Ipv6IpamPoolId`,ig=`Ipv4IpamPoolId`,ag=`InstanceIpv4PrefixList`,og=`InstanceIpv6PrefixList`,sg=`IamInstanceProfileSpecification`,cg=`InstanceIpv4Prefix`,lg=`InstanceIpv6Prefix`,ug=`InstanceInitiatedShutdownBehavior`,dg=`ImageIdStringList`,ene=`InstanceIdStringList`,fg=`ImageId`,tne=`ImageIds`,pg=`InstanceIds`,nne=`ImportKeyPair`,rne=`ImportKeyPairRequest`,ine=`ImportKeyPairResult`,ane=`ImageLocation`,one=`ImageList`,sne=`InstanceLifecycle`,cne=`InstanceList`,lne=`IpList`,une=`InstanceMaintenanceOptions`,dne=`InstanceMaintenanceOptionsRequest`,fne=`InstanceMarketOptionsRequest`,pne=`InstanceMetadataOptionsRequest`,mne=`InstanceMetadataOptionsResponse`,hne=`InstanceMarketOptions`,mg=`IncludeManagedResources`,hg=`InstanceMetadataTags`,gg=`Ipv6Native`,gne=`InstanceNetworkInterface`,_ne=`InstanceNetworkInterfaceAssociation`,vne=`InstanceNetworkInterfaceAttachment`,yne=`InstanceNetworkInterfaceList`,bne=`InstanceNetworkInterfaceSpecification`,xne=`InstanceNetworkInterfaceSpecificationList`,_g=`Ipv6NetmaskLength`,vg=`Ipv4NetmaskLength`,Sne=`InstanceNetworkPerformanceOptions`,Cne=`InstanceNetworkPerformanceOptionsRequest`,wne=`ImageOwnerAlias`,Tne=`IpOwnerId`,Ene=`InstanceOwnerId`,Dne=`InstanceOwningService`,One=`Ipv6PrefixCount`,kne=`Ipv4PrefixCount`,Ane=`IpPermissionsEgress`,jne=`IpamPoolId`,Mne=`InstancePrivateIpAddress`,Nne=`InstancePrivateIpAddressList`,Pne=`IsPrimaryIpv6`,Fne=`IncludePublicKey`,Ine=`IpPermissionList`,Lne=`Ipv4PrefixList`,Rne=`Ipv6PrefixList`,zne=`Ipv4PrefixSpecificationRequest`,Bne=`Ipv6PrefixSpecificationRequest`,yg=`Ipv6Prefixes`,bg=`IpPermissions`,Vne=`IpPermission`,xg=`IpProtocol`,Sg=`Ipv6Prefix`,Cg=`Ipv6Pool`,wg=`Ipv4Prefixes`,Tg=`Ipv4Prefix`,Hne=`IsPrimary`,Une=`IpRangeList`,Wne=`Ipv6RangeList`,Gne=`IpRanges`,Kne=`IpRange`,qne=`Ipv6Ranges`,Jne=`Ipv6Range`,Yne=`InstanceStateChange`,Xne=`InstanceStateChangeList`,Zne=`InstanceSecondaryInterfaceAttachment`,Qne=`InstanceSecondaryInterfaceList`,$ne=`InstanceSecondaryInterfacePrivateIpAddress`,ere=`InstanceSecondaryInterfacePrivateIpAddressList`,tre=`InstanceSecondaryInterfacePrivateIpAddressListRequest`,nre=`InstanceSecondaryInterfacePrivateIpAddressRequest`,rre=`InstanceSecondaryInterfaceSpecificationListRequest`,ire=`InstanceSecondaryInterfaceSpecificationRequest`,are=`InstanceSecondaryInterface`,Eg=`InstanceStorageInfo`,ore=`InstanceStorageSupported`,sre=`ImdsSupport`,cre=`InstanceState`,Dg=`IpSource`,lre=`Ipv6Supported`,Og=`InstanceType`,ure=`InstanceTypeInfoList`,dre=`InstanceTypeInfo`,fre=`ImageType`,kg=`InstanceTenancy`,Ag=`InstanceTypes`,jg=`InterfaceType`,pre=`IncludeUnsupportedInRegion`,mre=`Images`,hre=`Image`,gre=`Instance`,_re=`Instances`,Mg=`Iops`,vre=`Key`,yre=`KeyFormat`,Ng=`KeyFingerprint`,Pg=`KernelId`,Fg=`KmsKeyId`,bre=`KeyMaterial`,Ig=`KeyName`,xre=`KeyNameStringList`,Sre=`KeyNames`,Cre=`KeyPairs`,Lg=`KeyPairId`,wre=`KeyPairIdStringList`,Tre=`KeyPairIds`,Ere=`KeyPairInfo`,Dre=`KeyPairList`,Ore=`KeyPair`,Rg=`KeyType`,kre=`Kernel`,zg=`LicenseConfigurationArn`,Are=`LicenseConfigurationRequest`,jre=`LicenseConfiguration`,Mre=`LambdaExclusion`,Nre=`LogicalGpuCount`,Bg=`LocalGatewayId`,Vg=`LinkedGroupId`,Pre=`LicenseList`,Fre=`LastLaunchedTime`,Ire=`LicenseSpecifications`,Lre=`LicenseSpecificationListRequest`,Rre=`LicenseSpecification`,zre=`LaunchTemplate`,Bre=`LaunchTemplateId`,Vre=`LaunchTemplateName`,Hre=`LaunchTemplateSpecification`,Ure=`LaunchTime`,Wre=`Latest`,Gre=`Lambda`,Kre=`Licenses`,qre=`Location`,Hg=`MultiAttachEnabled`,Ug=`MediaAcceleratorInfo`,Wg=`MacAddress`,Gg=`MaximumBandwidthInMbps`,Kg=`MapCustomerOwnedIpOnLaunch`,Jre=`MaxCount`,Yre=`MinCount`,Xre=`MediaDeviceInfo`,Zre=`MediaDeviceInfoList`,Qre=`MediaDeviceMemoryInfo`,$re=`MaximumEbsAttachments`,eie=`MaximumEbsCards`,tie=`MaximumEfaInterfaces`,nie=`MaximumEnaQueueCount`,rie=`MaximumEnaQueueCountPerInterface`,qg=`MaximumIops`,iie=`ModifyInstanceAttributeRequest`,aie=`ModifyInstanceAttributeValue`,oie=`ModifyInstanceAttribute`,Jg=`MemoryInfo`,sie=`MaximumNetworkCards`,Yg=`MaximumNetworkInterfaces`,Xg=`MetadataOptions`,Zg=`MaintenanceOptions`,Qg=`MapPublicIpOnLaunch`,cie=`MaxPrice`,$g=`MaxResults`,lie=`ModifySubnetAttributeRequest`,uie=`ModifySubnetAttribute`,die=`MaximumSecondaryNetworkInterfaces`,e_=`ModificationState`,fie=`MarketType`,t_=`MaximumThroughputInMBps`,pie=`ModifyVolumeRequest`,mie=`ModifyVolumeResult`,hie=`ModifyVolume`,gie=`Main`,n_=`Manufacturer`,_ie=`Managed`,r_=`Message`,vie=`Messages`,i_=`Mode`,a_=`Monitoring`,o_=`Name`,s_=`NetworkBorderGroup`,yie=`NetworkCards`,c_=`NetworkCardIndex`,bie=`NetworkCardInfoList`,xie=`NetworkCardInfo`,l_=`NoDevice`,Sie=`NeuronDeviceCoreInfo`,Cie=`NeuronDeviceInfo`,wie=`NeuronDeviceInfoList`,Tie=`NeuronDeviceMemoryInfo`,Eie=`NeuronDevices`,Die=`NitroEnclavesSupport`,u_=`NatGateway`,Oie=`NatGatewayAddresses`,kie=`NatGatewayAttachedAppliance`,Aie=`NatGatewayAttachedApplianceList`,jie=`NatGatewayAddressList`,Mie=`NatGatewayAddress`,Nie=`NatGatewayExclusion`,d_=`NatGatewayId`,Pie=`NatGatewayIdStringList`,Fie=`NatGatewayIds`,Iie=`NatGatewayList`,Lie=`NatGateways`,f_=`NetworkInterfaceId`,Rie=`NetworkInterfaceOwnerId`,p_=`NetworkInterfaces`,m_=`NetworkInfo`,h_=`NeuronInfo`,g_=`NetworkPerformanceOptions`,__=`NetworkPerformance`,zie=`NoReboot`,v_=`NvmeSupport`,y_=`NitroTpmInfo`,Bie=`NitroTpmSupport`,Vie=`NitroTpmSupportedVersionsList`,y=`NextToken`,b_=`NestedVirtualization`,Hie=`Origin`,x_=`OutpostArn`,Uie=`OwnerAlias`,S_=`OwnerId`,C_=`OptInStatus`,Wie=`OriginalIops`,Gie=`OwnerIds`,Kie=`OriginalMultiAttachEnabled`,w_=`OdbNetworkArn`,qie=`OperatorRequest`,Jie=`OperatorResponse`,Yie=`OwnerStringList`,Xie=`OriginalSize`,Zie=`OriginalThroughput`,Qie=`OriginalVolumeType`,T_=`Operator`,$ie=`Output`,eae=`Owners`,E_=`Owner`,D_=`Principal`,O_=`ProvisionedBandwidth`,tae=`PeakBandwidthInGbps`,nae=`ProductCodeId`,rae=`ProductCodeList`,iae=`ProductCodeType`,k_=`PartitionCount`,aae=`ProductCode`,A_=`ProductCodes`,oae=`PrivateDnsHostnameTypeOnLaunch`,j_=`PrivateDnsName`,M_=`PrivateDnsNameOptions`,N_=`PrivateDnsNameOptionsOnLaunch`,sae=`PrivateDnsNameOptionsRequest`,cae=`PrivateDnsNameOptionsResponse`,P_=`PublicDnsName`,F_=`PlatformDetails`,I_=`PlacementGroup`,L_=`PlacementGroupInfo`,lae=`PlacementGroupIdStringList`,uae=`PlacementGroupList`,dae=`PlacementGroupStrategyList`,fae=`PlacementGroups`,R_=`PublicIp`,pae=`PrivateIpAddressCount`,mae=`PrivateIpAddressSpecification`,hae=`PrivateIpAddressSpecificationList`,z_=`PrivateIpAddress`,B_=`PrivateIpAddresses`,gae=`PublicIpAddress`,V_=`PublicIpv4Pool`,_ae=`PublicIpStringList`,vae=`PrimaryIpv6`,yae=`PrivateIp`,H_=`ProcessorInfo`,bae=`PublicIps`,xae=`PublicKey`,Sae=`PublicKeyMaterial`,U_=`PrefixListId`,Cae=`PrefixListIdList`,wae=`PrefixListIds`,Tae=`PartitionNumber`,W_=`PeeringStatus`,Eae=`PhcSupport`,Dae=`PreviousState`,Oae=`ProvisionTime`,kae=`PresignedUrl`,Aae=`PropagatingVgw`,jae=`PropagatingVgwList`,Mae=`PropagatingVgws`,Nae=`ParentZoneId`,Pae=`ParentZoneName`,G_=`Platform`,K_=`Placement`,q_=`Primary`,J_=`Progress`,Fae=`Provisioned`,Iae=`Public`,Y_=`Return`,Lae=`ReleaseAddressRequest`,Rae=`ReleaseAddress`,zae=`RestorableByUserIds`,Bae=`RestorableBy`,X_=`RootDeviceName`,Z_=`RootDeviceType`,Vae=`RootDeviceTypeList`,Hae=`RestoreExpiryTime`,Uae=`ResourceExclusions`,Wae=`RegionGeography`,Gae=`ReferencedGroupId`,Kae=`ReferencedGroupInfo`,qae=`RegionGeographyList`,Jae=`RunInstancesMonitoringEnabled`,Yae=`RebootInstancesRequest`,Xae=`RunInstancesRequest`,Zae=`RunInstancesUserData`,Q_=`RamdiskId`,Qae=`ReservationId`,$ae=`RebootInstances`,eoe=`RequesterId`,toe=`RunInstances`,noe=`RegionList`,roe=`ReservationList`,ioe=`RouteList`,aoe=`RebootMigrationSupport`,ooe=`RebootMigration`,$_=`RegionName`,soe=`RegionNameStringList`,coe=`RegionNames`,loe=`ReferencedSecurityGroup`,uoe=`RevokeSecurityGroupEgress`,doe=`RevokeSecurityGroupEgressRequest`,foe=`RevokeSecurityGroupEgressResult`,poe=`RevokeSecurityGroupIngressRequest`,moe=`RevokeSecurityGroupIngressResult`,hoe=`RevokeSecurityGroupIngress`,goe=`RevokedSecurityGroupRule`,_oe=`RevokedSecurityGroupRuleList`,ev=`RevokedSecurityGroupRules`,voe=`RouteTableAssociation`,yoe=`RouteTableAssociationId`,boe=`RouteTableAssociationList`,xoe=`RouteTableAssociationState`,tv=`RouteTableId`,Soe=`RouteTableIdStringList`,Coe=`RouteTableIds`,woe=`RouteTableList`,Toe=`ResourceType`,Eoe=`RequestTime`,nv=`RouteTable`,Doe=`RouteTables`,Ooe=`Ramdisk`,koe=`Regions`,Aoe=`Region`,joe=`Requested`,Moe=`Reservations`,Noe=`Reservation`,Poe=`Route`,Foe=`Routes`,Ioe=`SecondaryAllocationIds`,Loe=`SecondaryAllocationId`,Roe=`SupportedAdditionalProcessorFeatureList`,zoe=`SubnetArn`,Boe=`SupportedArchitectures`,Voe=`SupportedBootModes`,Hoe=`SubnetCidrBlockState`,Uoe=`SustainedClockSpeedInGhz`,rv=`SourceDestCheck`,Woe=`SpreadDomain`,Goe=`SupportedFeatures`,Koe=`SubGeography`,iv=`SecurityGroupArn`,av=`SecurityGroupId`,qoe=`SecurityGroupIdStringList`,Joe=`SecurityGroupIds`,Yoe=`SecurityGroupList`,ov=`SecurityGroupRules`,Xoe=`SecurityGroupRuleArn`,sv=`SecurityGroupRuleIds`,Zoe=`SecurityGroupRuleIdList`,cv=`SecurityGroupRuleId`,Qoe=`SecurityGroupRuleList`,$oe=`SecurityGroupRule`,ese=`SecurityGroupStringList`,lv=`SecurityGroups`,uv=`SecurityGroup`,dv=`SubnetIds`,fv=`SubnetIpv6CidrBlockAssociation`,pv=`SubnetIpv6CidrBlockAssociationSet`,mv=`SizeInGB`,hv=`SourceImageId`,gv=`SecondaryInterfaceId`,_v=`SourceInstanceId`,vv=`SizeInMiB`,yv=`SpotInstanceRequestId`,bv=`SourceImageRegion`,xv=`StartInstancesRequest`,Sv=`StartInstancesResult`,Cv=`StopInstancesRequest`,wv=`StopInstancesResult`,Tv=`SupportedInRegion`,Ev=`SnapshotIdStringList`,Dv=`SubnetIdStringList`,Ov=`SpotInstanceType`,kv=`SecondaryInterfaces`,Av=`SecondaryInterface`,jv=`SnapshotId`,Mv=`SnapshotIds`,Nv=`StartingInstances`,Pv=`StartInstances`,Fv=`StoppingInstances`,Iv=`StopInstances`,Lv=`SubnetId`,Rv=`SnapshotLocation`,zv=`SnapshotList`,Bv=`SpreadLevel`,Vv=`SubnetList`,Hv=`ServiceManaged`,Uv=`SpotMarketOptions`,Wv=`StatusMessage`,Gv=`StateMessage`,Kv=`SecondaryNetworkId`,qv=`SriovNetSupport`,Jv=`SecondaryNetworkSupported`,Yv=`SpotOptions`,Xv=`SkipOsShutdown`,Zv=`SecondaryPrivateIpAddresses`,Qv=`SecondaryPrivateIpAddressCount`,$v=`SecondaryPrivateIpAddress`,ey=`SupportedRootDeviceTypes`,ty=`SourceRegion`,ny=`StateReason`,ry=`SourceSecurityGroupName`,iy=`SourceSecurityGroupOwnerId`,ay=`SourceSnapshotId`,oy=`SecondarySubnetId`,sy=`SupportedStrategies`,cy=`StartTime`,ly=`StateTransitionReason`,uy=`SseType`,dy=`StorageTier`,fy=`SupportedUsageClasses`,py=`SensitiveUserData`,my=`SourceVolumeId`,hy=`SupportedVirtualizationTypes`,gy=`SupportedVersions`,_y=`Size`,vy=`Snapshots`,yy=`Snapshot`,by=`State`,xy=`Status`,Sy=`Strategy`,Cy=`Subnet`,wy=`Subnets`,Ty=`Tags`,Ey=`TcpEstablishedTimeout`,Dy=`TotalFpgaMemoryInMiB`,Oy=`TransitGatewayId`,ky=`TotalGpuMemoryInMiB`,Ay=`TerminateInstances`,jy=`TotalInferenceMemoryInMiB`,My=`TerminateInstancesRequest`,Ny=`TerminateInstancesResult`,Py=`TargetIops`,Fy=`TerminatingInstances`,Iy=`TagList`,Ly=`TargetMultiAttachEnabled`,Ry=`TotalMediaMemoryInMiB`,zy=`TotalNeuronDeviceMemoryInMiB`,By=`ToPort`,Vy=`ThreadsPerCore`,Hy=`ThreadsPerCoreList`,Uy=`TagSpecifications`,Wy=`TotalSizeInGB`,Gy=`TagSpecificationList`,Ky=`TagSpecification`,qy=`TargetSize`,Jy=`TpmSupport`,Yy=`TargetThroughput`,Xy=`TransferType`,Zy=`TargetVolumeType`,Qy=`Tag`,$y=`Tenancy`,eb=`Throughput`,tb=`Timestamp`,nb=`Type`,rb=`UsageClassTypeList`,ib=`UserData`,ab=`UserId`,ob=`UserIdGroupPairs`,sb=`UserIdGroupPairList`,cb=`UserIdGroupPair`,lb=`UnknownIpPermissions`,ub=`UsageOperation`,db=`UsageOperationUpdateTime`,fb=`UdpStreamTimeout`,pb=`UdpTimeout`,mb=`Value`,hb=`VolumeAttachmentList`,gb=`VolumeAttachment`,_b=`VpcCidrBlockAssociation`,vb=`VpcCidrBlockAssociationSet`,yb=`VpcCidrBlockState`,bb=`VCpuInfo`,xb=`ValidCores`,Sb=`VpcEncryptionControl`,Cb=`VpcEncryptionControlConfiguration`,wb=`VpcEncryptionControlExclusion`,Tb=`VpcEncryptionControlExclusions`,Eb=`VpcEncryptionControlId`,Db=`VpcEndpointId`,Ob=`VpcId`,kb=`VpcIpv6CidrBlockAssociation`,Ab=`VpcIpv6CidrBlockAssociationSet`,jb=`VolumeInitializationRate`,Mb=`VolumeIdStringList`,Nb=`VpcIdStringList`,Pb=`VolumeId`,Fb=`VolumeIds`,Ib=`VpcIds`,Lb=`VpcLattice`,Rb=`VpcLatticeExclusion`,zb=`VolumeList`,Bb=`VpcList`,Vb=`VolumeModification`,Hb=`VirtualName`,Ub=`VolumeOwnerId`,Wb=`VpcPeeringConnectionId`,Gb=`VpcPeeringExclusion`,Kb=`VirtualPrivateGateway`,qb=`VirtualPrivateGatewayExclusion`,Jb=`VpcPeering`,Yb=`ValueStringList`,Xb=`VolumeSize`,Zb=`VolumeType`,Qb=`VirtualizationTypeList`,$b=`ValidThreadsPerCore`,ex=`VirtualizationType`,tx=`ValidUntil`,nx=`Values`,rx=`Version`,ix=`Volumes`,ax=`Volume`,ox=`Vpc`,sx=`Vpcs`,cx=`WorkloadsList`,lx=`Workloads`,ux=`ZoneId`,dx=`ZoneIdStringList`,fx=`ZoneIds`,px=`ZoneName`,mx=`ZoneNameStringList`,hx=`ZoneNames`,gx=`ZoneType`,_x=`attachedApplianceSet`,vx=`applianceArn`,yx=`additionalFlexibleNetworkInterfaces`,bx=`allocationId`,xx=`availableIpAddressCount`,Sx=`assignIpv6AddressOnCreation`,Cx=`additionalInfo`,wx=`associationId`,Tx=`attachmentId`,Ex=`amiLaunchIndex`,Dx=`attachmentLimitType`,Ox=`availabilityMode`,kx=`associatePublicIpAddress`,Ax=`amazonProvidedIpv6CidrBlock`,jx=`autoProvisionZones`,Mx=`autoRecoverySupported`,Nx=`allowReassociation`,Px=`associatedResource`,Fx=`autoRecovery`,Ix=`associationState`,Lx=`autoScalingIps`,Rx=`amdSevSnp`,zx=`addressesSet`,Bx=`associationSet`,Vx=`attachmentSet`,Hx=`attachmentState`,Ux=`attachTime`,Wx=`availabilityZone`,Gx=`availabilityZoneId`,Kx=`availabilityZoneInfo`,qx=`accelerators`,Jx=`affinity`,Yx=`arn`,Xx=`architecture`,Zx=`association`,Qx=`attachment`,$x=`attribute`,eS=`baselineBandwidthInGbps`,tS=`baselineBandwidthInMbps`,nS=`blockDeviceMapping`,rS=`baselineIops`,iS=`bootMode`,aS=`bareMetal`,oS=`blockPublicAccessStates`,sS=`burstablePerformanceSupported`,cS=`baselineThroughputInMBps`,lS=`bandwidthWeighting`,uS=`bandwidthWeightings`,dS=`cidrBlock`,fS=`cidrBlockAssociationSet`,pS=`capacityBlockId`,mS=`cidrBlockState`,hS=`coreCount`,gS=`completionDurationMinutes`,_S=`creationDate`,vS=`carrierGatewayId`,yS=`currentGeneration`,bS=`carrierIp`,xS=`currentInstanceBootMode`,SS=`cidrIp`,CS=`cidrIpv6`,wS=`cidrIpv4`,TS=`coreInfo`,ES=`coreNetworkArn`,DS=`cpuOptions`,OS=`customerOwnedIp`,kS=`customerOwnedIpv4Pool`,AS=`capacityReservationId`,jS=`capacityReservationPreference`,MS=`capacityReservationResourceGroupArn`,NS=`capacityReservationSpecification`,PS=`capacityReservationTarget`,FS=`currentState`,IS=`clientToken`,LS=`connectionTrackingConfiguration`,RS=`completionTime`,zS=`connectivityType`,BS=`createTime`,VS=`code`,HS=`configured`,US=`count`,WS=`disableApiTermination`,GS=`destinationCidrBlock`,KS=`defaultCores`,qS=`dataEncryptionKeyId`,JS=`defaultEnaQueueCountPerInterface`,YS=`defaultForAz`,XS=`dedicatedHostsSupported`,ZS=`deviceIndex`,QS=`destinationIpv6CidrBlock`,$S=`deviceName`,eC=`defaultNetworkCardIndex`,tC=`dnsName`,nC=`dhcpOptionsId`,rC=`deleteOnTermination`,iC=`destinationPrefixListId`,aC=`deregistrationProtection`,b=`dryRun`,oC=`destinationRegion`,sC=`defaultTcpEstablishedTimeout`,cC=`defaultThreadsPerCore`,lC=`deprecationTime`,uC=`deleteTime`,dC=`defaultUdpStreamTimeout`,fC=`defaultUdpTimeout`,pC=`defaultVCpus`,mC=`description`,hC=`device`,gC=`disks`,_C=`domain`,vC=`ebsCardIndex`,yC=`ebsCardSet`,bC=`encryptionControl`,xC=`enableDns64`,SC=`elasticFileSystem`,CC=`elasticGpuAssociationId`,wC=`elasticGpuAssociationState`,TC=`elasticGpuAssociationSet`,EC=`elasticGpuAssociationTime`,DC=`elasticGpuId`,OC=`elasticInferenceAcceleratorArn`,kC=`elasticInferenceAcceleratorAssociationId`,AC=`elasticInferenceAcceleratorAssociationState`,jC=`elasticInferenceAcceleratorAssociationSet`,MC=`elasticInferenceAcceleratorAssociationTime`,NC=`encryptionInTransitSupported`,PC=`ebsInfo`,FC=`efaInfo`,IC=`enableLniAtDeviceIndex`,LC=`ebsOptimized`,RC=`ebsOptimizedInfo`,zC=`egressOnlyInternetGateway`,BC=`egressOnlyInternetGatewayId`,VC=`ebsOptimizedSupport`,HC=`enclaveOptions`,UC=`enaQueueCount`,x=`ec2QueryName`,WC=`enableResourceNameDnsAAAARecord`,GC=`enableResourceNameDnsARecord`,KC=`enaSrdEnabled`,qC=`enaSrdSpecification`,JC=`enaSrdSupported`,YC=`enaSrdUdpEnabled`,XC=`enaSrdUdpSpecification`,ZC=`efaSupported`,QC=`encryptionSupport`,$C=`enaSupport`,ew=`endTime`,tw=`ebs`,nw=`enabled`,rw=`encrypted`,iw=`failureCode`,aw=`flexibleEnaQueuesSupport`,ow=`fpgaInfo`,sw=`failureMessage`,cw=`fromPort`,lw=`fastRestored`,uw=`fullSnapshotSizeInBytes`,dw=`freeTierEligible`,fw=`force`,pw=`fpgas`,mw=`groupArn`,hw=`groupDescription`,gw=`gatewayId`,_w=`gpuInfo`,vw=`groupId`,yw=`groupLongName`,bw=`groupName`,xw=`groupOwnerId`,Sw=`gpuPartitionSize`,Cw=`geographySet`,ww=`groupSet`,Tw=`gpus`,Ew=`groups`,Dw=`hypervisor`,Ow=`hiddenByDefault`,kw=`httpEndpoint`,Aw=`hostId`,jw=`hibernationOptions`,Mw=`httpProtocolIpv6`,Nw=`httpPutResponseHopLimit`,Pw=`hostResourceGroupArn`,Fw=`hibernationSupported`,Iw=`httpTokens`,Lw=`hostnameType`,Rw=`id`,zw=`ipv6AddressAttribute`,Bw=`ipv6AddressCount`,Vw=`inferenceAcceleratorInfo`,Hw=`ipv4AddressesPerInterface`,Uw=`ipv6AddressesPerInterface`,Ww=`ipv4AddressesPerSecondaryInterface`,Gw=`ipv6AddressesSet`,Kw=`imageAllowed`,qw=`ipAddress`,Jw=`ipv6Address`,Yw=`ipv6CidrBlock`,Xw=`ipv6CidrBlockAssociationSet`,Zw=`ipv6CidrBlockState`,Qw=`isDefault`,$w=`isEgress`,eT=`internetGateway`,tT=`internetGatewayBlockMode`,nT=`internetGatewayId`,rT=`internetGatewaySet`,iT=`instanceId`,aT=`iamInstanceProfile`,oT=`iamInstanceProfileAssociation`,sT=`iamInstanceProfileAssociationSet`,cT=`instanceInitiatedShutdownBehavior`,lT=`imageId`,uT=`imageLocation`,dT=`instanceLifecycle`,fT=`instanceMetadataTags`,pT=`ipv6Native`,mT=`imageOwnerAlias`,hT=`imageOwnerId`,gT=`instanceOwnerId`,_T=`ipOwnerId`,vT=`instanceOwningService`,yT=`ipPermissionsEgress`,bT=`isPrimaryIpv6`,xT=`ipv4PrefixSet`,ST=`ipv6PrefixSet`,CT=`ipProtocol`,wT=`ipPermissions`,TT=`ipv4Prefix`,ET=`ipv6Pool`,DT=`ipv6Prefix`,OT=`isPublic`,kT=`isPrimary`,AT=`ipRanges`,jT=`ipv6Ranges`,MT=`instanceStorageInfo`,NT=`instanceStorageSupported`,PT=`imagesSet`,FT=`imageState`,IT=`imdsSupport`,LT=`instancesSet`,RT=`instanceState`,zT=`ipSource`,BT=`ipv6Supported`,VT=`instanceType`,HT=`instanceTypeSet`,UT=`idempotencyToken`,WT=`imageType`,GT=`instanceTenancy`,KT=`interfaceType`,qT=`iops`,S=`item`,JT=`key`,YT=`keyFingerprint`,XT=`kernelId`,ZT=`kmsKeyId`,QT=`keyMaterial`,$T=`keyName`,eE=`keyPairId`,tE=`keySet`,nE=`keyType`,rE=`kernel`,iE=`licenseConfigurationArn`,aE=`logicalGpuCount`,oE=`localGatewayId`,sE=`linkedGroupId`,cE=`lastLaunchedTime`,lE=`licenseSet`,uE=`launchTime`,dE=`lambda`,fE=`multiAttachEnabled`,pE=`mediaAcceleratorInfo`,mE=`macAddress`,hE=`maximumBandwidthInMbps`,gE=`mapCustomerOwnedIpOnLaunch`,_E=`maximumEbsAttachments`,vE=`maximumEbsCards`,yE=`maximumEfaInterfaces`,bE=`maximumEnaQueueCount`,xE=`maximumEnaQueueCountPerInterface`,SE=`maximumIops`,CE=`memoryInfo`,wE=`maximumNetworkCards`,TE=`maximumNetworkInterfaces`,EE=`metadataOptions`,DE=`maintenanceOptions`,OE=`mapPublicIpOnLaunch`,kE=`maxResults`,AE=`messageSet`,jE=`maximumSecondaryNetworkInterfaces`,ME=`modificationState`,NE=`maximumThroughputInMBps`,PE=`main`,FE=`manufacturer`,IE=`managed`,LE=`message`,RE=`monitoring`,zE=`mode`,BE=`name`,VE=`networkBorderGroup`,HE=`networkCards`,UE=`networkCardIndex`,WE=`noDevice`,GE=`neuronDevices`,KE=`nitroEnclavesSupport`,qE=`natGateway`,JE=`natGatewayAddressSet`,YE=`natGatewayId`,XE=`natGatewaySet`,ZE=`networkInterfaceId`,QE=`networkInterfaceOwnerId`,$E=`networkInterfaceSet`,eD=`networkInterface`,tD=`networkInfo`,nD=`neuronInfo`,rD=`networkPerformanceOptions`,iD=`networkPerformance`,aD=`noReboot`,oD=`nvmeSupport`,sD=`nitroTpmInfo`,cD=`nitroTpmSupport`,lD=`nextToken`,uD=`nestedVirtualization`,dD=`origin`,fD=`outpostArn`,pD=`ownerAlias`,mD=`ownerId`,hD=`optInStatus`,gD=`originalIops`,_D=`originalMultiAttachEnabled`,vD=`odbNetworkArn`,yD=`originalSize`,bD=`originalThroughput`,xD=`originalVolumeType`,SD=`operator`,CD=`output`,wD=`principal`,TD=`provisionedBandwidth`,ED=`peakBandwidthInGbps`,DD=`productCodes`,OD=`partitionCount`,kD=`productCode`,AD=`privateDnsNameOptions`,jD=`privateDnsNameOptionsOnLaunch`,MD=`privateDnsName`,ND=`publicDnsName`,PD=`platformDetails`,FD=`placementGroup`,ID=`placementGroupInfo`,LD=`placementGroupSet`,RD=`publicIp`,zD=`privateIpAddressesSet`,BD=`privateIpAddressSet`,VD=`privateIpAddress`,HD=`publicIpv4Pool`,UD=`processorInfo`,WD=`privateIp`,GD=`publicKey`,KD=`publicKeyMaterial`,qD=`prefixListId`,JD=`prefixListIds`,YD=`partitionNumber`,XD=`peeringStatus`,ZD=`phcSupport`,QD=`previousState`,$D=`provisionTime`,eO=`presignedUrl`,tO=`propagatingVgwSet`,nO=`parentZoneId`,rO=`parentZoneName`,iO=`platform`,aO=`placement`,oO=`primary`,sO=`progress`,cO=`provisioned`,lO=`return`,uO=`rootDeviceName`,dO=`rootDeviceType`,fO=`restoreExpiryTime`,pO=`regionEndpoint`,mO=`resourceExclusions`,hO=`referencedGroupId`,gO=`referencedGroupInfo`,_O=`reservationId`,vO=`ramdiskId`,yO=`regionInfo`,bO=`requesterId`,xO=`rebootMigrationSupport`,SO=`rebootMigration`,CO=`regionName`,wO=`revokedSecurityGroupRuleSet`,TO=`reservationSet`,EO=`routeSet`,DO=`routeTableAssociationId`,OO=`routeTableId`,kO=`routeTableSet`,AO=`resourceType`,jO=`requestTime`,MO=`routeTable`,NO=`ramdisk`,PO=`reason`,FO=`requested`,IO=`smithy.ts.sdk.synthetic.com.amazonaws.ec2`,LO=`supportedArchitectures`,RO=`subnetArn`,zO=`supportedBootModes`,BO=`sustainedClockSpeedInGhz`,VO=`sourceDestCheck`,HO=`spreadDomain`,UO=`supportedFeatures`,WO=`securityGroupArn`,GO=`securityGroupInfo`,KO=`securityGroupRuleArn`,qO=`securityGroupRuleId`,JO=`securityGroupRuleSet`,YO=`subGeographySet`,XO=`sizeInGB`,ZO=`sourceInstanceId`,QO=`secondaryInterfaceId`,$O=`sourceImageId`,ek=`sizeInMiB`,tk=`sourceImageRegion`,nk=`spotInstanceRequestId`,rk=`supportedInRegion`,ik=`secondaryInterfaceSet`,ak=`snapshotId`,ok=`subnetId`,sk=`spreadLevel`,ck=`serviceManaged`,lk=`statusMessage`,uk=`stateMessage`,dk=`secondaryNetworkId`,fk=`secondaryNetworkSupported`,pk=`sriovNetSupport`,mk=`secondaryPrivateIpAddressCount`,hk=`supportedRootDeviceTypes`,gk=`stateReason`,_k=`sourceSecurityGroupName`,vk=`sourceSecurityGroupOwnerId`,yk=`secondarySubnetId`,bk=`snapshotSet`,xk=`subnetSet`,Sk=`supportedStrategies`,Ck=`startTime`,wk=`sseType`,Tk=`storageTier`,Ek=`supportedUsageClasses`,Dk=`sourceVolumeId`,Ok=`supportedVirtualizationTypes`,kk=`supportedVersions`,Ak=`size`,jk=`state`,Mk=`status`,Nk=`strategy`,Pk=`subnet`,Fk=`tenancy`,Ik=`tcpEstablishedTimeout`,Lk=`totalFpgaMemoryInMiB`,Rk=`transitGatewayId`,zk=`totalGpuMemoryInMiB`,Bk=`totalInferenceMemoryInMiB`,Vk=`targetIops`,Hk=`targetMultiAttachEnabled`,Uk=`totalMediaMemoryInMiB`,Wk=`totalNeuronDeviceMemoryInMiB`,Gk=`toPort`,Kk=`threadsPerCore`,qk=`tagSet`,Jk=`totalSizeInGB`,Yk=`targetSize`,Xk=`tpmSupport`,Zk=`targetThroughput`,Qk=`transferType`,$k=`targetVolumeType`,eA=`throughput`,tA=`timestamp`,nA=`type`,rA=`userData`,iA=`userId`,aA=`unknownIpPermissionSet`,oA=`usageOperation`,sA=`usageOperationUpdateTime`,cA=`udpStreamTimeout`,lA=`udpTimeout`,uA=`value`,dA=`vCpuInfo`,fA=`validCores`,pA=`vpcEncryptionControlId`,mA=`vpcEndpointId`,hA=`vpcId`,gA=`volumeInitializationRate`,_A=`volumeId`,vA=`vpcLattice`,yA=`volumeModification`,bA=`virtualName`,xA=`volumeOwnerId`,SA=`vpcPeeringConnectionId`,CA=`virtualPrivateGateway`,wA=`vpcPeering`,TA=`volumeSet`,EA=`volumeSize`,DA=`vpcSet`,OA=`volumeType`,kA=`validThreadsPerCore`,AA=`virtualizationType`,jA=`version`,MA=`vpc`,NA=`workloadSet`,C=`xmlName`,PA=`zoneId`,FA=`zoneName`,IA=`zoneState`,LA=`zoneType`,w=`com.amazonaws.ec2`,RA=Lt.for(IO),zA=[-3,IO,`EC2ServiceException`,0,[],[]];RA.registerError(zA,Ts);var BA=[RA],VA=[0,w,hu,8,0],HA=[0,w,aie,8,0],UA=[0,w,Zae,8,0],WA=[0,w,py,8,0],GA=[3,w,Es,0,[Ys,Zs,tp,f_,Rie,z_,Ty,V_,s_,Kl,ql,Dl,Lv,Hv,v,R_],[[0,{[x]:`AllocationId`,[C]:bx}],[0,{[x]:`AssociationId`,[C]:wx}],[0,{[x]:`Domain`,[C]:_C}],[0,{[x]:`NetworkInterfaceId`,[C]:ZE}],[0,{[x]:`NetworkInterfaceOwnerId`,[C]:QE}],[0,{[x]:`PrivateIpAddress`,[C]:VD}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`PublicIpv4Pool`,[C]:HD}],[0,{[x]:`NetworkBorderGroup`,[C]:VE}],[0,{[x]:`CustomerOwnedIp`,[C]:OS}],[0,{[x]:`CustomerOwnedIpv4Pool`,[C]:kS}],[0,{[x]:`CarrierIp`,[C]:bS}],[0,{[x]:`SubnetId`,[C]:ok}],[0,{[x]:`ServiceManaged`,[C]:ck}],[0,{[x]:`InstanceId`,[C]:iT}],[0,{[x]:`PublicIp`,[C]:RD}]]],KA=[3,w,Ds,0,[tp,Es,V_,s_,ql,Uy,jne,_],[0,0,0,0,0,[()=>LL,{[C]:Ky}],0,[2,{[x]:`DryRun`,[C]:b}]]],qA=[3,w,Os,0,[Ys,V_,s_,tp,Kl,ql,Dl,R_],[[0,{[x]:`AllocationId`,[C]:bx}],[0,{[x]:`PublicIpv4Pool`,[C]:HD}],[0,{[x]:`NetworkBorderGroup`,[C]:VE}],[0,{[x]:`Domain`,[C]:_C}],[0,{[x]:`CustomerOwnedIp`,[C]:OS}],[0,{[x]:`CustomerOwnedIpv4Pool`,[C]:kS}],[0,{[x]:`CarrierIp`,[C]:bS}],[0,{[x]:`PublicIp`,[C]:RD}]]],JA=[3,w,ks,0,[Ys,v,R_,_,f_,z_,dc],[0,0,0,[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`NetworkInterfaceId`,[C]:ZE}],[0,{[x]:`PrivateIpAddress`,[C]:VD}],[2,{[x]:`AllowReassociation`,[C]:Nx}]]],YA=[3,w,As,0,[Zs],[[0,{[x]:`AssociationId`,[C]:wx}]]],XA=[3,w,Ws,0,[$h,v],[[()=>TN,0],0],2],ZA=[3,w,Gs,0,[eg],[[()=>wN,{[x]:`IamInstanceProfileAssociation`,[C]:oT}]]],QA=[3,w,cc,0,[tv,Rm,V_,_,Lv],[[0,{[x]:`RouteTableId`,[C]:OO}],0,0,[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`SubnetId`,[C]:ok}]],1],$A=[3,w,lc,0,[Zs,hc],[[0,{[x]:`AssociationId`,[C]:wx}],[()=>fF,{[x]:`AssociationState`,[C]:Ix}]]],ej=[3,w,Hs,0,[Gh,Ob,_],[[0,{[x]:`InternetGatewayId`,[C]:nT}],[0,{[x]:`VpcId`,[C]:hA}],[2,{[x]:`DryRun`,[C]:b}]],2],tj=[3,w,Dc,0,[$f,v,Pb,ap,_],[0,0,0,1,[2,{[x]:`DryRun`,[C]:b}]],3],nj=[3,w,Is,0,[mb],[[2,{[x]:`Value`,[C]:uA}]]],rj=[3,w,Oc,0,[mb],[[0,{[x]:`Value`,[C]:uA}]]],ij=[3,w,_c,0,[Hm,Uy,_,ry,iy,xg,Sm,By,Pl,bg],[[0,{[x]:`GroupId`,[C]:vw}],[()=>LL,{[C]:Ky}],[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`SourceSecurityGroupName`,[C]:_k}],[0,{[x]:`SourceSecurityGroupOwnerId`,[C]:vk}],[0,{[x]:`IpProtocol`,[C]:CT}],[1,{[x]:`FromPort`,[C]:cw}],[1,{[x]:`ToPort`,[C]:Gk}],[0,{[x]:`CidrIp`,[C]:SS}],[()=>HI,{[x]:`IpPermissions`,[C]:wT}]],1],aj=[3,w,vc,0,[Y_,ov],[[2,{[x]:`Return`,[C]:lO}],[()=>OL,{[x]:`SecurityGroupRuleSet`,[C]:JO}]]],oj=[3,w,yc,0,[Pl,Sm,Hm,Km,bg,xg,ry,iy,By,Uy,_],[0,1,0,0,[()=>HI,0],0,0,0,1,[()=>LL,{[C]:Ky}],[2,{[x]:`DryRun`,[C]:b}]]],sj=[3,w,bc,0,[Y_,ov],[[2,{[x]:`Return`,[C]:lO}],[()=>OL,{[x]:`SecurityGroupRuleSet`,[C]:JO}]]],cj=[3,w,Ac,0,[C_,vie,$_,px,ux,Km,s_,gx,Pae,Nae,Gm,Zm,Koe,by],[[0,{[x]:`OptInStatus`,[C]:hD}],[()=>QF,{[x]:`MessageSet`,[C]:AE}],[0,{[x]:`RegionName`,[C]:CO}],[0,{[x]:`ZoneName`,[C]:FA}],[0,{[x]:`ZoneId`,[C]:PA}],[0,{[x]:`GroupName`,[C]:bw}],[0,{[x]:`NetworkBorderGroup`,[C]:VE}],[0,{[x]:`ZoneType`,[C]:LA}],[0,{[x]:`ParentZoneName`,[C]:rO}],[0,{[x]:`ParentZoneId`,[C]:nO}],[0,{[x]:`GroupLongName`,[C]:yw}],[()=>XF,{[x]:`GeographySet`,[C]:Cw}],[()=>$F,{[x]:`SubGeographySet`,[C]:YO}],[0,{[x]:`ZoneState`,[C]:IA}]]],lj=[3,w,jc,0,[Ac,Fc,Xs],[0,0,[()=>KF,{[C]:Ys}]]],uj=[3,w,Nc,0,[o_],[[0,{[x]:`Name`,[C]:BE}]]],dj=[3,w,Lc,0,[r_],[[0,{[x]:`Message`,[C]:LE}]]],fj=[3,w,zc,0,[o_],[[0,{[x]:`Name`,[C]:BE}]]],pj=[3,w,Qc,0,[mb],[[21,{[x]:`Value`,[C]:uA}]]],mj=[3,w,tl,0,[lm,l_,zd,Hb],[[()=>XM,{[x]:`Ebs`,[C]:tw}],[0,{[x]:`NoDevice`,[C]:WE}],[0,{[x]:`DeviceName`,[C]:$S}],[0,{[x]:`VirtualName`,[C]:bA}]]],hj=[3,w,ul,0,[Uh],[[0,{[x]:`InternetGatewayBlockMode`,[C]:tT}]]],gj=[3,w,iu,0,[$l,au],[0,()=>vj]],_j=[3,w,ru,0,[$l,au],[[0,{[x]:`CapacityReservationPreference`,[C]:jS}],[()=>yj,{[x]:`CapacityReservationTarget`,[C]:PS}]]],vj=[3,w,au,0,[Ql,tu],[0,0]],yj=[3,w,ou,0,[Ql,tu],[[0,{[x]:`CapacityReservationId`,[C]:AS}],[0,{[x]:`CapacityReservationResourceGroupArn`,[C]:MS}]]],bj=[3,w,ku,0,[Ey,fb,pb],[1,1,1]],xj=[3,w,Au,0,[Ey,fb,pb],[[1,{[x]:`TcpEstablishedTimeout`,[C]:Ik}],[1,{[x]:`UdpStreamTimeout`,[C]:cA}],[1,{[x]:`UdpTimeout`,[C]:lA}]]],Sj=[3,w,mu,0,[ty,ay,Qf,qd,mf,dm,Fg,kae,Uy,Cl,qu,_],[0,0,0,0,[0,{[x]:`DestinationRegion`,[C]:oC}],[2,{[x]:`Encrypted`,[C]:rw}],[0,{[x]:`KmsKeyId`,[C]:ZT}],[()=>VA,{[x]:`PresignedUrl`,[C]:eO}],[()=>LL,{[C]:Ky}],1,0,[2,{[x]:`DryRun`,[C]:b}]],2],Cj=[3,w,gu,0,[Ty,jv],[[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`SnapshotId`,[C]:ak}]]],wj=[3,w,Gl,0,[xl,Vy,Cc,b_],[[1,{[x]:`CoreCount`,[C]:hS}],[1,{[x]:`ThreadsPerCore`,[C]:Kk}],[0,{[x]:`AmdSevSnp`,[C]:Rx}],[0,{[x]:`NestedVirtualization`,[C]:uD}]]],Tj=[3,w,Jl,0,[xl,Vy,Cc,b_],[1,1,0,0]],Ej=[3,w,Ml,0,[v,o_,Uy,Rv,_,Qf,zie,il],[[0,{[x]:`InstanceId`,[C]:iT}],[0,{[x]:`Name`,[C]:BE}],[()=>LL,{[C]:Ky}],0,[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`Description`,[C]:mC}],[2,{[x]:`NoReboot`,[C]:aD}],[()=>nI,{[x]:`BlockDeviceMapping`,[C]:nS}]],2],Dj=[3,w,Nl,0,[fg],[[0,{[x]:`ImageId`,[C]:lT}]]],Oj=[3,w,Al,0,[Uy,_],[[()=>LL,{[C]:Ky}],[2,{[x]:`DryRun`,[C]:b}]]],kj=[3,w,jl,0,[Bh],[[()=>sP,{[x]:`InternetGateway`,[C]:eT}]]],Aj=[3,w,Bl,0,[Ig,Rg,Uy,yre,_],[0,0,[()=>LL,{[C]:Ky}],0,[2,{[x]:`DryRun`,[C]:b}]],1],jj=[3,w,Ul,0,[rc,Ys,Eu,_,Lv,Ob,Mc,Uy,Mu,z_,Ioe,Zv,Qv],[0,0,[0,4],2,0,0,[()=>YF,{[C]:jc}],[()=>LL,{[C]:Ky}],0,0,[()=>KF,{[C]:Loe}],[()=>VI,{[C]:$v}],1]],Mj=[3,w,Wl,0,[Eu,u_],[[0,{[x]:`ClientToken`,[C]:IS}],[()=>OP,{[x]:`NatGateway`,[C]:qE}]]],Nj=[3,w,Xl,0,[k_,Uy,Bv,Vg,T_,_,Km,Sy],[1,[()=>LL,{[C]:Ky}],0,0,()=>RP,[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`GroupName`,[C]:bw}],[0,{[x]:`Strategy`,[C]:Nk}]]],Pj=[3,w,Zl,0,[I_],[[()=>VP,{[x]:`PlacementGroup`,[C]:FD}]]],Fj=[3,w,eu,0,[tv,tf,Db,Oy,Bg,Tl,Vl,w_,_,$u,Rm,sd,Vp,v,f_,Wb,d_],[[0,{[x]:`RouteTableId`,[C]:OO}],0,0,0,0,0,0,0,[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`DestinationCidrBlock`,[C]:GS}],[0,{[x]:`GatewayId`,[C]:gw}],[0,{[x]:`DestinationIpv6CidrBlock`,[C]:QS}],[0,{[x]:`EgressOnlyInternetGatewayId`,[C]:BC}],[0,{[x]:`InstanceId`,[C]:iT}],[0,{[x]:`NetworkInterfaceId`,[C]:ZE}],[0,{[x]:`VpcPeeringConnectionId`,[C]:SA}],[0,{[x]:`NatGatewayId`,[C]:YE}]],1],Ij=[3,w,nu,0,[Y_],[[2,{[x]:`Return`,[C]:lO}]]],Lj=[3,w,su,0,[Ob,Uy,Eu,_],[[0,{[x]:`VpcId`,[C]:hA}],[()=>LL,{[C]:Ky}],[0,4],[2,{[x]:`DryRun`,[C]:b}]],1],Rj=[3,w,cu,0,[nv,Eu],[[()=>uF,{[x]:`RouteTable`,[C]:MO}],[0,{[x]:`ClientToken`,[C]:IS}]]],zj=[3,w,fu,0,[Qf,Km,Ob,Uy,_],[[0,{[C]:Pm}],0,0,[()=>LL,{[C]:Ky}],[2,{[x]:`DryRun`,[C]:b}]],2],Bj=[3,w,pu,0,[Hm,Ty,iv],[[0,{[x]:`GroupId`,[C]:vw}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`SecurityGroupArn`,[C]:WO}]]],Vj=[3,w,_u,0,[Pb,Qf,x_,Uy,qre,_],[0,0,0,[()=>LL,{[C]:Ky}],0,[2,{[x]:`DryRun`,[C]:b}]],1],Hj=[3,w,vu,0,[Ob,Uy,Ac,Fc,gl,kh,x_,gg,ig,vg,rg,_g,_],[0,[()=>LL,{[C]:Ky}],0,0,0,0,0,2,0,1,0,1,[2,{[x]:`DryRun`,[C]:b}]],1],Uj=[3,w,yu,0,[Cy],[[()=>wF,{[x]:`Subnet`,[C]:Pk}]]],Wj=[3,w,Pu,0,[Ac,Fc,dm,Mg,Fg,x_,_y,jv,Zb,Uy,Hg,eb,Eu,jb,T_,_],[0,0,[2,{[x]:`Encrypted`,[C]:rw}],1,0,0,1,0,0,[()=>LL,{[C]:Ky}],2,1,[0,4],1,()=>RP,[2,{[x]:`DryRun`,[C]:b}]]],Gj=[3,w,Fu,0,[gl,Cg,kh,ig,vg,rg,_g,jh,Sb,Uy,_,kg,ac],[0,0,0,0,1,0,1,0,()=>BF,[()=>LL,{[C]:Ky}],[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`InstanceTenancy`,[C]:GT}],[2,{[x]:`AmazonProvidedIpv6CidrBlock`,[C]:Ax}]]],Kj=[3,w,Iu,0,[ox],[[()=>IF,{[x]:`Vpc`,[C]:MA}]]],qj=[3,w,bu,0,[Sl],[0],1],Jj=[3,w,ed,0,[Nf,Rf,Lf],[[1,{[x]:`DefaultTcpEstablishedTimeout`,[C]:sC}],[1,{[x]:`DefaultUdpTimeout`,[C]:fC}],[1,{[x]:`DefaultUdpStreamTimeout`,[C]:dC}]]],Yj=[3,w,ld,0,[Gh,_],[[0,{[x]:`InternetGatewayId`,[C]:nT}],[2,{[x]:`DryRun`,[C]:b}]],1],Xj=[3,w,Nd,0,[Ig,Lg,_],[0,0,[2,{[x]:`DryRun`,[C]:b}]]],Zj=[3,w,Pd,0,[Y_,Lg],[[2,{[x]:`Return`,[C]:lO}],[0,{[x]:`KeyPairId`,[C]:eE}]]],Qj=[3,w,Hd,0,[d_,_],[[0,{[C]:d_}],2],1],$j=[3,w,Ud,0,[d_],[[0,{[x]:`NatGatewayId`,[C]:YE}]]],eM=[3,w,Zd,0,[Km,_],[[0,{[x]:`GroupName`,[C]:bw}],[2,{[x]:`DryRun`,[C]:b}]],1],tM=[3,w,rf,0,[tv,tf,_,$u,sd],[[0,{[x]:`RouteTableId`,[C]:OO}],0,[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`DestinationCidrBlock`,[C]:GS}],[0,{[x]:`DestinationIpv6CidrBlock`,[C]:QS}]],1],nM=[3,w,cf,0,[tv,_],[[0,{[x]:`RouteTableId`,[C]:OO}],[2,{[x]:`DryRun`,[C]:b}]],1],rM=[3,w,vf,0,[Hm,Km,_],[0,0,[2,{[x]:`DryRun`,[C]:b}]]],iM=[3,w,yf,0,[Y_,Hm],[[2,{[x]:`Return`,[C]:lO}],[0,{[x]:`GroupId`,[C]:vw}]]],aM=[3,w,Cf,0,[jv,_],[0,[2,{[x]:`DryRun`,[C]:b}]],1],oM=[3,w,wf,0,[Lv,_],[0,[2,{[x]:`DryRun`,[C]:b}]],1],sM=[3,w,Bf,0,[Pb,_],[0,[2,{[x]:`DryRun`,[C]:b}]],1],cM=[3,w,Vf,0,[Ob,_],[0,[2,{[x]:`DryRun`,[C]:b}]],1],lM=[3,w,Hu,0,[bae,_,Em,Xs],[[()=>mL,{[C]:R_}],[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}],[()=>KF,{[C]:Ys}]]],uM=[3,w,Uu,0,[Uc],[[()=>GF,{[x]:`AddressesSet`,[C]:zx}]]],dM=[3,w,Ju,0,[hx,fx,js,_,Em],[[()=>QL,{[C]:px}],[()=>ZL,{[C]:ux}],2,[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}]]],fM=[3,w,Yu,0,[Vc],[[()=>ZF,{[x]:`AvailabilityZoneInfo`,[C]:Kx}]]],pM=[3,w,_d,0,[Qs,Em,$g,y],[[()=>JF,{[C]:Zs}],[()=>fI,{[C]:Dm}],1,0]],mM=[3,w,vd,0,[ng,y],[[()=>vI,{[x]:`IamInstanceProfileAssociationSet`,[C]:sT}],[0,{[x]:`NextToken`,[C]:lD}]]],hM=[3,w,Sd,0,[cm,tne,eae,Nh,Lh,$g,y,_,Em],[[()=>dI,{[C]:ip}],[()=>yI,{[C]:fg}],[()=>oL,{[C]:E_}],2,2,1,0,[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}]]],gM=[3,w,Cd,0,[y,mre],[[0,{[x]:`NextToken`,[C]:lD}],[()=>bI,{[x]:`ImagesSet`,[C]:PT}]]],_M=[3,w,wd,0,[pg,mg,_,Em,y,$g],[[()=>wI,{[C]:v}],2,[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}],[0,{[x]:`NextToken`,[C]:lD}],[1,{[x]:`MaxResults`,[C]:kE}]]],vM=[3,w,Td,0,[y,Moe],[[0,{[x]:`NextToken`,[C]:lD}],[()=>vL,{[x]:`ReservationSet`,[C]:TO}]]],yM=[3,w,Ed,0,[_,Ag,Em,$g,y,pre],[2,[64,{[C]:Og}],[()=>fI,{[C]:Dm}],1,0,2]],bM=[3,w,Dd,0,[Ag,y],[[()=>LI,{[x]:`InstanceTypeSet`,[C]:HT}],[0,{[x]:`NextToken`,[C]:lD}]]],xM=[3,w,ud,0,[y,$g,_,qh,Em],[0,1,[2,{[x]:`DryRun`,[C]:b}],[()=>zI,{[x]:`InternetGatewayId`,[C]:nT}],[()=>fI,{[C]:Dm}]]],SM=[3,w,dd,0,[Yh,y],[[()=>BI,{[x]:`InternetGatewaySet`,[C]:rT}],[0,{[x]:`NextToken`,[C]:lD}]]],CM=[3,w,Fd,0,[Sre,Tre,Fne,_,Em],[[()=>qI,{[C]:Ig}],[()=>JI,{[C]:Lg}],2,[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}]]],wM=[3,w,Id,0,[Cre],[[()=>YI,{[x]:`KeySet`,[C]:tE}]]],TM=[3,w,Wd,0,[_,Dm,$g,Fie,y],[2,[()=>fI,0],1,[()=>tL,{[C]:d_}],0]],EM=[3,w,Gd,0,[Lie,y],[[()=>nL,{[x]:`NatGatewaySet`,[C]:XE}],[0,{[x]:`NextToken`,[C]:lD}]]],DM=[3,w,Qd,0,[Um,_,Jm,Em],[[()=>sL,{[C]:Hm}],[2,{[x]:`DryRun`,[C]:b}],[64,{[x]:`GroupName`,[C]:bw}],[()=>fI,{[C]:Dm}]]],OM=[3,w,$d,0,[fae],[[()=>cL,{[x]:`PlacementGroupSet`,[C]:LD}]]],kM=[3,w,af,0,[coe,fc,_,Em],[[()=>_L,{[C]:$_}],2,[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}]]],AM=[3,w,of,0,[koe],[[()=>gL,{[x]:`RegionInfo`,[C]:yO}]]],jM=[3,w,lf,0,[y,$g,_,Coe,Em],[0,1,[2,{[x]:`DryRun`,[C]:b}],[()=>CL,{[C]:tv}],[()=>fI,{[C]:Dm}]]],MM=[3,w,uf,0,[Doe,y],[[()=>wL,{[x]:`RouteTableSet`,[C]:kO}],[0,{[x]:`NextToken`,[C]:lD}]]],NM=[3,w,bf,0,[Um,Jm,y,$g,_,Em],[[()=>gI,{[C]:Hm}],[()=>_I,{[C]:Km}],0,1,[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}]]],PM=[3,w,xf,0,[y,lv],[[0,{[x]:`NextToken`,[C]:lD}],[()=>EL,{[x]:`SecurityGroupInfo`,[C]:GO}]]],FM=[3,w,Tf,0,[$g,y,Gie,zae,Mv,_,Em],[1,0,[()=>oL,{[C]:E_}],[64,{[C]:Bae}],[()=>AL,{[C]:jv}],[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}]]],IM=[3,w,Ef,0,[y,vy],[[0,{[x]:`NextToken`,[C]:lD}],[()=>jL,{[x]:`SnapshotSet`,[C]:bk}]]],LM=[3,w,Df,0,[Em,dv,y,$g,_],[[()=>fI,{[C]:Dm}],[()=>ML,{[C]:Lv}],0,1,[2,{[x]:`DryRun`,[C]:b}]]],RM=[3,w,Of,0,[y,wy],[[0,{[x]:`NextToken`,[C]:lD}],[()=>PL,{[x]:`SubnetSet`,[C]:xk}]]],zM=[3,w,Hf,0,[Fb,mg,_,Em,y,$g],[[()=>WL,{[C]:Pb}],2,[2,{[x]:`DryRun`,[C]:b}],[()=>fI,{[C]:Dm}],[0,{[x]:`NextToken`,[C]:lD}],[1,{[x]:`MaxResults`,[C]:kE}]]],BM=[3,w,Uf,0,[y,ix],[[0,{[x]:`NextToken`,[C]:lD}],[()=>GL,{[x]:`VolumeSet`,[C]:TA}]]],VM=[3,w,Wf,0,[Em,Ib,y,$g,_],[[()=>fI,{[C]:Dm}],[()=>qL,{[C]:Ob}],0,1,[2,{[x]:`DryRun`,[C]:b}]]],HM=[3,w,Gf,0,[y,sx],[[0,{[x]:`NextToken`,[C]:lD}],[()=>YL,{[x]:`VpcSet`,[C]:DA}]]],UM=[3,w,fd,0,[Gh,Ob,_],[[0,{[x]:`InternetGatewayId`,[C]:nT}],[0,{[x]:`VpcId`,[C]:hA}],[2,{[x]:`DryRun`,[C]:b}]],2],WM=[3,w,Kf,0,[Pb,$f,pm,v,_],[0,0,2,0,[2,{[x]:`DryRun`,[C]:b}]],1],GM=[3,w,Wu,0,[Zs,R_,_],[0,0,[2,{[x]:`DryRun`,[C]:b}]]],KM=[3,w,yd,0,[Zs],[0],1],qM=[3,w,bd,0,[eg],[[()=>wN,{[x]:`IamInstanceProfileAssociation`,[C]:oT}]]],JM=[3,w,df,0,[Zs,_],[[0,{[x]:`AssociationId`,[C]:wx}],[2,{[x]:`DryRun`,[C]:b}]],1],YM=[3,w,jd,0,[mv,Vu,nb],[[1,{[x]:`SizeInGB`,[C]:XO}],[1,{[x]:`Count`,[C]:US}],[0,{[x]:`Type`,[C]:nA}]]],XM=[3,w,np,0,[Yd,Mg,jv,Xb,Zb,Fg,eb,x_,Ac,dm,jb,Fc,ap],[[2,{[x]:`DeleteOnTermination`,[C]:rC}],[1,{[x]:`Iops`,[C]:qT}],[0,{[x]:`SnapshotId`,[C]:ak}],[1,{[x]:`VolumeSize`,[C]:EA}],[0,{[x]:`VolumeType`,[C]:OA}],[0,{[x]:`KmsKeyId`,[C]:ZT}],[1,{[x]:`Throughput`,[C]:eA}],[0,{[x]:`OutpostArn`,[C]:fD}],[0,{[x]:`AvailabilityZone`,[C]:Wx}],[2,{[x]:`Encrypted`,[C]:rw}],1,0,1]],ZM=[3,w,sp,0,[ap,el,fl,ol,Gg,t_,qg],[[1,{[x]:`EbsCardIndex`,[C]:vC}],[1,{[x]:`BaselineBandwidthInMbps`,[C]:tS}],[1,{[x]:`BaselineThroughputInMBps`,[C]:cS}],[1,{[x]:`BaselineIops`,[C]:rS}],[1,{[x]:`MaximumBandwidthInMbps`,[C]:hE}],[1,{[x]:`MaximumThroughputInMBps`,[C]:NE}],[1,{[x]:`MaximumIops`,[C]:SE}]]],QM=[3,w,Pp,0,[Up,am,Rp,v_,$re,tc,eie,cp],[[0,{[x]:`EbsOptimizedSupport`,[C]:VC}],[0,{[x]:`EncryptionSupport`,[C]:QC}],[()=>tN,{[x]:`EbsOptimizedInfo`,[C]:RC}],[0,{[x]:`NvmeSupport`,[C]:oD}],[1,{[x]:`MaximumEbsAttachments`,[C]:_E}],[0,{[x]:`AttachmentLimitType`,[C]:Dx}],[1,{[x]:`MaximumEbsCards`,[C]:vE}],[()=>oI,{[x]:`EbsCardSet`,[C]:yC}]]],$M=[3,w,jp,0,[Ec,Yd,xy,Pb,pc,Ub,T_,ap],[[4,{[x]:`AttachTime`,[C]:Ux}],[2,{[x]:`DeleteOnTermination`,[C]:rC}],[0,{[x]:`Status`,[C]:Mk}],[0,{[x]:`VolumeId`,[C]:_A}],[0,{[x]:`AssociatedResource`,[C]:Px}],[0,{[x]:`VolumeOwnerId`,[C]:xA}],[()=>zP,{[x]:`Operator`,[C]:SD}],[1,{[x]:`EbsCardIndex`,[C]:vC}]]],eN=[3,w,Mp,0,[Pb,Yd],[[0,{[x]:`VolumeId`,[C]:_A}],[2,{[x]:`DeleteOnTermination`,[C]:rC}]]],tN=[3,w,Rp,0,[el,fl,ol,Gg,t_,qg],[[1,{[x]:`BaselineBandwidthInMbps`,[C]:tS}],[1,{[x]:`BaselineThroughputInMBps`,[C]:cS}],[1,{[x]:`BaselineIops`,[C]:rS}],[1,{[x]:`MaximumBandwidthInMbps`,[C]:hE}],[1,{[x]:`MaximumThroughputInMBps`,[C]:NE}],[1,{[x]:`MaximumIops`,[C]:SE}]]],nN=[3,w,Fp,0,[tie],[[1,{[x]:`MaximumEfaInterfaces`,[C]:yE}]]],rN=[3,w,pp,0,[yp,mp,gp,_p],[[0,{[x]:`ElasticGpuId`,[C]:DC}],[0,{[x]:`ElasticGpuAssociationId`,[C]:CC}],[0,{[x]:`ElasticGpuAssociationState`,[C]:wC}],[0,{[x]:`ElasticGpuAssociationTime`,[C]:EC}]]],iN=[3,w,bp,0,[nb],[0],1],aN=[3,w,Sp,0,[nb,Vu],[0,1],1],oN=[3,w,Cp,0,[Op,wp,Ep,Dp],[[0,{[x]:`ElasticInferenceAcceleratorArn`,[C]:OC}],[0,{[x]:`ElasticInferenceAcceleratorAssociationId`,[C]:kC}],[0,{[x]:`ElasticInferenceAcceleratorAssociationState`,[C]:AC}],[4,{[x]:`ElasticInferenceAcceleratorAssociationTime`,[C]:MC}]]],sN=[3,w,$p,0,[Zp,nm],[2,()=>cN]],cN=[3,w,rm,0,[tm],[2]],lN=[3,w,Wp,0,[um],[[2,{[x]:`Enabled`,[C]:nw}]]],uN=[3,w,Hp,0,[um],[2]],dN=[3,w,Dm,0,[o_,nx],[0,[()=>VL,{[C]:mb}]]],fN=[3,w,hm,0,[o_,n_,Vu,Jg],[[0,{[x]:`Name`,[C]:BE}],[0,{[x]:`Manufacturer`,[C]:FE}],[1,{[x]:`Count`,[C]:US}],[()=>pN,{[x]:`MemoryInfo`,[C]:CE}]]],pN=[3,w,_m,0,[vv],[[1,{[x]:`SizeInMiB`,[C]:ek}]]],mN=[3,w,ym,0,[Om,Dy],[[()=>pI,{[x]:`Fpgas`,[C]:pw}],[1,{[x]:`TotalFpgaMemoryInMiB`,[C]:Lk}]]],hN=[3,w,Mm,0,[v,Wre,_],[0,2,[2,{[x]:`DryRun`,[C]:b}]],1],gN=[3,w,Nm,0,[v,tb,$ie],[[0,{[x]:`InstanceId`,[C]:iT}],[4,{[x]:`Timestamp`,[C]:tA}],[0,{[x]:`Output`,[C]:CD}]]],_N=[3,w,Fm,0,[o_,n_,Vu,Nre,Xm,lx,Jg],[[0,{[x]:`Name`,[C]:BE}],[0,{[x]:`Manufacturer`,[C]:FE}],[1,{[x]:`Count`,[C]:US}],[1,{[x]:`LogicalGpuCount`,[C]:aE}],[1,{[x]:`GpuPartitionSize`,[C]:Sw}],[()=>XL,{[x]:`WorkloadSet`,[C]:NA}],[()=>vN,{[x]:`MemoryInfo`,[C]:CE}]]],vN=[3,w,Lm,0,[vv],[[1,{[x]:`SizeInMiB`,[C]:ek}]]],yN=[3,w,Vm,0,[Qm,ky],[[()=>mI,{[x]:`Gpus`,[C]:Tw}],[1,{[x]:`TotalGpuMemoryInMiB`,[C]:zk}]]],bN=[3,w,Wm,0,[Hm,Km],[[0,{[x]:`GroupId`,[C]:vw}],[0,{[x]:`GroupName`,[C]:bw}]]],xN=[3,w,nh,0,[Bu],[[2,{[x]:`Configured`,[C]:HS}]]],SN=[3,w,rh,0,[Bu],[2]],CN=[3,w,$h,0,[Gc,fh],[[0,{[x]:`Arn`,[C]:Yx}],[0,{[x]:`Id`,[C]:Rw}]]],wN=[3,w,eg,0,[Zs,v,$h,by,tb],[[0,{[x]:`AssociationId`,[C]:wx}],[0,{[x]:`InstanceId`,[C]:iT}],[()=>CN,{[x]:`IamInstanceProfile`,[C]:aT}],[0,{[x]:`State`,[C]:jk}],[4,{[x]:`Timestamp`,[C]:tA}]]],TN=[3,w,sg,0,[Gc,o_],[[0,{[x]:`Arn`,[C]:Yx}],[0,{[x]:`Name`,[C]:BE}]]],EN=[3,w,hre,0,[F_,ub,il,Qf,om,dh,wne,o_,X_,Z_,qv,ny,Ty,ex,sl,Jy,Ff,sre,_v,nf,Fre,Sh,hv,bv,Tm,fg,ane,by,S_,wl,Iae,A_,Kc,fre,Pg,Q_,G_],[[0,{[x]:`PlatformDetails`,[C]:PD}],[0,{[x]:`UsageOperation`,[C]:oA}],[()=>tI,{[x]:`BlockDeviceMapping`,[C]:nS}],[0,{[x]:`Description`,[C]:mC}],[2,{[x]:`EnaSupport`,[C]:$C}],[0,{[x]:`Hypervisor`,[C]:Dw}],[0,{[x]:`ImageOwnerAlias`,[C]:mT}],[0,{[x]:`Name`,[C]:BE}],[0,{[x]:`RootDeviceName`,[C]:uO}],[0,{[x]:`RootDeviceType`,[C]:dO}],[0,{[x]:`SriovNetSupport`,[C]:pk}],[()=>xF,{[x]:`StateReason`,[C]:gk}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`VirtualizationType`,[C]:AA}],[0,{[x]:`BootMode`,[C]:iS}],[0,{[x]:`TpmSupport`,[C]:Xk}],[0,{[x]:`DeprecationTime`,[C]:lC}],[0,{[x]:`ImdsSupport`,[C]:IT}],[0,{[x]:`SourceInstanceId`,[C]:ZO}],[0,{[x]:`DeregistrationProtection`,[C]:aC}],[0,{[x]:`LastLaunchedTime`,[C]:cE}],[2,{[x]:`ImageAllowed`,[C]:Kw}],[0,{[x]:`SourceImageId`,[C]:$O}],[0,{[x]:`SourceImageRegion`,[C]:tk}],[2,{[x]:`FreeTierEligible`,[C]:dw}],[0,{[x]:`ImageId`,[C]:lT}],[0,{[x]:`ImageLocation`,[C]:uT}],[0,{[x]:`ImageState`,[C]:FT}],[0,{[x]:`ImageOwnerId`,[C]:hT}],[0,{[x]:`CreationDate`,[C]:_S}],[2,{[x]:`IsPublic`,[C]:OT}],[()=>fL,{[x]:`ProductCodes`,[C]:DD}],[0,{[x]:`Architecture`,[C]:Xx}],[0,{[x]:`ImageType`,[C]:WT}],[0,{[x]:`KernelId`,[C]:XT}],[0,{[x]:`RamdiskId`,[C]:vO}],[0,{[x]:`Platform`,[C]:iO}]]],DN=[3,w,rne,0,[Ig,Sae,Uy,_],[[0,{[x]:`KeyName`,[C]:$T}],[21,{[x]:`PublicKeyMaterial`,[C]:KD}],[()=>LL,{[C]:Ky}],[2,{[x]:`DryRun`,[C]:b}]],2],ON=[3,w,ine,0,[Ng,Ig,Lg,Ty],[[0,{[x]:`KeyFingerprint`,[C]:YT}],[0,{[x]:`KeyName`,[C]:$T}],[0,{[x]:`KeyPairId`,[C]:eE}],[()=>IL,{[x]:`TagSet`,[C]:qk}]]],kN=[3,w,vh,0,[Hc,jy],[[()=>xI,{[x]:`Accelerators`,[C]:qx}],[1,{[x]:`TotalInferenceMemoryInMiB`,[C]:Bk}]]],AN=[3,w,Ph,0,[Vu,o_,n_,Jg],[[1,{[x]:`Count`,[C]:US}],[0,{[x]:`Name`,[C]:BE}],[0,{[x]:`Manufacturer`,[C]:FE}],[()=>jN,{[x]:`MemoryInfo`,[C]:CE}]]],jN=[3,w,Ih,0,[vv],[[1,{[x]:`SizeInMiB`,[C]:ek}]]],MN=[3,w,gre,0,[Kc,il,Eu,Lp,om,dh,$h,sne,vp,kp,p_,x_,X_,Z_,lv,rv,yv,qv,ny,Ty,ex,Gl,vl,Ql,iu,nh,Kre,Xg,Wp,sl,F_,ub,db,M_,wh,Jy,Zg,Ol,g_,T_,kv,v,fg,by,j_,P_,ly,Ig,ec,A_,Og,Ure,K_,Pg,Q_,G_,a_,Lv,Ob,z_,gae],[[0,{[x]:`Architecture`,[C]:Xx}],[()=>SI,{[x]:`BlockDeviceMapping`,[C]:nS}],[0,{[x]:`ClientToken`,[C]:IS}],[2,{[x]:`EbsOptimized`,[C]:LC}],[2,{[x]:`EnaSupport`,[C]:$C}],[0,{[x]:`Hypervisor`,[C]:Dw}],[()=>CN,{[x]:`IamInstanceProfile`,[C]:aT}],[0,{[x]:`InstanceLifecycle`,[C]:dT}],[()=>sI,{[x]:`ElasticGpuAssociationSet`,[C]:TC}],[()=>lI,{[x]:`ElasticInferenceAcceleratorAssociationSet`,[C]:jC}],[()=>kI,{[x]:`NetworkInterfaceSet`,[C]:$E}],[0,{[x]:`OutpostArn`,[C]:fD}],[0,{[x]:`RootDeviceName`,[C]:uO}],[0,{[x]:`RootDeviceType`,[C]:dO}],[()=>hI,{[x]:`GroupSet`,[C]:ww}],[2,{[x]:`SourceDestCheck`,[C]:VO}],[0,{[x]:`SpotInstanceRequestId`,[C]:nk}],[0,{[x]:`SriovNetSupport`,[C]:pk}],[()=>xF,{[x]:`StateReason`,[C]:gk}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`VirtualizationType`,[C]:AA}],[()=>wj,{[x]:`CpuOptions`,[C]:DS}],[0,{[x]:`CapacityBlockId`,[C]:pS}],[0,{[x]:`CapacityReservationId`,[C]:AS}],[()=>_j,{[x]:`CapacityReservationSpecification`,[C]:NS}],[()=>xN,{[x]:`HibernationOptions`,[C]:jw}],[()=>XI,{[x]:`LicenseSet`,[C]:lE}],[()=>WN,{[x]:`MetadataOptions`,[C]:EE}],[()=>lN,{[x]:`EnclaveOptions`,[C]:HC}],[0,{[x]:`BootMode`,[C]:iS}],[0,{[x]:`PlatformDetails`,[C]:PD}],[0,{[x]:`UsageOperation`,[C]:oA}],[4,{[x]:`UsageOperationUpdateTime`,[C]:sA}],[()=>KP,{[x]:`PrivateDnsNameOptions`,[C]:AD}],[0,{[x]:`Ipv6Address`,[C]:Jw}],[0,{[x]:`TpmSupport`,[C]:Xk}],[()=>BN,{[x]:`MaintenanceOptions`,[C]:DE}],[0,{[x]:`CurrentInstanceBootMode`,[C]:xS}],[()=>YN,{[x]:`NetworkPerformanceOptions`,[C]:rD}],[()=>zP,{[x]:`Operator`,[C]:SD}],[()=>MI,{[x]:`SecondaryInterfaceSet`,[C]:ik}],[0,{[x]:`InstanceId`,[C]:iT}],[0,{[x]:`ImageId`,[C]:lT}],[()=>rP,{[x]:`InstanceState`,[C]:RT}],[0,{[x]:`PrivateDnsName`,[C]:MD}],[0,{[x]:`DnsName`,[C]:tC}],[0,{[x]:`Reason`,[C]:PO}],[0,{[x]:`KeyName`,[C]:$T}],[1,{[x]:`AmiLaunchIndex`,[C]:Ex}],[()=>fL,{[x]:`ProductCodes`,[C]:DD}],[0,{[x]:`InstanceType`,[C]:VT}],[4,{[x]:`LaunchTime`,[C]:uE}],[()=>BP,{[x]:`Placement`,[C]:aO}],[0,{[x]:`KernelId`,[C]:XT}],[0,{[x]:`RamdiskId`,[C]:vO}],[0,{[x]:`Platform`,[C]:iO}],[()=>DP,{[x]:`Monitoring`,[C]:RE}],[0,{[x]:`SubnetId`,[C]:ok}],[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`PrivateIpAddress`,[C]:VD}],[0,{[x]:`IpAddress`,[C]:qw}]]],NN=[3,w,gh,0,[Zp,nm],[[2,{[x]:`EnaSrdEnabled`,[C]:KC}],[()=>PN,{[x]:`EnaSrdUdpSpecification`,[C]:XC}]]],PN=[3,w,_h,0,[tm],[[2,{[x]:`EnaSrdUdpEnabled`,[C]:YC}]]],FN=[3,w,Th,0,[zd,lm],[[0,{[x]:`DeviceName`,[C]:$S}],[()=>$M,{[x]:`Ebs`,[C]:tw}]]],IN=[3,w,Dh,0,[zd,lm,Hb,l_],[[0,{[x]:`DeviceName`,[C]:$S}],[()=>eN,{[x]:`Ebs`,[C]:tw}],[0,{[x]:`VirtualName`,[C]:bA}],[0,{[x]:`NoDevice`,[C]:WE}]]],LN=[3,w,cg,0,[Tg],[[0,{[x]:`Ipv4Prefix`,[C]:TT}]]],RN=[3,w,Xh,0,[wh,Pne],[[0,{[x]:`Ipv6Address`,[C]:Jw}],[2,{[x]:`IsPrimaryIpv6`,[C]:bT}]]],zN=[3,w,lg,0,[Sg],[[0,{[x]:`Ipv6Prefix`,[C]:DT}]]],BN=[3,w,une,0,[mc,ooe],[[0,{[x]:`AutoRecovery`,[C]:Fx}],[0,{[x]:`RebootMigration`,[C]:SO}]]],VN=[3,w,dne,0,[mc],[0]],HN=[3,w,fne,0,[fie,Yv],[0,()=>vF]],UN=[3,w,pne,0,[ch,ah,eh,ih,hg],[0,1,0,0,0]],WN=[3,w,mne,0,[by,ch,ah,eh,ih,hg],[[0,{[x]:`State`,[C]:jk}],[0,{[x]:`HttpTokens`,[C]:Iw}],[1,{[x]:`HttpPutResponseHopLimit`,[C]:Nw}],[0,{[x]:`HttpEndpoint`,[C]:kw}],[0,{[x]:`HttpProtocolIpv6`,[C]:Mw}],[0,{[x]:`InstanceMetadataTags`,[C]:fT}]]],GN=[3,w,gne,0,[Jc,Xc,Qf,km,ph,Wg,f_,S_,j_,z_,B_,rv,xy,Lv,Ob,jg,wg,yg,Du,T_],[[()=>KN,{[x]:`Association`,[C]:Zx}],[()=>qN,{[x]:`Attachment`,[C]:Qx}],[0,{[x]:`Description`,[C]:mC}],[()=>hI,{[x]:`GroupSet`,[C]:ww}],[()=>EI,{[x]:`Ipv6AddressesSet`,[C]:Gw}],[0,{[x]:`MacAddress`,[C]:mE}],[0,{[x]:`NetworkInterfaceId`,[C]:ZE}],[0,{[x]:`OwnerId`,[C]:mD}],[0,{[x]:`PrivateDnsName`,[C]:MD}],[0,{[x]:`PrivateIpAddress`,[C]:VD}],[()=>jI,{[x]:`PrivateIpAddressesSet`,[C]:zD}],[2,{[x]:`SourceDestCheck`,[C]:VO}],[0,{[x]:`Status`,[C]:Mk}],[0,{[x]:`SubnetId`,[C]:ok}],[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`InterfaceType`,[C]:KT}],[()=>TI,{[x]:`Ipv4PrefixSet`,[C]:xT}],[()=>DI,{[x]:`Ipv6PrefixSet`,[C]:ST}],[()=>xj,{[x]:`ConnectionTrackingConfiguration`,[C]:LS}],[()=>zP,{[x]:`Operator`,[C]:SD}]]],KN=[3,w,_ne,0,[Dl,Kl,Tne,P_,R_],[[0,{[x]:`CarrierIp`,[C]:bS}],[0,{[x]:`CustomerOwnedIp`,[C]:OS}],[0,{[x]:`IpOwnerId`,[C]:_T}],[0,{[x]:`PublicDnsName`,[C]:ND}],[0,{[x]:`PublicIp`,[C]:RD}]]],qN=[3,w,vne,0,[Ec,$s,Yd,od,xy,c_,Qp,Kp],[[4,{[x]:`AttachTime`,[C]:Ux}],[0,{[x]:`AttachmentId`,[C]:Tx}],[2,{[x]:`DeleteOnTermination`,[C]:rC}],[1,{[x]:`DeviceIndex`,[C]:ZS}],[0,{[x]:`Status`,[C]:Mk}],[1,{[x]:`NetworkCardIndex`,[C]:UE}],[()=>NN,{[x]:`EnaSrdSpecification`,[C]:qC}],[1,{[x]:`EnaQueueCount`,[C]:UC}]]],JN=[3,w,bne,0,[ic,Yd,Qf,od,km,hh,ph,f_,z_,B_,Qv,Lv,Ls,jg,c_,wg,kne,yg,One,vae,Qp,Ou,Kp],[[2,{[x]:`AssociatePublicIpAddress`,[C]:kx}],[2,{[x]:`DeleteOnTermination`,[C]:rC}],[0,{[x]:`Description`,[C]:mC}],[1,{[x]:`DeviceIndex`,[C]:ZS}],[()=>TL,{[C]:av}],[1,{[x]:`Ipv6AddressCount`,[C]:Bw}],[()=>EI,{[x]:`Ipv6Addresses`,[C]:Gw}],[0,{[x]:`NetworkInterfaceId`,[C]:ZE}],[0,{[x]:`PrivateIpAddress`,[C]:VD}],[()=>dL,{[x]:`PrivateIpAddresses`,[C]:zD}],[1,{[x]:`SecondaryPrivateIpAddressCount`,[C]:mk}],[0,{[x]:`SubnetId`,[C]:ok}],2,0,1,[()=>WI,{[C]:Tg}],1,[()=>GI,{[C]:Sg}],1,2,()=>sN,()=>bj,1]],YN=[3,w,Sne,0,[pl],[[0,{[x]:`BandwidthWeighting`,[C]:lS}]]],XN=[3,w,Cne,0,[pl],[0]],ZN=[3,w,Mne,0,[Jc,q_,j_,z_],[[()=>KN,{[x]:`Association`,[C]:Zx}],[2,{[x]:`Primary`,[C]:oO}],[0,{[x]:`PrivateDnsName`,[C]:MD}],[0,{[x]:`PrivateIpAddress`,[C]:VD}]]],QN=[3,w,are,0,[Xc,Wg,gv,S_,B_,rv,xy,oy,Kv,jg],[[()=>$N,{[x]:`Attachment`,[C]:Qx}],[0,{[x]:`MacAddress`,[C]:mE}],[0,{[x]:`SecondaryInterfaceId`,[C]:QO}],[0,{[x]:`OwnerId`,[C]:mD}],[()=>NI,{[x]:`PrivateIpAddressSet`,[C]:BD}],[2,{[x]:`SourceDestCheck`,[C]:VO}],[0,{[x]:`Status`,[C]:Mk}],[0,{[x]:`SecondarySubnetId`,[C]:yk}],[0,{[x]:`SecondaryNetworkId`,[C]:dk}],[0,{[x]:`InterfaceType`,[C]:KT}]]],$N=[3,w,Zne,0,[Ec,$s,Yd,od,xy,c_],[[4,{[x]:`AttachTime`,[C]:Ux}],[0,{[x]:`AttachmentId`,[C]:Tx}],[2,{[x]:`DeleteOnTermination`,[C]:rC}],[1,{[x]:`DeviceIndex`,[C]:ZS}],[0,{[x]:`Status`,[C]:Mk}],[1,{[x]:`NetworkCardIndex`,[C]:UE}]]],eP=[3,w,$ne,0,[z_],[[0,{[x]:`PrivateIpAddress`,[C]:VD}]]],tP=[3,w,nre,0,[z_],[0],1],nP=[3,w,ire,0,[Yd,od,B_,pae,oy,jg,c_],[2,1,[()=>PI,{[C]:z_}],1,0,0,1]],rP=[3,w,cre,0,[zu,o_],[[1,{[x]:`Code`,[C]:VS}],[0,{[x]:`Name`,[C]:BE}]]],iP=[3,w,Yne,0,[v,Tu,Dae],[[0,{[x]:`InstanceId`,[C]:iT}],[()=>rP,{[x]:`CurrentState`,[C]:FS}],[()=>rP,{[x]:`PreviousState`,[C]:QD}]]],aP=[3,w,Eg,0,[Wy,ep,v_,am],[[1,{[x]:`TotalSizeInGB`,[C]:Jk}],[()=>aI,{[x]:`Disks`,[C]:gC}],[0,{[x]:`NvmeSupport`,[C]:oD}],[0,{[x]:`EncryptionSupport`,[C]:QC}]]],oP=[3,w,dre,0,[Og,El,Tm,fy,ey,hy,ll,dh,H_,bb,Jg,ore,Eg,Pp,m_,Vm,ym,L_,vh,sh,dl,ad,sc,Voe,Die,Bie,y_,Ug,h_,Eae,aoe,Tv],[[0,{[x]:`InstanceType`,[C]:VT}],[2,{[x]:`CurrentGeneration`,[C]:yS}],[2,{[x]:`FreeTierEligible`,[C]:dw}],[()=>zL,{[x]:`SupportedUsageClasses`,[C]:Ek}],[()=>bL,{[x]:`SupportedRootDeviceTypes`,[C]:hk}],[()=>HL,{[x]:`SupportedVirtualizationTypes`,[C]:Ok}],[2,{[x]:`BareMetal`,[C]:aS}],[0,{[x]:`Hypervisor`,[C]:Dw}],[()=>JP,{[x]:`ProcessorInfo`,[C]:UD}],[()=>MF,{[x]:`VCpuInfo`,[C]:dA}],[()=>SP,{[x]:`MemoryInfo`,[C]:CE}],[2,{[x]:`InstanceStorageSupported`,[C]:NT}],[()=>aP,{[x]:`InstanceStorageInfo`,[C]:MT}],[()=>QM,{[x]:`EbsInfo`,[C]:PC}],[()=>MP,{[x]:`NetworkInfo`,[C]:tD}],[()=>yN,{[x]:`GpuInfo`,[C]:_w}],[()=>mN,{[x]:`FpgaInfo`,[C]:ow}],[()=>HP,{[x]:`PlacementGroupInfo`,[C]:ID}],[()=>kN,{[x]:`InferenceAcceleratorInfo`,[C]:Vw}],[2,{[x]:`HibernationSupported`,[C]:Fw}],[2,{[x]:`BurstablePerformanceSupported`,[C]:sS}],[2,{[x]:`DedicatedHostsSupported`,[C]:XS}],[2,{[x]:`AutoRecoverySupported`,[C]:Mx}],[()=>rI,{[x]:`SupportedBootModes`,[C]:zO}],[0,{[x]:`NitroEnclavesSupport`,[C]:KE}],[0,{[x]:`NitroTpmSupport`,[C]:cD}],[()=>LP,{[x]:`NitroTpmInfo`,[C]:sD}],[()=>yP,{[x]:`MediaAcceleratorInfo`,[C]:pE}],[()=>IP,{[x]:`NeuronInfo`,[C]:nD}],[0,{[x]:`PhcSupport`,[C]:ZD}],[0,{[x]:`RebootMigrationSupport`,[C]:xO}],[2,{[x]:`SupportedInRegion`,[C]:rk}]]],sP=[3,w,Bh,0,[Zc,Gh,S_,Ty],[[()=>RI,{[x]:`AttachmentSet`,[C]:Vx}],[0,{[x]:`InternetGatewayId`,[C]:nT}],[0,{[x]:`OwnerId`,[C]:mD}],[()=>IL,{[x]:`TagSet`,[C]:qk}]]],cP=[3,w,Vh,0,[by,Ob],[[0,{[x]:`State`,[C]:jk}],[0,{[x]:`VpcId`,[C]:hA}]]],lP=[3,w,Vne,0,[xg,Sm,By,ob,Gne,qne,wae],[[0,{[x]:`IpProtocol`,[C]:CT}],[1,{[x]:`FromPort`,[C]:cw}],[1,{[x]:`ToPort`,[C]:Gk}],[()=>BL,{[x]:`Groups`,[C]:Ew}],[()=>UI,{[x]:`IpRanges`,[C]:AT}],[()=>KI,{[x]:`Ipv6Ranges`,[C]:jT}],[()=>uL,{[x]:`PrefixListIds`,[C]:JD}]]],uP=[3,w,Kne,0,[Qf,Pl],[[0,{[x]:`Description`,[C]:mC}],[0,{[x]:`CidrIp`,[C]:SS}]]],dP=[3,w,zne,0,[Tg],[0]],fP=[3,w,Bne,0,[Sg],[0]],pP=[3,w,Jne,0,[Qf,Fl],[[0,{[x]:`Description`,[C]:mC}],[0,{[x]:`CidrIpv6`,[C]:CS}]]],mP=[3,w,Ore,0,[Lg,Ty,Ig,Ng,bre],[[0,{[x]:`KeyPairId`,[C]:eE}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`KeyName`,[C]:$T}],[0,{[x]:`KeyFingerprint`,[C]:YT}],[()=>WA,{[x]:`KeyMaterial`,[C]:QT}]]],hP=[3,w,Ere,0,[Lg,Rg,Ty,xae,Nu,Ig,Ng],[[0,{[x]:`KeyPairId`,[C]:eE}],[0,{[x]:`KeyType`,[C]:nE}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`PublicKey`,[C]:GD}],[4,{[x]:`CreateTime`,[C]:BS}],[0,{[x]:`KeyName`,[C]:$T}],[0,{[x]:`KeyFingerprint`,[C]:YT}]]],gP=[3,w,Hre,0,[Bre,Vre,rx],[0,0,0]],_P=[3,w,jre,0,[zg],[[0,{[x]:`LicenseConfigurationArn`,[C]:iE}]]],vP=[3,w,Are,0,[zg],[0]],yP=[3,w,Ug,0,[Hc,Ry],[[()=>QI,{[x]:`Accelerators`,[C]:qx}],[1,{[x]:`TotalMediaMemoryInMiB`,[C]:Uk}]]],bP=[3,w,Xre,0,[Vu,o_,n_,Jg],[[1,{[x]:`Count`,[C]:US}],[0,{[x]:`Name`,[C]:BE}],[0,{[x]:`Manufacturer`,[C]:FE}],[()=>xP,{[x]:`MemoryInfo`,[C]:CE}]]],xP=[3,w,Qre,0,[vv],[[1,{[x]:`SizeInMiB`,[C]:ek}]]],SP=[3,w,Jg,0,[vv],[[1,{[x]:`SizeInMiB`,[C]:ek}]]],CP=[3,w,iie,0,[v,rv,Wp,Gu,_,Yc,mb,il,Ku,Og,kre,Ooe,ib,ug,km,Lp,qv,om],[[0,{[x]:`InstanceId`,[C]:iT}],[()=>nj,0],()=>uN,[()=>nj,0],[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`Attribute`,[C]:$x}],[()=>HA,{[x]:`Value`,[C]:uA}],[()=>CI,{[x]:`BlockDeviceMapping`,[C]:nS}],[()=>nj,{[x]:`DisableApiTermination`,[C]:WS}],[()=>rj,{[x]:`InstanceType`,[C]:VT}],[()=>rj,{[x]:`Kernel`,[C]:rE}],[()=>rj,{[x]:`Ramdisk`,[C]:NO}],[()=>pj,{[x]:`UserData`,[C]:rA}],[()=>rj,{[x]:`InstanceInitiatedShutdownBehavior`,[C]:cT}],[()=>gI,{[C]:Hm}],[()=>nj,{[x]:`EbsOptimized`,[C]:LC}],[()=>rj,{[x]:`SriovNetSupport`,[C]:pk}],[()=>nj,{[x]:`EnaSupport`,[C]:$C}]],1],wP=[3,w,lie,0,[Lv,Bs,Qg,Kg,ql,up,oae,Xp,Jp,Ip,Rd],[[0,{[x]:`SubnetId`,[C]:ok}],[()=>nj,0],[()=>nj,0],[()=>nj,0],0,[()=>nj,0],0,[()=>nj,0],[()=>nj,0],1,[()=>nj,0]],1],TP=[3,w,pie,0,[Pb,_,_y,Zb,Mg,eb,Hg],[0,2,1,0,1,1,2],1],EP=[3,w,mie,0,[Vb],[[()=>FF,{[x]:`VolumeModification`,[C]:yA}]]],DP=[3,w,a_,0,[by],[[0,{[x]:`State`,[C]:jk}]]],OP=[3,w,u_,0,[Nu,If,mm,xm,Oie,d_,O_,by,Lv,Ob,Ty,Mu,rc,Sc,oc,Fs,tv],[[4,{[x]:`CreateTime`,[C]:BS}],[4,{[x]:`DeleteTime`,[C]:uC}],[0,{[x]:`FailureCode`,[C]:iw}],[0,{[x]:`FailureMessage`,[C]:sw}],[()=>$I,{[x]:`NatGatewayAddressSet`,[C]:JE}],[0,{[x]:`NatGatewayId`,[C]:YE}],[()=>ZP,{[x]:`ProvisionedBandwidth`,[C]:TD}],[0,{[x]:`State`,[C]:jk}],[0,{[x]:`SubnetId`,[C]:ok}],[0,{[x]:`VpcId`,[C]:hA}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`ConnectivityType`,[C]:zS}],[0,{[x]:`AvailabilityMode`,[C]:Ox}],[0,{[x]:`AutoScalingIps`,[C]:Lx}],[0,{[x]:`AutoProvisionZones`,[C]:jx}],[()=>eL,{[x]:`AttachedApplianceSet`,[C]:_x}],[0,{[x]:`RouteTableId`,[C]:OO}]]],kP=[3,w,Mie,0,[Ys,f_,yae,R_,Zs,Hne,xm,xy,Ac,Fc],[[0,{[x]:`AllocationId`,[C]:bx}],[0,{[x]:`NetworkInterfaceId`,[C]:ZE}],[0,{[x]:`PrivateIp`,[C]:WD}],[0,{[x]:`PublicIp`,[C]:RD}],[0,{[x]:`AssociationId`,[C]:wx}],[2,{[x]:`IsPrimary`,[C]:kT}],[0,{[x]:`FailureMessage`,[C]:sw}],[0,{[x]:`Status`,[C]:Mk}],[0,{[x]:`AvailabilityZone`,[C]:Wx}],[0,{[x]:`AvailabilityZoneId`,[C]:Gx}]]],AP=[3,w,kie,0,[nb,Ns,Db,wc,e_,mm,xm],[[0,{[x]:`Type`,[C]:nA}],[0,{[x]:`ApplianceArn`,[C]:vx}],[0,{[x]:`VpcEndpointId`,[C]:mA}],[0,{[x]:`AttachmentState`,[C]:Hx}],[0,{[x]:`ModificationState`,[C]:ME}],[0,{[x]:`FailureCode`,[C]:iw}],[0,{[x]:`FailureMessage`,[C]:sw}]]],jP=[3,w,xie,0,[c_,__,Yg,Rs,$c,tae,rd,nie,rie],[[1,{[x]:`NetworkCardIndex`,[C]:UE}],[0,{[x]:`NetworkPerformance`,[C]:iD}],[1,{[x]:`MaximumNetworkInterfaces`,[C]:TE}],[1,{[x]:`AdditionalFlexibleNetworkInterfaces`,[C]:yx}],[1,{[x]:`BaselineBandwidthInGbps`,[C]:eS}],[1,{[x]:`PeakBandwidthInGbps`,[C]:ED}],[1,{[x]:`DefaultEnaQueueCountPerInterface`,[C]:JS}],[1,{[x]:`MaximumEnaQueueCount`,[C]:bE}],[1,{[x]:`MaximumEnaQueueCountPerInterface`,[C]:xE}]]],MP=[3,w,m_,0,[__,Yg,sie,Bd,yie,yh,bh,lre,om,im,Fp,Np,em,hl,vm,Du,Jv,die,xh],[[0,{[x]:`NetworkPerformance`,[C]:iD}],[1,{[x]:`MaximumNetworkInterfaces`,[C]:TE}],[1,{[x]:`MaximumNetworkCards`,[C]:wE}],[1,{[x]:`DefaultNetworkCardIndex`,[C]:eC}],[()=>rL,{[x]:`NetworkCards`,[C]:HE}],[1,{[x]:`Ipv4AddressesPerInterface`,[C]:Hw}],[1,{[x]:`Ipv6AddressesPerInterface`,[C]:Uw}],[2,{[x]:`Ipv6Supported`,[C]:BT}],[0,{[x]:`EnaSupport`,[C]:$C}],[2,{[x]:`EfaSupported`,[C]:ZC}],[()=>nN,{[x]:`EfaInfo`,[C]:FC}],[2,{[x]:`EncryptionInTransitSupported`,[C]:NC}],[2,{[x]:`EnaSrdSupported`,[C]:JC}],[()=>eI,{[x]:`BandwidthWeightings`,[C]:uS}],[0,{[x]:`FlexibleEnaQueuesSupport`,[C]:aw}],[()=>Jj,{[x]:`ConnectionTrackingConfiguration`,[C]:LS}],[2,{[x]:`SecondaryNetworkSupported`,[C]:fk}],[1,{[x]:`MaximumSecondaryNetworkInterfaces`,[C]:jE}],[1,{[x]:`Ipv4AddressesPerSecondaryInterface`,[C]:Ww}]]],NP=[3,w,Sie,0,[Vu,rx],[[1,{[x]:`Count`,[C]:US}],[1,{[x]:`Version`,[C]:jA}]]],PP=[3,w,Cie,0,[Vu,o_,Ll,Jg],[[1,{[x]:`Count`,[C]:US}],[0,{[x]:`Name`,[C]:BE}],[()=>NP,{[x]:`CoreInfo`,[C]:TS}],[()=>FP,{[x]:`MemoryInfo`,[C]:CE}]]],FP=[3,w,Tie,0,[vv],[[1,{[x]:`SizeInMiB`,[C]:ek}]]],IP=[3,w,h_,0,[Eie,zy],[[()=>iL,{[x]:`NeuronDevices`,[C]:GE}],[1,{[x]:`TotalNeuronDeviceMemoryInMiB`,[C]:Wk}]]],LP=[3,w,y_,0,[gy],[[()=>aL,{[x]:`SupportedVersions`,[C]:kk}]]],RP=[3,w,qie,0,[D_],[0]],zP=[3,w,Jie,0,[_ie,D_,$m],[[2,{[x]:`Managed`,[C]:IE}],[0,{[x]:`Principal`,[C]:wD}],[2,{[x]:`HiddenByDefault`,[C]:Ow}]]],BP=[3,w,K_,0,[Fc,Wc,Km,Tae,th,$y,Woe,oh,Hm,Ac],[[0,{[x]:`AvailabilityZoneId`,[C]:Gx}],[0,{[x]:`Affinity`,[C]:Jx}],[0,{[x]:`GroupName`,[C]:bw}],[1,{[x]:`PartitionNumber`,[C]:YD}],[0,{[x]:`HostId`,[C]:Aw}],[0,{[x]:`Tenancy`,[C]:Fk}],[0,{[x]:`SpreadDomain`,[C]:HO}],[0,{[x]:`HostResourceGroupArn`,[C]:Pw}],[0,{[x]:`GroupId`,[C]:vw}],[0,{[x]:`AvailabilityZone`,[C]:Wx}]]],VP=[3,w,I_,0,[Km,by,Sy,k_,Hm,Ty,Am,Bv,Vg,T_],[[0,{[x]:`GroupName`,[C]:bw}],[0,{[x]:`State`,[C]:jk}],[0,{[x]:`Strategy`,[C]:Nk}],[1,{[x]:`PartitionCount`,[C]:OD}],[0,{[x]:`GroupId`,[C]:vw}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`GroupArn`,[C]:mw}],[0,{[x]:`SpreadLevel`,[C]:sk}],[0,{[x]:`LinkedGroupId`,[C]:sE}],[()=>zP,{[x]:`Operator`,[C]:SD}]]],HP=[3,w,L_,0,[sy],[[()=>lL,{[x]:`SupportedStrategies`,[C]:Sk}]]],UP=[3,w,U_,0,[Qf,U_],[[0,{[x]:`Description`,[C]:mC}],[0,{[x]:`PrefixListId`,[C]:qD}]]],WP=[3,w,N_,0,[lh,Yp,qp],[[0,{[x]:`HostnameType`,[C]:Lw}],[2,{[x]:`EnableResourceNameDnsARecord`,[C]:GC}],[2,{[x]:`EnableResourceNameDnsAAAARecord`,[C]:WC}]]],GP=[3,w,sae,0,[lh,Yp,qp],[0,2,2]],KP=[3,w,cae,0,[lh,Yp,qp],[[0,{[x]:`HostnameType`,[C]:Lw}],[2,{[x]:`EnableResourceNameDnsARecord`,[C]:GC}],[2,{[x]:`EnableResourceNameDnsAAAARecord`,[C]:WC}]]],qP=[3,w,mae,0,[q_,z_],[[2,{[x]:`Primary`,[C]:oO}],[0,{[x]:`PrivateIpAddress`,[C]:VD}]]],JP=[3,w,H_,0,[Boe,Uoe,Goe,n_],[[()=>qF,{[x]:`SupportedArchitectures`,[C]:LO}],[1,{[x]:`SustainedClockSpeedInGhz`,[C]:BO}],[()=>FL,{[x]:`SupportedFeatures`,[C]:UO}],[0,{[x]:`Manufacturer`,[C]:FE}]]],YP=[3,w,aae,0,[nae,iae],[[0,{[x]:`ProductCode`,[C]:kD}],[0,{[x]:`Type`,[C]:nA}]]],XP=[3,w,Aae,0,[Rm],[[0,{[x]:`GatewayId`,[C]:gw}]]],ZP=[3,w,O_,0,[Oae,Fae,Eoe,joe,xy],[[4,{[x]:`ProvisionTime`,[C]:$D}],[0,{[x]:`Provisioned`,[C]:cO}],[4,{[x]:`RequestTime`,[C]:jO}],[0,{[x]:`Requested`,[C]:FO}],[0,{[x]:`Status`,[C]:Mk}]]],QP=[3,w,Yae,0,[pg,_],[[()=>wI,{[C]:v}],[2,{[x]:`DryRun`,[C]:b}]],1],$P=[3,w,loe,0,[Hm,W_,ab,Ob,Wb],[[0,{[x]:`GroupId`,[C]:vw}],[0,{[x]:`PeeringStatus`,[C]:XD}],[0,{[x]:`UserId`,[C]:iA}],[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`VpcPeeringConnectionId`,[C]:SA}]]],eF=[3,w,Aoe,0,[C_,Zm,$_,fm],[[0,{[x]:`OptInStatus`,[C]:hD}],[()=>hL,{[x]:`GeographySet`,[C]:Cw}],[0,{[x]:`RegionName`,[C]:CO}],[0,{[x]:`RegionEndpoint`,[C]:pO}]]],tF=[3,w,Wae,0,[o_],[[0,{[x]:`Name`,[C]:BE}]]],nF=[3,w,Lae,0,[Ys,R_,s_,_],[0,0,0,[2,{[x]:`DryRun`,[C]:b}]]],rF=[3,w,Noe,0,[Qae,S_,eoe,km,_re],[[0,{[x]:`ReservationId`,[C]:_O}],[0,{[x]:`OwnerId`,[C]:mD}],[0,{[x]:`RequesterId`,[C]:bO}],[()=>hI,{[x]:`GroupSet`,[C]:ww}],[()=>OI,{[x]:`InstancesSet`,[C]:LT}]]],iF=[3,w,goe,0,[cv,Hm,zh,xg,Sm,By,Il,Fl,U_,Gae,Qf],[[0,{[x]:`SecurityGroupRuleId`,[C]:qO}],[0,{[x]:`GroupId`,[C]:vw}],[2,{[x]:`IsEgress`,[C]:$w}],[0,{[x]:`IpProtocol`,[C]:CT}],[1,{[x]:`FromPort`,[C]:cw}],[1,{[x]:`ToPort`,[C]:Gk}],[0,{[x]:`CidrIpv4`,[C]:wS}],[0,{[x]:`CidrIpv6`,[C]:CS}],[0,{[x]:`PrefixListId`,[C]:qD}],[0,{[x]:`ReferencedGroupId`,[C]:hO}],[0,{[x]:`Description`,[C]:mC}]]],aF=[3,w,doe,0,[Hm,sv,_,ry,iy,xg,Sm,By,Pl,bg],[[0,{[x]:`GroupId`,[C]:vw}],[()=>DL,{[C]:cv}],[2,{[x]:`DryRun`,[C]:b}],[0,{[x]:`SourceSecurityGroupName`,[C]:_k}],[0,{[x]:`SourceSecurityGroupOwnerId`,[C]:vk}],[0,{[x]:`IpProtocol`,[C]:CT}],[1,{[x]:`FromPort`,[C]:cw}],[1,{[x]:`ToPort`,[C]:Gk}],[0,{[x]:`CidrIp`,[C]:SS}],[()=>HI,{[x]:`IpPermissions`,[C]:wT}]],1],oF=[3,w,foe,0,[Y_,lb,ev],[[2,{[x]:`Return`,[C]:lO}],[()=>HI,{[x]:`UnknownIpPermissionSet`,[C]:aA}],[()=>yL,{[x]:`RevokedSecurityGroupRuleSet`,[C]:wO}]]],sF=[3,w,poe,0,[Pl,Sm,Hm,Km,bg,xg,ry,iy,By,sv,_],[0,1,0,0,[()=>HI,0],0,0,0,1,[()=>DL,{[C]:cv}],[2,{[x]:`DryRun`,[C]:b}]]],cF=[3,w,moe,0,[Y_,lb,ev],[[2,{[x]:`Return`,[C]:lO}],[()=>HI,{[x]:`UnknownIpPermissionSet`,[C]:aA}],[()=>yL,{[x]:`RevokedSecurityGroupRuleSet`,[C]:wO}]]],lF=[3,w,Poe,0,[$u,sd,tf,Vp,Rm,v,Ene,d_,Oy,Bg,Tl,f_,Hie,by,Wb,Vl,w_,Ch],[[0,{[x]:`DestinationCidrBlock`,[C]:GS}],[0,{[x]:`DestinationIpv6CidrBlock`,[C]:QS}],[0,{[x]:`DestinationPrefixListId`,[C]:iC}],[0,{[x]:`EgressOnlyInternetGatewayId`,[C]:BC}],[0,{[x]:`GatewayId`,[C]:gw}],[0,{[x]:`InstanceId`,[C]:iT}],[0,{[x]:`InstanceOwnerId`,[C]:gT}],[0,{[x]:`NatGatewayId`,[C]:YE}],[0,{[x]:`TransitGatewayId`,[C]:Rk}],[0,{[x]:`LocalGatewayId`,[C]:oE}],[0,{[x]:`CarrierGatewayId`,[C]:vS}],[0,{[x]:`NetworkInterfaceId`,[C]:ZE}],[0,{[x]:`Origin`,[C]:dD}],[0,{[x]:`State`,[C]:jk}],[0,{[x]:`VpcPeeringConnectionId`,[C]:SA}],[0,{[x]:`CoreNetworkArn`,[C]:ES}],[0,{[x]:`OdbNetworkArn`,[C]:vD}],[0,{[x]:`IpAddress`,[C]:qw}]]],uF=[3,w,nv,0,[qc,Mae,tv,Foe,Ty,Ob,S_],[[()=>SL,{[x]:`AssociationSet`,[C]:Bx}],[()=>pL,{[x]:`PropagatingVgwSet`,[C]:tO}],[0,{[x]:`RouteTableId`,[C]:OO}],[()=>xL,{[x]:`RouteSet`,[C]:EO}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`OwnerId`,[C]:mD}]]],dF=[3,w,voe,0,[gie,yoe,tv,Lv,Rm,V_,hc],[[2,{[x]:`Main`,[C]:PE}],[0,{[x]:`RouteTableAssociationId`,[C]:DO}],[0,{[x]:`RouteTableId`,[C]:OO}],[0,{[x]:`SubnetId`,[C]:ok}],[0,{[x]:`GatewayId`,[C]:gw}],[0,{[x]:`PublicIpv4Pool`,[C]:HD}],[()=>fF,{[x]:`AssociationState`,[C]:Ix}]]],fF=[3,w,xoe,0,[by,Wv],[[0,{[x]:`State`,[C]:jk}],[0,{[x]:`StatusMessage`,[C]:lk}]]],pF=[3,w,Jae,0,[um],[[2,{[x]:`Enabled`,[C]:nw}]],1],mF=[3,w,Xae,0,[Jre,Yre,il,fg,Og,hh,ph,Pg,Ig,a_,K_,Q_,Joe,lv,Lv,ib,bp,Ap,Uy,zre,hne,Su,Gl,iu,nh,Ire,Xg,Wp,M_,Zg,Gu,Gp,g_,T_,kv,_,Ku,ug,z_,Eu,Js,p_,$h,Lp],[1,1,[()=>nI,{[C]:tl}],0,0,1,[()=>EI,{[C]:wh}],0,0,[()=>pF,0],[()=>BP,0],0,[()=>TL,{[C]:av}],[()=>kL,{[C]:uv}],0,[()=>UA,0],[()=>cI,0],[()=>uI,{[C]:Sp}],[()=>LL,{[C]:Ky}],()=>gP,()=>HN,()=>qj,()=>Tj,()=>gj,()=>SN,[()=>ZI,{[C]:Rre}],()=>UN,()=>uN,()=>GP,()=>VN,2,2,()=>XN,()=>RP,[()=>FI,{[C]:Av}],[2,{[x]:`DryRun`,[C]:b}],[2,{[x]:`DisableApiTermination`,[C]:WS}],[0,{[x]:`InstanceInitiatedShutdownBehavior`,[C]:cT}],[0,{[x]:`PrivateIpAddress`,[C]:VD}],[0,{[x]:`ClientToken`,[UT]:1,[C]:IS}],[0,{[x]:`AdditionalInfo`,[C]:Cx}],[()=>AI,{[x]:`NetworkInterface`,[C]:eD}],[()=>TN,{[x]:`IamInstanceProfile`,[C]:aT}],[2,{[x]:`EbsOptimized`,[C]:LC}]],2],hF=[3,w,uv,0,[Hm,Ane,Ty,Ob,iv,S_,Km,Qf,bg],[[0,{[x]:`GroupId`,[C]:vw}],[()=>HI,{[x]:`IpPermissionsEgress`,[C]:yT}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`SecurityGroupArn`,[C]:WO}],[0,{[x]:`OwnerId`,[C]:mD}],[0,{[x]:`GroupName`,[C]:bw}],[0,{[x]:`GroupDescription`,[C]:hw}],[()=>HI,{[x]:`IpPermissions`,[C]:wT}]]],gF=[3,w,$oe,0,[cv,Hm,Ym,zh,xg,Sm,By,Il,Fl,U_,Kae,Qf,Ty,Xoe],[[0,{[x]:`SecurityGroupRuleId`,[C]:qO}],[0,{[x]:`GroupId`,[C]:vw}],[0,{[x]:`GroupOwnerId`,[C]:xw}],[2,{[x]:`IsEgress`,[C]:$w}],[0,{[x]:`IpProtocol`,[C]:CT}],[1,{[x]:`FromPort`,[C]:cw}],[1,{[x]:`ToPort`,[C]:Gk}],[0,{[x]:`CidrIpv4`,[C]:wS}],[0,{[x]:`CidrIpv6`,[C]:CS}],[0,{[x]:`PrefixListId`,[C]:qD}],[()=>$P,{[x]:`ReferencedGroupInfo`,[C]:gO}],[0,{[x]:`Description`,[C]:mC}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`SecurityGroupRuleArn`,[C]:KO}]]],_F=[3,w,yy,0,[Uie,x_,Ty,dy,Hae,uy,Ac,Xy,Cl,ju,wm,jv,Pb,by,Gv,cy,J_,S_,Qf,Xb,dm,Fg,nd],[[0,{[x]:`OwnerAlias`,[C]:pD}],[0,{[x]:`OutpostArn`,[C]:fD}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`StorageTier`,[C]:Tk}],[4,{[x]:`RestoreExpiryTime`,[C]:fO}],[0,{[x]:`SseType`,[C]:wk}],[0,{[x]:`AvailabilityZone`,[C]:Wx}],[0,{[x]:`TransferType`,[C]:Qk}],[1,{[x]:`CompletionDurationMinutes`,[C]:gS}],[4,{[x]:`CompletionTime`,[C]:RS}],[1,{[x]:`FullSnapshotSizeInBytes`,[C]:uw}],[0,{[x]:`SnapshotId`,[C]:ak}],[0,{[x]:`VolumeId`,[C]:_A}],[0,{[x]:`Status`,[C]:Mk}],[0,{[x]:`StatusMessage`,[C]:lk}],[4,{[x]:`StartTime`,[C]:Ck}],[0,{[x]:`Progress`,[C]:sO}],[0,{[x]:`OwnerId`,[C]:mD}],[0,{[x]:`Description`,[C]:mC}],[1,{[x]:`VolumeSize`,[C]:EA}],[2,{[x]:`Encrypted`,[C]:rw}],[0,{[x]:`KmsKeyId`,[C]:ZT}],[0,{[x]:`DataEncryptionKeyId`,[C]:qS}]]],vF=[3,w,Uv,0,[cie,Ov,al,tx,Qh],[0,0,1,4,0]],yF=[3,w,xv,0,[pg,Js,_],[[()=>wI,{[C]:v}],[0,{[x]:`AdditionalInfo`,[C]:Cx}],[2,{[x]:`DryRun`,[C]:b}]],1],bF=[3,w,Sv,0,[Nv],[[()=>II,{[x]:`InstancesSet`,[C]:LT}]]],xF=[3,w,ny,0,[zu,r_],[[0,{[x]:`Code`,[C]:VS}],[0,{[x]:`Message`,[C]:LE}]]],SF=[3,w,Cv,0,[pg,uh,Xv,_,pm],[[()=>wI,{[C]:v}],2,2,[2,{[x]:`DryRun`,[C]:b}],[2,{[x]:`Force`,[C]:fw}]],1],CF=[3,w,wv,0,[Fv],[[()=>II,{[x]:`InstancesSet`,[C]:LT}]]],wF=[3,w,Cy,0,[Fc,Ip,Kg,ql,S_,Bs,Ah,Ty,zoe,x_,up,gg,N_,ul,nb,Lv,by,Ob,gl,zs,Ac,id,Qg],[[0,{[x]:`AvailabilityZoneId`,[C]:Gx}],[1,{[x]:`EnableLniAtDeviceIndex`,[C]:IC}],[2,{[x]:`MapCustomerOwnedIpOnLaunch`,[C]:gE}],[0,{[x]:`CustomerOwnedIpv4Pool`,[C]:kS}],[0,{[x]:`OwnerId`,[C]:mD}],[2,{[x]:`AssignIpv6AddressOnCreation`,[C]:Sx}],[()=>NL,{[x]:`Ipv6CidrBlockAssociationSet`,[C]:Xw}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`SubnetArn`,[C]:RO}],[0,{[x]:`OutpostArn`,[C]:fD}],[2,{[x]:`EnableDns64`,[C]:xC}],[2,{[x]:`Ipv6Native`,[C]:pT}],[()=>WP,{[x]:`PrivateDnsNameOptionsOnLaunch`,[C]:jD}],[()=>hj,{[x]:`BlockPublicAccessStates`,[C]:oS}],[0,{[x]:`Type`,[C]:nA}],[0,{[x]:`SubnetId`,[C]:ok}],[0,{[x]:`State`,[C]:jk}],[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`CidrBlock`,[C]:dS}],[1,{[x]:`AvailableIpAddressCount`,[C]:xx}],[0,{[x]:`AvailabilityZone`,[C]:Wx}],[2,{[x]:`DefaultForAz`,[C]:YS}],[2,{[x]:`MapPublicIpOnLaunch`,[C]:OE}]]],TF=[3,w,Hoe,0,[by,Wv],[[0,{[x]:`State`,[C]:jk}],[0,{[x]:`StatusMessage`,[C]:lk}]]],EF=[3,w,fv,0,[Zs,kh,Mh,mh,Dg],[[0,{[x]:`AssociationId`,[C]:wx}],[0,{[x]:`Ipv6CidrBlock`,[C]:Yw}],[()=>TF,{[x]:`Ipv6CidrBlockState`,[C]:Zw}],[0,{[x]:`Ipv6AddressAttribute`,[C]:zw}],[0,{[x]:`IpSource`,[C]:zT}]]],DF=[3,w,Qy,0,[vre,mb],[[0,{[x]:`Key`,[C]:JT}],[0,{[x]:`Value`,[C]:uA}]]],OF=[3,w,Ky,0,[Toe,Ty],[[0,{[x]:`ResourceType`,[C]:AO}],[()=>IL,{[C]:Qy}]]],kF=[3,w,My,0,[pg,pm,Xv,_],[[()=>wI,{[C]:v}],2,2,[2,{[x]:`DryRun`,[C]:b}]],1],AF=[3,w,Ny,0,[Fy],[[()=>II,{[x]:`InstancesSet`,[C]:LT}]]],jF=[3,w,cb,0,[Qf,ab,Km,Hm,Ob,Wb,W_],[[0,{[x]:`Description`,[C]:mC}],[0,{[x]:`UserId`,[C]:iA}],[0,{[x]:`GroupName`,[C]:bw}],[0,{[x]:`GroupId`,[C]:vw}],[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`VpcPeeringConnectionId`,[C]:SA}],[0,{[x]:`PeeringStatus`,[C]:XD}]]],MF=[3,w,bb,0,[zf,td,Pf,xb,$b],[[1,{[x]:`DefaultVCpus`,[C]:pC}],[1,{[x]:`DefaultCores`,[C]:KS}],[1,{[x]:`DefaultThreadsPerCore`,[C]:cC}],[()=>iI,{[x]:`ValidCores`,[C]:fA}],[()=>RL,{[x]:`ValidThreadsPerCore`,[C]:kA}]]],NF=[3,w,ax,0,[Fc,x_,my,Mg,Ty,Zb,Cm,Hg,eb,uy,T_,jb,Pb,_y,jv,Ac,by,Nu,Zc,dm,Fg],[[0,{[x]:`AvailabilityZoneId`,[C]:Gx}],[0,{[x]:`OutpostArn`,[C]:fD}],[0,{[x]:`SourceVolumeId`,[C]:Dk}],[1,{[x]:`Iops`,[C]:qT}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[0,{[x]:`VolumeType`,[C]:OA}],[2,{[x]:`FastRestored`,[C]:lw}],[2,{[x]:`MultiAttachEnabled`,[C]:fE}],[1,{[x]:`Throughput`,[C]:eA}],[0,{[x]:`SseType`,[C]:wk}],[()=>zP,{[x]:`Operator`,[C]:SD}],[1,{[x]:`VolumeInitializationRate`,[C]:gA}],[0,{[x]:`VolumeId`,[C]:_A}],[1,{[x]:`Size`,[C]:Ak}],[0,{[x]:`SnapshotId`,[C]:ak}],[0,{[x]:`AvailabilityZone`,[C]:Wx}],[0,{[x]:`Status`,[C]:Mk}],[4,{[x]:`CreateTime`,[C]:BS}],[()=>UL,{[x]:`AttachmentSet`,[C]:Vx}],[2,{[x]:`Encrypted`,[C]:rw}],[0,{[x]:`KmsKeyId`,[C]:ZT}]]],PF=[3,w,gb,0,[Yd,pc,Dne,ap,Pb,v,$f,by,Ec],[[2,{[x]:`DeleteOnTermination`,[C]:rC}],[0,{[x]:`AssociatedResource`,[C]:Px}],[0,{[x]:`InstanceOwningService`,[C]:vT}],[1,{[x]:`EbsCardIndex`,[C]:vC}],[0,{[x]:`VolumeId`,[C]:_A}],[0,{[x]:`InstanceId`,[C]:iT}],[0,{[x]:`Device`,[C]:hC}],[0,{[x]:`Status`,[C]:Mk}],[4,{[x]:`AttachTime`,[C]:Ux}]]],FF=[3,w,Vb,0,[Pb,e_,Wv,qy,Py,Zy,Yy,Ly,Xie,Wie,Qie,Zie,Kie,J_,cy,sm,T_],[[0,{[x]:`VolumeId`,[C]:_A}],[0,{[x]:`ModificationState`,[C]:ME}],[0,{[x]:`StatusMessage`,[C]:lk}],[1,{[x]:`TargetSize`,[C]:Yk}],[1,{[x]:`TargetIops`,[C]:Vk}],[0,{[x]:`TargetVolumeType`,[C]:$k}],[1,{[x]:`TargetThroughput`,[C]:Zk}],[2,{[x]:`TargetMultiAttachEnabled`,[C]:Hk}],[1,{[x]:`OriginalSize`,[C]:yD}],[1,{[x]:`OriginalIops`,[C]:gD}],[0,{[x]:`OriginalVolumeType`,[C]:xD}],[1,{[x]:`OriginalThroughput`,[C]:bD}],[2,{[x]:`OriginalMultiAttachEnabled`,[C]:_D}],[1,{[x]:`Progress`,[C]:sO}],[4,{[x]:`StartTime`,[C]:Ck}],[4,{[x]:`EndTime`,[C]:ew}],[()=>zP,{[x]:`Operator`,[C]:SD}]]],IF=[3,w,ox,0,[S_,kg,Ah,_l,Rh,lp,Ty,ul,Ob,by,gl,Jd],[[0,{[x]:`OwnerId`,[C]:mD}],[0,{[x]:`InstanceTenancy`,[C]:GT}],[()=>JL,{[x]:`Ipv6CidrBlockAssociationSet`,[C]:Xw}],[()=>KL,{[x]:`CidrBlockAssociationSet`,[C]:fS}],[2,{[x]:`IsDefault`,[C]:Qw}],[()=>zF,{[x]:`EncryptionControl`,[C]:bC}],[()=>IL,{[x]:`TagSet`,[C]:qk}],[()=>hj,{[x]:`BlockPublicAccessStates`,[C]:oS}],[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`State`,[C]:jk}],[0,{[x]:`CidrBlock`,[C]:dS}],[0,{[x]:`DhcpOptionsId`,[C]:nC}]]],LF=[3,w,_b,0,[Zs,gl,yl],[[0,{[x]:`AssociationId`,[C]:wx}],[0,{[x]:`CidrBlock`,[C]:dS}],[()=>RF,{[x]:`CidrBlockState`,[C]:mS}]]],RF=[3,w,yb,0,[by,Wv],[[0,{[x]:`State`,[C]:jk}],[0,{[x]:`StatusMessage`,[C]:lk}]]],zF=[3,w,Sb,0,[Ob,Eb,i_,by,Gv,Uae,Ty],[[0,{[x]:`VpcId`,[C]:hA}],[0,{[x]:`VpcEncryptionControlId`,[C]:pA}],[0,{[x]:`Mode`,[C]:zE}],[0,{[x]:`State`,[C]:jk}],[0,{[x]:`StateMessage`,[C]:uk}],[()=>HF,{[x]:`ResourceExclusions`,[C]:mO}],[()=>IL,{[x]:`TagSet`,[C]:qk}]]],BF=[3,w,Cb,0,[i_,Wh,Bp,Nie,qb,Gb,Mre,Rb,fp],[0,0,0,0,0,0,0,0,0],1],VF=[3,w,wb,0,[by,Gv],[[0,{[x]:`State`,[C]:jk}],[0,{[x]:`StateMessage`,[C]:uk}]]],HF=[3,w,Tb,0,[Bh,zp,u_,Kb,Jb,Gre,Lb,dp],[[()=>VF,{[x]:`InternetGateway`,[C]:eT}],[()=>VF,{[x]:`EgressOnlyInternetGateway`,[C]:zC}],[()=>VF,{[x]:`NatGateway`,[C]:qE}],[()=>VF,{[x]:`VirtualPrivateGateway`,[C]:CA}],[()=>VF,{[x]:`VpcPeering`,[C]:wA}],[()=>VF,{[x]:`Lambda`,[C]:dE}],[()=>VF,{[x]:`VpcLattice`,[C]:vA}],[()=>VF,{[x]:`ElasticFileSystem`,[C]:SC}]]],UF=[3,w,kb,0,[Zs,kh,Mh,s_,Cg,mh,Dg],[[0,{[x]:`AssociationId`,[C]:wx}],[0,{[x]:`Ipv6CidrBlock`,[C]:Yw}],[()=>RF,{[x]:`Ipv6CidrBlockState`,[C]:Zw}],[0,{[x]:`NetworkBorderGroup`,[C]:VE}],[0,{[x]:`Ipv6Pool`,[C]:ET}],[0,{[x]:`Ipv6AddressAttribute`,[C]:zw}],[0,{[x]:`IpSource`,[C]:zT}]]],WF=`unit`,GF=[1,w,nc,0,[()=>GA,{[C]:S}]],KF=[1,w,Ks,0,[0,{[C]:Ys}]],qF=[1,w,Tc,0,[0,{[C]:S}]],JF=[1,w,qs,0,[0,{[C]:Zs}]],YF=[1,w,Mc,0,[()=>lj,{[C]:jc}]],XF=[1,w,Pc,0,[()=>uj,{[C]:S}]],ZF=[1,w,Ic,0,[()=>cj,{[C]:S}]],QF=[1,w,Rc,0,[()=>dj,{[C]:S}]],$F=[1,w,Bc,0,[()=>fj,{[C]:S}]],eI=[1,w,ml,0,[0,{[C]:S}]],tI=[1,w,nl,0,[()=>mj,{[C]:S}]],nI=[1,w,rl,0,[()=>mj,{[C]:tl}]],rI=[1,w,cl,0,[0,{[C]:S}]],iI=[1,w,bl,0,[1,{[C]:S}]],aI=[1,w,xd,0,[()=>YM,{[C]:S}]],oI=[1,w,op,0,[()=>ZM,{[C]:S}]],sI=[1,w,hp,0,[()=>rN,{[C]:S}]],cI=[1,w,xp,0,[()=>iN,{[C]:S}]],lI=[1,w,Tp,0,[()=>oN,{[C]:S}]],uI=[1,w,Ap,0,[()=>aN,{[C]:S}]],dI=[1,w,rp,0,[0,{[C]:ip}]],fI=[1,w,bm,0,[()=>dN,{[C]:Dm}]],pI=[1,w,gm,0,[()=>fN,{[C]:S}]],mI=[1,w,Im,0,[()=>_N,{[C]:S}]],hI=[1,w,zm,0,[()=>bN,{[C]:S}]],gI=[1,w,Bm,0,[0,{[C]:vw}]],_I=[1,w,qm,0,[0,{[C]:Km}]],vI=[1,w,tg,0,[()=>wN,{[C]:S}]],yI=[1,w,dg,0,[0,{[C]:fg}]],bI=[1,w,one,0,[()=>EN,{[C]:S}]],xI=[1,w,Fh,0,[()=>AN,0]],SI=[1,w,Eh,0,[()=>FN,{[C]:S}]],CI=[1,w,Oh,0,[()=>IN,{[C]:S}]],wI=[1,w,ene,0,[0,{[C]:v}]],TI=[1,w,ag,0,[()=>LN,{[C]:S}]],EI=[1,w,Zh,0,[()=>RN,{[C]:S}]],DI=[1,w,og,0,[()=>zN,{[C]:S}]],OI=[1,w,cne,0,[()=>MN,{[C]:S}]],kI=[1,w,yne,0,[()=>GN,{[C]:S}]],AI=[1,w,xne,0,[()=>JN,{[C]:S}]],jI=[1,w,Nne,0,[()=>ZN,{[C]:S}]],MI=[1,w,Qne,0,[()=>QN,{[C]:S}]],NI=[1,w,ere,0,[()=>eP,{[C]:S}]],PI=[1,w,tre,0,[()=>tP,{[C]:S}]],FI=[1,w,rre,0,[()=>nP,{[C]:S}]],II=[1,w,Xne,0,[()=>iP,{[C]:S}]],LI=[1,w,ure,0,[()=>oP,{[C]:S}]],RI=[1,w,Hh,0,[()=>cP,{[C]:S}]],zI=[1,w,Kh,0,[0,{[C]:S}]],BI=[1,w,Jh,0,[()=>sP,{[C]:S}]],VI=[1,w,lne,0,[0,{[C]:S}]],HI=[1,w,Ine,0,[()=>lP,{[C]:S}]],UI=[1,w,Une,0,[()=>uP,{[C]:S}]],WI=[1,w,Lne,0,[()=>dP,{[C]:S}]],GI=[1,w,Rne,0,[()=>fP,{[C]:S}]],KI=[1,w,Wne,0,[()=>pP,{[C]:S}]],qI=[1,w,xre,0,[0,{[C]:Ig}]],JI=[1,w,wre,0,[0,{[C]:Lg}]],YI=[1,w,Dre,0,[()=>hP,{[C]:S}]],XI=[1,w,Pre,0,[()=>_P,{[C]:S}]],ZI=[1,w,Lre,0,[()=>vP,{[C]:S}]],QI=[1,w,Zre,0,[()=>bP,{[C]:S}]],$I=[1,w,jie,0,[()=>kP,{[C]:S}]],eL=[1,w,Aie,0,[()=>AP,{[C]:S}]],tL=[1,w,Pie,0,[0,{[C]:S}]],nL=[1,w,Iie,0,[()=>OP,{[C]:S}]],rL=[1,w,bie,0,[()=>jP,{[C]:S}]],iL=[1,w,wie,0,[()=>PP,{[C]:S}]],aL=[1,w,Vie,0,[0,{[C]:S}]],oL=[1,w,Yie,0,[0,{[C]:E_}]],sL=[1,w,lae,0,[0,{[C]:Hm}]],cL=[1,w,uae,0,[()=>VP,{[C]:S}]],lL=[1,w,dae,0,[0,{[C]:S}]],uL=[1,w,Cae,0,[()=>UP,{[C]:S}]],dL=[1,w,hae,0,[()=>qP,{[C]:S}]],fL=[1,w,rae,0,[()=>YP,{[C]:S}]],pL=[1,w,jae,0,[()=>XP,{[C]:S}]],mL=[1,w,_ae,0,[0,{[C]:R_}]],hL=[1,w,qae,0,[()=>tF,{[C]:S}]],gL=[1,w,noe,0,[()=>eF,{[C]:S}]],_L=[1,w,soe,0,[0,{[C]:$_}]],vL=[1,w,roe,0,[()=>rF,{[C]:S}]],yL=[1,w,_oe,0,[()=>iF,{[C]:S}]],bL=[1,w,Vae,0,[0,{[C]:S}]],xL=[1,w,ioe,0,[()=>lF,{[C]:S}]],SL=[1,w,boe,0,[()=>dF,{[C]:S}]],CL=[1,w,Soe,0,[0,{[C]:S}]],wL=[1,w,woe,0,[()=>uF,{[C]:S}]],TL=[1,w,qoe,0,[0,{[C]:av}]],EL=[1,w,Yoe,0,[()=>hF,{[C]:S}]],DL=[1,w,Zoe,0,[0,{[C]:S}]],OL=[1,w,Qoe,0,[()=>gF,{[C]:S}]],kL=[1,w,ese,0,[0,{[C]:uv}]],AL=[1,w,Ev,0,[0,{[C]:jv}]],jL=[1,w,zv,0,[()=>_F,{[C]:S}]],ML=[1,w,Dv,0,[0,{[C]:Lv}]],NL=[1,w,pv,0,[()=>EF,{[C]:S}]],PL=[1,w,Vv,0,[()=>wF,{[C]:S}]],FL=[1,w,Roe,0,[0,{[C]:S}]],IL=[1,w,Iy,0,[()=>DF,{[C]:S}]],LL=[1,w,Gy,0,[()=>OF,{[C]:S}]],RL=[1,w,Hy,0,[1,{[C]:S}]],zL=[1,w,rb,0,[0,{[C]:S}]],BL=[1,w,sb,0,[()=>jF,{[C]:S}]],VL=[1,w,Yb,0,[0,{[C]:S}]],HL=[1,w,Qb,0,[0,{[C]:S}]],UL=[1,w,hb,0,[()=>PF,{[C]:S}]],WL=[1,w,Mb,0,[0,{[C]:Pb}]],GL=[1,w,zb,0,[()=>NF,{[C]:S}]],KL=[1,w,vb,0,[()=>LF,{[C]:S}]],qL=[1,w,Nb,0,[0,{[C]:Ob}]],JL=[1,w,Ab,0,[()=>UF,{[C]:S}]],YL=[1,w,Bb,0,[()=>IF,{[C]:S}]],XL=[1,w,cx,0,[0,{[C]:S}]],ZL=[1,w,dx,0,[0,{[C]:ux}]],QL=[1,w,mx,0,[0,{[C]:px}]],$L=[9,w,Ms,0,()=>KA,()=>qA],eR=[9,w,Ps,0,()=>JA,()=>YA],tR=[9,w,Us,0,()=>XA,()=>ZA],nR=[9,w,uc,0,()=>QA,()=>$A],rR=[9,w,Vs,0,()=>ej,()=>WF],iR=[9,w,kc,0,()=>tj,()=>PF],aR=[9,w,gc,0,()=>ij,()=>aj],oR=[9,w,xc,0,()=>oj,()=>sj],sR=[9,w,xu,0,()=>Sj,()=>Cj],cR=[9,w,Rl,0,()=>Ej,()=>Dj],lR=[9,w,kl,0,()=>Oj,()=>kj],uR=[9,w,zl,0,()=>Aj,()=>mP],dR=[9,w,Hl,0,()=>jj,()=>Mj],fR=[9,w,Yl,0,()=>Nj,()=>Pj],pR=[9,w,uu,0,()=>Fj,()=>Ij],mR=[9,w,lu,0,()=>Lj,()=>Rj],hR=[9,w,du,0,()=>zj,()=>Bj],gR=[9,w,Cu,0,()=>Vj,()=>_F],_R=[9,w,wu,0,()=>Hj,()=>Uj],vR=[9,w,Lu,0,()=>Wj,()=>NF],yR=[9,w,Ru,0,()=>Gj,()=>Kj],bR=[9,w,cd,0,()=>Yj,()=>WF],xR=[9,w,Md,0,()=>Xj,()=>Zj],SR=[9,w,Vd,0,()=>Qj,()=>$j],CR=[9,w,Xd,0,()=>eM,()=>WF],wR=[9,w,hf,0,()=>tM,()=>WF],TR=[9,w,sf,0,()=>nM,()=>WF],ER=[9,w,_f,0,()=>rM,()=>iM],DR=[9,w,kf,0,()=>aM,()=>WF],OR=[9,w,Af,0,()=>oM,()=>WF],kR=[9,w,qf,0,()=>sM,()=>WF],AR=[9,w,Jf,0,()=>cM,()=>WF],jR=[9,w,Zu,0,()=>lM,()=>uM],MR=[9,w,Xu,0,()=>dM,()=>fM],NR=[9,w,gd,0,()=>pM,()=>mM],PR=[9,w,kd,0,()=>hM,()=>gM],FR=[9,w,Ad,0,()=>_M,()=>vM],IR=[9,w,Od,0,()=>yM,()=>bM],LR=[9,w,pd,0,()=>xM,()=>SM],RR=[9,w,Ld,0,()=>CM,()=>wM],zR=[9,w,Kd,0,()=>TM,()=>EM],BR=[9,w,ef,0,()=>DM,()=>OM],VR=[9,w,gf,0,()=>kM,()=>AM],HR=[9,w,ff,0,()=>jM,()=>MM],UR=[9,w,Sf,0,()=>NM,()=>PM],WR=[9,w,jf,0,()=>FM,()=>IM],GR=[9,w,Mf,0,()=>LM,()=>RM],KR=[9,w,Yf,0,()=>zM,()=>BM],qR=[9,w,Xf,0,()=>VM,()=>HM],JR=[9,w,md,0,()=>UM,()=>WF],YR=[9,w,Zf,0,()=>WM,()=>PF],XR=[9,w,Qu,0,()=>GM,()=>WF],ZR=[9,w,hd,0,()=>KM,()=>qM],QR=[9,w,pf,0,()=>JM,()=>WF],$R=[9,w,jm,0,()=>hN,()=>gN],ez=[9,w,nne,0,()=>DN,()=>ON],tz=[9,w,oie,0,()=>CP,()=>WF],nz=[9,w,uie,0,()=>wP,()=>WF],rz=[9,w,hie,0,()=>TP,()=>EP],iz=[9,w,$ae,0,()=>QP,()=>WF],az=[9,w,Rae,0,()=>nF,()=>WF],oz=[9,w,uoe,0,()=>aF,()=>oF],sz=[9,w,hoe,0,()=>sF,()=>cF],cz=[9,w,toe,0,()=>mF,()=>rF],lz=[9,w,Pv,0,()=>yF,()=>bF],uz=[9,w,Iv,0,()=>SF,()=>CF],dz=[9,w,Ay,0,()=>kF,()=>AF],fz=e=>({apiVersion:`2016-11-15`,base64Decoder:e?.base64Decoder??fe,base64Encoder:e?.base64Encoder??le,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??ws,extensions:e?.extensions??[],httpAuthSchemeProvider:e?.httpAuthSchemeProvider??rs,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new Yi}],logger:e?.logger??new $t,protocol:e?.protocol??Ja,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.ec2`,errorTypeRegistries:BA,xmlNamespace:`http://ec2.amazonaws.com/doc/2016-11-15`,version:`2016-11-15`,serviceTarget:`AmazonEC2`},serviceId:e?.serviceId??`EC2`,urlParser:e?.urlParser??nt,utf8Decoder:e?.utf8Decoder??ce,utf8Encoder:e?.utf8Encoder??me}),pz=e=>{let t=zi(e),n=()=>t().then(Wt),r=fz(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??Ee,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??Ei({serviceId:r.serviceId,clientVersion:ss.version}),maxAttempts:e?.maxAttempts??3,region:e?.region??yt(`Region is missing`),requestHandler:da.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||Nr),sha256:e?.sha256??ee,streamCollector:e?.streamCollector??pa,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},mz=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},hz=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),gz=(e,t)=>{let n=Object.assign(Vi(e),Xt(e),Xn(e),mz(e));return t.forEach(e=>e.configure(n)),Object.assign(e,Hi(n),Zt(n),Zn(n),hz(n))},_z=class extends bt{config;constructor(...[e]){let t=pz(e||{});super(t),this.initConfig=t;let n=gz(is(Nn(zr(Li(Ir(li(as(t))))))),e?.extensions||[]);this.config=n,this.middlewareStack.use(Ot(this.config)),this.middlewareStack.use(Ti(this.config)),this.middlewareStack.use(Lr(this.config)),this.middlewareStack.use(tr(this.config)),this.middlewareStack.use(Hr(this.config)),this.middlewareStack.use(Gr(this.config)),this.middlewareStack.use(Kr(this.config)),this.middlewareStack.use(Zr(this.config,{httpAuthSchemeParametersProvider:ts,identityProviderConfigProvider:async e=>new ii({"aws.auth#sigv4":e.credentials})})),this.middlewareStack.use(ni(this.config))}destroy(){super.destroy()}},vz=e=>typeof e==`string`&&e.indexOf(`arn:`)===0&&e.split(`:`).length>=6,yz=async(e,t,n)=>({operation:Ze(t).operation,region:await re(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()});function bz(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`eks`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var xz=e=>{let t=[];switch(e.operation){default:t.push(bz(e))}return t},Sz=e=>{let t=Qi(e);return Object.assign(t,{authSchemePreference:re(e.authSchemePreference??[])})},Cz=e=>Object.assign(e,{useDualstackEndpoint:e.useDualstackEndpoint??!1,useFipsEndpoint:e.useFipsEndpoint??!1,defaultSigningName:`eks`}),wz={UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},Tz={name:`@aws-sdk/client-eks`,description:`AWS SDK for JavaScript Eks Client for Node.js, Browser and React Native`,version:`3.1063.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-eks`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo eks`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.18`,"@aws-sdk/credential-provider-node":`^3.972.52`,"@aws-sdk/types":`^3.973.11`,"@smithy/core":`^3.24.6`,"@smithy/fetch-http-handler":`^5.4.6`,"@smithy/node-http-handler":`^4.7.6`,"@smithy/types":`^4.14.3`,tslib:`^2.6.2`},devDependencies:{"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/sdk-for-javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-eks`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-eks`}},Ez=`ref`,Dz=-1,Oz=!0,kz=`isSet`,Az=`PartitionResult`,jz=`booleanEquals`,Mz=`getAttr`,Nz=`stringEquals`,Pz={[Ez]:`Endpoint`},Fz={[Ez]:Az},Iz={fn:Mz,argv:[Fz,`name`]},Lz={},Rz=[{[Ez]:`Region`}],zz={conditions:[[kz,[Pz]],[kz,Rz],[`aws.partition`,Rz,Az],[jz,[{[Ez]:`UseFIPS`},Oz]],[jz,[{[Ez]:`UseDualStack`},Oz]],[jz,[{fn:Mz,argv:[Fz,`supportsDualStack`]},Oz]],[jz,[{fn:Mz,argv:[Fz,`supportsFIPS`]},Oz]],[Nz,[Iz,`aws`]],[Nz,[Iz,`aws-us-gov`]]],results:[[Dz],[Dz,`Invalid Configuration: FIPS and custom endpoint are not supported`],[Dz,`Invalid Configuration: Dualstack and custom endpoint are not supported`],[Pz,Lz],[`https://eks-fips.{Region}.{PartitionResult#dualStackDnsSuffix}`,Lz],[Dz,`FIPS and DualStack are enabled, but this partition does not support one or both`],[`https://fips.eks.{Region}.amazonaws.com`,Lz],[`https://eks.{Region}.amazonaws.com`,Lz],[`https://eks-fips.{Region}.{PartitionResult#dnsSuffix}`,Lz],[Dz,`FIPS is enabled but this partition does not support FIPS`],[`https://eks.{Region}.{PartitionResult#dualStackDnsSuffix}`,Lz],[Dz,`DualStack is enabled but this partition does not support DualStack`],[`https://eks.{Region}.{PartitionResult#dnsSuffix}`,Lz],[Dz,`Invalid Configuration: Missing Region`]]},Bz=2,Vz=new Int32Array([-1,1,-1,0,14,3,1,4,100000013,2,5,100000013,3,8,6,4,7,100000012,5,100000010,100000011,4,12,9,6,10,100000009,7,100000006,11,8,100000007,100000008,5,13,100000005,6,100000004,100000005,3,100000001,15,4,100000002,100000003]),Hz=on.from(Vz,Bz,zz.conditions,zz.results),Uz=new sn({size:50,params:[`Endpoint`,`Region`,`UseDualStack`,`UseFIPS`]}),Wz=(e,t={})=>Uz.get(e,()=>jn(Hz,{endpointParams:e,logger:t.logger}));dn.aws=ji;var Gz=class e extends Ht{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},Kz=class e extends Gz{name=`AccessDeniedException`;$fault=`client`;constructor(t){super({name:`AccessDeniedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},qz=class e extends Gz{name=`InvalidParameterException`;$fault=`client`;clusterName;nodegroupName;fargateProfileName;addonName;subscriptionId;constructor(t){super({name:`InvalidParameterException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName,this.nodegroupName=t.nodegroupName,this.fargateProfileName=t.fargateProfileName,this.addonName=t.addonName,this.subscriptionId=t.subscriptionId}},Jz=class e extends Gz{name=`InvalidRequestException`;$fault=`client`;clusterName;nodegroupName;addonName;subscriptionId;constructor(t){super({name:`InvalidRequestException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName,this.nodegroupName=t.nodegroupName,this.addonName=t.addonName,this.subscriptionId=t.subscriptionId}},Yz=class e extends Gz{name=`ResourceNotFoundException`;$fault=`client`;clusterName;nodegroupName;fargateProfileName;addonName;subscriptionId;constructor(t){super({name:`ResourceNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName,this.nodegroupName=t.nodegroupName,this.fargateProfileName=t.fargateProfileName,this.addonName=t.addonName,this.subscriptionId=t.subscriptionId}},Xz=class e extends Gz{name=`ServerException`;$fault=`server`;clusterName;nodegroupName;addonName;subscriptionId;constructor(t){super({name:`ServerException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName,this.nodegroupName=t.nodegroupName,this.addonName=t.addonName,this.subscriptionId=t.subscriptionId}},Zz=class e extends Gz{name=`ClientException`;$fault=`client`;clusterName;nodegroupName;addonName;subscriptionId;constructor(t){super({name:`ClientException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName,this.nodegroupName=t.nodegroupName,this.addonName=t.addonName,this.subscriptionId=t.subscriptionId}},Qz=class e extends Gz{name=`ResourceInUseException`;$fault=`client`;clusterName;nodegroupName;addonName;constructor(t){super({name:`ResourceInUseException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName,this.nodegroupName=t.nodegroupName,this.addonName=t.addonName}},$z=class e extends Gz{name=`ThrottlingException`;$fault=`client`;clusterName;constructor(t){super({name:`ThrottlingException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName}},eB=class e extends Gz{name=`ResourceLimitExceededException`;$fault=`client`;clusterName;nodegroupName;subscriptionId;constructor(t){super({name:`ResourceLimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName,this.nodegroupName=t.nodegroupName,this.subscriptionId=t.subscriptionId}},tB=class e extends Gz{name=`ServiceUnavailableException`;$fault=`server`;constructor(t){super({name:`ServiceUnavailableException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype)}},nB=class e extends Gz{name=`UnsupportedAvailabilityZoneException`;$fault=`client`;clusterName;nodegroupName;validZones;constructor(t){super({name:`UnsupportedAvailabilityZoneException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName,this.nodegroupName=t.nodegroupName,this.validZones=t.validZones}},rB=class e extends Gz{name=`BadRequestException`;$fault=`client`;constructor(t){super({name:`BadRequestException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},iB=class e extends Gz{name=`NotFoundException`;$fault=`client`;constructor(t){super({name:`NotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},aB=class e extends Gz{name=`ResourcePropagationDelayException`;$fault=`client`;constructor(t){super({name:`ResourcePropagationDelayException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},oB=class e extends Gz{name=`InvalidStateException`;$fault=`client`;clusterName;constructor(t){super({name:`InvalidStateException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.clusterName=t.clusterName}},sB=`Addon`,cB=`AssociatedAccessPolicy`,lB=`AssociatedAccessPoliciesList`,uB=`AssociateAccessPolicyRequest`,dB=`AssociateAccessPolicyResponse`,fB=`AssociateAccessPolicy`,pB=`AccessConfigResponse`,mB=`AccessDeniedException`,hB=`AccessEntry`,gB=`AddonHealth`,_B=`AddonInfo`,vB=`AddonIssueList`,yB=`AddonIssue`,bB=`AddonNamespaceConfigRequest`,xB=`AddonNamespaceConfigResponse`,SB=`AccessPolicy`,CB=`AddonPodIdentityAssociations`,wB=`AddonPodIdentityAssociationsList`,TB=`AccessPoliciesList`,EB=`AccessScope`,DB=`AutoScalingGroup`,OB=`AutoScalingGroupList`,kB=`AddonVersionInfo`,AB=`AddonVersionInfoList`,jB=`Addons`,MB=`BadRequestException`,NB=`BlockStorage`,PB=`CreateAddon`,FB=`CreateAccessConfigRequest`,IB=`CreateAccessEntry`,LB=`CreateAccessEntryRequest`,RB=`CreateAccessEntryResponse`,zB=`CreateAddonRequest`,BB=`CreateAddonResponse`,VB=`ComputeConfigRequest`,HB=`ComputeConfigResponse`,UB=`ConnectorConfigResponse`,WB=`CreateClusterRequest`,GB=`CreateClusterResponse`,KB=`CreateCluster`,qB=`ClientException`,JB=`ClusterHealth`,YB=`ClusterIssueList`,XB=`ClusterIssue`,ZB=`CreateNodegroup`,QB=`CreateNodegroupRequest`,$B=`CreateNodegroupResponse`,eV=`ControlPlanePlacementRequest`,tV=`ControlPlanePlacementResponse`,nV=`ControlPlaneScalingConfig`,rV=`Certificate`,iV=`Cluster`,aV=`Compatibility`,oV=`Compatibilities`,sV=`DeleteAddon`,cV=`DeleteAccessEntry`,lV=`DeleteAccessEntryRequest`,uV=`DeleteAccessEntryResponse`,dV=`DescribeAccessEntryRequest`,fV=`DescribeAccessEntryResponse`,pV=`DescribeAccessEntry`,mV=`DisassociateAccessPolicy`,hV=`DisassociateAccessPolicyRequest`,gV=`DisassociateAccessPolicyResponse`,_V=`DeleteAddonRequest`,vV=`DeleteAddonResponse`,yV=`DescribeAddonRequest`,bV=`DescribeAddonResponse`,xV=`DescribeAddonVersions`,SV=`DescribeAddonVersionsRequest`,CV=`DescribeAddonVersionsResponse`,wV=`DescribeAddon`,TV=`DeleteClusterRequest`,EV=`DeleteClusterResponse`,DV=`DescribeClusterRequest`,OV=`DescribeClusterResponse`,kV=`DeleteCluster`,AV=`DescribeCluster`,jV=`DeleteNodegroup`,MV=`DeleteNodegroupRequest`,NV=`DeleteNodegroupResponse`,PV=`DescribeNodegroupRequest`,FV=`DescribeNodegroupResponse`,IV=`DescribeNodegroup`,LV=`EncryptionConfig`,RV=`EncryptionConfigList`,zV=`ErrorDetail`,BV=`ErrorDetails`,VV=`ElasticLoadBalancing`,HV=`Identity`,UV=`IssueList`,WV=`InvalidParameterException`,GV=`InvalidRequestException`,KV=`InvalidStateException`,qV=`Issue`,JV=`KubernetesNetworkConfigRequest`,YV=`KubernetesNetworkConfigResponse`,XV=`ListAddons`,ZV=`ListAssociatedAccessPolicies`,QV=`ListAssociatedAccessPoliciesRequest`,$V=`ListAssociatedAccessPoliciesResponse`,eH=`ListAccessEntries`,tH=`ListAccessEntriesRequest`,nH=`ListAccessEntriesResponse`,rH=`ListAccessPolicies`,iH=`ListAccessPoliciesRequest`,aH=`ListAccessPoliciesResponse`,oH=`ListAddonsRequest`,sH=`ListAddonsResponse`,cH=`ListClustersRequest`,lH=`ListClustersResponse`,uH=`ListClusters`,dH=`ListNodegroups`,fH=`ListNodegroupsRequest`,pH=`ListNodegroupsResponse`,mH=`LogSetup`,hH=`LogSetups`,gH=`LaunchTemplateSpecification`,_H=`Logging`,vH=`MarketplaceInformation`,yH=`Nodegroup`,bH=`NotFoundException`,xH=`NodegroupHealth`,SH=`NodegroupResources`,CH=`NodeRepairConfig`,wH=`NodeRepairConfigOverrides`,TH=`NodeRepairConfigOverridesList`,EH=`NodegroupScalingConfig`,DH=`NodegroupUpdateConfig`,OH=`OutpostConfigRequest`,kH=`OutpostConfigResponse`,AH=`OIDC`,jH=`Provider`,MH=`RemoteAccessConfig`,NH=`ResourceInUseException`,PH=`ResourceLimitExceededException`,FH=`RemoteNetworkConfigRequest`,IH=`RemoteNetworkConfigResponse`,LH=`ResourceNotFoundException`,RH=`RemoteNodeNetwork`,zH=`RemoteNodeNetworkList`,BH=`ResourcePropagationDelayException`,VH=`RemotePodNetwork`,HH=`RemotePodNetworkList`,UH=`StorageConfigRequest`,WH=`StorageConfigResponse`,GH=`ServerException`,KH=`ServiceUnavailableException`,qH=`Taint`,JH=`ThrottlingException`,YH=`Update`,XH=`UpdateAddon`,ZH=`UpdateAddonRequest`,QH=`UpdateAddonResponse`,$H=`UnsupportedAvailabilityZoneException`,eU=`UpdateLabelsPayload`,tU=`UpdateNodegroupConfig`,nU=`UpdateNodegroupConfigRequest`,rU=`UpdateNodegroupConfigResponse`,iU=`UpdateNodegroupVersion`,aU=`UpdateNodegroupVersionRequest`,oU=`UpdateNodegroupVersionResponse`,sU=`UpdateParam`,cU=`UpgradePolicyRequest`,lU=`UpgradePolicyResponse`,uU=`UpdateParams`,dU=`UpdateTaintsPayload`,fU=`VpcConfigRequest`,pU=`VpcConfigResponse`,mU=`WarmPoolConfig`,hU=`ZonalShiftConfigRequest`,gU=`ZonalShiftConfigResponse`,_U=`arn`,vU=`addonArn`,yU=`associatedAccessPolicy`,bU=`associatedAccessPolicies`,xU=`associatedAt`,SU=`accessConfig`,CU=`activationCode`,wU=`activationExpiry`,TU=`accessEntryArn`,EU=`accessEntry`,DU=`accessEntries`,OU=`activationId`,kU=`authenticationMode`,AU=`addonName`,jU=`addOrUpdateLabels`,MU=`addOrUpdateTaints`,NU=`accessPolicies`,PU=`associatedPolicyArn`,FU=`accessScope`,IU=`autoScalingGroups`,LU=`amiType`,RU=`addonVersion`,zU=`addonVersions`,BU=`addon`,VU=`addons`,HU=`architecture`,UU=`bootstrapClusterCreatorAdminPermissions`,WU=`blockStorage`,GU=`bootstrapSelfManagedAddons`,KU=`client`,qU=`createdAt`,JU=`certificateAuthority`,YU=`connectorConfig`,XU=`computeConfig`,ZU=`clusterLogging`,T=`clusterName`,QU=`controlPlaneInstanceType`,$U=`controlPlanePlacement`,eW=`controlPlaneScalingConfig`,tW=`clientRequestToken`,nW=`clusterSecurityGroupId`,rW=`computeTypes`,iW=`capacityType`,aW=`configurationValues`,oW=`clusterVersion`,sW=`cidrs`,cW=`cluster`,lW=`clusters`,uW=`code`,dW=`compatibilities`,fW=`data`,pW=`defaultNamespace`,mW=`deletionProtection`,hW=`diskSize`,gW=`desiredSize`,_W=`defaultVersion`,vW=`error`,yW=`encryptionConfig`,bW=`errorCode`,xW=`elasticLoadBalancing`,SW=`errorMessage`,CW=`endpointPublicAccess`,wW=`endpointPrivateAccess`,TW=`ec2SshKey`,EW=`effect`,DW=`enabled`,OW=`endpoint`,kW=`errors`,AW=`fargateProfileName`,jW=`force`,MW=`groupName`,NW=`health`,PW=`httpError`,E=`httpQuery`,D=`http`,FW=`issues`,IW=`ipFamily`,LW=`instanceTypes`,RW=`id`,zW=`identity`,BW=`include`,VW=`issuer`,HW=`key`,UW=`keyArn`,WW=`kubernetesGroups`,GW=`kubernetesNetworkConfig`,KW=`kubernetesVersion`,qW=`logging`,JW=`launchTemplate`,YW=`labels`,XW=`message`,ZW=`modifiedAt`,QW=`maxGroupPreparedCapacity`,$W=`marketplaceInformation`,eG=`maxParallelNodesRepairedCount`,tG=`maxParallelNodesRepairedPercentage`,nG=`maxResults`,rG=`minRepairWaitTimeMins`,iG=`minSize`,aG=`maxSize`,oG=`maxUnavailable`,sG=`maxUnhealthyNodeThresholdCount`,cG=`maxUnhealthyNodeThresholdPercentage`,lG=`maxUnavailablePercentage`,uG=`name`,dG=`nodegroupArn`,fG=`namespaceConfig`,pG=`nodeMonitoringCondition`,mG=`nodegroupName`,hG=`nodePools`,gG=`nodeRole`,_G=`nodeRoleArn`,vG=`nodeRepairConfig`,yG=`nodeRepairConfigOverrides`,O=`nextToken`,bG=`nodeUnhealthyReason`,xG=`namespaces`,SG=`namespace`,CG=`nodegroup`,wG=`nodegroups`,TG=`owner`,EG=`outpostArns`,DG=`outpostConfig`,OG=`oidc`,kG=`owners`,AG=`publisher`,jG=`principalArn`,MG=`publicAccessCidrs`,NG=`policyArn`,PG=`productId`,FG=`podIdentityAssociations`,IG=`poolState`,LG=`productUrl`,RG=`platformVersion`,zG=`platformVersions`,BG=`params`,VG=`provider`,HG=`preserve`,UG=`publishers`,WG=`roleArn`,GG=`remoteAccessSecurityGroup`,KG=`remoteAccess`,qG=`repairAction`,JG=`requiresConfiguration`,YG=`resolveConflicts`,XG=`resourceIds`,ZG=`requiresIamPermissions`,QG=`removeLabels`,$G=`remoteNetworkConfig`,eK=`remoteNodeNetworks`,tK=`reuseOnScaleIn`,nK=`remotePodNetworks`,rK=`removeTaints`,iK=`releaseVersion`,aK=`resourcesVpcConfig`,oK=`resources`,sK=`smithy.ts.sdk.synthetic.com.amazonaws.eks`,cK=`serviceAccount`,lK=`serviceAccountRoleArn`,uK=`storageConfig`,dK=`scalingConfig`,fK=`securityGroupIds`,pK=`subscriptionId`,mK=`serviceIpv4Cidr`,hK=`serviceIpv6Cidr`,gK=`subnetIds`,_K=`sourceSecurityGroups`,vK=`supportType`,yK=`server`,bK=`status`,xK=`subnets`,SK=`tags`,CK=`taintsList`,wK=`taints`,TK=`tier`,EK=`type`,DK=`types`,OK=`username`,kK=`updateConfig`,AK=`upgradePolicy`,jK=`updateStrategy`,MK=`update`,NK=`version`,PK=`vpcId`,FK=`validZones`,IK=`value`,LK=`warmPoolConfig`,RK=`zonalShiftConfig`,k=`com.amazonaws.eks`,zK=Lt.for(sK),BK=[-3,sK,`EKSServiceException`,0,[],[]];zK.registerError(BK,Gz);var VK=Lt.for(k),HK=[-3,k,mB,{[vW]:KU,[PW]:403},[XW],[0]];VK.registerError(HK,Kz);var UK=[-3,k,MB,{[vW]:KU,[PW]:400},[XW],[0]];VK.registerError(UK,rB);var WK=[-3,k,qB,{[vW]:KU,[PW]:400},[T,mG,AU,pK,XW],[0,0,0,0,0]];VK.registerError(WK,Zz);var GK=[-3,k,WV,{[vW]:KU,[PW]:400},[T,mG,AW,AU,pK,XW],[0,0,0,0,0,0]];VK.registerError(GK,qz);var KK=[-3,k,GV,{[vW]:KU,[PW]:400},[T,mG,AU,pK,XW],[0,0,0,0,0]];VK.registerError(KK,Jz);var qK=[-3,k,KV,{[vW]:KU,[PW]:400},[T,XW],[0,0]];VK.registerError(qK,oB);var JK=[-3,k,bH,{[vW]:KU,[PW]:404},[XW],[0]];VK.registerError(JK,iB);var YK=[-3,k,NH,{[vW]:KU,[PW]:409},[T,mG,AU,XW],[0,0,0,0]];VK.registerError(YK,Qz);var XK=[-3,k,PH,{[vW]:KU,[PW]:400},[T,mG,pK,XW],[0,0,0,0]];VK.registerError(XK,eB);var ZK=[-3,k,LH,{[vW]:KU,[PW]:404},[T,mG,AW,AU,pK,XW],[0,0,0,0,0,0]];VK.registerError(ZK,Yz);var QK=[-3,k,BH,{[vW]:KU,[PW]:428},[XW],[0]];VK.registerError(QK,aB);var $K=[-3,k,GH,{[vW]:yK,[PW]:500},[T,mG,AU,pK,XW],[0,0,0,0,0]];VK.registerError($K,Xz);var eq=[-3,k,KH,{[vW]:yK,[PW]:503},[XW],[0]];VK.registerError(eq,tB);var tq=[-3,k,JH,{[vW]:KU,[PW]:429},[T,XW],[0,0]];VK.registerError(tq,$z);var nq=[-3,k,$H,{[vW]:KU,[PW]:400},[XW,T,mG,FK],[0,0,0,64]];VK.registerError(nq,nB);var rq=[zK,VK],iq=[3,k,pB,0,[UU,kU],[2,0]],aq=[3,k,hB,0,[T,jG,WW,TU,qU,ZW,SK,OK,EK],[0,0,64,0,4,4,128,0,0]],oq=[3,k,SB,0,[uG,_U],[0,0]],sq=[3,k,EB,0,[EK,xG],[0,64]],cq=[3,k,sB,0,[AU,T,bK,RU,NW,vU,qU,ZW,lK,SK,AG,TG,$W,aW,FG,fG],[0,0,0,0,()=>lq,0,4,4,0,128,0,0,()=>kJ,0,64,()=>pq]],lq=[3,k,gB,0,[FW],[()=>pY]],uq=[3,k,_B,0,[AU,EK,zU,AG,TG,$W,pW],[0,0,()=>gY,0,0,()=>kJ,0]],dq=[3,k,yB,0,[uW,XW,XG],[0,0,64]],fq=[3,k,bB,0,[SG],[0]],pq=[3,k,xB,0,[SG],[0]],mq=[3,k,CB,0,[cK,WG],[0,0],2],hq=[3,k,kB,0,[RU,HU,rW,dW,JG,ZG],[0,64,64,()=>bY,2,2]],gq=[3,k,uB,0,[T,jG,NG,FU],[[0,1],[0,1],0,()=>sq],4],_q=[3,k,dB,0,[T,jG,yU],[0,0,()=>vq]],vq=[3,k,cB,0,[NG,FU,xU,ZW],[0,()=>sq,4,4]],yq=[3,k,DB,0,[uG],[0]],bq=[3,k,NB,0,[DW],[2]],xq=[3,k,rV,0,[fW],[0]],Sq=[3,k,iV,0,[uG,_U,qU,NK,OW,WG,aK,GW,qW,zW,bK,JU,tW,RG,SK,yW,YU,RW,NW,DG,SU,AK,RK,$G,XU,uK,mW,eW],[0,0,4,0,0,0,()=>cY,()=>pJ,()=>DJ,()=>uJ,0,()=>xq,0,0,128,()=>xY,()=>Oq,0,()=>Cq,()=>zJ,()=>iq,()=>oY,()=>dY,()=>UJ,()=>Dq,()=>qJ,2,()=>jq]],Cq=[3,k,JB,0,[FW],[()=>yY]],wq=[3,k,XB,0,[uW,XW,XG],[0,0,64]],Tq=[3,k,aV,0,[oW,zG,_W],[0,64,2]],Eq=[3,k,VB,0,[DW,hG,_G],[2,64,0]],Dq=[3,k,HB,0,[DW,hG,_G],[2,64,0]],Oq=[3,k,UB,0,[OU,CU,wU,VG,WG],[0,0,4,0,0]],kq=[3,k,eV,0,[MW],[0]],Aq=[3,k,tV,0,[MW],[0]],jq=[3,k,nV,0,[TK],[0]],Mq=[3,k,FB,0,[UU,kU],[2,0]],Nq=[3,k,LB,0,[T,jG,WW,SK,tW,OK,EK],[[0,1],0,64,128,[0,4],0,0],2],Pq=[3,k,RB,0,[EU],[()=>aq]],Fq=[3,k,zB,0,[T,AU,RU,lK,YG,tW,SK,aW,FG,fG],[[0,1],0,0,0,0,[0,4],128,0,()=>mY,()=>fq],2],Iq=[3,k,BB,0,[BU],[()=>cq]],Lq=[3,k,WB,0,[uG,WG,aK,NK,GW,qW,tW,SK,yW,DG,SU,GU,AK,RK,$G,XU,uK,mW,eW],[0,0,()=>sY,0,()=>fJ,()=>DJ,[0,4],128,()=>xY,()=>RJ,()=>Mq,2,()=>aY,()=>uY,()=>HJ,()=>Eq,()=>KJ,2,()=>jq],3],Rq=[3,k,GB,0,[cW],[()=>Sq]],zq=[3,k,QB,0,[T,mG,xK,gG,dK,hW,LW,LU,KG,YW,wK,SK,tW,JW,kK,vG,iW,NK,iK,LK],[[0,1],0,64,0,()=>NJ,1,64,0,()=>VJ,128,()=>OY,128,[0,4],()=>mJ,()=>PJ,()=>FJ,0,0,0,()=>lY],4],Bq=[3,k,$B,0,[CG],[()=>AJ]],Vq=[3,k,lV,0,[T,jG],[[0,1],[0,1]],2],Hq=[3,k,uV,0,[],[]],Uq=[3,k,_V,0,[T,AU,HG],[[0,1],[0,1],[2,{[E]:HG}]],2],Wq=[3,k,vV,0,[BU],[()=>cq]],Gq=[3,k,TV,0,[uG],[[0,1]],1],Kq=[3,k,EV,0,[cW],[()=>Sq]],qq=[3,k,MV,0,[T,mG],[[0,1],[0,1]],2],Jq=[3,k,NV,0,[CG],[()=>AJ]],Yq=[3,k,dV,0,[T,jG],[[0,1],[0,1]],2],Xq=[3,k,fV,0,[EU],[()=>aq]],Zq=[3,k,yV,0,[T,AU],[[0,1],[0,1]],2],Qq=[3,k,bV,0,[BU],[()=>cq]],$q=[3,k,SV,0,[KW,nG,O,AU,DK,UG,kG],[[0,{[E]:KW}],[1,{[E]:nG}],[0,{[E]:O}],[0,{[E]:AU}],[64,{[E]:DK}],[64,{[E]:UG}],[64,{[E]:kG}]]],eJ=[3,k,CV,0,[VU,O],[()=>hY,0]],tJ=[3,k,DV,0,[uG],[[0,1]],1],nJ=[3,k,OV,0,[cW],[()=>Sq]],rJ=[3,k,PV,0,[T,mG],[[0,1],[0,1]],2],iJ=[3,k,FV,0,[CG],[()=>AJ]],aJ=[3,k,hV,0,[T,jG,NG],[[0,1],[0,1],[0,1]],3],oJ=[3,k,gV,0,[],[]],sJ=[3,k,VV,0,[DW],[2]],cJ=[3,k,LV,0,[oK,VG],[64,()=>BJ]],lJ=[3,k,zV,0,[bW,SW,XG],[0,0,64]],uJ=[3,k,HV,0,[OG],[()=>LJ]],dJ=[3,k,qV,0,[uW,XW,XG],[0,0,64]],fJ=[3,k,JV,0,[mK,IW,xW],[0,0,()=>sJ]],pJ=[3,k,YV,0,[mK,hK,IW,xW],[0,0,0,()=>sJ]],mJ=[3,k,gH,0,[uG,NK,RW],[0,0,0]],hJ=[3,k,tH,0,[T,PU,nG,O],[[0,1],[0,{[E]:PU}],[1,{[E]:nG}],[0,{[E]:O}]],1],gJ=[3,k,nH,0,[DU,O],[64,0]],_J=[3,k,iH,0,[nG,O],[[1,{[E]:nG}],[0,{[E]:O}]]],vJ=[3,k,aH,0,[NU,O],[()=>fY,0]],yJ=[3,k,oH,0,[T,nG,O],[[0,1],[1,{[E]:nG}],[0,{[E]:O}]],1],bJ=[3,k,sH,0,[VU,O],[64,0]],xJ=[3,k,QV,0,[T,jG,nG,O],[[0,1],[0,1],[1,{[E]:nG}],[0,{[E]:O}]],2],SJ=[3,k,$V,0,[T,jG,O,bU],[0,0,0,()=>_Y]],CJ=[3,k,cH,0,[nG,O,BW],[[1,{[E]:nG}],[0,{[E]:O}],[64,{[E]:BW}]]],wJ=[3,k,lH,0,[lW,O],[64,0]],TJ=[3,k,fH,0,[T,nG,O],[[0,1],[1,{[E]:nG}],[0,{[E]:O}]],1],EJ=[3,k,pH,0,[wG,O],[64,0]],DJ=[3,k,_H,0,[ZU],[()=>wY]],OJ=[3,k,mH,0,[DK,DW],[64,2]],kJ=[3,k,vH,0,[PG,LG],[0,0]],AJ=[3,k,yH,0,[mG,dG,T,NK,iK,qU,ZW,bK,iW,dK,LW,xK,KG,LU,gG,YW,wK,oK,hW,NW,kK,vG,JW,SK,LK],[0,0,0,0,0,4,4,0,0,()=>NJ,64,64,()=>VJ,0,0,128,()=>OY,()=>MJ,1,()=>jJ,()=>PJ,()=>FJ,()=>mJ,128,()=>lY]],jJ=[3,k,xH,0,[FW],[()=>CY]],MJ=[3,k,SH,0,[IU,GG],[()=>vY,0]],NJ=[3,k,EH,0,[iG,aG,gW],[1,1,1]],PJ=[3,k,DH,0,[oG,lG,jK],[1,1,0]],FJ=[3,k,CH,0,[DW,sG,cG,eG,tG,yG],[2,1,1,1,1,()=>TY]],IJ=[3,k,wH,0,[pG,bG,rG,qG],[0,0,1,0]],LJ=[3,k,AH,0,[VW],[0]],RJ=[3,k,OH,0,[EG,QU,$U],[64,0,()=>kq],2],zJ=[3,k,kH,0,[EG,QU,$U],[64,0,()=>Aq],2],BJ=[3,k,jH,0,[UW],[0]],VJ=[3,k,MH,0,[TW,_K],[0,64]],HJ=[3,k,FH,0,[eK,nK],[()=>EY,()=>DY]],UJ=[3,k,IH,0,[eK,nK],[()=>EY,()=>DY]],WJ=[3,k,RH,0,[sW],[64]],GJ=[3,k,VH,0,[sW],[64]],KJ=[3,k,UH,0,[WU],[()=>bq]],qJ=[3,k,WH,0,[WU],[()=>bq]],JJ=[3,k,qH,0,[HW,IK,EW],[0,0,0]],YJ=[3,k,YH,0,[RW,bK,EK,BG,qU,kW],[0,0,0,()=>kY,4,()=>SY]],XJ=[3,k,ZH,0,[T,AU,RU,lK,YG,tW,aW,FG],[[0,1],[0,1],0,0,0,[0,4],0,()=>mY],2],ZJ=[3,k,QH,0,[MK],[()=>YJ]],QJ=[3,k,eU,0,[jU,QG],[128,64]],$J=[3,k,nU,0,[T,mG,YW,wK,dK,kK,vG,LK,tW],[[0,1],[0,1],()=>QJ,()=>iY,()=>NJ,()=>PJ,()=>FJ,()=>lY,[0,4]],2],eY=[3,k,rU,0,[MK],[()=>YJ]],tY=[3,k,aU,0,[T,mG,NK,iK,JW,jW,tW],[[0,1],[0,1],0,0,()=>mJ,2,[0,4]],2],nY=[3,k,oU,0,[MK],[()=>YJ]],rY=[3,k,sU,0,[EK,IK],[0,0]],iY=[3,k,dU,0,[MU,rK],[()=>OY,()=>OY]],aY=[3,k,cU,0,[vK],[0]],oY=[3,k,lU,0,[vK],[0]],sY=[3,k,fU,0,[gK,fK,CW,wW,MG],[64,64,2,2,64]],cY=[3,k,pU,0,[gK,fK,nW,PK,CW,wW,MG],[64,64,0,0,2,2,64]],lY=[3,k,mU,0,[DW,iG,QW,IG,tK],[2,1,1,0,2]],uY=[3,k,hU,0,[DW],[2]],dY=[3,k,gU,0,[DW],[2]],fY=[1,k,TB,0,()=>oq],pY=[1,k,vB,0,()=>dq],mY=[1,k,wB,0,()=>mq],hY=[1,k,jB,0,()=>uq],gY=[1,k,AB,0,()=>hq],_Y=[1,k,lB,0,()=>vq],vY=[1,k,OB,0,()=>yq],yY=[1,k,YB,0,()=>wq],bY=[1,k,oV,0,()=>Tq],xY=[1,k,RV,0,()=>cJ],SY=[1,k,BV,0,()=>lJ],CY=[1,k,UV,0,()=>dJ],wY=[1,k,hH,0,()=>OJ],TY=[1,k,TH,0,()=>IJ],EY=[1,k,zH,0,()=>WJ],DY=[1,k,HH,0,()=>GJ],OY=[1,k,CK,0,()=>JJ],kY=[1,k,uU,0,()=>rY],AY=[9,k,fB,{[D]:[`POST`,`/clusters/{clusterName}/access-entries/{principalArn}/access-policies`,200]},()=>gq,()=>_q],jY=[9,k,IB,{[D]:[`POST`,`/clusters/{clusterName}/access-entries`,200]},()=>Nq,()=>Pq],MY=[9,k,PB,{[D]:[`POST`,`/clusters/{clusterName}/addons`,200]},()=>Fq,()=>Iq],NY=[9,k,KB,{[D]:[`POST`,`/clusters`,200]},()=>Lq,()=>Rq],PY=[9,k,ZB,{[D]:[`POST`,`/clusters/{clusterName}/node-groups`,200]},()=>zq,()=>Bq],FY=[9,k,cV,{[D]:[`DELETE`,`/clusters/{clusterName}/access-entries/{principalArn}`,200]},()=>Vq,()=>Hq],IY=[9,k,sV,{[D]:[`DELETE`,`/clusters/{clusterName}/addons/{addonName}`,200]},()=>Uq,()=>Wq],LY=[9,k,kV,{[D]:[`DELETE`,`/clusters/{name}`,200]},()=>Gq,()=>Kq],RY=[9,k,jV,{[D]:[`DELETE`,`/clusters/{clusterName}/node-groups/{nodegroupName}`,200]},()=>qq,()=>Jq],zY=[9,k,pV,{[D]:[`GET`,`/clusters/{clusterName}/access-entries/{principalArn}`,200]},()=>Yq,()=>Xq],BY=[9,k,wV,{[D]:[`GET`,`/clusters/{clusterName}/addons/{addonName}`,200]},()=>Zq,()=>Qq],VY=[9,k,xV,{[D]:[`GET`,`/addons/supported-versions`,200]},()=>$q,()=>eJ],HY=[9,k,AV,{[D]:[`GET`,`/clusters/{name}`,200]},()=>tJ,()=>nJ],UY=[9,k,IV,{[D]:[`GET`,`/clusters/{clusterName}/node-groups/{nodegroupName}`,200]},()=>rJ,()=>iJ],WY=[9,k,mV,{[D]:[`DELETE`,`/clusters/{clusterName}/access-entries/{principalArn}/access-policies/{policyArn}`,200]},()=>aJ,()=>oJ],GY=[9,k,eH,{[D]:[`GET`,`/clusters/{clusterName}/access-entries`,200]},()=>hJ,()=>gJ],KY=[9,k,rH,{[D]:[`GET`,`/access-policies`,200]},()=>_J,()=>vJ],qY=[9,k,XV,{[D]:[`GET`,`/clusters/{clusterName}/addons`,200]},()=>yJ,()=>bJ],JY=[9,k,ZV,{[D]:[`GET`,`/clusters/{clusterName}/access-entries/{principalArn}/access-policies`,200]},()=>xJ,()=>SJ],YY=[9,k,uH,{[D]:[`GET`,`/clusters`,200]},()=>CJ,()=>wJ],XY=[9,k,dH,{[D]:[`GET`,`/clusters/{clusterName}/node-groups`,200]},()=>TJ,()=>EJ],ZY=[9,k,XH,{[D]:[`POST`,`/clusters/{clusterName}/addons/{addonName}/update`,200]},()=>XJ,()=>ZJ],QY=[9,k,tU,{[D]:[`POST`,`/clusters/{clusterName}/node-groups/{nodegroupName}/update-config`,200]},()=>$J,()=>eY],$Y=[9,k,iU,{[D]:[`POST`,`/clusters/{clusterName}/node-groups/{nodegroupName}/update-version`,200]},()=>tY,()=>nY],eX=e=>({apiVersion:`2017-11-01`,base64Decoder:e?.base64Decoder??fe,base64Encoder:e?.base64Encoder??le,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??Wz,extensions:e?.extensions??[],httpAuthSchemeProvider:e?.httpAuthSchemeProvider??xz,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new Yi}],logger:e?.logger??new $t,protocol:e?.protocol??Pa,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.eks`,errorTypeRegistries:rq,version:`2017-11-01`,serviceTarget:`AWSWesleyFrontend`},serviceId:e?.serviceId??`EKS`,urlParser:e?.urlParser??nt,utf8Decoder:e?.utf8Decoder??ce,utf8Encoder:e?.utf8Encoder??me}),tX=e=>{let t=zi(e),n=()=>t().then(Wt),r=eX(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??Ee,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??Ei({serviceId:r.serviceId,clientVersion:Tz.version}),maxAttempts:e?.maxAttempts??3,region:e?.region??yt(`Region is missing`),requestHandler:da.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||Nr),sha256:e?.sha256??ee,streamCollector:e?.streamCollector??pa,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},nX=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},rX=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),iX=(e,t)=>{let n=Object.assign(Vi(e),Xt(e),Xn(e),nX(e));return t.forEach(e=>e.configure(n)),Object.assign(e,Hi(n),Zt(n),Zn(n),rX(n))},aX=class extends bt{config;constructor(...[e]){let t=tX(e||{});super(t),this.initConfig=t;let n=iX(Sz(Nn(zr(Li(Ir(li(Cz(t))))))),e?.extensions||[]);this.config=n,this.middlewareStack.use(Ot(this.config)),this.middlewareStack.use(Ti(this.config)),this.middlewareStack.use(Lr(this.config)),this.middlewareStack.use(tr(this.config)),this.middlewareStack.use(Hr(this.config)),this.middlewareStack.use(Gr(this.config)),this.middlewareStack.use(Kr(this.config)),this.middlewareStack.use(Zr(this.config,{httpAuthSchemeParametersProvider:yz,identityProviderConfigProvider:async e=>new ii({"aws.auth#sigv4":e.credentials})})),this.middlewareStack.use(ni(this.config))}destroy(){super.destroy()}},oX=async(e,t,n)=>({operation:Ze(t).operation,region:await re(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()});function sX(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`elasticloadbalancing`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var cX=e=>{let t=[];switch(e.operation){default:t.push(sX(e))}return t},lX=e=>{let t=Qi(e);return Object.assign(t,{authSchemePreference:re(e.authSchemePreference??[])})},uX=e=>Object.assign(e,{useDualstackEndpoint:e.useDualstackEndpoint??!1,useFipsEndpoint:e.useFipsEndpoint??!1,defaultSigningName:`elasticloadbalancing`}),dX={UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},fX={name:`@aws-sdk/client-elastic-load-balancing-v2`,description:`AWS SDK for JavaScript Elastic Load Balancing V2 Client for Node.js, Browser and React Native`,version:`3.1063.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-elastic-load-balancing-v2`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo elastic-load-balancing-v2`,"test:e2e":`yarn g:vitest run -c vitest.config.e2e.mts --mode development`,"test:e2e:watch":`yarn g:vitest watch -c vitest.config.e2e.mts`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.18`,"@aws-sdk/credential-provider-node":`^3.972.52`,"@aws-sdk/types":`^3.973.11`,"@smithy/core":`^3.24.6`,"@smithy/fetch-http-handler":`^5.4.6`,"@smithy/node-http-handler":`^4.7.6`,"@smithy/types":`^4.14.3`,tslib:`^2.6.2`},devDependencies:{"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/sdk-for-javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-elastic-load-balancing-v2`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-elastic-load-balancing-v2`}},pX=`ref`,mX=-1,hX=!0,gX=`isSet`,_X=`PartitionResult`,vX=`booleanEquals`,yX=`getAttr`,bX={[pX]:`Endpoint`},xX={[pX]:_X},SX={},CX=[{[pX]:`Region`}],wX={conditions:[[gX,[bX]],[gX,CX],[`aws.partition`,CX,_X],[vX,[{[pX]:`UseFIPS`},hX]],[vX,[{[pX]:`UseDualStack`},hX]],[vX,[{fn:yX,argv:[xX,`supportsDualStack`]},hX]],[vX,[{fn:yX,argv:[xX,`supportsFIPS`]},hX]],[`stringEquals`,[{fn:yX,argv:[xX,`name`]},`aws-us-gov`]]],results:[[mX],[mX,`Invalid Configuration: FIPS and custom endpoint are not supported`],[mX,`Invalid Configuration: Dualstack and custom endpoint are not supported`],[bX,SX],[`https://elasticloadbalancing-fips.{Region}.{PartitionResult#dualStackDnsSuffix}`,SX],[mX,`FIPS and DualStack are enabled, but this partition does not support one or both`],[`https://elasticloadbalancing.{Region}.amazonaws.com`,SX],[`https://elasticloadbalancing-fips.{Region}.{PartitionResult#dnsSuffix}`,SX],[mX,`FIPS is enabled but this partition does not support FIPS`],[`https://elasticloadbalancing.{Region}.{PartitionResult#dualStackDnsSuffix}`,SX],[mX,`DualStack is enabled but this partition does not support DualStack`],[`https://elasticloadbalancing.{Region}.{PartitionResult#dnsSuffix}`,SX],[mX,`Invalid Configuration: Missing Region`]]},TX=2,EX=new Int32Array([-1,1,-1,0,13,3,1,4,100000012,2,5,100000012,3,8,6,4,7,100000011,5,100000009,100000010,4,11,9,6,10,100000008,7,100000006,100000007,5,12,100000005,6,100000004,100000005,3,100000001,14,4,100000002,100000003]),DX=on.from(EX,TX,wX.conditions,wX.results),OX=new sn({size:50,params:[`Endpoint`,`Region`,`UseDualStack`,`UseFIPS`]}),kX=(e,t={})=>OX.get(e,()=>jn(DX,{endpointParams:e,logger:t.logger}));dn.aws=ji;var A=class e extends Ht{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},AX=class e extends A{name=`CertificateNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`CertificateNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},jX=class e extends A{name=`ListenerNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`ListenerNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},MX=class e extends A{name=`TooManyCertificatesException`;$fault=`client`;Message;constructor(t){super({name:`TooManyCertificatesException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},NX=class e extends A{name=`DuplicateTagKeysException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateTagKeysException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},PX=class e extends A{name=`LoadBalancerNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`LoadBalancerNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},FX=class e extends A{name=`RuleNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`RuleNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},IX=class e extends A{name=`TargetGroupNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`TargetGroupNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},LX=class e extends A{name=`TooManyTagsException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTagsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},RX=class e extends A{name=`TrustStoreNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`TrustStoreNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},zX=class e extends A{name=`InvalidRevocationContentException`;$fault=`client`;Message;constructor(t){super({name:`InvalidRevocationContentException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},BX=class e extends A{name=`RevocationContentNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`RevocationContentNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},VX=class e extends A{name=`TooManyTrustStoreRevocationEntriesException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTrustStoreRevocationEntriesException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},HX=class e extends A{name=`AllocationIdNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`AllocationIdNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},UX=class e extends A{name=`ALPNPolicyNotSupportedException`;$fault=`client`;Message;constructor(t){super({name:`ALPNPolicyNotSupportedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},WX=class e extends A{name=`AvailabilityZoneNotSupportedException`;$fault=`client`;Message;constructor(t){super({name:`AvailabilityZoneNotSupportedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},GX=class e extends A{name=`CaCertificatesBundleNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`CaCertificatesBundleNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},KX=class e extends A{name=`CapacityDecreaseRequestsLimitExceededException`;$fault=`client`;Message;constructor(t){super({name:`CapacityDecreaseRequestsLimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},qX=class e extends A{name=`CapacityReservationPendingException`;$fault=`client`;Message;constructor(t){super({name:`CapacityReservationPendingException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},JX=class e extends A{name=`CapacityUnitsLimitExceededException`;$fault=`client`;Message;constructor(t){super({name:`CapacityUnitsLimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},YX=class e extends A{name=`DuplicateListenerException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateListenerException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},XX=class e extends A{name=`IncompatibleProtocolsException`;$fault=`client`;Message;constructor(t){super({name:`IncompatibleProtocolsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},ZX=class e extends A{name=`InvalidConfigurationRequestException`;$fault=`client`;Message;constructor(t){super({name:`InvalidConfigurationRequestException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},QX=class e extends A{name=`InvalidLoadBalancerActionException`;$fault=`client`;Message;constructor(t){super({name:`InvalidLoadBalancerActionException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},$X=class e extends A{name=`SSLPolicyNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`SSLPolicyNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},eZ=class e extends A{name=`TargetGroupAssociationLimitException`;$fault=`client`;Message;constructor(t){super({name:`TargetGroupAssociationLimitException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},tZ=class e extends A{name=`TooManyActionsException`;$fault=`client`;Message;constructor(t){super({name:`TooManyActionsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},nZ=class e extends A{name=`TooManyListenersException`;$fault=`client`;Message;constructor(t){super({name:`TooManyListenersException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},rZ=class e extends A{name=`TooManyRegistrationsForTargetIdException`;$fault=`client`;Message;constructor(t){super({name:`TooManyRegistrationsForTargetIdException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},iZ=class e extends A{name=`TooManyTargetsException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTargetsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},aZ=class e extends A{name=`TooManyUniqueTargetGroupsPerLoadBalancerException`;$fault=`client`;Message;constructor(t){super({name:`TooManyUniqueTargetGroupsPerLoadBalancerException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},oZ=class e extends A{name=`TrustStoreNotReadyException`;$fault=`client`;Message;constructor(t){super({name:`TrustStoreNotReadyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},sZ=class e extends A{name=`UnsupportedProtocolException`;$fault=`client`;Message;constructor(t){super({name:`UnsupportedProtocolException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},cZ=class e extends A{name=`DuplicateLoadBalancerNameException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateLoadBalancerNameException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},lZ=class e extends A{name=`InvalidSchemeException`;$fault=`client`;Message;constructor(t){super({name:`InvalidSchemeException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},uZ=class e extends A{name=`InvalidSecurityGroupException`;$fault=`client`;Message;constructor(t){super({name:`InvalidSecurityGroupException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},dZ=class e extends A{name=`InvalidSubnetException`;$fault=`client`;Message;constructor(t){super({name:`InvalidSubnetException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},fZ=class e extends A{name=`OperationNotPermittedException`;$fault=`client`;Message;constructor(t){super({name:`OperationNotPermittedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},pZ=class e extends A{name=`ResourceInUseException`;$fault=`client`;Message;constructor(t){super({name:`ResourceInUseException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},mZ=class e extends A{name=`SubnetNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`SubnetNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},hZ=class e extends A{name=`TooManyLoadBalancersException`;$fault=`client`;Message;constructor(t){super({name:`TooManyLoadBalancersException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},gZ=class e extends A{name=`PriorityInUseException`;$fault=`client`;Message;constructor(t){super({name:`PriorityInUseException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},_Z=class e extends A{name=`TooManyRulesException`;$fault=`client`;Message;constructor(t){super({name:`TooManyRulesException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},vZ=class e extends A{name=`TooManyTargetGroupsException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTargetGroupsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},yZ=class e extends A{name=`DuplicateTargetGroupNameException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateTargetGroupNameException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},bZ=class e extends A{name=`DuplicateTrustStoreNameException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateTrustStoreNameException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},xZ=class e extends A{name=`InvalidCaCertificatesBundleException`;$fault=`client`;Message;constructor(t){super({name:`InvalidCaCertificatesBundleException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},SZ=class e extends A{name=`TooManyTrustStoresException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTrustStoresException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},CZ=class e extends A{name=`DeleteAssociationSameAccountException`;$fault=`client`;Message;constructor(t){super({name:`DeleteAssociationSameAccountException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},wZ=class e extends A{name=`TrustStoreAssociationNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`TrustStoreAssociationNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},TZ=class e extends A{name=`TrustStoreInUseException`;$fault=`client`;Message;constructor(t){super({name:`TrustStoreInUseException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},EZ=class e extends A{name=`InvalidTargetException`;$fault=`client`;Message;constructor(t){super({name:`InvalidTargetException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},DZ=class e extends A{name=`HealthUnavailableException`;$fault=`server`;Message;constructor(t){super({name:`HealthUnavailableException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},OZ=class e extends A{name=`RevocationIdNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`RevocationIdNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},kZ=class e extends A{name=`ResourceNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`ResourceNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},AZ=class e extends A{name=`InsufficientCapacityException`;$fault=`server`;Message;constructor(t){super({name:`InsufficientCapacityException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},jZ=class e extends A{name=`PriorRequestNotCompleteException`;$fault=`client`;Message;constructor(t){super({name:`PriorRequestNotCompleteException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},MZ=`Action`,NZ=`AdditionalClaims`,PZ=`AuthenticateCognitoActionConfig`,FZ=`AuthenticateCognitoConfig`,IZ=`AnomalyDetection`,LZ=`AuthorizationEndpoint`,RZ=`AllocationId`,zZ=`AllocationIdNotFoundException`,BZ=`ALPNPolicyNotSupportedException`,VZ=`AdministrativeOverride`,HZ=`AuthenticateOidcActionConfig`,UZ=`AuthenticateOidcConfig`,WZ=`AlpnPolicy`,GZ=`AuthenticationRequestExtraParams`,KZ=`AddTags`,qZ=`AddTagsInput`,JZ=`AddTagsOutput`,YZ=`AdvertiseTrustStoreCaNames`,XZ=`AvailabilityZone`,ZZ=`AvailabilityZoneNotSupportedException`,QZ=`AvailabilityZones`,$Z=`Actions`,eQ=`Attributes`,tQ=`Certificates`,nQ=`CertificateArn`,rQ=`CaCertificatesBundleNotFoundException`,iQ=`CapacityDecreaseRequestsLimitExceededException`,aQ=`CanonicalHostedZoneId`,oQ=`ClientId`,sQ=`CertificateList`,cQ=`CreateLoadBalancer`,lQ=`CreateLoadBalancerInput`,uQ=`CreateLoadBalancerOutput`,dQ=`CreateListenerInput`,fQ=`CreateListenerOutput`,pQ=`CreateListener`,mQ=`CertificateNotFoundException`,hQ=`CustomerOwnedIpv4Pool`,gQ=`CreateRule`,_Q=`CreateRuleInput`,vQ=`CreateRuleOutput`,yQ=`CapacityReservationPendingException`,bQ=`ClientSecret`,xQ=`ContentType`,SQ=`CreateTargetGroup`,CQ=`CreateTargetGroupInput`,wQ=`CreateTargetGroupOutput`,TQ=`CreatedTime`,EQ=`CapacityUnitsLimitExceededException`,DQ=`Certificate`,OQ=`Cipher`,kQ=`Ciphers`,AQ=`Code`,jQ=`Conditions`,MQ=`Description`,NQ=`DefaultActions`,PQ=`DeleteAssociationSameAccountException`,FQ=`DeleteListener`,IQ=`DeleteLoadBalancer`,LQ=`DescribeLoadBalancerAttributes`,RQ=`DescribeLoadBalancerAttributesInput`,zQ=`DescribeLoadBalancerAttributesOutput`,BQ=`DeleteLoadBalancerInput`,VQ=`DescribeLoadBalancersInput`,HQ=`DuplicateLoadBalancerNameException`,UQ=`DeleteLoadBalancerOutput`,WQ=`DescribeLoadBalancersOutput`,GQ=`DescribeLoadBalancers`,KQ=`DuplicateListenerException`,qQ=`DeleteListenerInput`,JQ=`DescribeListenersInput`,YQ=`DeleteListenerOutput`,XQ=`DescribeListenersOutput`,ZQ=`DescribeListeners`,QQ=`DNSName`,$Q=`DeleteRule`,e$=`DeleteRuleInput`,t$=`DescribeRulesInput`,n$=`DeleteRuleOutput`,r$=`DescribeRulesOutput`,i$=`DescribeRules`,a$=`DurationSeconds`,o$=`DescribeSSLPolicies`,s$=`DescribeSSLPoliciesInput`,c$=`DescribeSSLPoliciesOutput`,l$=`DeregisterTargets`,u$=`DeleteTargetGroup`,d$=`DescribeTargetGroupAttributes`,f$=`DescribeTargetGroupAttributesInput`,p$=`DescribeTargetGroupAttributesOutput`,m$=`DeleteTargetGroupInput`,h$=`DescribeTargetGroupsInput`,g$=`DuplicateTargetGroupNameException`,_$=`DeleteTargetGroupOutput`,v$=`DescribeTargetGroupsOutput`,y$=`DescribeTargetGroups`,b$=`DescribeTargetHealth`,x$=`DescribeTargetHealthInput`,tse=`DescribeTargetHealthOutput`,S$=`DeregisterTargetsInput`,C$=`DescribeTagsInput`,w$=`DuplicateTagKeysException`,T$=`DeregisterTargetsOutput`,E$=`DescribeTagsOutput`,D$=`DuplicateTrustStoreNameException`,O$=`DescribeTags`,k$=`Enabled`,A$=`EnablePrefixForIpv6SourceNat`,j$=`EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic`,M$=`Format`,N$=`ForwardActionConfig`,P$=`ForwardConfig`,F$=`FixedResponseActionConfig`,I$=`FixedResponseConfig`,L$=`Field`,R$=`GrpcCode`,z$=`Host`,B$=`HttpCode`,V$=`HealthCheckEnabled`,H$=`HealthCheckIntervalSeconds`,U$=`HealthCheckProtocol`,W$=`HealthCheckPort`,G$=`HealthCheckPath`,K$=`HealthCheckTimeoutSeconds`,q$=`HostHeaderConfig`,J$=`HostHeaderConditionConfig`,Y$=`HttpHeaderConditionConfig`,X$=`HttpHeaderConfig`,Z$=`HttpHeaderName`,Q$=`HostHeaderRewriteConfig`,$$=`HttpRequestMethodConfig`,e1=`HttpRequestMethodConditionConfig`,t1=`HealthyThresholdCount`,n1=`HealthUnavailableException`,r1=`Issuer`,i1=`IpAddress`,a1=`IpAddressType`,o1=`InvalidCaCertificatesBundleException`,s1=`IgnoreClientCertificateExpiry`,c1=`InsufficientCapacityException`,l1=`InvalidConfigurationRequestException`,u1=`IsDefault`,d1=`Ipv4IpamPoolId`,f1=`InvalidLoadBalancerActionException`,p1=`IpamPools`,m1=`IPv6Address`,h1=`IncompatibleProtocolsException`,g1=`InvalidRevocationContentException`,_1=`InvalidSchemeException`,v1=`InvalidSubnetException`,y1=`InvalidSecurityGroupException`,b1=`InvalidTargetException`,x1=`Id`,S1=`Include`,C1=`JwksEndpoint`,w1=`JwtValidationActionAdditionalClaim`,T1=`JwtValidationActionAdditionalClaims`,E1=`JwtValidationActionConfig`,D1=`JwtValidationConfig`,O1=`Key`,k1=`Listeners`,A1=`ListenerArn`,j1=`ListenerArns`,M1=`LoadBalancers`,N1=`LoadBalancerAddresses`,P1=`LoadBalancerArn`,F1=`LoadBalancerArns`,I1=`LoadBalancerAddress`,L1=`LoadBalancerAttribute`,R1=`LoadBalancerAttributes`,z1=`LoadBalancerName`,B1=`LoadBalancerNotFoundException`,V1=`LoadBalancerState`,H1=`LoadBalancerType`,U1=`LoadBalancer`,W1=`ListenerNotFoundException`,G1=`Listener`,j=`Message`,K1=`MutualAuthentication`,q1=`MutualAuthenticationAttributes`,J1=`MessageBody`,Y1=`MitigationInEffect`,X1=`ModifyListener`,Z1=`ModifyLoadBalancerAttributes`,Q1=`ModifyLoadBalancerAttributesInput`,$1=`ModifyLoadBalancerAttributesOutput`,e0=`ModifyListenerInput`,t0=`ModifyListenerOutput`,n0=`ModifyRule`,r0=`ModifyRuleInput`,i0=`ModifyRuleOutput`,a0=`ModifyTargetGroupAttributes`,o0=`ModifyTargetGroupAttributesInput`,s0=`ModifyTargetGroupAttributesOutput`,c0=`Matcher`,l0=`Marker`,u0=`Mode`,d0=`Name`,f0=`NextMarker`,p0=`Names`,m0=`Order`,h0=`OutpostId`,g0=`OperationNotPermittedException`,_0=`OnUnauthenticatedRequest`,v0=`Priority`,y0=`PrivateIPv4Address`,b0=`PriorityInUseException`,x0=`PathPatternConfig`,S0=`PathPatternConditionConfig`,C0=`PriorRequestNotCompleteException`,w0=`PageSize`,T0=`ProtocolVersion`,E0=`Path`,D0=`Port`,O0=`Protocol`,k0=`Query`,A0=`QueryStringConfig`,j0=`QueryStringConditionConfig`,M0=`QuicServerId`,N0=`QueryStringKeyValuePair`,P0=`QueryStringKeyValuePairList`,F0=`Reason`,I0=`ResourceArns`,L0=`RedirectActionConfig`,R0=`ResourceArn`,z0=`RuleArn`,B0=`RuleArns`,V0=`RedirectConfig`,H0=`RewriteConfigList`,U0=`RuleConditionList`,W0=`RevocationContentNotFoundException`,G0=`RewriteConfig`,K0=`RuleCondition`,q0=`RevocationIdNotFoundException`,J0=`ResourceInUseException`,Y0=`ResourceNotFoundException`,X0=`RuleNotFoundException`,Z0=`RegisterTargetsInput`,Q0=`RemoveTagsInput`,$0=`RuleTransformList`,e2=`RegisterTargetsOutput`,t2=`RemoveTagsOutput`,n2=`ResetTransforms`,r2=`RegisterTargets`,i2=`RemoveTags`,a2=`RuleTransform`,o2=`RegexValues`,s2=`Result`,c2=`Regex`,l2=`Replace`,u2=`Rewrites`,d2=`Rules`,f2=`Rule`,p2=`State`,m2=`StatusCode`,h2=`SessionCookieName`,g2=`SecurityGroups`,_2=`SecurityGroupIds`,v2=`SubnetId`,y2=`SourceIpConfig`,b2=`SourceIpConditionConfig`,x2=`SupportedLoadBalancerTypes`,S2=`SubnetMappings`,C2=`SubnetMapping`,w2=`SubnetNotFoundException`,T2=`SourceNatIpv6Prefixes`,E2=`SourceNatIpv6Prefix`,D2=`SslPolicy`,O2=`SslPolicies`,k2=`SslProtocols`,A2=`SetSubnets`,j2=`SetSecurityGroups`,M2=`SetSecurityGroupsInput`,N2=`SetSecurityGroupsOutput`,P2=`SetSubnetsInput`,F2=`SSLPolicyNotFoundException`,I2=`SetSubnetsOutput`,L2=`SessionTimeout`,R2=`Scope`,z2=`Scheme`,B2=`Subnets`,V2=`Type`,H2=`TargetControlPort`,U2=`TagDescriptions`,W2=`TagDescription`,G2=`TargetDescription`,K2=`TargetDescriptions`,q2=`TokenEndpoint`,J2=`TargetGroups`,Y2=`TargetGroupArn`,X2=`TargetGroupAssociationLimitException`,Z2=`TargetGroupArns`,Q2=`TargetGroupAttribute`,$2=`TargetGroupAttributes`,e4=`TargetGroupList`,t4=`TargetGroupName`,n4=`TargetGroupNotFoundException`,r4=`TargetGroupStickinessConfig`,i4=`TargetGroupTuple`,a4=`TargetGroup`,o4=`TargetHealth`,s4=`TargetHealthDescriptions`,c4=`TargetHealthDescription`,l4=`TagKeys`,u4=`TagList`,d4=`TooManyActionsException`,f4=`TooManyCertificatesException`,p4=`TooManyLoadBalancersException`,m4=`TooManyListenersException`,h4=`TooManyRulesException`,g4=`TooManyRegistrationsForTargetIdException`,_4=`TooManyTagsException`,v4=`TooManyTargetsException`,y4=`TooManyTargetGroupsException`,b4=`TooManyTrustStoresException`,nse=`TooManyTrustStoreRevocationEntriesException`,rse=`TooManyUniqueTargetGroupsPerLoadBalancerException`,ise=`TrustStoreArn`,ase=`TrustStoreAssociationNotFoundException`,ose=`TrustStoreAssociationStatus`,sse=`TrustStoreInUseException`,cse=`TrustStoreNotFoundException`,lse=`TrustStoreNotReadyException`,x4=`TargetType`,S4=`Tags`,use=`Tag`,C4=`Targets`,dse=`Target`,w4=`Transforms`,fse=`UseExistingClientSecret`,pse=`UserInfoEndpoint`,mse=`UserPoolArn`,hse=`UserPoolClientId`,gse=`UserPoolDomain`,_se=`UnsupportedProtocolException`,T4=`UrlRewriteConfig`,E4=`UnhealthyThresholdCount`,D4=`Values`,O4=`VpcId`,k4=`Value`,vse=`Weight`,yse=`ZoneName`,M=`awsQueryError`,N=`client`,P=`error`,F=`httpError`,A4=`smithy.ts.sdk.synthetic.com.amazonaws.elasticloadbalancingv2`,j4=`server`,I=`com.amazonaws.elasticloadbalancingv2`,M4=Lt.for(A4),bse=[-3,A4,`ElasticLoadBalancingV2ServiceException`,0,[],[]];M4.registerError(bse,A);var L=Lt.for(I),xse=[-3,I,zZ,{[M]:[`AllocationIdNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(xse,HX);var Sse=[-3,I,BZ,{[M]:[`ALPNPolicyNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Sse,UX);var Cse=[-3,I,ZZ,{[M]:[`AvailabilityZoneNotSupported`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Cse,WX);var wse=[-3,I,rQ,{[M]:[`CaCertificatesBundleNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(wse,GX);var Tse=[-3,I,iQ,{[M]:[`CapacityDecreaseRequestLimitExceeded`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Tse,KX);var Ese=[-3,I,yQ,{[M]:[`CapacityReservationPending`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Ese,qX);var Dse=[-3,I,EQ,{[M]:[`CapacityUnitsLimitExceeded`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Dse,JX);var Ose=[-3,I,mQ,{[M]:[`CertificateNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Ose,AX);var kse=[-3,I,PQ,{[M]:[`DeleteAssociationSameAccount`,400],[P]:N,[F]:400},[j],[0]];L.registerError(kse,CZ);var Ase=[-3,I,KQ,{[M]:[`DuplicateListener`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Ase,YX);var jse=[-3,I,HQ,{[M]:[`DuplicateLoadBalancerName`,400],[P]:N,[F]:400},[j],[0]];L.registerError(jse,cZ);var Mse=[-3,I,w$,{[M]:[`DuplicateTagKeys`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Mse,NX);var Nse=[-3,I,g$,{[M]:[`DuplicateTargetGroupName`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Nse,yZ);var Pse=[-3,I,D$,{[M]:[`DuplicateTrustStoreName`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Pse,bZ);var Fse=[-3,I,n1,{[M]:[`HealthUnavailable`,500],[P]:j4,[F]:500},[j],[0]];L.registerError(Fse,DZ);var Ise=[-3,I,h1,{[M]:[`IncompatibleProtocols`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Ise,XX);var Lse=[-3,I,c1,{[M]:[`InsufficientCapacity`,500],[P]:j4,[F]:500},[j],[0]];L.registerError(Lse,AZ);var Rse=[-3,I,o1,{[M]:[`InvalidCaCertificatesBundle`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Rse,xZ);var zse=[-3,I,l1,{[M]:[`InvalidConfigurationRequest`,400],[P]:N,[F]:400},[j],[0]];L.registerError(zse,ZX);var Bse=[-3,I,f1,{[M]:[`InvalidLoadBalancerAction`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Bse,QX);var Vse=[-3,I,g1,{[M]:[`InvalidRevocationContent`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Vse,zX);var Hse=[-3,I,_1,{[M]:[`InvalidScheme`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Hse,lZ);var Use=[-3,I,y1,{[M]:[`InvalidSecurityGroup`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Use,uZ);var Wse=[-3,I,v1,{[M]:[`InvalidSubnet`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Wse,dZ);var Gse=[-3,I,b1,{[M]:[`InvalidTarget`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Gse,EZ);var Kse=[-3,I,W1,{[M]:[`ListenerNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Kse,jX);var qse=[-3,I,B1,{[M]:[`LoadBalancerNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(qse,PX);var Jse=[-3,I,g0,{[M]:[`OperationNotPermitted`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Jse,fZ);var Yse=[-3,I,b0,{[M]:[`PriorityInUse`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Yse,gZ);var Xse=[-3,I,C0,{[M]:[`PriorRequestNotComplete`,429],[P]:N,[F]:429},[j],[0]];L.registerError(Xse,jZ);var Zse=[-3,I,J0,{[M]:[`ResourceInUse`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Zse,pZ);var Qse=[-3,I,Y0,{[M]:[`ResourceNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Qse,kZ);var $se=[-3,I,W0,{[M]:[`RevocationContentNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError($se,BX);var ece=[-3,I,q0,{[M]:[`RevocationIdNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(ece,OZ);var tce=[-3,I,X0,{[M]:[`RuleNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(tce,FX);var nce=[-3,I,F2,{[M]:[`SSLPolicyNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(nce,$X);var rce=[-3,I,w2,{[M]:[`SubnetNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(rce,mZ);var ice=[-3,I,X2,{[M]:[`TargetGroupAssociationLimit`,400],[P]:N,[F]:400},[j],[0]];L.registerError(ice,eZ);var ace=[-3,I,n4,{[M]:[`TargetGroupNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(ace,IX);var oce=[-3,I,d4,{[M]:[`TooManyActions`,400],[P]:N,[F]:400},[j],[0]];L.registerError(oce,tZ);var sce=[-3,I,f4,{[M]:[`TooManyCertificates`,400],[P]:N,[F]:400},[j],[0]];L.registerError(sce,MX);var cce=[-3,I,m4,{[M]:[`TooManyListeners`,400],[P]:N,[F]:400},[j],[0]];L.registerError(cce,nZ);var lce=[-3,I,p4,{[M]:[`TooManyLoadBalancers`,400],[P]:N,[F]:400},[j],[0]];L.registerError(lce,hZ);var uce=[-3,I,g4,{[M]:[`TooManyRegistrationsForTargetId`,400],[P]:N,[F]:400},[j],[0]];L.registerError(uce,rZ);var dce=[-3,I,h4,{[M]:[`TooManyRules`,400],[P]:N,[F]:400},[j],[0]];L.registerError(dce,_Z);var fce=[-3,I,_4,{[M]:[`TooManyTags`,400],[P]:N,[F]:400},[j],[0]];L.registerError(fce,LX);var pce=[-3,I,y4,{[M]:[`TooManyTargetGroups`,400],[P]:N,[F]:400},[j],[0]];L.registerError(pce,vZ);var mce=[-3,I,v4,{[M]:[`TooManyTargets`,400],[P]:N,[F]:400},[j],[0]];L.registerError(mce,iZ);var hce=[-3,I,nse,{[M]:[`TooManyTrustStoreRevocationEntries`,400],[P]:N,[F]:400},[j],[0]];L.registerError(hce,VX);var gce=[-3,I,b4,{[M]:[`TooManyTrustStores`,400],[P]:N,[F]:400},[j],[0]];L.registerError(gce,SZ);var _ce=[-3,I,rse,{[M]:[`TooManyUniqueTargetGroupsPerLoadBalancer`,400],[P]:N,[F]:400},[j],[0]];L.registerError(_ce,aZ);var vce=[-3,I,ase,{[M]:[`AssociationNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(vce,wZ);var yce=[-3,I,sse,{[M]:[`TrustStoreInUse`,400],[P]:N,[F]:400},[j],[0]];L.registerError(yce,TZ);var bce=[-3,I,cse,{[M]:[`TrustStoreNotFound`,400],[P]:N,[F]:400},[j],[0]];L.registerError(bce,RX);var xce=[-3,I,lse,{[M]:[`TrustStoreNotReady`,400],[P]:N,[F]:400},[j],[0]];L.registerError(xce,oZ);var Sce=[-3,I,_se,{[M]:[`UnsupportedProtocol`,400],[P]:N,[F]:400},[j],[0]];L.registerError(Sce,sZ);var Cce=[M4,L],wce=[3,I,MZ,0,[V2,Y2,UZ,FZ,m0,V0,I$,P$,D1],[0,0,()=>Ace,()=>kce,1,()=>Ble,()=>gle,()=>_le,()=>Cle],1],Tce=[3,I,qZ,0,[I0,S4],[64,()=>J4],2],Ece=[3,I,JZ,0,[],[]],Dce=[3,I,VZ,0,[p2,F0,MQ],[0,0,0]],Oce=[3,I,IZ,0,[s2,Y1],[0,0]],kce=[3,I,PZ,0,[mse,hse,gse,h2,R2,L2,GZ,_0],[0,0,0,0,0,1,128,0],3],Ace=[3,I,HZ,0,[r1,LZ,q2,pse,oQ,bQ,h2,R2,L2,GZ,_0,fse],[0,0,0,0,0,0,0,0,1,128,0,2],5],jce=[3,I,XZ,0,[yse,v2,h0,N1,T2],[0,0,0,()=>pue,64]],Mce=[3,I,DQ,0,[nQ,u1],[0,2]],Nce=[3,I,OQ,0,[d0,v0],[0,1]],Pce=[3,I,dQ,0,[P1,NQ,O0,D0,D2,tQ,WZ,S4,K1],[0,()=>L4,0,1,0,()=>z4,64,()=>J4,()=>F4],2],Fce=[3,I,fQ,0,[k1],[()=>B4]],Ice=[3,I,lQ,0,[d0,B2,S2,g2,z2,S4,V2,a1,hQ,A$,p1],[0,64,()=>q4,64,0,()=>J4,0,0,0,0,()=>N4],1],Lce=[3,I,uQ,0,[M1],[()=>H4]],Rce=[3,I,_Q,0,[A1,jQ,v0,$Z,S4,w4],[0,()=>W4,1,()=>L4,()=>J4,()=>K4],4],zce=[3,I,vQ,0,[d2],[()=>G4]],Bce=[3,I,CQ,0,[d0,O0,T0,D0,O4,U$,W$,V$,G$,H$,K$,t1,E4,c0,x4,S4,a1,H2],[0,0,0,1,0,0,0,2,0,1,1,1,1,()=>P4,0,()=>J4,0,1],1],Vce=[3,I,wQ,0,[J2],[()=>Z4]],Hce=[3,I,qQ,0,[A1],[0],1],Uce=[3,I,YQ,0,[],[]],Wce=[3,I,BQ,0,[P1],[0],1],Gce=[3,I,UQ,0,[],[]],Kce=[3,I,e$,0,[z0],[0],1],qce=[3,I,n$,0,[],[]],Jce=[3,I,m$,0,[Y2],[0],1],Yce=[3,I,_$,0,[],[]],Xce=[3,I,S$,0,[Y2,C4],[0,()=>Y4],2],Zce=[3,I,T$,0,[],[]],Qce=[3,I,JQ,0,[P1,j1,l0,w0],[0,64,0,1]],$ce=[3,I,XQ,0,[k1,f0],[()=>B4,0]],ele=[3,I,RQ,0,[P1],[0],1],tle=[3,I,zQ,0,[eQ],[()=>V4]],nle=[3,I,VQ,0,[F1,p0,l0,w0],[64,64,0,1]],rle=[3,I,WQ,0,[M1,f0],[()=>H4,0]],ile=[3,I,t$,0,[A1,B0,l0,w0],[0,64,0,1]],ale=[3,I,r$,0,[d2,f0],[()=>G4,0]],ole=[3,I,s$,0,[p0,l0,w0,H1],[64,0,1,0]],sle=[3,I,c$,0,[O2,f0],[()=>hue,0]],cle=[3,I,C$,0,[I0],[64],1],lle=[3,I,E$,0,[U2],[()=>gue]],ule=[3,I,f$,0,[Y2],[0],1],dle=[3,I,p$,0,[eQ],[()=>X4]],fle=[3,I,h$,0,[P1,Z2,p0,l0,w0],[0,64,64,0,1]],ple=[3,I,v$,0,[J2,f0],[()=>Z4,0]],mle=[3,I,x$,0,[Y2,C4,S1],[0,()=>Y4,64],1],hle=[3,I,tse,0,[s4],[()=>vue]],gle=[3,I,F$,0,[m2,J1,xQ],[0,0,0],1],_le=[3,I,N$,0,[J2,r4],[()=>_ue,()=>oue]],vle=[3,I,J$,0,[D4,o2],[64,64]],yle=[3,I,Q$,0,[u2],[()=>U4]],ble=[3,I,Y$,0,[Z$,D4,o2],[0,64,64]],xle=[3,I,e1,0,[D4],[64]],N4=[3,I,p1,0,[d1],[0]],Sle=[3,I,w1,0,[M$,d0,D4],[0,0,64],3],Cle=[3,I,E1,0,[C1,r1,NZ],[0,0,()=>fue],2],wle=[3,I,G1,0,[A1,P1,D0,O0,tQ,D2,NQ,WZ,K1],[0,0,1,0,()=>z4,0,()=>L4,64,()=>F4]],Tle=[3,I,U1,0,[P1,QQ,aQ,TQ,z1,z2,O4,p2,V2,QZ,g2,a1,hQ,j$,A$,p1],[0,0,0,4,0,0,0,()=>Ole,0,()=>R4,64,0,0,0,0,()=>N4]],Ele=[3,I,I1,0,[i1,RZ,y0,m1],[0,0,0,0]],Dle=[3,I,L1,0,[O1,k4],[0,0]],Ole=[3,I,V1,0,[AQ,F0],[0,0]],P4=[3,I,c0,0,[B$,R$],[0,0]],kle=[3,I,e0,0,[A1,D0,O0,D2,tQ,NQ,WZ,K1],[0,1,0,0,()=>z4,()=>L4,64,()=>F4],1],Ale=[3,I,t0,0,[k1],[()=>B4]],jle=[3,I,Q1,0,[P1,eQ],[0,()=>V4],2],Mle=[3,I,$1,0,[eQ],[()=>V4]],Nle=[3,I,r0,0,[z0,jQ,$Z,w4,n2],[0,()=>W4,()=>L4,()=>K4,2],1],Ple=[3,I,i0,0,[d2],[()=>G4]],Fle=[3,I,o0,0,[Y2,eQ],[0,()=>X4],2],Ile=[3,I,s0,0,[eQ],[()=>X4]],F4=[3,I,q1,0,[u0,ise,s1,ose,YZ],[0,0,2,0,0]],Lle=[3,I,S0,0,[D4,o2],[64,64]],Rle=[3,I,j0,0,[D4],[()=>mue]],zle=[3,I,N0,0,[O1,k4],[0,0]],Ble=[3,I,L0,0,[m2,O0,D0,z$,E0,k0],[0,0,0,0,0,0],1],Vle=[3,I,Z0,0,[Y2,C4],[0,()=>Y4],2],Hle=[3,I,e2,0,[],[]],Ule=[3,I,Q0,0,[I0,l4],[64,64],2],Wle=[3,I,t2,0,[],[]],Gle=[3,I,G0,0,[c2,l2],[0,0],2],Kle=[3,I,f2,0,[z0,v0,jQ,$Z,u1,w4],[0,0,()=>W4,()=>L4,2,()=>K4]],qle=[3,I,K0,0,[L$,D4,q$,x0,X$,A0,$$,y2,o2],[0,64,()=>vle,()=>Lle,()=>ble,()=>Rle,()=>xle,()=>$le,64]],Jle=[3,I,a2,0,[V2,Q$,T4],[0,()=>yle,()=>uue],1],Yle=[3,I,M2,0,[P1,g2,j$],[0,64,0],2],Xle=[3,I,N2,0,[_2,j$],[64,0]],Zle=[3,I,P2,0,[P1,B2,S2,a1,A$],[0,64,()=>q4,0,0],1],Qle=[3,I,I2,0,[QZ,a1,A$],[()=>R4,0,0]],$le=[3,I,b2,0,[D4],[64]],eue=[3,I,D2,0,[k2,kQ,d0,x2],[64,()=>due,0,64]],tue=[3,I,C2,0,[v2,RZ,y0,m1,E2],[0,0,0,0,0]],nue=[3,I,use,0,[O1,k4],[0,0],1],rue=[3,I,W2,0,[R0,S4],[0,()=>J4]],I4=[3,I,G2,0,[x1,D0,XZ,M0],[0,1,0,0],1],iue=[3,I,a4,0,[Y2,t4,O0,D0,O4,U$,W$,V$,H$,K$,t1,E4,G$,c0,F1,x4,T0,a1,H2],[0,0,0,1,0,0,0,2,1,1,1,1,0,()=>P4,64,0,0,0,1]],aue=[3,I,Q2,0,[O1,k4],[0,0]],oue=[3,I,r4,0,[k$,a$],[2,1]],sue=[3,I,i4,0,[Y2,vse],[0,1]],cue=[3,I,o4,0,[p2,F0,MQ],[0,0,0]],lue=[3,I,c4,0,[dse,W$,o4,IZ,VZ],[()=>I4,0,()=>cue,()=>Oce,()=>Dce]],uue=[3,I,T4,0,[u2],[()=>U4]],L4=[1,I,$Z,0,()=>wce],R4=[1,I,QZ,0,()=>jce],z4=[1,I,sQ,0,()=>Mce],due=[1,I,kQ,0,()=>Nce],fue=[1,I,T1,0,()=>Sle],B4=[1,I,k1,0,()=>wle],pue=[1,I,N1,0,()=>Ele],V4=[1,I,R1,0,()=>Dle],H4=[1,I,M1,0,()=>Tle],mue=[1,I,P0,0,()=>zle],U4=[1,I,H0,0,()=>Gle],W4=[1,I,U0,0,()=>qle],G4=[1,I,d2,0,()=>Kle],K4=[1,I,$0,0,()=>Jle],hue=[1,I,O2,0,()=>eue],q4=[1,I,S2,0,()=>tue],gue=[1,I,U2,0,()=>rue],J4=[1,I,u4,0,()=>nue],Y4=[1,I,K2,0,()=>I4],X4=[1,I,$2,0,()=>aue],_ue=[1,I,e4,0,()=>sue],Z4=[1,I,J2,0,()=>iue],vue=[1,I,s4,0,()=>lue],yue=[9,I,KZ,0,()=>Tce,()=>Ece],bue=[9,I,pQ,0,()=>Pce,()=>Fce],xue=[9,I,cQ,0,()=>Ice,()=>Lce],Sue=[9,I,gQ,0,()=>Rce,()=>zce],Cue=[9,I,SQ,0,()=>Bce,()=>Vce],wue=[9,I,FQ,0,()=>Hce,()=>Uce],Tue=[9,I,IQ,0,()=>Wce,()=>Gce],Eue=[9,I,$Q,0,()=>Kce,()=>qce],Due=[9,I,u$,0,()=>Jce,()=>Yce],Oue=[9,I,l$,0,()=>Xce,()=>Zce],kue=[9,I,ZQ,0,()=>Qce,()=>$ce],Aue=[9,I,LQ,0,()=>ele,()=>tle],jue=[9,I,GQ,0,()=>nle,()=>rle],Mue=[9,I,i$,0,()=>ile,()=>ale],Nue=[9,I,o$,0,()=>ole,()=>sle],Pue=[9,I,O$,0,()=>cle,()=>lle],Fue=[9,I,d$,0,()=>ule,()=>dle],Iue=[9,I,y$,0,()=>fle,()=>ple],Lue=[9,I,b$,0,()=>mle,()=>hle],Rue=[9,I,X1,0,()=>kle,()=>Ale],zue=[9,I,Z1,0,()=>jle,()=>Mle],Bue=[9,I,n0,0,()=>Nle,()=>Ple],Vue=[9,I,a0,0,()=>Fle,()=>Ile],Hue=[9,I,r2,0,()=>Vle,()=>Hle],Uue=[9,I,i2,0,()=>Ule,()=>Wle],Wue=[9,I,j2,0,()=>Yle,()=>Xle],Gue=[9,I,A2,0,()=>Zle,()=>Qle],Kue=e=>({apiVersion:`2015-12-01`,base64Decoder:e?.base64Decoder??fe,base64Encoder:e?.base64Encoder??le,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??kX,extensions:e?.extensions??[],httpAuthSchemeProvider:e?.httpAuthSchemeProvider??cX,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new Yi}],logger:e?.logger??new $t,protocol:e?.protocol??qa,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.elasticloadbalancingv2`,errorTypeRegistries:Cce,xmlNamespace:`http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/`,version:`2015-12-01`,serviceTarget:`ElasticLoadBalancing_v10`},serviceId:e?.serviceId??`Elastic Load Balancing v2`,urlParser:e?.urlParser??nt,utf8Decoder:e?.utf8Decoder??ce,utf8Encoder:e?.utf8Encoder??me}),que=e=>{let t=zi(e),n=()=>t().then(Wt),r=Kue(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??Ee,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??Ei({serviceId:r.serviceId,clientVersion:fX.version}),maxAttempts:e?.maxAttempts??3,region:e?.region??yt(`Region is missing`),requestHandler:da.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||Nr),sha256:e?.sha256??ee,streamCollector:e?.streamCollector??pa,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},Jue=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},Yue=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),Xue=(e,t)=>{let n=Object.assign(Vi(e),Xt(e),Xn(e),Jue(e));return t.forEach(e=>e.configure(n)),Object.assign(e,Hi(n),Zt(n),Zn(n),Yue(n))},Zue=class extends bt{config;constructor(...[e]){let t=que(e||{});super(t),this.initConfig=t;let n=Xue(lX(Nn(zr(Li(Ir(li(uX(t))))))),e?.extensions||[]);this.config=n,this.middlewareStack.use(Ot(this.config)),this.middlewareStack.use(Ti(this.config)),this.middlewareStack.use(Lr(this.config)),this.middlewareStack.use(tr(this.config)),this.middlewareStack.use(Hr(this.config)),this.middlewareStack.use(Gr(this.config)),this.middlewareStack.use(Kr(this.config)),this.middlewareStack.use(Zr(this.config,{httpAuthSchemeParametersProvider:oX,identityProviderConfigProvider:async e=>new ii({"aws.auth#sigv4":e.credentials})})),this.middlewareStack.use(ni(this.config))}destroy(){super.destroy()}},Que=async(e,t,n)=>({operation:Ze(t).operation,region:await re(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()});function $ue(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`iam`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var ede=e=>{let t=[];switch(e.operation){default:t.push($ue(e))}return t},tde=e=>{let t=Qi(e);return Object.assign(t,{authSchemePreference:re(e.authSchemePreference??[])})},nde=e=>Object.assign(e,{useDualstackEndpoint:e.useDualstackEndpoint??!1,useFipsEndpoint:e.useFipsEndpoint??!1,defaultSigningName:`iam`}),rde={UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},ide={name:`@aws-sdk/client-iam`,description:`AWS SDK for JavaScript Iam Client for Node.js, Browser and React Native`,version:`3.1063.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-iam`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo iam`,test:`yarn g:vitest run --passWithNoTests`,"test:e2e":`yarn g:vitest run -c vitest.config.e2e.mts --mode development`,"test:e2e:watch":`yarn g:vitest watch -c vitest.config.e2e.mts`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`,"test:integration":`yarn g:vitest run --passWithNoTests -c vitest.config.integ.mts`,"test:integration:watch":`yarn g:vitest run --passWithNoTests -c vitest.config.integ.mts`,"test:watch":`yarn g:vitest watch --passWithNoTests`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.18`,"@aws-sdk/credential-provider-node":`^3.972.52`,"@aws-sdk/types":`^3.973.11`,"@smithy/core":`^3.24.6`,"@smithy/fetch-http-handler":`^5.4.6`,"@smithy/node-http-handler":`^4.7.6`,"@smithy/types":`^4.14.3`,tslib:`^2.6.2`},devDependencies:{"@smithy/snapshot-testing":`^2.1.7`,"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`,vitest:`^4.0.17`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/sdk-for-javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-iam`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-iam`}},Q4=`ref`,$4=`authSchemes`,e3=`name`,t3=`signingRegion`,n3=-1,r3=!0,i3=`isSet`,a3=`PartitionResult`,o3=`booleanEquals`,s3=`stringEquals`,c3=`getAttr`,l3=`sigv4`,u3={[Q4]:`Endpoint`},d3={fn:c3,argv:[{[Q4]:a3},`name`]},f3={[Q4]:a3},p3={[$4]:[{[e3]:l3,[t3]:`us-east-1`}]},m3={[$4]:[{[e3]:l3,[t3]:`cn-north-1`}]},h3={[$4]:[{[e3]:l3,[t3]:`us-gov-west-1`}]},g3={[$4]:[{[e3]:l3,[t3]:`us-iso-east-1`}]},_3={[$4]:[{[e3]:l3,[t3]:`us-isob-east-1`}]},v3={[$4]:[{[e3]:l3,[t3]:`{PartitionResult#implicitGlobalRegion}`}]},y3=[{[Q4]:`Region`}],b3={conditions:[[i3,[u3]],[i3,y3],[`aws.partition`,y3,a3],[o3,[{[Q4]:`UseFIPS`},r3]],[o3,[{[Q4]:`UseDualStack`},r3]],[s3,[d3,`aws`]],[s3,[d3,`aws-cn`]],[s3,[d3,`aws-us-gov`]],[o3,[{fn:c3,argv:[f3,`supportsDualStack`]},r3]],[s3,[d3,`aws-iso`]],[s3,[d3,`aws-iso-b`]],[o3,[{fn:c3,argv:[f3,`supportsFIPS`]},r3]],[s3,[d3,`aws-iso-e`]],[s3,[d3,`aws-iso-f`]],[s3,[d3,`aws-eusc`]]],results:[[n3],[n3,`Invalid Configuration: FIPS and custom endpoint are not supported`],[n3,`Invalid Configuration: Dualstack and custom endpoint are not supported`],[u3,{}],[`https://iam.global.api.aws`,p3],[`https://iam-fips.global.api.aws`,p3],[`https://iam.global.api.amazonwebservices.com.cn`,m3],[`https://iam.cn-north-1.amazonaws.com.cn`,m3],[`https://iam.us-gov.api.aws`,h3],[`https://iam.us-gov.amazonaws.com`,h3],[`https://iam.us-iso-east-1.c2s.ic.gov`,g3],[`https://iam-fips.us-iso-east-1.c2s.ic.gov`,g3],[`https://iam.us-isob-east-1.sc2s.sgov.gov`,_3],[`https://iam-fips.us-isob-east-1.sc2s.sgov.gov`,_3],[`https://iam.eu-isoe-west-1.cloud.adc-e.uk`,{[$4]:[{[e3]:l3,[t3]:`eu-isoe-west-1`}]}],[`https://iam.us-isof-south-1.csp.hci.ic.gov`,{[$4]:[{[e3]:l3,[t3]:`us-isof-south-1`}]}],[`https://iam.eusc-de-east-1.amazonaws.eu`,{[$4]:[{[e3]:l3,[t3]:`eusc-de-east-1`}]}],[`https://iam-fips.{PartitionResult#dualStackDnsSuffix}`,v3],[n3,`FIPS and DualStack are enabled, but this partition does not support one or both`],[`https://iam-fips.{PartitionResult#dnsSuffix}`,v3],[n3,`FIPS is enabled but this partition does not support FIPS`],[`https://iam.{PartitionResult#dualStackDnsSuffix}`,v3],[n3,`DualStack is enabled but this partition does not support DualStack`],[`https://iam.{PartitionResult#dnsSuffix}`,v3],[n3,`Invalid Configuration: Missing Region`]]},ade=2,ode=new Int32Array([-1,1,-1,0,27,3,1,4,100000024,2,5,100000024,3,18,6,4,14,7,6,100000007,8,7,100000009,9,9,100000010,10,10,100000012,11,12,100000014,12,13,100000015,13,14,100000016,100000023,5,100000004,15,6,100000006,16,7,100000008,17,8,100000021,100000022,4,23,19,7,100000009,20,9,100000011,21,10,100000013,22,11,100000019,100000020,5,100000005,24,7,100000008,25,8,26,100000018,11,100000017,100000018,3,100000001,28,4,100000002,100000003]),sde=on.from(ode,ade,b3.conditions,b3.results),cde=new sn({size:50,params:[`Endpoint`,`Region`,`UseDualStack`,`UseFIPS`]}),lde=(e,t={})=>cde.get(e,()=>jn(sde,{endpointParams:e,logger:t.logger}));dn.aws=ji;var R=class e extends Ht{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},ude=class e extends R{name=`ConcurrentModificationException`;$fault=`client`;constructor(t){super({name:`ConcurrentModificationException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},dde=class e extends R{name=`NoSuchEntityException`;$fault=`client`;constructor(t){super({name:`NoSuchEntityException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},fde=class e extends R{name=`ServiceFailureException`;$fault=`server`;constructor(t){super({name:`ServiceFailureException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype)}},pde=class e extends R{name=`AccountNotManagementOrDelegatedAdministratorException`;$fault=`client`;Message;constructor(t){super({name:`AccountNotManagementOrDelegatedAdministratorException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},mde=class e extends R{name=`InvalidInputException`;$fault=`client`;constructor(t){super({name:`InvalidInputException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},hde=class e extends R{name=`LimitExceededException`;$fault=`client`;constructor(t){super({name:`LimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},gde=class e extends R{name=`EntityAlreadyExistsException`;$fault=`client`;constructor(t){super({name:`EntityAlreadyExistsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},_de=class e extends R{name=`UnmodifiableEntityException`;$fault=`client`;constructor(t){super({name:`UnmodifiableEntityException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},vde=class e extends R{name=`PolicyNotAttachableException`;$fault=`client`;constructor(t){super({name:`PolicyNotAttachableException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},yde=class e extends R{name=`EntityTemporarilyUnmodifiableException`;$fault=`client`;constructor(t){super({name:`EntityTemporarilyUnmodifiableException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},bde=class e extends R{name=`InvalidUserTypeException`;$fault=`client`;constructor(t){super({name:`InvalidUserTypeException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},xde=class e extends R{name=`PasswordPolicyViolationException`;$fault=`client`;constructor(t){super({name:`PasswordPolicyViolationException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Sde=class e extends R{name=`OpenIdIdpCommunicationErrorException`;$fault=`client`;constructor(t){super({name:`OpenIdIdpCommunicationErrorException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Cde=class e extends R{name=`MalformedPolicyDocumentException`;$fault=`client`;constructor(t){super({name:`MalformedPolicyDocumentException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},wde=class e extends R{name=`ServiceNotSupportedException`;$fault=`client`;constructor(t){super({name:`ServiceNotSupportedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Tde=class e extends R{name=`DeleteConflictException`;$fault=`client`;constructor(t){super({name:`DeleteConflictException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Ede=class e extends R{name=`OrganizationNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`OrganizationNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Dde=class e extends R{name=`OrganizationNotInAllFeaturesModeException`;$fault=`client`;Message;constructor(t){super({name:`OrganizationNotInAllFeaturesModeException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Ode=class e extends R{name=`ServiceAccessNotEnabledException`;$fault=`client`;Message;constructor(t){super({name:`ServiceAccessNotEnabledException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},kde=class e extends R{name=`FeatureDisabledException`;$fault=`client`;constructor(t){super({name:`FeatureDisabledException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Ade=class e extends R{name=`InvalidAuthenticationCodeException`;$fault=`client`;constructor(t){super({name:`InvalidAuthenticationCodeException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},jde=class e extends R{name=`CallerIsNotManagementAccountException`;$fault=`client`;Message;constructor(t){super({name:`CallerIsNotManagementAccountException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Mde=class e extends R{name=`FeatureEnabledException`;$fault=`client`;constructor(t){super({name:`FeatureEnabledException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Nde=class e extends R{name=`ReportGenerationLimitExceededException`;$fault=`client`;constructor(t){super({name:`ReportGenerationLimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Pde=class e extends R{name=`CredentialReportExpiredException`;$fault=`client`;constructor(t){super({name:`CredentialReportExpiredException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Fde=class e extends R{name=`CredentialReportNotPresentException`;$fault=`client`;constructor(t){super({name:`CredentialReportNotPresentException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Ide=class e extends R{name=`CredentialReportNotReadyException`;$fault=`client`;constructor(t){super({name:`CredentialReportNotReadyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Lde=class e extends R{name=`UnrecognizedPublicKeyEncodingException`;$fault=`client`;constructor(t){super({name:`UnrecognizedPublicKeyEncodingException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Rde=class e extends R{name=`PolicyEvaluationException`;$fault=`server`;constructor(t){super({name:`PolicyEvaluationException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype)}},zde=class e extends R{name=`KeyPairMismatchException`;$fault=`client`;constructor(t){super({name:`KeyPairMismatchException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Bde=class e extends R{name=`MalformedCertificateException`;$fault=`client`;constructor(t){super({name:`MalformedCertificateException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Vde=class e extends R{name=`DuplicateCertificateException`;$fault=`client`;constructor(t){super({name:`DuplicateCertificateException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Hde=class e extends R{name=`InvalidCertificateException`;$fault=`client`;constructor(t){super({name:`InvalidCertificateException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Ude=class e extends R{name=`DuplicateSSHPublicKeyException`;$fault=`client`;constructor(t){super({name:`DuplicateSSHPublicKeyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Wde=class e extends R{name=`InvalidPublicKeyException`;$fault=`client`;constructor(t){super({name:`InvalidPublicKeyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},x3=`Arn`,Gde=`AttachmentCount`,S3=`AccessKey`,C3=`AccessKeyId`,w3=`AccessKeyMetadata`,Kde=`AccountNotManagementOrDelegatedAdministratorException`,qde=`AttachedPolicy`,Jde=`AttachedPermissionsBoundary`,T3=`AttachedPolicies`,Yde=`AttachRolePolicy`,E3=`AssumeRolePolicyDocument`,Xde=`AttachRolePolicyRequest`,Zde=`AddRoleToInstanceProfile`,Qde=`AddRoleToInstanceProfileRequest`,$de=`AttachUserPolicy`,efe=`AttachUserPolicyRequest`,tfe=`CreateAccessKey`,nfe=`CreateAccessKeyRequest`,rfe=`CreateAccessKeyResponse`,D3=`CreateDate`,ife=`CallerIsNotManagementAccountException`,afe=`CreateInstanceProfile`,ofe=`CreateInstanceProfileRequest`,sfe=`CreateInstanceProfileResponse`,cfe=`ConcurrentModificationException`,lfe=`CreatePolicyRequest`,ufe=`CreatePolicyResponse`,dfe=`CreatePolicy`,ffe=`CreateRole`,pfe=`CredentialReportExpiredException`,mfe=`CredentialReportNotPresentException`,hfe=`CredentialReportNotReadyException`,gfe=`CreateRoleRequest`,_fe=`CreateRoleResponse`,vfe=`CreateUser`,yfe=`CreateUserRequest`,bfe=`CreateUserResponse`,O3=`Description`,xfe=`DeleteAccessKey`,Sfe=`DeleteAccessKeyRequest`,Cfe=`DeleteConflictException`,wfe=`DuplicateCertificateException`,Tfe=`DeleteInstanceProfile`,Efe=`DeleteInstanceProfileRequest`,Dfe=`DeletePolicyRequest`,Ofe=`DeletePolicy`,kfe=`DetachRolePolicyRequest`,Afe=`DetachRolePolicy`,jfe=`DeleteRoleRequest`,Mfe=`DeleteRole`,Nfe=`DuplicateSSHPublicKeyException`,Pfe=`DeleteUser`,Ffe=`DetachUserPolicyRequest`,Ife=`DetachUserPolicy`,Lfe=`DeleteUserRequest`,Rfe=`DefaultVersionId`,zfe=`Document`,Bfe=`EntityAlreadyExistsException`,Vfe=`EntityTemporarilyUnmodifiableException`,Hfe=`FeatureDisabledException`,Ufe=`FeatureEnabledException`,Wfe=`GetInstanceProfile`,Gfe=`GetInstanceProfileRequest`,Kfe=`GetInstanceProfileResponse`,qfe=`GetPolicy`,Jfe=`GetPolicyRequest`,Yfe=`GetPolicyResponse`,Xfe=`GetPolicyVersion`,Zfe=`GetPolicyVersionRequest`,Qfe=`GetPolicyVersionResponse`,$fe=`GetRole`,epe=`GetRoleRequest`,tpe=`GetRoleResponse`,npe=`GetUser`,rpe=`GetUserRequest`,ipe=`GetUserResponse`,ape=`IsAttachable`,ope=`InvalidAuthenticationCodeException`,spe=`InvalidCertificateException`,cpe=`IsDefaultVersion`,lpe=`InvalidInputException`,k3=`InstanceProfile`,upe=`InstanceProfileId`,dpe=`InvalidPublicKeyException`,A3=`InstanceProfileName`,j3=`InstanceProfiles`,M3=`IsTruncated`,fpe=`InvalidUserTypeException`,ppe=`Key`,mpe=`KeyPairMismatchException`,hpe=`ListAccessKeys`,gpe=`ListAccessKeysRequest`,_pe=`ListAccessKeysResponse`,vpe=`ListAttachedRolePolicies`,ype=`ListAttachedRolePoliciesRequest`,bpe=`ListAttachedRolePoliciesResponse`,xpe=`ListAttachedUserPolicies`,Spe=`ListAttachedUserPoliciesRequest`,Cpe=`ListAttachedUserPoliciesResponse`,wpe=`LimitExceededException`,Tpe=`ListInstanceProfiles`,Epe=`ListInstanceProfilesForRole`,Dpe=`ListInstanceProfilesForRoleRequest`,Ope=`ListInstanceProfilesForRoleResponse`,kpe=`ListInstanceProfilesRequest`,Ape=`ListInstanceProfilesResponse`,jpe=`ListPoliciesRequest`,Mpe=`ListPoliciesResponse`,Npe=`ListPolicies`,Ppe=`ListRoles`,Fpe=`ListRolesRequest`,Ipe=`ListRolesResponse`,Lpe=`ListUsers`,Rpe=`LastUsedDate`,zpe=`ListUsersRequest`,Bpe=`ListUsersResponse`,N3=`Message`,Vpe=`MalformedCertificateException`,P3=`MaxItems`,Hpe=`MalformedPolicyDocumentException`,F3=`MaxSessionDuration`,I3=`Marker`,Upe=`NoSuchEntityException`,Wpe=`OnlyAttached`,Gpe=`OpenIdIdpCommunicationErrorException`,Kpe=`OrganizationNotFoundException`,qpe=`OrganizationNotInAllFeaturesModeException`,L3=`PolicyArn`,R3=`PermissionsBoundary`,Jpe=`PermissionsBoundaryArn`,Ype=`PermissionsBoundaryType`,Xpe=`PermissionsBoundaryUsageCount`,Zpe=`PolicyDocument`,Qpe=`PolicyEvaluationException`,$pe=`PolicyId`,eme=`PasswordLastUsed`,z3=`PolicyName`,tme=`PolicyNotAttachableException`,nme=`PasswordPolicyViolationException`,B3=`PathPrefix`,rme=`PolicyUsageFilter`,V3=`PolicyVersion`,H3=`Path`,U3=`Policy`,ime=`Policies`,ame=`Region`,ome=`ReportGenerationLimitExceededException`,sme=`RoleId`,W3=`RoleLastUsed`,G3=`RoleName`,cme=`RemoveRoleFromInstanceProfile`,lme=`RemoveRoleFromInstanceProfileRequest`,K3=`Role`,q3=`Roles`,J3=`Status`,ume=`SecretAccessKey`,dme=`ServiceAccessNotEnabledException`,fme=`ServiceFailureException`,pme=`ServiceNotSupportedException`,mme=`Scope`,Y3=`Tags`,hme=`Tag`,gme=`UpdateAccessKey`,_me=`UpdateAccessKeyRequest`,vme=`UpdateDate`,yme=`UnmodifiableEntityException`,bme=`UserId`,X3=`UserName`,xme=`UnrecognizedPublicKeyEncodingException`,Z3=`User`,Sme=`Users`,Q3=`VersionId`,Cme=`Value`,wme=`accessKeyMetadataListType`,Tme=`accessKeySecretType`,Eme=`attachedPoliciesListType`,z=`awsQueryError`,B=`client`,V=`error`,H=`httpError`,Dme=`instanceProfileListType`,U=`message`,Ome=`policyListType`,kme=`roleListType`,$3=`smithy.ts.sdk.synthetic.com.amazonaws.iam`,e6=`server`,Ame=`tagListType`,jme=`userListType`,W=`com.amazonaws.iam`,t6=Lt.for($3),Mme=[-3,$3,`IAMServiceException`,0,[],[]];t6.registerError(Mme,R);var G=Lt.for(W),Nme=[-3,W,Kde,{[V]:B,[H]:400},[N3],[0]];G.registerError(Nme,pde);var Pme=[-3,W,ife,{[V]:B,[H]:400},[N3],[0]];G.registerError(Pme,jde);var Fme=[-3,W,cfe,{[z]:[`ConcurrentModification`,409],[V]:B,[H]:409},[U],[0]];G.registerError(Fme,ude);var Ime=[-3,W,pfe,{[z]:[`ReportExpired`,410],[V]:B,[H]:410},[U],[0]];G.registerError(Ime,Pde);var Lme=[-3,W,mfe,{[z]:[`ReportNotPresent`,410],[V]:B,[H]:410},[U],[0]];G.registerError(Lme,Fde);var Rme=[-3,W,hfe,{[z]:[`ReportInProgress`,404],[V]:B,[H]:404},[U],[0]];G.registerError(Rme,Ide);var zme=[-3,W,Cfe,{[z]:[`DeleteConflict`,409],[V]:B,[H]:409},[U],[0]];G.registerError(zme,Tde);var Bme=[-3,W,wfe,{[z]:[`DuplicateCertificate`,409],[V]:B,[H]:409},[U],[0]];G.registerError(Bme,Vde);var Vme=[-3,W,Nfe,{[z]:[`DuplicateSSHPublicKey`,400],[V]:B,[H]:400},[U],[0]];G.registerError(Vme,Ude);var Hme=[-3,W,Bfe,{[z]:[`EntityAlreadyExists`,409],[V]:B,[H]:409},[U],[0]];G.registerError(Hme,gde);var Ume=[-3,W,Vfe,{[z]:[`EntityTemporarilyUnmodifiable`,409],[V]:B,[H]:409},[U],[0]];G.registerError(Ume,yde);var Wme=[-3,W,Hfe,{[z]:[`FeatureDisabled`,404],[V]:B,[H]:404},[U],[0]];G.registerError(Wme,kde);var Gme=[-3,W,Ufe,{[z]:[`FeatureEnabled`,409],[V]:B,[H]:409},[U],[0]];G.registerError(Gme,Mde);var Kme=[-3,W,ope,{[z]:[`InvalidAuthenticationCode`,403],[V]:B,[H]:403},[U],[0]];G.registerError(Kme,Ade);var qme=[-3,W,spe,{[z]:[`InvalidCertificate`,400],[V]:B,[H]:400},[U],[0]];G.registerError(qme,Hde);var Jme=[-3,W,lpe,{[z]:[`InvalidInput`,400],[V]:B,[H]:400},[U],[0]];G.registerError(Jme,mde);var Yme=[-3,W,dpe,{[z]:[`InvalidPublicKey`,400],[V]:B,[H]:400},[U],[0]];G.registerError(Yme,Wde);var Xme=[-3,W,fpe,{[z]:[`InvalidUserType`,400],[V]:B,[H]:400},[U],[0]];G.registerError(Xme,bde);var Zme=[-3,W,mpe,{[z]:[`KeyPairMismatch`,400],[V]:B,[H]:400},[U],[0]];G.registerError(Zme,zde);var Qme=[-3,W,wpe,{[z]:[`LimitExceeded`,409],[V]:B,[H]:409},[U],[0]];G.registerError(Qme,hde);var $me=[-3,W,Vpe,{[z]:[`MalformedCertificate`,400],[V]:B,[H]:400},[U],[0]];G.registerError($me,Bde);var ehe=[-3,W,Hpe,{[z]:[`MalformedPolicyDocument`,400],[V]:B,[H]:400},[U],[0]];G.registerError(ehe,Cde);var the=[-3,W,Upe,{[z]:[`NoSuchEntity`,404],[V]:B,[H]:404},[U],[0]];G.registerError(the,dde);var nhe=[-3,W,Gpe,{[z]:[`OpenIdIdpCommunicationError`,400],[V]:B,[H]:400},[U],[0]];G.registerError(nhe,Sde);var rhe=[-3,W,Kpe,{[V]:B,[H]:400},[N3],[0]];G.registerError(rhe,Ede);var ihe=[-3,W,qpe,{[V]:B,[H]:400},[N3],[0]];G.registerError(ihe,Dde);var ahe=[-3,W,nme,{[z]:[`PasswordPolicyViolation`,400],[V]:B,[H]:400},[U],[0]];G.registerError(ahe,xde);var ohe=[-3,W,Qpe,{[z]:[`PolicyEvaluation`,500],[V]:e6,[H]:500},[U],[0]];G.registerError(ohe,Rde);var she=[-3,W,tme,{[z]:[`PolicyNotAttachable`,400],[V]:B,[H]:400},[U],[0]];G.registerError(she,vde);var che=[-3,W,ome,{[z]:[`ReportGenerationLimitExceeded`,409],[V]:B,[H]:409},[U],[0]];G.registerError(che,Nde);var lhe=[-3,W,dme,{[V]:B,[H]:400},[N3],[0]];G.registerError(lhe,Ode);var uhe=[-3,W,fme,{[z]:[`ServiceFailure`,500],[V]:e6,[H]:500},[U],[0]];G.registerError(uhe,fde);var dhe=[-3,W,pme,{[z]:[`NotSupportedService`,404],[V]:B,[H]:404},[U],[0]];G.registerError(dhe,wde);var fhe=[-3,W,yme,{[z]:[`UnmodifiableEntity`,400],[V]:B,[H]:400},[U],[0]];G.registerError(fhe,_de);var phe=[-3,W,xme,{[z]:[`UnrecognizedPublicKeyEncoding`,400],[V]:B,[H]:400},[U],[0]];G.registerError(phe,Lde);var mhe=[t6,G],hhe=[0,W,Tme,8,0],ghe=[3,W,S3,0,[X3,C3,J3,ume,D3],[0,0,0,[()=>hhe,0],4],4],_he=[3,W,w3,0,[X3,C3,J3,D3],[0,0,0,4]],vhe=[3,W,Qde,0,[A3,G3],[0,0],2],n6=[3,W,Jde,0,[Ype,Jpe],[0,0]],yhe=[3,W,qde,0,[z3,L3],[0,0]],bhe=[3,W,Xde,0,[G3,L3],[0,0],2],xhe=[3,W,efe,0,[X3,L3],[0,0],2],She=[3,W,nfe,0,[X3],[0]],Che=[3,W,rfe,0,[S3],[[()=>ghe,0]],1],whe=[3,W,ofe,0,[A3,H3,Y3],[0,0,()=>d6],1],The=[3,W,sfe,0,[k3],[()=>r6],1],Ehe=[3,W,lfe,0,[z3,Zpe,H3,O3,Y3],[0,0,0,0,()=>d6],2],Dhe=[3,W,ufe,0,[U3],[()=>i6]],Ohe=[3,W,gfe,0,[G3,E3,H3,O3,F3,R3,Y3],[0,0,0,0,1,0,()=>d6],2],khe=[3,W,_fe,0,[K3],[()=>a6],1],Ahe=[3,W,yfe,0,[X3,H3,R3,Y3],[0,0,0,()=>d6],1],jhe=[3,W,bfe,0,[Z3],[()=>o6]],Mhe=[3,W,Sfe,0,[C3,X3],[0,0],1],Nhe=[3,W,Efe,0,[A3],[0],1],Phe=[3,W,Dfe,0,[L3],[0],1],Fhe=[3,W,jfe,0,[G3],[0],1],Ihe=[3,W,Lfe,0,[X3],[0],1],Lhe=[3,W,kfe,0,[G3,L3],[0,0],2],Rhe=[3,W,Ffe,0,[X3,L3],[0,0],2],zhe=[3,W,Gfe,0,[A3],[0],1],Bhe=[3,W,Kfe,0,[k3],[()=>r6],1],Vhe=[3,W,Jfe,0,[L3],[0],1],Hhe=[3,W,Yfe,0,[U3],[()=>i6]],Uhe=[3,W,Zfe,0,[L3,Q3],[0,0],2],Whe=[3,W,Qfe,0,[V3],[()=>dge]],Ghe=[3,W,epe,0,[G3],[0],1],Khe=[3,W,tpe,0,[K3],[()=>a6],1],qhe=[3,W,rpe,0,[X3],[0]],Jhe=[3,W,ipe,0,[Z3],[()=>o6],1],r6=[3,W,k3,0,[H3,A3,upe,x3,D3,q3,Y3],[0,0,0,0,4,()=>u6,()=>d6],6],Yhe=[3,W,gpe,0,[X3,I3,P3],[0,0,1]],Xhe=[3,W,_pe,0,[w3,M3,I3],[()=>gge,2,0],1],Zhe=[3,W,ype,0,[G3,B3,I3,P3],[0,0,0,1],1],Qhe=[3,W,bpe,0,[T3,M3,I3],[()=>c6,2,0]],$he=[3,W,Spe,0,[X3,B3,I3,P3],[0,0,0,1],1],ege=[3,W,Cpe,0,[T3,M3,I3],[()=>c6,2,0]],tge=[3,W,Dpe,0,[G3,I3,P3],[0,0,1],1],nge=[3,W,Ope,0,[j3,M3,I3],[()=>l6,2,0],1],rge=[3,W,kpe,0,[B3,I3,P3],[0,0,1]],ige=[3,W,Ape,0,[j3,M3,I3],[()=>l6,2,0],1],age=[3,W,jpe,0,[mme,Wpe,B3,rme,I3,P3],[0,2,0,0,0,1]],oge=[3,W,Mpe,0,[ime,M3,I3],[()=>_ge,2,0]],sge=[3,W,Fpe,0,[B3,I3,P3],[0,0,1]],cge=[3,W,Ipe,0,[q3,M3,I3],[()=>u6,2,0],1],lge=[3,W,zpe,0,[B3,I3,P3],[0,0,1]],uge=[3,W,Bpe,0,[Sme,M3,I3],[()=>vge,2,0],1],i6=[3,W,U3,0,[z3,$pe,x3,H3,Rfe,Gde,Xpe,ape,O3,D3,vme,Y3],[0,0,0,0,0,1,1,2,0,4,4,()=>d6]],dge=[3,W,V3,0,[zfe,Q3,cpe,D3],[0,0,2,4]],fge=[3,W,lme,0,[A3,G3],[0,0],2],a6=[3,W,K3,0,[H3,G3,sme,x3,D3,E3,O3,F3,R3,Y3,W3],[0,0,0,0,4,0,0,1,()=>n6,()=>d6,()=>pge],5],pge=[3,W,W3,0,[Rpe,ame],[4,0]],mge=[3,W,hme,0,[ppe,Cme],[0,0],2],hge=[3,W,_me,0,[C3,J3,X3],[0,0,0],2],o6=[3,W,Z3,0,[H3,X3,bme,x3,D3,eme,R3,Y3],[0,0,0,0,4,4,()=>n6,()=>d6],5],s6=`unit`,gge=[1,W,wme,0,()=>_he],c6=[1,W,Eme,0,()=>yhe],l6=[1,W,Dme,0,()=>r6],_ge=[1,W,Ome,0,()=>i6],u6=[1,W,kme,0,()=>a6],d6=[1,W,Ame,0,()=>mge],vge=[1,W,jme,0,()=>o6],yge=[9,W,Zde,0,()=>vhe,()=>s6],bge=[9,W,Yde,0,()=>bhe,()=>s6],xge=[9,W,$de,0,()=>xhe,()=>s6],Sge=[9,W,tfe,0,()=>She,()=>Che],Cge=[9,W,afe,0,()=>whe,()=>The],wge=[9,W,dfe,0,()=>Ehe,()=>Dhe],Tge=[9,W,ffe,0,()=>Ohe,()=>khe],Ege=[9,W,vfe,0,()=>Ahe,()=>jhe],Dge=[9,W,xfe,0,()=>Mhe,()=>s6],Oge=[9,W,Tfe,0,()=>Nhe,()=>s6],kge=[9,W,Ofe,0,()=>Phe,()=>s6],Age=[9,W,Mfe,0,()=>Fhe,()=>s6],jge=[9,W,Pfe,0,()=>Ihe,()=>s6],Mge=[9,W,Afe,0,()=>Lhe,()=>s6],Nge=[9,W,Ife,0,()=>Rhe,()=>s6],Pge=[9,W,Wfe,0,()=>zhe,()=>Bhe],Fge=[9,W,qfe,0,()=>Vhe,()=>Hhe],Ige=[9,W,Xfe,0,()=>Uhe,()=>Whe],Lge=[9,W,$fe,0,()=>Ghe,()=>Khe],Rge=[9,W,npe,0,()=>qhe,()=>Jhe],zge=[9,W,hpe,0,()=>Yhe,()=>Xhe],Bge=[9,W,vpe,0,()=>Zhe,()=>Qhe],Vge=[9,W,xpe,0,()=>$he,()=>ege],Hge=[9,W,Tpe,0,()=>rge,()=>ige],Uge=[9,W,Epe,0,()=>tge,()=>nge],Wge=[9,W,Npe,0,()=>age,()=>oge],Gge=[9,W,Ppe,0,()=>sge,()=>cge],Kge=[9,W,Lpe,0,()=>lge,()=>uge],qge=[9,W,cme,0,()=>fge,()=>s6],Jge=[9,W,gme,0,()=>hge,()=>s6],Yge=e=>({apiVersion:`2010-05-08`,base64Decoder:e?.base64Decoder??fe,base64Encoder:e?.base64Encoder??le,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??lde,extensions:e?.extensions??[],httpAuthSchemeProvider:e?.httpAuthSchemeProvider??ede,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new Yi}],logger:e?.logger??new $t,protocol:e?.protocol??qa,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.iam`,errorTypeRegistries:mhe,xmlNamespace:`https://iam.amazonaws.com/doc/2010-05-08/`,version:`2010-05-08`,serviceTarget:`AWSIdentityManagementV20100508`},serviceId:e?.serviceId??`IAM`,urlParser:e?.urlParser??nt,utf8Decoder:e?.utf8Decoder??ce,utf8Encoder:e?.utf8Encoder??me}),Xge=e=>{let t=zi(e),n=()=>t().then(Wt),r=Yge(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??Ee,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??Ei({serviceId:r.serviceId,clientVersion:ide.version}),maxAttempts:e?.maxAttempts??3,region:e?.region??yt(`Region is missing`),requestHandler:da.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||Nr),sha256:e?.sha256??ee,streamCollector:e?.streamCollector??pa,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},Zge=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},Qge=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),$ge=(e,t)=>{let n=Object.assign(Vi(e),Xt(e),Xn(e),Zge(e));return t.forEach(e=>e.configure(n)),Object.assign(e,Hi(n),Zt(n),Zn(n),Qge(n))},e_e=class extends bt{config;constructor(...[e]){let t=Xge(e||{});super(t),this.initConfig=t;let n=$ge(tde(Nn(zr(Li(Ir(li(nde(t))))))),e?.extensions||[]);this.config=n,this.middlewareStack.use(Ot(this.config)),this.middlewareStack.use(Ti(this.config)),this.middlewareStack.use(Lr(this.config)),this.middlewareStack.use(tr(this.config)),this.middlewareStack.use(Hr(this.config)),this.middlewareStack.use(Gr(this.config)),this.middlewareStack.use(Kr(this.config)),this.middlewareStack.use(Zr(this.config,{httpAuthSchemeParametersProvider:Que,identityProviderConfigProvider:async e=>new ii({"aws.auth#sigv4":e.credentials})})),this.middlewareStack.use(ni(this.config))}destroy(){super.destroy()}},f6={WHEN_SUPPORTED:`WHEN_SUPPORTED`,WHEN_REQUIRED:`WHEN_REQUIRED`},t_e=f6.WHEN_SUPPORTED,n_e={WHEN_SUPPORTED:`WHEN_SUPPORTED`,WHEN_REQUIRED:`WHEN_REQUIRED`},r_e=f6.WHEN_SUPPORTED,p6;(function(e){e.MD5=`MD5`,e.CRC32=`CRC32`,e.CRC32C=`CRC32C`,e.CRC64NVME=`CRC64NVME`,e.SHA1=`SHA1`,e.SHA256=`SHA256`})(p6||={});var i_e;(function(e){e.HEADER=`header`,e.TRAILER=`trailer`})(i_e||={});var a_e=p6.CRC32,o_e=e=>{let{requestChecksumCalculation:t,responseChecksumValidation:n,requestStreamBufferSize:r}=e;return Object.assign(e,{requestChecksumCalculation:re(t??t_e),responseChecksumValidation:re(n??r_e),requestStreamBufferSize:Number(r??0),checksumAlgorithms:e.checksumAlgorithms??{}})},s_e=e=>(t,n)=>async r=>{let i=await e.region(),a=e.region,o=()=>{};n.__s3RegionRedirect&&(Object.defineProperty(e,"region",{writable:!1,value:async()=>n.__s3RegionRedirect}),o=()=>Object.defineProperty(e,"region",{writable:!0,value:a}));try{let a=await t(r);if(n.__s3RegionRedirect&&(o(),i!==await e.region()))throw Error(`Region was not restored following S3 region redirect.`);return a}catch(e){throw o(),e}},c_e={tags:[`REGION_REDIRECT`,`S3`],name:`regionRedirectEndpointMiddleware`,override:!0,relation:`before`,toMiddleware:`endpointV2Middleware`};function l_e(e){return(t,n)=>async r=>{try{return await t(r)}catch(i){if(e.followRegionRedirects){let a=i?.$metadata?.httpStatusCode,o=n.commandName===`HeadBucketCommand`,s=i?.$response?.headers?.[`x-amz-bucket-region`];if(s&&(a===301||a===400&&(i?.name===`IllegalLocationConstraintException`||o))){try{let t=s;n.logger?.debug(`Redirecting from ${await e.region()} to ${t}`),n.__s3RegionRedirect=t}catch(e){throw Error(`Region redirect failed: `+e)}return t(r)}}throw i}}}var u_e={step:`initialize`,tags:[`REGION_REDIRECT`,`S3`],name:`regionRedirectMiddleware`,override:!0},d_e=e=>({applyToStack:t=>{t.add(l_e(e),u_e),t.addRelativeTo(s_e(e),c_e)}}),f_e=class e{data;lastPurgeTime=Date.now();static EXPIRED_CREDENTIAL_PURGE_INTERVAL_MS=3e4;constructor(e={}){this.data=e}get(e){let t=this.data[e];if(t)return t}set(e,t){return this.data[e]=t,t}delete(e){delete this.data[e]}async purgeExpired(){let t=Date.now();if(!(this.lastPurgeTime+e.EXPIRED_CREDENTIAL_PURGE_INTERVAL_MS>t))for(let e in this.data){let n=this.data[e];if(!n.isRefreshing){let r=await n.identity;r.expiration&&r.expiration.getTime()(t.expiration?.getTime()??0){i.set(r,new m6(Promise.resolve(e)))})),t)):i.set(r,new m6(this.getIdentity(r))).identity}async getIdentity(e){await this.cache.purgeExpired().catch(e=>{console.warn(`Error while clearing expired entries in S3ExpressIdentityCache: +`+e)});let t=await this.createSessionFn(e);if(!t.Credentials?.AccessKeyId||!t.Credentials?.SecretAccessKey)throw Error(`s3#createSession response credential missing AccessKeyId or SecretAccessKey.`);return{accessKeyId:t.Credentials.AccessKeyId,secretAccessKey:t.Credentials.SecretAccessKey,sessionToken:t.Credentials.SessionToken,expiration:t.Credentials.Expiration?new Date(t.Credentials.Expiration):void 0}}},m_e=(e,{session:t})=>{let[n,r]=t,{forcePathStyle:i,useAccelerateEndpoint:a,disableMultiregionAccessPoints:o,followRegionRedirects:s,s3ExpressIdentityProvider:c,bucketEndpoint:l,expectContinueHeader:u}=e;return Object.assign(e,{forcePathStyle:i??!1,useAccelerateEndpoint:a??!1,disableMultiregionAccessPoints:o??!1,followRegionRedirects:s??!1,s3ExpressIdentityProvider:c??new p_e(async e=>n().send(new r({Bucket:e}))),bucketEndpoint:l??!1,expectContinueHeader:u??2097152})},h6={CrtSignerV4:null},g6=`X-Amz-S3session-Token`,_6=g6.toLowerCase(),h_e=class extends ae{async signWithCredentials(e,t,n){let r=v6(t);e.headers[_6]=t.sessionToken;let i=this;return y6(i,r),i.signRequest(e,n??{})}async presignWithCredentials(e,t,n){let r=v6(t);return delete e.headers[_6],e.headers[g6]=t.sessionToken,e.query=e.query??{},e.query[g6]=t.sessionToken,y6(this,r),this.presign(e,n)}};function v6(e){return{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey,expiration:e.expiration}}function y6(e,t){let n=e.credentialProvider;e.credentialProvider=()=>(e.credentialProvider=n,Promise.resolve(t))}var b6=class{sigv4aSigner;sigv4Signer;signerOptions;static sigv4aDependency(){return typeof h6.CrtSignerV4==`function`?`crt`:typeof rr.SignatureV4a==`function`?`js`:`none`}constructor(e){this.sigv4Signer=new h_e(e),this.signerOptions=e}async sign(e,t={}){return t.signingRegion===`*`?this.getSigv4aSigner().sign(e,t):this.sigv4Signer.sign(e,t)}async signWithCredentials(e,t,n={}){if(n.signingRegion===`*`){let r=this.getSigv4aSigner(),i=h6.CrtSignerV4;if(i&&r instanceof i)return r.signWithCredentials(e,t,n);throw Error(`signWithCredentials with signingRegion '*' is only supported when using the CRT dependency @aws-sdk/signature-v4-crt. Please check whether you have installed the "@aws-sdk/signature-v4-crt" package explicitly. You must also register the package by calling [require("@aws-sdk/signature-v4-crt");] or an ESM equivalent such as [import "@aws-sdk/signature-v4-crt";]. For more information please go to https://github.com/aws/aws-sdk-js-v3#functionality-requiring-aws-common-runtime-crt`)}return this.sigv4Signer.signWithCredentials(e,t,n)}async presign(e,t={}){if(t.signingRegion===`*`){let n=this.getSigv4aSigner(),r=h6.CrtSignerV4;if(r&&n instanceof r)return n.presign(e,t);throw Error(`presign with signingRegion '*' is only supported when using the CRT dependency @aws-sdk/signature-v4-crt. Please check whether you have installed the "@aws-sdk/signature-v4-crt" package explicitly. You must also register the package by calling [require("@aws-sdk/signature-v4-crt");] or an ESM equivalent such as [import "@aws-sdk/signature-v4-crt";]. For more information please go to https://github.com/aws/aws-sdk-js-v3#functionality-requiring-aws-common-runtime-crt`)}return this.sigv4Signer.presign(e,t)}async presignWithCredentials(e,t,n={}){if(n.signingRegion===`*`)throw Error(`Method presignWithCredentials is not supported for [signingRegion=*].`);return this.sigv4Signer.presignWithCredentials(e,t,n)}getSigv4aSigner(){if(!this.sigv4aSigner){let e=h6.CrtSignerV4,t=rr.SignatureV4a;if(this.signerOptions.runtime===`node`){if(!e&&!t)throw Error(`Neither CRT nor JS SigV4a implementation is available. Please load either @aws-sdk/signature-v4-crt or @aws-sdk/signature-v4a. For more information please go to https://github.com/aws/aws-sdk-js-v3#functionality-requiring-aws-common-runtime-crt`);if(e&&typeof e==`function`)this.sigv4aSigner=new e({...this.signerOptions,signingAlgorithm:1});else if(t&&typeof t==`function`)this.sigv4aSigner=new t({...this.signerOptions});else throw Error(`Available SigV4a implementation is not a valid constructor. Please ensure you've properly imported @aws-sdk/signature-v4-crt or @aws-sdk/signature-v4a.For more information please go to https://github.com/aws/aws-sdk-js-v3#functionality-requiring-aws-common-runtime-crt`)}else{if(!t||typeof t!=`function`)throw Error(`JS SigV4a implementation is not available or not a valid constructor. Please check whether you have installed the @aws-sdk/signature-v4a package explicitly. The CRT implementation is not available for browsers. You must also register the package by calling [require('@aws-sdk/signature-v4a');] or an ESM equivalent such as [import '@aws-sdk/signature-v4a';]. For more information please go to https://github.com/aws/aws-sdk-js-v3#using-javascript-non-crt-implementation-of-sigv4a`);this.sigv4aSigner=new t({...this.signerOptions})}}return this.sigv4aSigner}},g_e=`sigv4-s3express`,__e=`x-amz-s3session-token`,v_e=e=>(t,n)=>async r=>{if(n.endpointV2){let t=n.endpointV2,i=t.properties?.authSchemes?.[0]?.name===g_e;if((t.properties?.backend===`S3Express`||t.properties?.bucketType===`Directory`)&&(Rr(n,`S3_EXPRESS_BUCKET`,`J`),n.isS3ExpressBucket=!0),i){let t=r.input.Bucket;if(t){let i=await e.s3ExpressIdentityProvider.getS3ExpressIdentity(await e.credentials(),{Bucket:t});n.s3ExpressIdentity=i,m.isInstance(r.request)&&i.sessionToken&&(r.request.headers[__e]=i.sessionToken)}}}return t(r)},y_e={name:`s3ExpressMiddleware`,step:`build`,tags:[`S3`,`S3_EXPRESS`],override:!0},b_e=e=>({applyToStack:t=>{t.add(v_e(e),y_e)}}),x_e=async(e,t,n,r)=>{let i=await r.signWithCredentials(n,e,{});if(i.headers[`X-Amz-Security-Token`]||i.headers[`x-amz-security-token`])throw Error(`X-Amz-Security-Token must not be set for s3-express requests.`);return i},S_e=e=>e=>{throw e},C_e=(e,t)=>{},w_e=e=>(t,n)=>async r=>{if(!m.isInstance(r.request))return t(r);let i=Ze(n).selectedHttpAuthScheme;if(!i)throw Error(`No HttpAuthScheme was selected: unable to sign request`);let{httpAuthOption:{signingProperties:a={}},identity:o,signer:s}=i,c;c=n.s3ExpressIdentity?await x_e(n.s3ExpressIdentity,a,r.request,await e.signer()):await s.sign(r.request,o,a);let l=await t({...r,request:c}).catch((s.errorHandler||S_e)(a));return(s.successHandler||C_e)(l.response,a),l},T_e=e=>({applyToStack:t=>{t.addRelativeTo(w_e(e),ti)}});function E_e(e){return new ReadableStream({start(t){t.enqueue(e),t.close()}})}var D_e={CopyObjectCommand:!0,UploadPartCopyCommand:!0,CompleteMultipartUploadCommand:!0},O_e=e=>(t,n)=>async r=>{let i=await t(r),{response:a}=i;if(!Qe.isInstance(a))return i;let{statusCode:o,body:s}=a;if(o<200||o>=300)return i;let c=await k_e(s,e);if(a.body=E_e(c),c.length===0&&D_e[n.commandName]){let e=Error(`S3 aborted request`);throw e.$metadata={httpStatusCode:503},e.name=`InternalError`,e}let l=e.utf8Encoder(c.subarray(c.length-16));return l&&l.endsWith(``)&&(a.statusCode=503),i},k_e=(e=new Uint8Array,t)=>e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array),A_e={relation:`after`,toMiddleware:`deserializerMiddleware`,tags:[`THROW_200_EXCEPTIONS`,`S3`],name:`throw200ExceptionsMiddleware`,override:!0},x6=e=>({applyToStack:t=>{t.addRelativeTo(O_e(e),A_e)}});function j_e(e){return(t,n)=>async r=>{if(e.bucketEndpoint){let e=n.endpointV2;if(e){let t=r.input.Bucket;if(typeof t==`string`)try{let r=new URL(t);n.endpointV2={...e,url:r}}catch(e){let r=`@aws-sdk/middleware-sdk-s3: bucketEndpoint=true was set but Bucket=${t} could not be parsed as URL.`;throw n.logger?.constructor?.name===`NoOpLogger`?console.warn(r):n.logger?.warn?.(r),e}}}return t(r)}}var M_e={name:`bucketEndpointMiddleware`,override:!0,relation:`after`,toMiddleware:`endpointV2Middleware`};function N_e({bucketEndpoint:e}){return t=>async n=>{let{input:{Bucket:r}}=n;if(!e&&typeof r==`string`&&!vz(r)&&r.indexOf(`/`)>=0){let e=Error(`Bucket name shouldn't contain '/', received '${r}'`);throw e.name=`InvalidBucketName`,e}return t({...n})}}var P_e={step:`initialize`,tags:[`VALIDATE_BUCKET_NAME`],name:`validateBucketNameMiddleware`,override:!0},F_e=e=>({applyToStack:t=>{t.add(N_e(e),P_e),t.addRelativeTo(j_e(e),M_e)}}),I_e=class extends Qa{async serializeRequest(e,t,n){let r=await super.serializeRequest(e,t,n),i=h.of(e.input),a=i.getSchema(),o=0,s=a[6]??0;if(t&&typeof t==`object`)for(let[e,n]of i.structIterator()){if(++o>s)break;if(e===`Bucket`){if(!t.Bucket&&n.getMergedTraits().httpLabel)throw Error(`No value provided for input HTTP label: Bucket.`);break}}return r}};function L_e(e){return t=>async n=>{let{request:r}=n;if(e.expectContinueHeader!==!1&&m.isInstance(r)&&r.body&&e.runtime===`node`&&e.requestHandler?.constructor?.name!==`FetchHttpHandler`){let t=!0;if(typeof e.expectContinueHeader==`number`)try{t=(Number(r.headers?.[`content-length`])??e.bodyLengthChecker?.(r.body)??1/0)>=e.expectContinueHeader}catch{}else t=!!e.expectContinueHeader;t&&(r.headers.Expect=`100-continue`)}return t({...n,request:r})}}var R_e={step:`build`,tags:[`SET_EXPECT_HEADER`,`EXPECT_HEADER`],name:`addExpectContinueMiddleware`,override:!0},z_e=e=>({applyToStack:t=>{t.add(L_e(e),R_e)}}),K=`ref`,q=`argv`,S6=`backend`,C6=`authSchemes`,w6=`disableDoubleEncoding`,T6=`signingName`,E6=`signingRegion`,D6=`signingRegionSet`,J=-1,Y=!0,O6=!1,k6=`isSet`,A6=`booleanEquals`,X=`stringEquals`,j6=`coalesce`,M6=`substring`,N6=``,P6=`aws.partition`,F6=`partitionResult`,I6=`accessPointSuffix`,L6=`regionPrefix`,R6=e=>`outpostId_ssa_`+e+N6,z6=`hardwareType`,B6=`ite`,V6=`isValidHostLabel`,H6=`sigv4`,U6=`aws.isVirtualHostableS3Bucket`,W6=`url`,G6=`getAttr`,K6=`bucketArn`,q6=`--`,J6=`arnType`,Y6=`accesspoint`,X6=e=>`accessPointName_ssa_`+e+N6,Z6=`s3-object-lambda`,Q6=`s3-outposts`,$6=`bucketPartition`,e8=`us-east-1`,t8=`outpostType`,n8=`name`,r8=`s3`,i8=`{url#scheme}://{Bucket}.{url#authority}{url#path}`,a8=`{url#scheme}://{url#authority}{url#path}`,o8=`{url#scheme}://{url#authority}{url#normalizedPath}{Bucket}`,s8=`https://{Bucket}.s3-accelerate.{partitionResult#dnsSuffix}`,c8=`https://{Bucket}.s3.{partitionResult#dnsSuffix}`,l8=e=>`{url#scheme}://{accessPointName_ssa_`+e+`}-{bucketArn#accountId}.{url#authority}{url#path}`,u8=e=>"Invalid ARN: The access point name may only contain a-z, A-Z, 0-9 and `-`. Found: `{accessPointName_ssa_"+e+"}`",d8=`sigv4a`,f8=`{url#scheme}://{url#authority}{url#normalizedPath}{uri_encoded_bucket}`,p8=`https://s3.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,m8=`https://s3.{partitionResult#dnsSuffix}`,h8={[K]:`UseFIPS`},g8={[K]:`UseDualStack`},_8={[K]:`Bucket`},v8={fn:G6,[q]:[{[K]:F6},n8]},y8={[K]:W6},b8={[K]:`Region`},x8={[K]:K6},S8={[K]:J6},C8={[K]:`accessPointName_ssa_1`},w8={fn:G6,[q]:[x8,`region`]},T8={[K]:z6},E8={fn:G6,[q]:[x8,`service`]},D8={fn:G6,[q]:[x8,`accountId`]},O8={[S6]:`S3Express`,[C6]:[{[w6]:!0,[n8]:`{_s3e_auth}`,[T6]:`s3express`,[E6]:`{Region}`}]},k8={[S6]:`S3Express`,[C6]:[{[w6]:!0,[n8]:H6,[T6]:`s3express`,[E6]:`{Region}`}]},A8={[C6]:[{[w6]:!0,[n8]:d8,[T6]:Q6,[D6]:[`*`]},{[w6]:!0,[n8]:H6,[T6]:Q6,[E6]:`{Region}`}]},j8={[C6]:[{[w6]:!0,[n8]:H6,[T6]:r8,[E6]:e8}]},M8={[C6]:[{[w6]:!0,[n8]:H6,[T6]:r8,[E6]:`{Region}`}]},N8={[C6]:[{[w6]:!0,[n8]:H6,[T6]:Z6,[E6]:`{bucketArn#region}`}]},P8={[C6]:[{[w6]:!0,[n8]:H6,[T6]:r8,[E6]:`{bucketArn#region}`}]},F8={[C6]:[{[w6]:!0,[n8]:d8,[T6]:Q6,[D6]:[`*`]},{[w6]:!0,[n8]:H6,[T6]:Q6,[E6]:`{bucketArn#region}`}]},I8={[C6]:[{[w6]:!0,[n8]:H6,[T6]:Z6,[E6]:`{Region}`}]},L8=[b8],R8=[{[K]:`Endpoint`}],z8=[_8],B8=[_8,0,7,!0],V8=[x8,`resourceId[1]`],B_e=[`*`],H8={conditions:[[k6,L8],[A6,[{[K]:`Accelerate`},Y]],[A6,[h8,Y]],[A6,[g8,Y]],[k6,R8],[k6,z8],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,0,6,Y]},N6]},`--x-s3`]],[X,[{fn:j6,[q]:[{fn:M6,[q]:B8},N6]},`--xa-s3`]],[P6,L8,F6],[M6,B8,I6],[X,[{[K]:I6},`--op-s3`]],[M6,[_8,8,12,Y],L6],[M6,[_8,32,49,Y],R6(2)],[M6,[_8,49,50,Y],z6],[A6,[{[K]:`ForcePathStyle`},Y]],[X,[v8,`aws-cn`]],[B6,[g8,`.dualstack`,N6],`_s3e_ds`],[V6,[{[K]:R6(2)},O6]],[B6,[h8,`-fips`,N6],`_s3e_fips`],[B6,[{fn:j6,[q]:[{[K]:`DisableS3ExpressSessionAuth`},O6]},H6,`sigv4-s3express`],`_s3e_auth`],[U6,[_8,O6]],[`parseURL`,R8,W6],[A6,[{fn:j6,[q]:[{[K]:`UseS3ExpressControlEndpoint`},O6]},Y]],[U6,[_8,Y]],[X,[{fn:G6,[q]:[y8,`scheme`]},`http`]],[V6,[b8,O6]],[`aws.parseArn`,z8,K6],[G6,[{fn:`split`,[q]:[_8,q6,0]},`[-2]`],`s3expressAvailabilityZoneId`],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,0,4,O6]},N6]},`arn:`]],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,16,18,Y]},N6]},q6]],[A6,[{fn:G6,[q]:[y8,`isIp`]},Y]],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,21,23,Y]},N6]},q6]],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,27,29,Y]},N6]},q6]],[X,[{[K]:L6},`beta`]],[`uriEncode`,z8,`uri_encoded_bucket`],[V6,[b8,Y]],[A6,[{fn:j6,[q]:[{[K]:`UseObjectLambdaEndpoint`},O6]},Y]],[G6,[x8,`resourceId[0]`],J6],[X,[S8,N6]],[X,[S8,Y6]],[G6,V8,X6(1)],[X,[C8,N6]],[X,[w8,N6]],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,14,16,Y]},N6]},q6]],[X,[T8,`e`]],[X,[T8,`o`]],[X,[b8,`aws-global`]],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,19,21,Y]},N6]},q6]],[X,[E8,Z6]],[A6,[{fn:j6,[q]:[{[K]:`DisableAccessPoints`},O6]},Y]],[X,[E8,Q6]],[P6,[w8],$6],[V6,[C8,Y]],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,26,28,Y]},N6]},q6]],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,15,17,Y]},N6]},q6]],[G6,[x8,`resourceId[4]`]],[X,[{fn:j6,[q]:[{fn:M6,[q]:[_8,20,22,Y]},N6]},q6]],[A6,[{[K]:`UseGlobalEndpoint`},Y]],[X,[b8,e8]],[G6,V8,R6(1)],[A6,[{fn:j6,[q]:[{[K]:`UseArnRegion`},Y]},Y]],[V6,[{[K]:R6(1)},O6]],[G6,[x8,`resourceId[2]`],t8],[X,[b8,w8]],[X,[{fn:G6,[q]:[{[K]:$6},n8]},v8]],[A6,[{[K]:`DisableMultiRegionAccessPoints`},Y]],[V6,[w8,Y]],[X,[{fn:G6,[q]:[x8,`partition`]},v8]],[X,[D8,N6]],[X,[E8,r8]],[V6,[D8,O6]],[G6,[x8,`resourceId[3]`],X6(2)],[V6,[C8,O6]],[X,[{[K]:t8},Y6]],[V6,[{[K]:X6(2)},O6]]],results:[[J],[J,`Accelerate cannot be used with FIPS`],[J,`Cannot set dual-stack in combination with a custom endpoint.`],[J,`A custom endpoint cannot be combined with FIPS`],[J,`A custom endpoint cannot be combined with S3 Accelerate`],[J,`Partition does not support FIPS`],[J,`S3Express does not support S3 Accelerate.`],[`{url#scheme}://{url#authority}/{uri_encoded_bucket}{url#path}`,O8],[i8,O8],[J,`S3Express bucket name is not a valid virtual hostable name.`],[`https://s3express-control{_s3e_fips}{_s3e_ds}.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,k8],[`https://{Bucket}.s3express{_s3e_fips}-{s3expressAvailabilityZoneId}{_s3e_ds}.{Region}.{partitionResult#dnsSuffix}`,O8],[J,`Unrecognized S3Express bucket name format.`],[a8,O8],[`https://s3express-control{_s3e_fips}{_s3e_ds}.{Region}.{partitionResult#dnsSuffix}`,k8],[J,`Expected a endpoint to be specified but no endpoint was found`],[`https://{Bucket}.ec2.{url#authority}`,A8],[`https://{Bucket}.ec2.s3-outposts.{Region}.{partitionResult#dnsSuffix}`,A8],[`https://{Bucket}.op-{outpostId_ssa_2}.{url#authority}`,A8],[`https://{Bucket}.op-{outpostId_ssa_2}.s3-outposts.{Region}.{partitionResult#dnsSuffix}`,A8],[J,`Unrecognized hardware type: "Expected hardware type o or e but got {hardwareType}"`],[J,`Invalid Outposts Bucket alias - it must be a valid bucket name.`],[J,"Invalid ARN: The outpost Id must only contain a-z, A-Z, 0-9 and `-`."],[J,"Custom endpoint `{Endpoint}` was not a valid URI"],[J,`S3 Accelerate cannot be used in this region`],[`https://{Bucket}.s3-fips.dualstack.us-east-1.{partitionResult#dnsSuffix}`,j8],[`https://{Bucket}.s3-fips.dualstack.{Region}.{partitionResult#dnsSuffix}`,M8],[`https://{Bucket}.s3-fips.us-east-1.{partitionResult#dnsSuffix}`,j8],[`https://{Bucket}.s3-fips.{Region}.{partitionResult#dnsSuffix}`,M8],[`https://{Bucket}.s3-accelerate.dualstack.us-east-1.{partitionResult#dnsSuffix}`,j8],[`https://{Bucket}.s3-accelerate.dualstack.{partitionResult#dnsSuffix}`,M8],[`https://{Bucket}.s3.dualstack.us-east-1.{partitionResult#dnsSuffix}`,j8],[`https://{Bucket}.s3.dualstack.{Region}.{partitionResult#dnsSuffix}`,M8],[o8,j8],[i8,j8],[o8,M8],[i8,M8],[s8,j8],[s8,M8],[c8,j8],[c8,M8],[`https://{Bucket}.s3.{Region}.{partitionResult#dnsSuffix}`,M8],[J,`Invalid region: region was not a valid DNS name.`],[J,`S3 Object Lambda does not support Dual-stack`],[J,`S3 Object Lambda does not support S3 Accelerate`],[J,`Access points are not supported for this operation`],[J,"Invalid configuration: region from ARN `{bucketArn#region}` does not match client region `{Region}` and UseArnRegion is `false`"],[J,`Invalid ARN: Missing account id`],[l8(1),N8],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-object-lambda-fips.{bucketArn#region}.{bucketPartition#dnsSuffix}`,N8],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-object-lambda.{bucketArn#region}.{bucketPartition#dnsSuffix}`,N8],[J,u8(1)],[J,"Invalid ARN: The account id may only contain a-z, A-Z, 0-9 and `-`. Found: `{bucketArn#accountId}`"],[J,"Invalid region in ARN: `{bucketArn#region}` (invalid DNS name)"],[J,"Client was configured for partition `{partitionResult#name}` but ARN (`{Bucket}`) has `{bucketPartition#name}`"],[J,"Invalid ARN: The ARN may only contain a single resource component after `accesspoint`."],[J,`Invalid ARN: bucket ARN is missing a region`],[J,"Invalid ARN: Expected a resource of the format `accesspoint:` but no name was provided"],[J,"Invalid ARN: Object Lambda ARNs only support `accesspoint` arn types, but found: `{arnType}`"],[J,`Access Points do not support S3 Accelerate`],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-accesspoint-fips.dualstack.{bucketArn#region}.{bucketPartition#dnsSuffix}`,P8],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-accesspoint-fips.{bucketArn#region}.{bucketPartition#dnsSuffix}`,P8],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-accesspoint.dualstack.{bucketArn#region}.{bucketPartition#dnsSuffix}`,P8],[l8(1),P8],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-accesspoint.{bucketArn#region}.{bucketPartition#dnsSuffix}`,P8],[J,`Invalid ARN: The ARN was not for the S3 service, found: {bucketArn#service}`],[J,`S3 MRAP does not support dual-stack`],[J,`S3 MRAP does not support FIPS`],[J,`S3 MRAP does not support S3 Accelerate`],[J,`Invalid configuration: Multi-Region Access Point ARNs are disabled.`],[`https://{accessPointName_ssa_1}.accesspoint.s3-global.{partitionResult#dnsSuffix}`,{[C6]:[{[w6]:Y,name:d8,[T6]:r8,[D6]:B_e}]}],[J,"Client was configured for partition `{partitionResult#name}` but bucket referred to partition `{bucketArn#partition}`"],[J,`Invalid Access Point Name`],[J,`S3 Outposts does not support Dual-stack`],[J,`S3 Outposts does not support FIPS`],[J,`S3 Outposts does not support S3 Accelerate`],[J,`Invalid Arn: Outpost Access Point ARN contains sub resources`],[`https://{accessPointName_ssa_2}-{bucketArn#accountId}.{outpostId_ssa_1}.{url#authority}`,F8],[`https://{accessPointName_ssa_2}-{bucketArn#accountId}.{outpostId_ssa_1}.s3-outposts.{bucketArn#region}.{bucketPartition#dnsSuffix}`,F8],[J,u8(2)],[J,"Expected an outpost type `accesspoint`, found {outpostType}"],[J,`Invalid ARN: expected an access point name`],[J,`Invalid ARN: Expected a 4-component resource`],[J,"Invalid ARN: The outpost Id may only contain a-z, A-Z, 0-9 and `-`. Found: `{outpostId_ssa_1}`"],[J,`Invalid ARN: The Outpost Id was not set`],[J,`Invalid ARN: Unrecognized format: {Bucket} (type: {arnType})`],[J,`Invalid ARN: No ARN type specified`],[J,"Invalid ARN: `{Bucket}` was not a valid ARN"],[J,`Path-style addressing cannot be used with ARN buckets`],[`https://s3-fips.dualstack.us-east-1.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,j8],[`https://s3-fips.dualstack.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,M8],[`https://s3-fips.us-east-1.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,j8],[`https://s3-fips.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,M8],[`https://s3.dualstack.us-east-1.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,j8],[`https://s3.dualstack.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,M8],[f8,j8],[f8,M8],[p8,j8],[p8,M8],[`https://s3.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,M8],[J,`Path-style addressing cannot be used with S3 Accelerate`],[a8,I8],[`https://s3-object-lambda-fips.{Region}.{partitionResult#dnsSuffix}`,I8],[`https://s3-object-lambda.{Region}.{partitionResult#dnsSuffix}`,I8],[`https://s3-fips.dualstack.us-east-1.{partitionResult#dnsSuffix}`,j8],[`https://s3-fips.dualstack.{Region}.{partitionResult#dnsSuffix}`,M8],[`https://s3-fips.us-east-1.{partitionResult#dnsSuffix}`,j8],[`https://s3-fips.{Region}.{partitionResult#dnsSuffix}`,M8],[`https://s3.dualstack.us-east-1.{partitionResult#dnsSuffix}`,j8],[`https://s3.dualstack.{Region}.{partitionResult#dnsSuffix}`,M8],[a8,j8],[a8,M8],[m8,j8],[m8,M8],[`https://s3.{Region}.{partitionResult#dnsSuffix}`,M8],[J,`A region must be set when sending requests to S3.`]]},V_e=2,H_e=new Int32Array([-1,1,-1,0,3,100000115,1,424,4,2,272,5,3,233,6,4,85,7,5,15,8,8,9,100000115,16,10,13,18,11,13,19,12,13,22,100000014,13,35,14,100000042,36,100000103,435,6,271,16,7,270,17,8,19,18,14,501,106,9,20,24,10,21,24,11,22,24,12,23,24,13,547,24,14,77,25,20,73,26,26,27,78,37,28,100000086,38,100000086,29,39,47,30,48,100000058,31,50,32,100000085,51,33,136,55,100000076,34,59,35,100000084,60,39,36,61,37,100000083,62,38,146,63,41,100000046,61,40,100000083,62,41,150,64,42,100000054,66,43,100000053,70,44,100000052,71,45,100000081,73,46,100000080,74,100000078,100000079,40,48,100000057,41,100000057,49,42,185,50,48,62,51,49,100000045,52,51,53,526,60,56,54,62,100000055,55,63,57,100000046,62,100000055,57,64,58,100000054,66,59,100000053,69,60,100000065,70,61,100000052,72,100000064,100000051,49,100000045,63,51,64,526,60,67,65,62,100000055,66,63,68,100000046,62,100000055,68,64,69,100000054,66,70,100000053,68,100000047,71,70,72,100000052,72,100000050,100000051,25,74,100000042,46,100000039,75,57,76,100000041,58,100000040,100000041,26,100000088,78,28,100000087,79,34,82,80,35,81,545,36,100000103,100000115,46,100000097,83,57,84,100000099,58,100000098,100000099,5,101,86,8,87,100000115,16,88,89,18,91,89,19,90,92,21,97,95,19,93,92,21,98,95,21,97,94,22,100000014,95,35,96,100000042,36,100000103,100000042,22,100000013,98,35,99,100000042,36,100000101,100,46,100000110,100000111,6,214,102,7,208,103,8,119,104,14,118,105,21,106,100000023,26,107,502,37,108,100000086,38,100000086,109,39,112,110,48,100000058,111,50,136,100000085,40,113,100000057,41,100000057,114,42,115,500,48,100000056,116,52,117,100000072,65,100000069,100000072,21,501,100000023,9,120,124,10,121,124,11,122,124,12,123,124,13,202,124,14,195,125,20,190,126,21,127,100000023,23,128,129,24,189,129,26,130,197,37,131,100000086,38,100000086,132,39,159,133,48,100000058,134,50,135,100000085,51,141,136,55,100000076,137,59,138,100000084,60,100000083,139,61,140,100000083,63,100000083,100000046,55,100000076,142,59,143,100000084,60,148,144,61,145,100000083,62,147,146,63,150,100000046,63,153,100000046,61,149,100000083,62,153,150,64,151,100000054,66,152,100000053,70,100000082,100000052,64,154,100000054,66,155,100000053,70,156,100000052,71,157,100000081,73,158,100000080,74,100000077,100000079,40,160,100000057,41,100000057,161,42,185,162,48,174,163,49,100000045,164,51,165,526,60,168,166,62,100000055,167,63,169,100000046,62,100000055,169,64,170,100000054,66,171,100000053,69,172,100000065,70,173,100000052,72,100000063,100000051,49,100000045,175,51,176,526,60,179,177,62,100000055,178,63,180,100000046,62,100000055,180,64,181,100000054,66,182,100000053,68,100000047,183,70,184,100000052,72,100000048,100000051,48,100000056,186,52,187,100000072,65,100000069,188,67,100000070,100000071,25,100000036,100000042,21,191,100000023,25,192,100000042,30,194,193,46,100000034,100000036,46,100000033,100000035,21,196,100000023,26,100000088,197,28,100000087,198,34,201,199,35,200,545,36,100000101,100000115,46,100000095,100000096,17,203,100000022,20,204,100000021,21,205,550,33,206,550,44,100000016,207,45,100000018,100000020,8,209,215,16,210,220,18,211,220,19,212,224,20,213,227,21,231,401,8,218,215,19,216,100000009,20,217,227,21,231,100000009,16,219,220,18,223,220,19,221,224,20,222,227,21,231,100000012,19,226,224,20,225,100000009,21,100000009,100000012,20,230,227,21,228,100000009,30,229,100000009,34,100000007,100000009,21,231,415,30,232,100000008,34,100000007,100000008,4,100000002,234,5,235,480,6,271,236,7,270,237,8,238,491,9,239,243,10,240,243,11,241,243,12,242,243,13,547,243,14,266,244,20,264,245,26,246,267,37,247,100000086,38,100000086,248,39,249,518,40,250,100000057,41,100000057,251,42,538,252,48,100000043,253,49,100000045,254,51,255,526,60,258,256,62,100000055,257,63,259,100000046,62,100000055,259,64,260,100000054,66,261,100000053,69,262,100000065,70,263,100000052,72,100000062,100000051,25,265,100000042,46,100000031,100000032,26,100000088,267,28,100000087,268,34,269,544,46,100000093,100000094,8,397,100000009,8,407,100000009,3,346,273,4,100000003,274,5,284,275,8,276,100000115,15,100000005,277,16,278,281,18,279,281,19,280,281,22,100000014,281,35,282,100000042,36,100000102,283,46,100000106,100000107,6,405,285,7,395,286,8,295,287,14,501,288,26,289,502,37,290,100000086,38,100000086,291,39,292,307,40,293,100000057,41,100000057,294,42,335,500,9,296,300,10,297,300,11,298,300,12,299,300,13,394,300,14,339,301,15,100000005,302,20,337,303,26,304,341,37,305,100000086,38,100000086,306,39,309,307,48,100000058,308,50,100000074,100000085,40,310,100000057,41,100000057,311,42,335,312,48,324,313,49,100000045,314,51,315,526,60,318,316,62,100000055,317,63,319,100000046,62,100000055,319,64,320,100000054,66,321,100000053,69,322,100000065,70,323,100000052,72,100000061,100000051,49,100000045,325,51,326,526,60,329,327,62,100000055,328,63,330,100000046,62,100000055,330,64,331,100000054,66,332,100000053,68,100000047,333,70,334,100000052,72,100000049,100000051,48,100000056,336,52,100000067,100000072,25,338,100000042,46,100000027,100000028,15,100000005,340,26,100000088,341,28,100000087,342,34,345,343,35,344,545,36,100000102,100000115,46,100000091,100000092,4,100000002,347,5,357,348,8,349,100000115,15,100000005,350,16,351,354,18,352,354,19,353,354,22,100000014,354,35,355,100000042,36,100000043,356,46,100000104,100000105,6,405,358,7,395,359,8,360,491,9,361,365,10,362,365,11,363,365,12,364,365,13,394,365,14,389,366,15,100000005,367,20,387,368,26,369,391,37,370,100000086,38,100000086,371,39,372,518,40,373,100000057,41,100000057,374,42,538,375,48,100000043,376,49,100000045,377,51,378,526,60,381,379,62,100000055,380,63,382,100000046,62,100000055,382,64,383,100000054,66,384,100000053,69,385,100000065,70,386,100000052,72,100000060,100000051,25,388,100000042,46,100000025,100000026,15,100000005,390,26,100000088,391,28,100000087,392,34,393,544,46,100000089,100000090,15,100000005,547,8,396,100000009,15,100000005,397,16,398,410,18,399,410,19,400,410,20,401,100000009,27,402,100000012,29,100000011,403,31,100000011,404,32,100000011,422,8,406,100000009,15,100000005,407,16,408,410,18,409,410,19,411,410,20,100000012,100000009,20,414,412,22,413,100000009,34,100000010,100000009,22,416,415,27,419,100000012,27,418,417,34,100000010,100000012,34,100000010,419,43,100000011,420,47,100000011,421,53,100000011,422,54,100000011,423,56,100000011,100000012,2,100000001,425,3,478,426,4,100000004,427,5,438,428,8,429,100000115,16,430,433,18,431,433,19,432,433,22,100000014,433,35,434,100000042,36,100000044,435,46,100000112,436,57,437,100000114,58,100000113,100000114,6,100000006,439,7,100000006,440,8,450,441,14,501,442,26,443,502,37,444,100000086,38,100000086,445,39,446,465,40,447,100000057,41,100000057,448,42,471,449,48,100000044,500,9,451,455,10,452,455,11,453,455,12,454,455,13,547,455,14,473,456,15,460,457,20,458,461,25,459,100000042,46,100000037,100000038,20,540,461,26,462,474,37,463,100000086,38,100000086,464,39,467,465,48,100000058,466,50,100000075,100000085,40,468,100000057,41,100000057,469,42,471,470,48,100000044,524,48,100000044,472,52,100000068,100000072,26,100000088,474,28,100000087,475,34,100000100,476,35,477,545,36,100000044,100000115,4,100000002,479,5,488,480,8,481,100000115,16,482,485,18,483,485,19,484,485,22,100000014,485,35,486,100000042,36,100000043,487,46,100000108,100000109,6,100000006,489,7,100000006,490,8,503,491,14,501,492,26,493,502,37,494,100000086,38,100000086,495,39,496,518,40,497,100000057,41,100000057,498,42,538,499,48,100000043,500,49,100000045,526,26,100000088,502,28,100000087,100000115,9,504,508,10,505,508,11,506,508,12,507,508,13,547,508,14,541,509,15,513,510,20,511,514,25,512,100000042,46,100000029,100000030,20,540,514,26,515,542,37,516,100000086,38,100000086,517,39,520,518,48,100000058,519,50,100000073,100000085,40,521,100000057,41,100000057,522,42,538,523,48,100000043,524,49,100000045,525,51,529,526,60,100000055,527,62,100000055,528,63,100000055,100000046,60,532,530,62,100000055,531,63,533,100000046,62,100000055,533,64,534,100000054,66,535,100000053,69,536,100000065,70,537,100000052,72,100000059,100000051,48,100000043,539,52,100000066,100000072,25,100000024,100000042,26,100000088,542,28,100000087,543,34,100000100,544,35,546,545,36,100000042,100000115,36,100000043,100000115,17,548,100000022,20,549,100000021,33,552,550,44,100000017,551,45,100000019,100000020,44,100000015,553,45,100000015,100000020]),U_e=on.from(H_e,V_e,H8.conditions,H8.results),W_e=new sn({size:50,params:[`Accelerate`,`Bucket`,`DisableAccessPoints`,`DisableMultiRegionAccessPoints`,`DisableS3ExpressSessionAuth`,`Endpoint`,`ForcePathStyle`,`Region`,`UseArnRegion`,`UseDualStack`,`UseFIPS`,`UseGlobalEndpoint`,`UseObjectLambdaEndpoint`,`UseS3ExpressControlEndpoint`]}),U8=(e,t={})=>W_e.get(e,()=>jn(U_e,{endpointParams:e,logger:t.logger}));dn.aws=ji;var G_e=(e=>async(t,n,r)=>{if(!r)throw Error("Could not find `input` for `defaultEndpointRuleSetHttpAuthSchemeParametersProvider`");let i=await e(t,n,r),a=Ze(n)?.commandInstance?.constructor?.getEndpointParameterInstructions;if(!a)throw Error(`getEndpointParameterInstructions() is not defined on '${n.commandName}'`);let o=await pt(r,{getEndpointParameterInstructions:a},t);return Object.assign(i,o)})(async(e,t,n)=>({operation:Ze(t).operation,region:await re(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()}));function W8(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`s3`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}function G8(e){return{schemeId:`aws.auth#sigv4a`,signingProperties:{name:`s3`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var K_e=((e,t,n)=>r=>{let i=e(r).properties?.authSchemes;if(!i)return t(r);let a=[];for(let e of i){let{name:t,properties:o={},...s}=e,c=t.toLowerCase();t!==c&&console.warn(`HttpAuthScheme has been normalized with lowercasing: '${t}' to '${c}'`);let l;if(c===`sigv4a`){l=`aws.auth#sigv4a`;let e=i.find(e=>{let t=e.name.toLowerCase();return t!==`sigv4a`&&t.startsWith(`sigv4`)});if(b6.sigv4aDependency()===`none`&&e)continue}else if(c.startsWith(`sigv4`))l=`aws.auth#sigv4`;else throw Error(`Unknown HttpAuthScheme found in '@smithy.rules#endpointRuleSet': '${c}'`);let u=n[l];if(!u)throw Error(`Could not find HttpAuthOption create function for '${l}'`);let d=u(r);d.schemeId=l,d.signingProperties={...d.signingProperties||{},...s,...o},a.push(d)}return a})(U8,e=>{let t=[];switch(e.operation){default:t.push(W8(e)),t.push(G8(e))}return t},{"aws.auth#sigv4":W8,"aws.auth#sigv4a":G8}),q_e=e=>{let t=Zi(Qi(e));return Object.assign(t,{authSchemePreference:re(e.authSchemePreference??[])})},J_e=e=>Object.assign(e,{useFipsEndpoint:e.useFipsEndpoint??!1,useDualstackEndpoint:e.useDualstackEndpoint??!1,forcePathStyle:e.forcePathStyle??!1,useAccelerateEndpoint:e.useAccelerateEndpoint??!1,useGlobalEndpoint:e.useGlobalEndpoint??!1,disableMultiregionAccessPoints:e.disableMultiregionAccessPoints??!1,defaultSigningName:`s3`,clientContextParams:e.clientContextParams??{}}),K8={ForcePathStyle:{type:`clientContextParams`,name:`forcePathStyle`},UseArnRegion:{type:`clientContextParams`,name:`useArnRegion`},DisableMultiRegionAccessPoints:{type:`clientContextParams`,name:`disableMultiregionAccessPoints`},Accelerate:{type:`clientContextParams`,name:`useAccelerateEndpoint`},DisableS3ExpressSessionAuth:{type:`clientContextParams`,name:`disableS3ExpressSessionAuth`},UseGlobalEndpoint:{type:`builtInParams`,name:`useGlobalEndpoint`},UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},q8=class e extends Ht{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},Y_e=class e extends q8{name=`NoSuchUpload`;$fault=`client`;constructor(t){super({name:`NoSuchUpload`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},X_e=class e extends q8{name=`AccessDenied`;$fault=`client`;constructor(t){super({name:`AccessDenied`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Z_e=class e extends q8{name=`ObjectNotInActiveTierError`;$fault=`client`;constructor(t){super({name:`ObjectNotInActiveTierError`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Q_e=class e extends q8{name=`BucketAlreadyExists`;$fault=`client`;constructor(t){super({name:`BucketAlreadyExists`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},$_e=class e extends q8{name=`BucketAlreadyOwnedByYou`;$fault=`client`;constructor(t){super({name:`BucketAlreadyOwnedByYou`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},eve=class e extends q8{name=`NoSuchBucket`;$fault=`client`;constructor(t){super({name:`NoSuchBucket`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},tve=class e extends q8{name=`InvalidObjectState`;$fault=`client`;StorageClass;AccessTier;constructor(t){super({name:`InvalidObjectState`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.StorageClass=t.StorageClass,this.AccessTier=t.AccessTier}},nve=class e extends q8{name=`NoSuchKey`;$fault=`client`;constructor(t){super({name:`NoSuchKey`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},rve=class e extends q8{name=`NotFound`;$fault=`client`;constructor(t){super({name:`NotFound`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},ive=class e extends q8{name=`EncryptionTypeMismatch`;$fault=`client`;constructor(t){super({name:`EncryptionTypeMismatch`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},ave=class e extends q8{name=`InvalidRequest`;$fault=`client`;constructor(t){super({name:`InvalidRequest`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},ove=class e extends q8{name=`InvalidWriteOffset`;$fault=`client`;constructor(t){super({name:`InvalidWriteOffset`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},sve=class e extends q8{name=`TooManyParts`;$fault=`client`;constructor(t){super({name:`TooManyParts`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},cve=class e extends q8{name=`IdempotencyParameterMismatch`;$fault=`client`;constructor(t){super({name:`IdempotencyParameterMismatch`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},lve=class e extends q8{name=`ObjectAlreadyInActiveTierError`;$fault=`client`;constructor(t){super({name:`ObjectAlreadyInActiveTierError`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},J8=`ACL`,uve=`AccessDenied`,dve=`AbortDate`,Y8=`AccessKeyId`,fve=`AbortMultipartUpload`,pve=`AbortMultipartUploadOutput`,mve=`AbortMultipartUploadRequest`,hve=`AcceptRanges`,gve=`AbortRuleId`,_ve=`AccessTier`,X8=`Bucket`,Z8=`BucketArn`,vve=`BucketAlreadyExists`,yve=`BucketAlreadyOwnedByYou`,bve=`BypassGovernanceRetention`,xve=`BucketInfo`,Q8=`BucketKeyEnabled`,Sve=`BucketNamespace`,$8=`BucketRegion`,e5=`Body`,t5=`Buckets`,n5=`ChecksumAlgorithm`,Cve=`CreateBucket`,r5=`CreateBucketConfiguration`,wve=`CreateBucketOutput`,Tve=`CreateBucketRequest`,i5=`CacheControl`,a5=`ChecksumCRC32`,o5=`ChecksumCRC32C`,s5=`ChecksumCRC64NVME`,c5=`Cache-Control`,Eve=`CreationDate`,l5=`Content-Disposition`,u5=`ContentDisposition`,d5=`Content-Encoding`,f5=`ContentEncoding`,p5=`ContentLanguage`,m5=`Content-Language`,h5=`Content-Length`,g5=`ContentLength`,_5=`Content-MD5`,v5=`ChecksumMD5`,y5=`ContentMD5`,Dve=`CompletedMultipartUpload`,Ove=`CompleteMultipartUploadOutput`,kve=`CreateMultipartUploadOutput`,Ave=`CompleteMultipartUploadResult`,jve=`CompleteMultipartUploadRequest`,Mve=`CreateMultipartUploadRequest`,b5=`CompleteMultipartUpload`,Nve=`CreateMultipartUpload`,Pve=`ChecksumMode`,Fve=`CommonPrefix`,Ive=`CommonPrefixList`,Lve=`CompletedPartList`,Rve=`CompletedPart`,zve=`CommonPrefixes`,Bve=`ContentRange`,Vve=`Content-Range`,x5=`ChecksumSHA1`,S5=`ChecksumSHA256`,C5=`ChecksumSHA512`,Hve=`CreateSessionOutput`,Uve=`CreateSessionResult`,Wve=`CreateSessionRequest`,Gve=`CreateSession`,w5=`ChecksumType`,T5=`Content-Type`,E5=`ContentType`,D5=`ContinuationToken`,O5=`ChecksumXXHASH64`,k5=`ChecksumXXHASH3`,A5=`ChecksumXXHASH128`,Kve=`Contents`,j5=`Credentials`,M5=`DeleteMarker`,qve=`DisplayName`,Jve=`DeleteObjectOutput`,Yve=`DeleteObjectRequest`,Xve=`DeleteObject`,Zve=`DataRedundancy`,N5=`Delimiter`,P5=`Expiration`,F5=`ExpectedBucketOwner`,I5=`ExpiresString`,Qve=`EncryptionTypeMismatch`,L5=`ETag`,R5=`EncodingType`,z5=`Expires`,$ve=`FetchOwner`,B5=`GrantFullControl`,eye=`GetObject`,tye=`GetObjectOutput`,nye=`GetObjectRequest`,V5=`GrantRead`,H5=`GrantReadACP`,rye=`GrantWrite`,U5=`GrantWriteACP`,iye=`ID`,W5=`IfMatch`,aye=`IfMatchInitiatedTime`,oye=`IfMatchLastModifiedTime`,sye=`IfMatchSize`,cye=`If-Modified-Since`,lye=`IfModifiedSince`,uye=`InitiateMultipartUploadResult`,G5=`If-Match`,K5=`IfNoneMatch`,q5=`If-None-Match`,dye=`InvalidObjectState`,fye=`IdempotencyParameterMismatch`,pye=`InvalidRequest`,mye=`IsRestoreInProgress`,hye=`IsTruncated`,gye=`IfUnmodifiedSince`,_ye=`If-Unmodified-Since`,vye=`InvalidWriteOffset`,J5=`Key`,yye=`KeyCount`,Y5=`Location`,bye=`ListAllMyBucketsResult`,xye=`ListBuckets`,Sye=`ListBucketsOutput`,Cye=`ListBucketsRequest`,wye=`ListBucketResult`,Tye=`LocationConstraint`,Eye=`LocationInfo`,X5=`LastModified`,Dye=`Last-Modified`,Oye=`ListObjectsV2`,kye=`ListObjectsV2Output`,Aye=`ListObjectsV2Request`,Z5=`Metadata`,jye=`MaxBuckets`,Mye=`MFA`,Q5=`MaxKeys`,Nye=`MissingMeta`,Pye=`MpuObjectSize`,Fye=`MultipartUpload`,$5=`Name`,Iye=`NextContinuationToken`,Lye=`NotFound`,Rye=`NoSuchBucket`,zye=`NoSuchKey`,Bye=`NoSuchUpload`,e7=`Owner`,Vye=`ObjectAlreadyInActiveTierError`,Hye=`ObjectLockEnabledForBucket`,t7=`ObjectLockLegalHoldStatus`,n7=`ObjectLockMode`,r7=`ObjectLockRetainUntilDate`,Uye=`ObjectList`,Wye=`ObjectNotInActiveTierError`,Gye=`ObjectOwnership`,Kye=`OptionalObjectAttributes`,qye=`Object`,i7=`Prefix`,Jye=`PartsCount`,a7=`PartNumber`,Yye=`PutObject`,Xye=`PutObjectOutput`,Zye=`PutObjectRequest`,Qye=`PutObjectTagging`,$ye=`PutObjectTaggingOutput`,ebe=`PutObjectTaggingRequest`,o7=`Parts`,tbe=`Part`,s7=`RequestCharged`,nbe=`ResponseCacheControl`,rbe=`ResponseContentDisposition`,ibe=`ResponseContentEncoding`,abe=`ResponseContentLanguage`,obe=`ResponseContentType`,sbe=`ResponseExpires`,cbe=`RestoreExpiryDate`,c7=`RequestPayer`,lbe=`ReplicationStatus`,l7=`RestoreStatus`,u7=`Range`,ube=`Restore`,d7=`StartAfter`,f7=`SecretAccessKey`,dbe=`StreamingBlob`,p7=`StorageClass`,fbe=`SessionCredentialValue`,pbe=`SessionCredentials`,mbe=`SessionMode`,m7=`ServerSideEncryption`,h7=`SSECustomerAlgorithm`,g7=`SSECustomerKey`,_7=`SSECustomerKeyMD5`,v7=`SSEKMSEncryptionContext`,y7=`SSEKMSKeyId`,b7=`SessionToken`,x7=`Size`,hbe=`Tags`,gbe=`TagCount`,_be=`TooManyParts`,S7=`TagSet`,C7=`Tag`,w7=`Tagging`,T7=`Type`,E7=`UploadId`,vbe=`UploadPart`,ybe=`UploadPartOutput`,bbe=`UploadPartRequest`,xbe=`Value`,D7=`VersionId`,Sbe=`WriteOffsetBytes`,O7=`WebsiteRedirectLocation`,Cbe=`accept-ranges`,wbe=`bucket-region`,k7=`client`,A7=`continuation-token`,Tbe=`delimiter`,j7=`error`,Ebe=`encoding-type`,Dbe=`fetch-owner`,M7=`http`,N7=`httpChecksum`,P7=`httpError`,Z=`httpHeader`,F7=`httpPayload`,I7=`httpPrefixHeaders`,Q=`httpQuery`,Obe=`max-buckets`,kbe=`max-keys`,L7=`prefix`,R7=`partNumber`,Abe=`response-cache-control`,jbe=`response-content-disposition`,Mbe=`response-content-encoding`,Nbe=`response-content-language`,Pbe=`response-content-type`,Fbe=`response-expires`,z7=`smithy.ts.sdk.synthetic.com.amazonaws.s3`,Ibe=`start-after`,Lbe=`streaming`,B7=`uploadId`,V7=`versionId`,H7=`xmlFlattened`,U7=`xmlName`,W7=`x-amz-acl`,Rbe=`x-amz-abort-date`,zbe=`x-amz-abort-rule-id`,Bbe=`x-amz-bucket-arn`,Vbe=`x-amz-bypass-governance-retention`,Hbe=`x-amz-bucket-namespace`,Ube=`x-amz-bucket-object-lock-enabled`,G7=`x-amz-checksum-algorithm`,K7=`x-amz-checksum-crc32`,q7=`x-amz-checksum-crc32c`,J7=`x-amz-checksum-crc64nvme`,Y7=`x-amz-checksum-md5`,Wbe=`x-amz-checksum-mode`,X7=`x-amz-checksum-sha1`,Z7=`x-amz-checksum-sha256`,Q7=`x-amz-checksum-sha512`,Gbe=`x-amz-create-session-mode`,$7=`x-amz-checksum-type`,e9=`x-amz-checksum-xxhash64`,t9=`x-amz-checksum-xxhash3`,n9=`x-amz-checksum-xxhash128`,r9=`x-amz-delete-marker`,i9=`x-amz-expiration`,a9=`x-amz-expected-bucket-owner`,o9=`x-amz-grant-full-control`,s9=`x-amz-grant-read`,c9=`x-amz-grant-read-acp`,Kbe=`x-amz-grant-write`,l9=`x-amz-grant-write-acp`,qbe=`x-amz-if-match-initiated-time`,Jbe=`x-amz-if-match-last-modified-time`,Ybe=`x-amz-if-match-size`,u9=`x-amz-meta-`,Xbe=`x-amz-mfa`,Zbe=`x-amz-missing-meta`,Qbe=`x-amz-mp-object-size`,$be=`x-amz-mp-parts-count`,d9=`x-amz-object-lock-legal-hold`,f9=`x-amz-object-lock-mode`,p9=`x-amz-object-lock-retain-until-date`,exe=`x-amz-object-ownership`,txe=`x-amz-optional-object-attributes`,nxe=`x-amz-object-size`,rxe=`x-amz-restore`,m9=`x-amz-request-charged`,h9=`x-amz-request-payer`,ixe=`x-amz-replication-status`,g9=`x-amz-storage-class`,_9=`x-amz-sdk-checksum-algorithm`,v9=`x-amz-server-side-encryption`,y9=`x-amz-server-side-encryption-aws-kms-key-id`,b9=`x-amz-server-side-encryption-bucket-key-enabled`,x9=`x-amz-server-side-encryption-context`,S9=`x-amz-server-side-encryption-customer-algorithm`,C9=`x-amz-server-side-encryption-customer-key`,w9=`x-amz-server-side-encryption-customer-key-MD5`,T9=`x-amz-tagging`,axe=`x-amz-tagging-count`,E9=`x-amz-version-id`,oxe=`x-amz-write-offset-bytes`,D9=`x-amz-website-redirect-location`,$=`com.amazonaws.s3`,O9=Lt.for(z7),sxe=[-3,z7,`S3ServiceException`,0,[],[]];O9.registerError(sxe,q8);var k9=Lt.for($),cxe=[-3,$,uve,{[j7]:k7,[P7]:403},[],[]];k9.registerError(cxe,X_e);var lxe=[-3,$,vve,{[j7]:k7,[P7]:409},[],[]];k9.registerError(lxe,Q_e);var uxe=[-3,$,yve,{[j7]:k7,[P7]:409},[],[]];k9.registerError(uxe,$_e);var dxe=[-3,$,Qve,{[j7]:k7,[P7]:400},[],[]];k9.registerError(dxe,ive);var fxe=[-3,$,fye,{[j7]:k7,[P7]:400},[],[]];k9.registerError(fxe,cve);var pxe=[-3,$,dye,{[j7]:k7,[P7]:403},[p7,_ve],[0,0]];k9.registerError(pxe,tve);var mxe=[-3,$,pye,{[j7]:k7,[P7]:400},[],[]];k9.registerError(mxe,ave);var hxe=[-3,$,vye,{[j7]:k7,[P7]:400},[],[]];k9.registerError(hxe,ove);var gxe=[-3,$,Rye,{[j7]:k7,[P7]:404},[],[]];k9.registerError(gxe,eve);var _xe=[-3,$,zye,{[j7]:k7,[P7]:404},[],[]];k9.registerError(_xe,nve);var vxe=[-3,$,Bye,{[j7]:k7,[P7]:404},[],[]];k9.registerError(vxe,Y_e);var yxe=[-3,$,Lye,{[j7]:k7},[],[]];k9.registerError(yxe,rve);var bxe=[-3,$,Vye,{[j7]:k7,[P7]:403},[],[]];k9.registerError(bxe,lve);var xxe=[-3,$,Wye,{[j7]:k7,[P7]:403},[],[]];k9.registerError(xxe,Z_e);var Sxe=[-3,$,_be,{[j7]:k7,[P7]:400},[],[]];k9.registerError(Sxe,sve);var Cxe=[O9,k9],A9=[0,$,fbe,8,0],j9=[0,$,g7,8,0],M9=[0,$,v7,8,0],N9=[0,$,y7,8,0],P9=[0,$,dbe,{[Lbe]:1},42],wxe=[3,$,pve,0,[s7],[[0,{[Z]:m9}]]],Txe=[3,$,mve,0,[X8,J5,E7,c7,F5,aye],[[0,1],[0,1],[0,{[Q]:B7}],[0,{[Z]:h9}],[0,{[Z]:a9}],[6,{[Z]:qbe}]],3],Exe=[3,$,X8,0,[$5,Eve,$8,Z8],[0,4,0,0]],Dxe=[3,$,xve,0,[Zve,T7],[0,0]],Oxe=[3,$,Fve,0,[i7],[0]],kxe=[3,$,Dve,0,[o7],[[()=>cSe,{[H7]:1,[U7]:tbe}]]],Axe=[3,$,Rve,0,[L5,a5,o5,s5,x5,S5,C5,v5,O5,k5,A5,a7],[0,0,0,0,0,0,0,0,0,0,0,1]],jxe=[3,$,Ove,{[U7]:Ave},[Y5,X8,J5,P5,L5,a5,o5,s5,x5,S5,C5,v5,O5,k5,A5,w5,m7,D7,y7,Q8,s7],[0,0,0,[0,{[Z]:i9}],0,0,0,0,0,0,0,0,0,0,0,0,[0,{[Z]:v9}],[0,{[Z]:E9}],[()=>N9,{[Z]:y9}],[2,{[Z]:b9}],[0,{[Z]:m9}]]],Mxe=[3,$,jve,0,[X8,J5,E7,Fye,a5,o5,s5,x5,S5,C5,v5,O5,k5,A5,w5,Pye,c7,F5,W5,K5,h7,g7,_7],[[0,1],[0,1],[0,{[Q]:B7}],[()=>kxe,{[F7]:1,[U7]:b5}],[0,{[Z]:K7}],[0,{[Z]:q7}],[0,{[Z]:J7}],[0,{[Z]:X7}],[0,{[Z]:Z7}],[0,{[Z]:Q7}],[0,{[Z]:Y7}],[0,{[Z]:e9}],[0,{[Z]:t9}],[0,{[Z]:n9}],[0,{[Z]:$7}],[1,{[Z]:Qbe}],[0,{[Z]:h9}],[0,{[Z]:a9}],[0,{[Z]:G5}],[0,{[Z]:q5}],[0,{[Z]:S9}],[()=>j9,{[Z]:C9}],[0,{[Z]:w9}]],3],Nxe=[3,$,r5,0,[Tye,Y5,X8,hbe],[0,()=>Jxe,()=>Dxe,[()=>I9,0]]],Pxe=[3,$,wve,0,[Y5,Z8],[[0,{[Z]:Y5}],[0,{[Z]:Bbe}]]],Fxe=[3,$,Tve,0,[X8,J8,r5,B5,V5,H5,rye,U5,Hye,Gye,Sve],[[0,1],[0,{[Z]:W7}],[()=>Nxe,{[F7]:1,[U7]:r5}],[0,{[Z]:o9}],[0,{[Z]:s9}],[0,{[Z]:c9}],[0,{[Z]:Kbe}],[0,{[Z]:l9}],[2,{[Z]:Ube}],[0,{[Z]:exe}],[0,{[Z]:Hbe}]],1],Ixe=[3,$,kve,{[U7]:uye},[dve,gve,X8,J5,E7,m7,h7,_7,y7,v7,Q8,s7,n5,w5],[[4,{[Z]:Rbe}],[0,{[Z]:zbe}],[0,{[U7]:X8}],0,0,[0,{[Z]:v9}],[0,{[Z]:S9}],[0,{[Z]:w9}],[()=>N9,{[Z]:y9}],[()=>M9,{[Z]:x9}],[2,{[Z]:b9}],[0,{[Z]:m9}],[0,{[Z]:G7}],[0,{[Z]:$7}]]],Lxe=[3,$,Mve,0,[X8,J5,J8,i5,u5,f5,p5,E5,z5,B5,V5,H5,U5,Z5,m7,p7,O7,h7,g7,_7,y7,v7,Q8,c7,w7,n7,r7,t7,F5,n5,w5],[[0,1],[0,1],[0,{[Z]:W7}],[0,{[Z]:c5}],[0,{[Z]:l5}],[0,{[Z]:d5}],[0,{[Z]:m5}],[0,{[Z]:T5}],[4,{[Z]:z5}],[0,{[Z]:o9}],[0,{[Z]:s9}],[0,{[Z]:c9}],[0,{[Z]:l9}],[128,{[I7]:u9}],[0,{[Z]:v9}],[0,{[Z]:g9}],[0,{[Z]:D9}],[0,{[Z]:S9}],[()=>j9,{[Z]:C9}],[0,{[Z]:w9}],[()=>N9,{[Z]:y9}],[()=>M9,{[Z]:x9}],[2,{[Z]:b9}],[0,{[Z]:h9}],[0,{[Z]:T9}],[0,{[Z]:f9}],[5,{[Z]:p9}],[0,{[Z]:d9}],[0,{[Z]:a9}],[0,{[Z]:G7}],[0,{[Z]:$7}]],2],Rxe=[3,$,Hve,{[U7]:Uve},[j5,m7,y7,v7,Q8],[[()=>tSe,{[U7]:j5}],[0,{[Z]:v9}],[()=>N9,{[Z]:y9}],[()=>M9,{[Z]:x9}],[2,{[Z]:b9}]],1],zxe=[3,$,Wve,0,[X8,mbe,m7,y7,v7,Q8],[[0,1],[0,{[Z]:Gbe}],[0,{[Z]:v9}],[()=>N9,{[Z]:y9}],[()=>M9,{[Z]:x9}],[2,{[Z]:b9}]],1],Bxe=[3,$,Jve,0,[M5,D7,s7],[[2,{[Z]:r9}],[0,{[Z]:E9}],[0,{[Z]:m9}]]],Vxe=[3,$,Yve,0,[X8,J5,Mye,D7,c7,bve,F5,W5,oye,sye],[[0,1],[0,1],[0,{[Z]:Xbe}],[0,{[Q]:V7}],[0,{[Z]:h9}],[2,{[Z]:Vbe}],[0,{[Z]:a9}],[0,{[Z]:G5}],[6,{[Z]:Jbe}],[1,{[Z]:Ybe}]],2],Hxe=[3,$,tye,0,[e5,M5,hve,P5,ube,X5,g5,L5,a5,o5,s5,x5,S5,C5,v5,O5,k5,A5,w5,Nye,D7,i5,u5,f5,p5,Bve,E5,z5,I5,O7,m7,Z5,h7,_7,y7,Q8,p7,s7,lbe,Jye,gbe,n7,r7,t7],[[()=>P9,16],[2,{[Z]:r9}],[0,{[Z]:Cbe}],[0,{[Z]:i9}],[0,{[Z]:rxe}],[4,{[Z]:Dye}],[1,{[Z]:h5}],[0,{[Z]:L5}],[0,{[Z]:K7}],[0,{[Z]:q7}],[0,{[Z]:J7}],[0,{[Z]:X7}],[0,{[Z]:Z7}],[0,{[Z]:Q7}],[0,{[Z]:Y7}],[0,{[Z]:e9}],[0,{[Z]:t9}],[0,{[Z]:n9}],[0,{[Z]:$7}],[1,{[Z]:Zbe}],[0,{[Z]:E9}],[0,{[Z]:c5}],[0,{[Z]:l5}],[0,{[Z]:d5}],[0,{[Z]:m5}],[0,{[Z]:Vve}],[0,{[Z]:T5}],[4,{[Z]:z5}],[0,{[Z]:I5}],[0,{[Z]:D9}],[0,{[Z]:v9}],[128,{[I7]:u9}],[0,{[Z]:S9}],[0,{[Z]:w9}],[()=>N9,{[Z]:y9}],[2,{[Z]:b9}],[0,{[Z]:g9}],[0,{[Z]:m9}],[0,{[Z]:ixe}],[1,{[Z]:$be}],[1,{[Z]:axe}],[0,{[Z]:f9}],[5,{[Z]:p9}],[0,{[Z]:d9}]]],Uxe=[3,$,nye,0,[X8,J5,W5,lye,K5,gye,u7,nbe,rbe,ibe,abe,obe,sbe,D7,h7,g7,_7,c7,a7,F5,Pve],[[0,1],[0,1],[0,{[Z]:G5}],[4,{[Z]:cye}],[0,{[Z]:q5}],[4,{[Z]:_ye}],[0,{[Z]:u7}],[0,{[Q]:Abe}],[0,{[Q]:jbe}],[0,{[Q]:Mbe}],[0,{[Q]:Nbe}],[0,{[Q]:Pbe}],[6,{[Q]:Fbe}],[0,{[Q]:V7}],[0,{[Z]:S9}],[()=>j9,{[Z]:C9}],[0,{[Z]:w9}],[0,{[Z]:h9}],[1,{[Q]:R7}],[0,{[Z]:a9}],[0,{[Z]:Wbe}]],2],Wxe=[3,$,Sye,{[U7]:bye},[t5,e7,D5,i7],[[()=>oSe,0],()=>F9,0,0]],Gxe=[3,$,Cye,0,[jye,D5,i7,$8],[[1,{[Q]:Obe}],[0,{[Q]:A7}],[0,{[Q]:L7}],[0,{[Q]:wbe}]]],Kxe=[3,$,kye,{[U7]:wye},[hye,Kve,$5,i7,N5,Q5,zve,R5,yye,D5,Iye,d7,s7],[2,[()=>lSe,{[H7]:1}],0,0,0,1,[()=>sSe,{[H7]:1}],0,1,0,0,0,[0,{[Z]:m9}]]],qxe=[3,$,Aye,0,[X8,N5,R5,Q5,i7,D5,$ve,d7,c7,F5,Kye],[[0,1],[0,{[Q]:Tbe}],[0,{[Q]:Ebe}],[1,{[Q]:kbe}],[0,{[Q]:L7}],[0,{[Q]:A7}],[2,{[Q]:Dbe}],[0,{[Q]:Ibe}],[0,{[Z]:h9}],[0,{[Z]:a9}],[64,{[Z]:txe}]],1],Jxe=[3,$,Eye,0,[T7,$5],[0,0]],Yxe=[3,$,qye,0,[J5,X5,L5,n5,w5,x7,p7,e7,l7],[0,4,0,[64,{[H7]:1}],0,1,0,()=>F9,()=>eSe]],F9=[3,$,e7,0,[qve,iye],[0,0]],Xxe=[3,$,Xye,0,[P5,L5,a5,o5,s5,x5,S5,C5,v5,O5,k5,A5,w5,m7,D7,h7,_7,y7,v7,Q8,x7,s7],[[0,{[Z]:i9}],[0,{[Z]:L5}],[0,{[Z]:K7}],[0,{[Z]:q7}],[0,{[Z]:J7}],[0,{[Z]:X7}],[0,{[Z]:Z7}],[0,{[Z]:Q7}],[0,{[Z]:Y7}],[0,{[Z]:e9}],[0,{[Z]:t9}],[0,{[Z]:n9}],[0,{[Z]:$7}],[0,{[Z]:v9}],[0,{[Z]:E9}],[0,{[Z]:S9}],[0,{[Z]:w9}],[()=>N9,{[Z]:y9}],[()=>M9,{[Z]:x9}],[2,{[Z]:b9}],[1,{[Z]:nxe}],[0,{[Z]:m9}]]],Zxe=[3,$,Zye,0,[X8,J5,J8,e5,i5,u5,f5,p5,g5,y5,E5,n5,a5,o5,s5,x5,S5,C5,v5,O5,k5,A5,z5,W5,K5,B5,V5,H5,U5,Sbe,Z5,m7,p7,O7,h7,g7,_7,y7,v7,Q8,c7,w7,n7,r7,t7,F5],[[0,1],[0,1],[0,{[Z]:W7}],[()=>P9,16],[0,{[Z]:c5}],[0,{[Z]:l5}],[0,{[Z]:d5}],[0,{[Z]:m5}],[1,{[Z]:h5}],[0,{[Z]:_5}],[0,{[Z]:T5}],[0,{[Z]:_9}],[0,{[Z]:K7}],[0,{[Z]:q7}],[0,{[Z]:J7}],[0,{[Z]:X7}],[0,{[Z]:Z7}],[0,{[Z]:Q7}],[0,{[Z]:Y7}],[0,{[Z]:e9}],[0,{[Z]:t9}],[0,{[Z]:n9}],[4,{[Z]:z5}],[0,{[Z]:G5}],[0,{[Z]:q5}],[0,{[Z]:o9}],[0,{[Z]:s9}],[0,{[Z]:c9}],[0,{[Z]:l9}],[1,{[Z]:oxe}],[128,{[I7]:u9}],[0,{[Z]:v9}],[0,{[Z]:g9}],[0,{[Z]:D9}],[0,{[Z]:S9}],[()=>j9,{[Z]:C9}],[0,{[Z]:w9}],[()=>N9,{[Z]:y9}],[()=>M9,{[Z]:x9}],[2,{[Z]:b9}],[0,{[Z]:h9}],[0,{[Z]:T9}],[0,{[Z]:f9}],[5,{[Z]:p9}],[0,{[Z]:d9}],[0,{[Z]:a9}]],2],Qxe=[3,$,$ye,0,[D7],[[0,{[Z]:E9}]]],$xe=[3,$,ebe,0,[X8,J5,w7,D7,y5,n5,F5,c7],[[0,1],[0,1],[()=>rSe,{[F7]:1,[U7]:w7}],[0,{[Q]:V7}],[0,{[Z]:_5}],[0,{[Z]:_9}],[0,{[Z]:a9}],[0,{[Z]:h9}]],3],eSe=[3,$,l7,0,[mye,cbe],[2,4]],tSe=[3,$,pbe,0,[Y8,f7,b7,P5],[[0,{[U7]:Y8}],[()=>A9,{[U7]:f7}],[()=>A9,{[U7]:b7}],[4,{[U7]:P5}]],4],nSe=[3,$,C7,0,[J5,xbe],[0,0],2],rSe=[3,$,w7,0,[S7],[[()=>I9,0]],1],iSe=[3,$,ybe,0,[m7,L5,a5,o5,s5,x5,S5,C5,v5,O5,k5,A5,h7,_7,y7,Q8,s7],[[0,{[Z]:v9}],[0,{[Z]:L5}],[0,{[Z]:K7}],[0,{[Z]:q7}],[0,{[Z]:J7}],[0,{[Z]:X7}],[0,{[Z]:Z7}],[0,{[Z]:Q7}],[0,{[Z]:Y7}],[0,{[Z]:e9}],[0,{[Z]:t9}],[0,{[Z]:n9}],[0,{[Z]:S9}],[0,{[Z]:w9}],[()=>N9,{[Z]:y9}],[2,{[Z]:b9}],[0,{[Z]:m9}]]],aSe=[3,$,bbe,0,[X8,J5,a7,E7,e5,g5,y5,n5,a5,o5,s5,x5,S5,C5,v5,O5,k5,A5,h7,g7,_7,c7,F5],[[0,1],[0,1],[1,{[Q]:R7}],[0,{[Q]:B7}],[()=>P9,16],[1,{[Z]:h5}],[0,{[Z]:_5}],[0,{[Z]:_9}],[0,{[Z]:K7}],[0,{[Z]:q7}],[0,{[Z]:J7}],[0,{[Z]:X7}],[0,{[Z]:Z7}],[0,{[Z]:Q7}],[0,{[Z]:Y7}],[0,{[Z]:e9}],[0,{[Z]:t9}],[0,{[Z]:n9}],[0,{[Z]:S9}],[()=>j9,{[Z]:C9}],[0,{[Z]:w9}],[0,{[Z]:h9}],[0,{[Z]:a9}]],4],oSe=[1,$,t5,0,[()=>Exe,{[U7]:X8}]],sSe=[1,$,Ive,0,()=>Oxe],cSe=[1,$,Lve,0,()=>Axe],lSe=[1,$,Uye,0,[()=>Yxe,0]],I9=[1,$,S7,0,[()=>nSe,{[U7]:C7}]],uSe=[9,$,fve,{[M7]:[`DELETE`,`/{Key+}?x-id=AbortMultipartUpload`,204]},()=>Txe,()=>wxe],dSe=[9,$,b5,{[M7]:[`POST`,`/{Key+}`,200]},()=>Mxe,()=>jxe],fSe=[9,$,Cve,{[M7]:[`PUT`,`/`,200]},()=>Fxe,()=>Pxe],pSe=[9,$,Nve,{[M7]:[`POST`,`/{Key+}?uploads`,200]},()=>Lxe,()=>Ixe],mSe=[9,$,Gve,{[M7]:[`GET`,`/?session`,200]},()=>zxe,()=>Rxe],hSe=[9,$,Xve,{[M7]:[`DELETE`,`/{Key+}?x-id=DeleteObject`,204]},()=>Vxe,()=>Bxe],gSe=[9,$,eye,{[N7]:`-`,[M7]:[`GET`,`/{Key+}?x-id=GetObject`,200]},()=>Uxe,()=>Hxe],_Se=[9,$,xye,{[M7]:[`GET`,`/?x-id=ListBuckets`,200]},()=>Gxe,()=>Wxe],vSe=[9,$,Oye,{[M7]:[`GET`,`/?list-type=2`,200]},()=>qxe,()=>Kxe],ySe=[9,$,Yye,{[N7]:`-`,[M7]:[`PUT`,`/{Key+}?x-id=PutObject`,200]},()=>Zxe,()=>Xxe],bSe=[9,$,Qye,{[N7]:`-`,[M7]:[`PUT`,`/{Key+}?tagging`,200]},()=>$xe,()=>Qxe],xSe=[9,$,vbe,{[N7]:`-`,[M7]:[`PUT`,`/{Key+}?x-id=UploadPart`,200]},()=>aSe,()=>iSe],SSe=class extends Bt.classBuilder().ep({...K8,DisableS3ExpressSessionAuth:{type:`staticContextParams`,value:!0},Bucket:{type:`contextParams`,name:`Bucket`}}).m(function(e,t,n,r){return[Pn(n,e.getEndpointParameterInstructions()),x6(n)]}).s(`AmazonS3`,`CreateSession`,{}).n(`S3Client`,`CreateSessionCommand`).sc(mSe).build(){},CSe={name:`@aws-sdk/client-s3`,description:`AWS SDK for JavaScript S3 Client for Node.js, Browser and React Native`,version:`3.1063.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-s3`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo s3`,test:`yarn g:vitest run`,"test:browser":`node ./test/browser-build/esbuild && yarn g:vitest run -c vitest.config.browser.mts`,"test:browser:watch":`node ./test/browser-build/esbuild && yarn g:vitest watch -c vitest.config.browser.mts`,"test:e2e":`yarn g:vitest run -c vitest.config.e2e.mts && yarn test:browser`,"test:e2e:watch":`yarn g:vitest watch -c vitest.config.e2e.mts`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`,"test:integration":`yarn g:vitest run -c vitest.config.integ.mts`,"test:integration:watch":`yarn g:vitest watch -c vitest.config.integ.mts`,"test:watch":`yarn g:vitest watch`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha1-browser":`5.2.0`,"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.18`,"@aws-sdk/credential-provider-node":`^3.972.52`,"@aws-sdk/middleware-flexible-checksums":`^3.974.27`,"@aws-sdk/middleware-sdk-s3":`^3.972.48`,"@aws-sdk/signature-v4-multi-region":`^3.996.32`,"@aws-sdk/types":`^3.973.11`,"@smithy/core":`^3.24.6`,"@smithy/fetch-http-handler":`^5.4.6`,"@smithy/node-http-handler":`^4.7.6`,"@smithy/types":`^4.14.3`,tslib:`^2.6.2`},devDependencies:{"@aws-sdk/signature-v4-crt":`3.1063.0`,"@smithy/snapshot-testing":`^2.1.7`,"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`,vitest:`^4.0.17`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/sdk-for-javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-s3`}};function L9(e){return typeof e==`string`?e.length===0:e.byteLength===0}var R9={name:`SHA-1`},z9={name:`HMAC`,hash:R9},wSe=new Uint8Array([218,57,163,238,94,107,75,13,50,85,191,239,149,96,24,144,175,216,7,9]),TSe=function(){function e(e){this.toHash=new Uint8Array,e!==void 0&&(this.key=new Promise(function(t,n){ie().crypto.subtle.importKey(`raw`,B9(e),z9,!1,[`sign`]).then(t,n)}),this.key.catch(function(){}))}return e.prototype.update=function(e){if(!L9(e)){var t=B9(e),n=new Uint8Array(this.toHash.byteLength+t.byteLength);n.set(this.toHash,0),n.set(t,this.toHash.byteLength),this.toHash=n}},e.prototype.digest=function(){var e=this;return this.key?this.key.then(function(t){return ie().crypto.subtle.sign(z9,t,e.toHash).then(function(e){return new Uint8Array(e)})}):L9(this.toHash)?Promise.resolve(wSe):Promise.resolve().then(function(){return ie().crypto.subtle.digest(R9,e.toHash)}).then(function(e){return Promise.resolve(new Uint8Array(e))})},e.prototype.reset=function(){this.toHash=new Uint8Array},e}();function B9(e){return typeof e==`string`?de(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}var ESe=function(){function e(e){if(ne(ie()))this.hash=new TSe(e);else throw Error(`SHA1 not supported`)}return e.prototype.update=function(e,t){this.hash.update(ue(e))},e.prototype.digest=function(){return this.hash.digest()},e.prototype.reset=function(){this.hash.reset()},e}();async function DSe(e,t,n=1024*1024){let r=e.size,i=0;for(;i{n.update(e)}),n.digest()},kSe=[1732584193,4023233417,2562383102,271733878],ASe=class{state;buffer;bufferLength;bytesHashed;finished;constructor(){this.reset()}update(e){if(jSe(e))return;if(this.finished)throw Error(`Attempted to update an already finished hash.`);let t=MSe(e),n=0,{byteLength:r}=t;for(this.bytesHashed+=r;r>0;)this.buffer.setUint8(this.bufferLength++,t[n++]),r--,this.bufferLength===64&&(this.hashBuffer(),this.bufferLength=0)}async digest(){if(!this.finished){let{buffer:e,bufferLength:t,bytesHashed:n}=this,r=n*8;if(e.setUint8(this.bufferLength++,128),t%64>=56){for(let t=this.bufferLength;t<64;t++)e.setUint8(t,0);this.hashBuffer(),this.bufferLength=0}for(let t=this.bufferLength;t<56;t++)e.setUint8(t,0);e.setUint32(56,r>>>0,!0),e.setUint32(60,Math.floor(r/4294967296),!0),this.hashBuffer(),this.finished=!0}let e=new DataView(new ArrayBuffer(16));for(let t=0;t<4;t++)e.setUint32(t*4,this.state[t],!0);return new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}hashBuffer(){let{buffer:e,state:t}=this,n=t[0],r=t[1],i=t[2],a=t[3];n=H9(n,r,i,a,e.getUint32(0,!0),7,3614090360),a=H9(a,n,r,i,e.getUint32(4,!0),12,3905402710),i=H9(i,a,n,r,e.getUint32(8,!0),17,606105819),r=H9(r,i,a,n,e.getUint32(12,!0),22,3250441966),n=H9(n,r,i,a,e.getUint32(16,!0),7,4118548399),a=H9(a,n,r,i,e.getUint32(20,!0),12,1200080426),i=H9(i,a,n,r,e.getUint32(24,!0),17,2821735955),r=H9(r,i,a,n,e.getUint32(28,!0),22,4249261313),n=H9(n,r,i,a,e.getUint32(32,!0),7,1770035416),a=H9(a,n,r,i,e.getUint32(36,!0),12,2336552879),i=H9(i,a,n,r,e.getUint32(40,!0),17,4294925233),r=H9(r,i,a,n,e.getUint32(44,!0),22,2304563134),n=H9(n,r,i,a,e.getUint32(48,!0),7,1804603682),a=H9(a,n,r,i,e.getUint32(52,!0),12,4254626195),i=H9(i,a,n,r,e.getUint32(56,!0),17,2792965006),r=H9(r,i,a,n,e.getUint32(60,!0),22,1236535329),n=U9(n,r,i,a,e.getUint32(4,!0),5,4129170786),a=U9(a,n,r,i,e.getUint32(24,!0),9,3225465664),i=U9(i,a,n,r,e.getUint32(44,!0),14,643717713),r=U9(r,i,a,n,e.getUint32(0,!0),20,3921069994),n=U9(n,r,i,a,e.getUint32(20,!0),5,3593408605),a=U9(a,n,r,i,e.getUint32(40,!0),9,38016083),i=U9(i,a,n,r,e.getUint32(60,!0),14,3634488961),r=U9(r,i,a,n,e.getUint32(16,!0),20,3889429448),n=U9(n,r,i,a,e.getUint32(36,!0),5,568446438),a=U9(a,n,r,i,e.getUint32(56,!0),9,3275163606),i=U9(i,a,n,r,e.getUint32(12,!0),14,4107603335),r=U9(r,i,a,n,e.getUint32(32,!0),20,1163531501),n=U9(n,r,i,a,e.getUint32(52,!0),5,2850285829),a=U9(a,n,r,i,e.getUint32(8,!0),9,4243563512),i=U9(i,a,n,r,e.getUint32(28,!0),14,1735328473),r=U9(r,i,a,n,e.getUint32(48,!0),20,2368359562),n=W9(n,r,i,a,e.getUint32(20,!0),4,4294588738),a=W9(a,n,r,i,e.getUint32(32,!0),11,2272392833),i=W9(i,a,n,r,e.getUint32(44,!0),16,1839030562),r=W9(r,i,a,n,e.getUint32(56,!0),23,4259657740),n=W9(n,r,i,a,e.getUint32(4,!0),4,2763975236),a=W9(a,n,r,i,e.getUint32(16,!0),11,1272893353),i=W9(i,a,n,r,e.getUint32(28,!0),16,4139469664),r=W9(r,i,a,n,e.getUint32(40,!0),23,3200236656),n=W9(n,r,i,a,e.getUint32(52,!0),4,681279174),a=W9(a,n,r,i,e.getUint32(0,!0),11,3936430074),i=W9(i,a,n,r,e.getUint32(12,!0),16,3572445317),r=W9(r,i,a,n,e.getUint32(24,!0),23,76029189),n=W9(n,r,i,a,e.getUint32(36,!0),4,3654602809),a=W9(a,n,r,i,e.getUint32(48,!0),11,3873151461),i=W9(i,a,n,r,e.getUint32(60,!0),16,530742520),r=W9(r,i,a,n,e.getUint32(8,!0),23,3299628645),n=G9(n,r,i,a,e.getUint32(0,!0),6,4096336452),a=G9(a,n,r,i,e.getUint32(28,!0),10,1126891415),i=G9(i,a,n,r,e.getUint32(56,!0),15,2878612391),r=G9(r,i,a,n,e.getUint32(20,!0),21,4237533241),n=G9(n,r,i,a,e.getUint32(48,!0),6,1700485571),a=G9(a,n,r,i,e.getUint32(12,!0),10,2399980690),i=G9(i,a,n,r,e.getUint32(40,!0),15,4293915773),r=G9(r,i,a,n,e.getUint32(4,!0),21,2240044497),n=G9(n,r,i,a,e.getUint32(32,!0),6,1873313359),a=G9(a,n,r,i,e.getUint32(60,!0),10,4264355552),i=G9(i,a,n,r,e.getUint32(24,!0),15,2734768916),r=G9(r,i,a,n,e.getUint32(52,!0),21,1309151649),n=G9(n,r,i,a,e.getUint32(16,!0),6,4149444226),a=G9(a,n,r,i,e.getUint32(44,!0),10,3174756917),i=G9(i,a,n,r,e.getUint32(8,!0),15,718787259),r=G9(r,i,a,n,e.getUint32(36,!0),21,3951481745),t[0]=n+t[0]&4294967295,t[1]=r+t[1]&4294967295,t[2]=i+t[2]&4294967295,t[3]=a+t[3]&4294967295}reset(){this.state=Uint32Array.from(kSe),this.buffer=new DataView(new ArrayBuffer(64)),this.bufferLength=0,this.bytesHashed=0,this.finished=!1}};function V9(e,t,n,r,i,a){return t=(t+e&4294967295)+(r+a&4294967295)&4294967295,(t<>>32-i)+n&4294967295}function H9(e,t,n,r,i,a,o){return V9(t&n|~t&r,e,t,i,a,o)}function U9(e,t,n,r,i,a,o){return V9(t&r|n&~r,e,t,i,a,o)}function W9(e,t,n,r,i,a,o){return V9(t^n^r,e,t,i,a,o)}function G9(e,t,n,r,i,a,o){return V9(n^(t|~r),e,t,i,a,o)}function jSe(e){return typeof e==`string`?e.length===0:e.byteLength===0}function MSe(e){return typeof e==`string`?ce(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}var NSe=e=>({apiVersion:`2006-03-01`,base64Decoder:e?.base64Decoder??fe,base64Encoder:e?.base64Encoder??le,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??U8,extensions:e?.extensions??[],getAwsChunkedEncodingStream:e?.getAwsChunkedEncodingStream??ge,httpAuthSchemeProvider:e?.httpAuthSchemeProvider??K_e,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new Yi},{schemeId:`aws.auth#sigv4a`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4a`),signer:new Xi}],logger:e?.logger??new $t,protocol:e?.protocol??I_e,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.s3`,errorTypeRegistries:Cxe,xmlNamespace:`http://s3.amazonaws.com/doc/2006-03-01/`,version:`2006-03-01`,serviceTarget:`AmazonS3`},sdkStreamMixin:e?.sdkStreamMixin??be,serviceId:e?.serviceId??`S3`,signerConstructor:e?.signerConstructor??b6,signingEscapePath:e?.signingEscapePath??!1,urlParser:e?.urlParser??nt,useArnRegion:e?.useArnRegion??void 0,utf8Decoder:e?.utf8Decoder??ce,utf8Encoder:e?.utf8Encoder??me}),PSe=e=>{let t=zi(e),n=()=>t().then(Wt),r=NSe(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??Ee,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??Ei({serviceId:r.serviceId,clientVersion:CSe.version}),eventStreamSerdeProvider:e?.eventStreamSerdeProvider??je,maxAttempts:e?.maxAttempts??3,md5:e?.md5??ASe,region:e?.region??yt(`Region is missing`),requestHandler:da.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||Nr),sha1:e?.sha1??ESe,sha256:e?.sha256??ee,streamCollector:e?.streamCollector??pa,streamHasher:e?.streamHasher??OSe,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},FSe=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},ISe=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),LSe=(e,t)=>{let n=Object.assign(Vi(e),Xt(e),Xn(e),FSe(e));return t.forEach(e=>e.configure(n)),Object.assign(e,Hi(n),Zt(n),Zn(n),ISe(n))},RSe=class extends bt{config;constructor(...[e]){let t=PSe(e||{});super(t),this.initConfig=t;let n=LSe(m_e(q_e(eee(Nn(zr(Li(Ir(o_e(li(J_e(t))))))))),{session:[()=>this,SSe]}),e?.extensions||[]);this.config=n,this.middlewareStack.use(Ot(this.config)),this.middlewareStack.use(Ti(this.config)),this.middlewareStack.use(Lr(this.config)),this.middlewareStack.use(tr(this.config)),this.middlewareStack.use(Hr(this.config)),this.middlewareStack.use(Gr(this.config)),this.middlewareStack.use(Kr(this.config)),this.middlewareStack.use(Zr(this.config,{httpAuthSchemeParametersProvider:G_e,identityProviderConfigProvider:async e=>new ii({"aws.auth#sigv4":e.credentials,"aws.auth#sigv4a":e.credentials})})),this.middlewareStack.use(ni(this.config)),this.middlewareStack.use(F_e(this.config)),this.middlewareStack.use(z_e(this.config)),this.middlewareStack.use(d_e(this.config)),this.middlewareStack.use(b_e(this.config)),this.middlewareStack.use(T_e(this.config))}destroy(){super.destroy()}},K9=`ap-southeast-2`,q9=`${window.location.protocol}//localhost:9999`,zSe=`${window.location.protocol}//localhost:8443`,J9=null,Y9=null,X9=null,Z9=null,Q9=null,$9=null;function BSe(){if(!J9){let e=oe();if(!e)throw Error(`AWS credentials not configured`);J9=new _z({endpoint:q9,region:K9,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}}),J9.middlewareStack.add(e=>async t=>(m.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/awsgw${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return J9}function VSe(){if(!Y9){let e=oe();if(!e)throw Error(`AWS credentials not configured`);Y9=new aX({endpoint:q9,region:K9,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}}),Y9.middlewareStack.add(e=>async t=>(m.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/awsgw${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return Y9}function HSe(){if(!X9){let e=oe();if(!e)throw Error(`AWS credentials not configured`);X9=new Zue({endpoint:q9,region:K9,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}}),X9.middlewareStack.add(e=>async t=>(m.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/awsgw${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return X9}function USe(){if(!Z9){let e=oe();if(!e)throw Error(`AWS credentials not configured`);Z9=new es({endpoint:q9,region:K9,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}}),Z9.middlewareStack.add(e=>async t=>(m.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/awsgw${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return Z9}function WSe(){if(!Q9){let e=oe();if(!e)throw Error(`AWS credentials not configured`);Q9=new e_e({endpoint:q9,region:K9,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}}),Q9.middlewareStack.add(e=>async t=>(m.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/awsgw${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return Q9}function GSe(){if(!$9){let e=oe();if(!e)throw Error(`AWS credentials not configured`);$9=new RSe({endpoint:zSe,region:K9,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey},forcePathStyle:!0}),$9.middlewareStack.add(e=>async t=>(m.isInstance(t.request)&&t.request.path.endsWith(`/`)&&t.request.path!==`/`&&(t.request.path=t.request.path.slice(0,-1)),await e(t)),{step:`build`,name:`removeTrailingSlash`}),$9.middlewareStack.add(e=>async t=>(m.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/s3${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return $9}function KSe(){J9=null,Y9=null,X9=null,Z9=null,Q9=null,$9=null}export{qge as $,ZR as $n,$Y as $t,wge as A,OR as An,AY as At,Pge as B,RR as Bn,BY as Bt,f6 as C,xR as Cn,Pn as Cr,Bue as Ct,xge as D,TR as Dn,Qe as Dr,Wue as Dt,bge as E,wR as En,rt as Er,Uue as Et,kge as F,NR as Fn,FY as Ft,zge as G,UR as Gn,GY as Gt,Ige as H,BR as Hn,HY as Ht,Age as I,PR as In,IY as It,Hge as J,KR as Jn,JY as Jt,Bge as K,WR as Kn,KY as Kt,jge as L,IR as Ln,LY as Lt,Ege as M,AR as Mn,MY as Mt,Dge as N,jR as Nn,NY as Nt,Sge as O,ER as On,qe as Or,Gue as Ot,Oge as P,MR as Pn,PY as Pt,Kge as Q,XR as Qn,QY as Qt,Mge as R,FR as Rn,RY as Rt,a_e as S,bR as Sn,Mn as Sr,zue as St,yge as T,CR as Tn,Bt as Tr,Hue as Tt,Lge as U,VR as Un,UY as Ut,Fge as V,zR as Vn,VY as Vt,Rge as W,HR as Wn,WY as Wt,Wge as X,JR as Xn,XY as Xt,Uge as Y,qR as Yn,YY as Yt,Gge as Z,YR as Zn,ZY as Zt,bSe as _,hR as _n,oa as _r,Pue as _t,HSe as a,rR as an,rz as ar,Sue as at,x6 as b,vR as bn,Vn as br,Lue as bt,uSe as c,oR as cn,oz as cr,Tue as ct,pSe as d,lR as dn,lz as dr,Oue as dt,wz as en,QR as er,Jge as et,hSe as f,uR as fn,uz as fr,kue as ft,ySe as g,mR as gn,Jo as gr,Nue as gt,vSe as h,pR as hn,qo as hr,Mue as ht,VSe as i,nR as in,nz as ir,xue as it,Tge as j,kR as jn,jY as jt,Cge as k,DR as kn,Pe as kr,dX as kt,dSe as l,sR as ln,sz as lr,Eue as lt,_Se as m,fR as mn,os as mr,jue as mt,USe as n,eR as nn,ez as nr,yue as nt,WSe as o,iR as on,iz as or,Cue as ot,gSe as p,dR as pn,dz as pr,Aue as pt,Vge as q,GR as qn,qY as qt,BSe as r,tR as rn,tz as rr,bue as rt,GSe as s,aR as sn,az as sr,wue as st,KSe as t,$L as tn,$R as tr,rde as tt,fSe as u,cR as un,cz as ur,Due as ut,xSe as v,gR as vn,Rr as vr,Fue as vt,n_e as w,SR as wn,$t as wr,Vue as wt,p6 as x,yR as xn,In as xr,Rue as xt,K8 as y,_R as yn,nr as yr,Iue as yt,Nge as z,LR as zn,zY as zt}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/back-link.js b/spinifex/services/spinifexui/frontend/dist/assets/back-link.js index 3c6671eae..829a71053 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/back-link.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/back-link.js @@ -1 +1 @@ -import{Fo as e,Ho as t,Lo as n,wo as r}from"./index.js";var i=r(`arrow-left`,[[`path`,{d:`m12 19-7-7 7-7`,key:`1l729n`}],[`path`,{d:`M19 12H5`,key:`x3x0zl`}]]),a=e(),o=t();function s(e){let t=(0,a.c)(7),r,s;t[0]===e?(r=t[1],s=t[2]):({children:r,...s}=e,t[0]=e,t[1]=r,t[2]=s);let c;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,o.jsx)(i,{className:`size-4`}),t[3]=c):c=t[3];let l;return t[4]!==r||t[5]!==s?(l=(0,o.jsxs)(n,{className:`mb-6 inline-flex items-center gap-2 text-sm`,...s,children:[c,r]}),t[4]=r,t[5]=s,t[6]=l):l=t[6],l}export{s as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{Or as n}from"./awsClient.js";import{Nr as r}from"./index.js";var i=r(`arrow-left`,[[`path`,{d:`m12 19-7-7 7-7`,key:`1l729n`}],[`path`,{d:`M19 12H5`,key:`x3x0zl`}]]),a=t(),o=e();function s(e){let t=(0,a.c)(7),r,s;t[0]===e?(r=t[1],s=t[2]):({children:r,...s}=e,t[0]=e,t[1]=r,t[2]=s);let c;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,o.jsx)(i,{className:`size-4`}),t[3]=c):c=t[3];let l;return t[4]!==r||t[5]!==s?(l=(0,o.jsxs)(n,{className:`mb-6 inline-flex items-center gap-2 text-sm`,...s,children:[c,r]}),t[4]=r,t[5]=s,t[6]=l):l=t[6],l}export{s as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/badge.js b/spinifex/services/spinifexui/frontend/dist/assets/badge.js index b38ffe7c7..b34159e56 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/badge.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/badge.js @@ -1 +1 @@ -import{$a as e,Eo as t,Fo as n,Po as r,To as i}from"./index.js";var a=n(),o=i(`group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-[0.625rem] font-medium whitespace-nowrap transition-all transition-colors focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-2.5!`,{variants:{variant:{default:`bg-primary text-primary-foreground [a]:hover:bg-primary/80`,secondary:`bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80`,destructive:`bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20`,outline:`border-border bg-input/20 text-foreground dark:bg-input/30 [a]:hover:bg-muted [a]:hover:text-muted-foreground`,ghost:`hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50`,link:`text-primary underline-offset-4 hover:underline`}},defaultVariants:{variant:`default`}});function s(n){let i=(0,a.c)(13),s,c,l,u,d;if(i[0]!==n){let{className:a,variant:f,render:p,...m}=n;s=p,d=f===void 0?`default`:f,u=t,c=`span`,l=r({className:e(o({className:a,variant:d}))},m),i[0]=n,i[1]=s,i[2]=c,i[3]=l,i[4]=u,i[5]=d}else s=i[1],c=i[2],l=i[3],u=i[4],d=i[5];let f;i[6]===d?f=i[7]:(f={slot:`badge`,variant:d},i[6]=d,i[7]=f);let p;return i[8]!==s||i[9]!==c||i[10]!==l||i[11]!==f?(p={defaultTagName:c,props:l,render:s,state:f},i[8]=s,i[9]=c,i[10]=l,i[11]=f,i[12]=p):p=i[12],u(p)}export{s as t}; \ No newline at end of file +import{t as e}from"./compiler-runtime.js";import{f as t,t as n}from"./dist.js";import{w as r}from"./SignatureV4.js";import{Pr as i}from"./index.js";var a=e(),o=n(`group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-[0.625rem] font-medium whitespace-nowrap transition-all transition-colors focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-2.5!`,{variants:{variant:{default:`bg-primary text-primary-foreground [a]:hover:bg-primary/80`,secondary:`bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80`,destructive:`bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20`,outline:`border-border bg-input/20 text-foreground dark:bg-input/30 [a]:hover:bg-muted [a]:hover:text-muted-foreground`,ghost:`hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50`,link:`text-primary underline-offset-4 hover:underline`}},defaultVariants:{variant:`default`}});function s(e){let n=(0,a.c)(13),s,c,l,u,d;if(n[0]!==e){let{className:a,variant:f,render:p,...m}=e;s=p,d=f===void 0?`default`:f,u=i,c=`span`,l=t({className:r(o({className:a,variant:d}))},m),n[0]=e,n[1]=s,n[2]=c,n[3]=l,n[4]=u,n[5]=d}else s=n[1],c=n[2],l=n[3],u=n[4],d=n[5];let f;n[6]===d?f=n[7]:(f={slot:`badge`,variant:d},n[6]=d,n[7]=f);let p;return n[8]!==s||n[9]!==c||n[10]!==l||n[11]!==f?(p={defaultTagName:c,props:l,render:s,state:f},n[8]=s,n[9]=c,n[10]=l,n[11]=f,n[12]=p):p=n[12],u(p)}export{s as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/button.js b/spinifex/services/spinifexui/frontend/dist/assets/button.js new file mode 100644 index 000000000..d3baa494c --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/button.js @@ -0,0 +1 @@ +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{d as i,f as a,l as o,n as s,t as c,u as l}from"./dist.js";import{w as u}from"./SignatureV4.js";var d=t(n());function f(){return typeof window<`u`}function p(e){return g(e)?(e.nodeName||``).toLowerCase():`#document`}function m(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function h(e){return((g(e)?e.ownerDocument:e.document)||window.document)?.documentElement}function g(e){return f()?e instanceof Node||e instanceof m(e).Node:!1}function _(e){return f()?e instanceof Element||e instanceof m(e).Element:!1}function v(e){return f()?e instanceof HTMLElement||e instanceof m(e).HTMLElement:!1}function y(e){return!f()||typeof ShadowRoot>`u`?!1:e instanceof ShadowRoot||e instanceof m(e).ShadowRoot}function b(e){let{overflow:t,overflowX:n,overflowY:r,display:i}=j(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&i!==`inline`&&i!==`contents`}function x(e){return/^(table|td|th)$/.test(p(e))}function S(e){try{if(e.matches(`:popover-open`))return!0}catch{}try{return e.matches(`:modal`)}catch{return!1}}var C=/transform|translate|scale|rotate|perspective|filter/,w=/paint|layout|strict|content/,T=e=>!!e&&e!==`none`,E;function D(e){let t=_(e)?j(e):e;return T(t.transform)||T(t.translate)||T(t.scale)||T(t.rotate)||T(t.perspective)||!k()&&(T(t.backdropFilter)||T(t.filter))||C.test(t.willChange||``)||w.test(t.contain||``)}function O(e){let t=N(e);for(;v(t)&&!A(t);){if(D(t))return t;if(S(t))return null;t=N(t)}return null}function k(){return E??=typeof CSS<`u`&&CSS.supports&&CSS.supports(`-webkit-backdrop-filter`,`none`),E}function A(e){return/^(html|body|#document)$/.test(p(e))}function j(e){return m(e).getComputedStyle(e)}function M(e){return _(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function N(e){if(p(e)===`html`)return e;let t=e.assignedSlot||e.parentNode||y(e)&&e.host||h(e);return y(t)?t.host:t}function P(e){let t=N(e);return A(t)?e.ownerDocument?e.ownerDocument.body:e.body:v(t)&&b(t)?t:P(t)}function F(e,t,n){t===void 0&&(t=[]),n===void 0&&(n=!0);let r=P(e),i=r===e.ownerDocument?.body,a=m(r);if(i){let e=I(a);return t.concat(a,a.visualViewport||[],b(r)?r:[],e&&n?F(e):[])}else return t.concat(r,F(r,[],n))}function I(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}var L={...d},R=L.useInsertionEffect,z=R&&R!==L.useLayoutEffect?R:e=>e();function B(e){let t=o(V).current;return t.next=e,z(t.effect),t.trampoline}function V(){let e={next:void 0,callback:H,trampoline:(...t)=>e.callback?.(...t),effect:()=>{e.callback=e.next}};return e}function H(){}var U=typeof document<`u`?d.useLayoutEffect:()=>{},W=d.createContext(void 0);function G(e=!1){let t=d.useContext(W);if(t===void 0&&!e)throw Error(l(16));return t}function K(e){let{focusableWhenDisabled:t,disabled:n,composite:r=!1,tabIndex:i=0,isNativeButton:a}=e,o=r&&t!==!1,s=r&&t===!1;return{props:d.useMemo(()=>{let e={onKeyDown(e){n&&t&&e.key!==`Tab`&&e.preventDefault()}};return r||(e.tabIndex=i,!a&&n&&(e.tabIndex=t?i:-1)),(a&&(t||o)||!a&&n)&&(e[`aria-disabled`]=n),a&&(!t||s)&&(e.disabled=n),e},[r,n,t,o,s,a,i])}}function q(e={}){let{disabled:t=!1,focusableWhenDisabled:n,tabIndex:r=0,native:o=!0,composite:s}=e,c=d.useRef(null),l=G(!0),u=s??l!==void 0,{props:f}=K({focusableWhenDisabled:n,disabled:t,composite:u,tabIndex:r,isNativeButton:o}),p=d.useCallback(()=>{let e=c.current;J(e)&&u&&t&&f.disabled===void 0&&e.disabled&&(e.disabled=!1)},[t,f.disabled,u]);return U(p,[p]),{getButtonProps:d.useCallback((e={})=>{let{onClick:n,onMouseDown:r,onKeyUp:s,onKeyDown:c,onPointerDown:l,...d}=e;return a({onClick(e){if(t){e.preventDefault();return}n?.(e)},onMouseDown(e){t||r?.(e)},onKeyDown(e){if(t||(i(e),c?.(e),e.baseUIHandlerPrevented))return;let r=e.target===e.currentTarget,a=e.currentTarget,s=J(a),l=!o&&Y(a),d=r&&(o?s:!l),f=e.key===`Enter`,p=e.key===` `,m=a.getAttribute(`role`),h=m?.startsWith(`menuitem`)||m===`option`||m===`gridcell`;if(r&&u&&p){if(e.defaultPrevented&&h)return;e.preventDefault(),l||o&&s?(a.click(),e.preventBaseUIHandler()):d&&(n?.(e),e.preventBaseUIHandler());return}d&&(!o&&(p||f)&&e.preventDefault(),!o&&f&&n?.(e))},onKeyUp(e){if(!t){if(i(e),s?.(e),e.target===e.currentTarget&&o&&u&&J(e.currentTarget)&&e.key===` `){e.preventDefault();return}e.baseUIHandlerPrevented||e.target===e.currentTarget&&!o&&!u&&e.key===` `&&n?.(e)}},onPointerDown(e){if(t){e.preventDefault();return}l?.(e)}},o?{type:`button`}:{role:`button`},f,d)},[t,f,u,o]),buttonRef:B(e=>{c.current=e,p()})}}function J(e){return v(e)&&e.tagName===`BUTTON`}function Y(e){return!!(e?.tagName===`A`&&e?.href)}var X=d.forwardRef(function(e,t){let{render:n,className:r,disabled:i=!1,focusableWhenDisabled:a=!1,nativeButton:o=!0,style:c,...l}=e,{getButtonProps:u,buttonRef:d}=q({disabled:i,focusableWhenDisabled:a,native:o});return s(`button`,e,{state:{disabled:i},ref:[t,d],props:[l,u]})}),Z=r(),Q=e(),$=c(`group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-xs/relaxed font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-[2px] focus-visible:ring-ring/30 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[2px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,{variants:{variant:{default:`bg-primary text-primary-foreground hover:bg-primary/80`,outline:`border-border hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-input/30`,secondary:`bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground`,ghost:`hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50`,destructive:`bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40`,link:`text-primary underline-offset-4 hover:underline`},size:{default:`h-7 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5`,xs:`h-5 gap-1 rounded-sm px-2 text-[0.625rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-2.5`,sm:`h-6 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3`,lg:`h-8 gap-1 px-2.5 text-xs/relaxed has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4`,icon:`size-7 [&_svg:not([class*='size-'])]:size-3.5`,"icon-xs":`size-5 rounded-sm [&_svg:not([class*='size-'])]:size-2.5`,"icon-sm":`size-6 [&_svg:not([class*='size-'])]:size-3`,"icon-lg":`size-8 [&_svg:not([class*='size-'])]:size-4`}},defaultVariants:{variant:`default`,size:`default`}});function ee(e){let t=(0,Z.c)(12),n,r,i,a;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4]):({className:n,variant:i,size:a,...r}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a);let o=i===void 0?`default`:i,s=a===void 0?`default`:a,c;t[5]!==n||t[6]!==s||t[7]!==o?(c=u($({variant:o,size:s,className:n})),t[5]=n,t[6]=s,t[7]=o,t[8]=c):c=t[8];let l;return t[9]!==r||t[10]!==c?(l=(0,Q.jsx)(X,{className:c,"data-slot":`button`,...r}),t[9]=r,t[10]=c,t[11]=l):l=t[11],l}export{b as C,k as D,S as E,g as S,x as T,m as _,G as a,v as b,L as c,h as d,I as f,N as g,F as h,W as i,j as l,M as m,$ as n,U as o,p,q as r,B as s,ee as t,O as u,D as v,y as w,A as x,_ as y}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/card.js b/spinifex/services/spinifexui/frontend/dist/assets/card.js index aaa70f591..550f7630d 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/card.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/card.js @@ -1 +1 @@ -import{$a as e,Fo as t,Ho as n}from"./index.js";var r=t(),i=n();function a(t){let n=(0,r.c)(10),a,o,s;n[0]===t?(a=n[1],o=n[2],s=n[3]):({className:a,size:s,...o}=t,n[0]=t,n[1]=a,n[2]=o,n[3]=s);let c=s===void 0?`default`:s,l;n[4]===a?l=n[5]:(l=e(`group/card flex flex-col gap-4 overflow-hidden rounded-lg bg-card py-4 text-xs/relaxed text-card-foreground ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg`,a),n[4]=a,n[5]=l);let u;return n[6]!==o||n[7]!==c||n[8]!==l?(u=(0,i.jsx)(`div`,{className:l,"data-size":c,"data-slot":`card`,...o}),n[6]=o,n[7]=c,n[8]=l,n[9]=u):u=n[9],u}function o(t){let n=(0,r.c)(8),a,o;n[0]===t?(a=n[1],o=n[2]):({className:a,...o}=t,n[0]=t,n[1]=a,n[2]=o);let s;n[3]===a?s=n[4]:(s=e(`group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-lg px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3`,a),n[3]=a,n[4]=s);let c;return n[5]!==o||n[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-header`,...o}),n[5]=o,n[6]=s,n[7]=c):c=n[7],c}function s(t){let n=(0,r.c)(8),a,o;n[0]===t?(a=n[1],o=n[2]):({className:a,...o}=t,n[0]=t,n[1]=a,n[2]=o);let s;n[3]===a?s=n[4]:(s=e(`text-sm font-medium`,a),n[3]=a,n[4]=s);let c;return n[5]!==o||n[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-title`,...o}),n[5]=o,n[6]=s,n[7]=c):c=n[7],c}function c(t){let n=(0,r.c)(8),a,o;n[0]===t?(a=n[1],o=n[2]):({className:a,...o}=t,n[0]=t,n[1]=a,n[2]=o);let s;n[3]===a?s=n[4]:(s=e(`col-start-2 row-span-2 row-start-1 self-start justify-self-end`,a),n[3]=a,n[4]=s);let c;return n[5]!==o||n[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-action`,...o}),n[5]=o,n[6]=s,n[7]=c):c=n[7],c}function l(t){let n=(0,r.c)(8),a,o;n[0]===t?(a=n[1],o=n[2]):({className:a,...o}=t,n[0]=t,n[1]=a,n[2]=o);let s;n[3]===a?s=n[4]:(s=e(`px-4 group-data-[size=sm]/card:px-3`,a),n[3]=a,n[4]=s);let c;return n[5]!==o||n[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-content`,...o}),n[5]=o,n[6]=s,n[7]=c):c=n[7],c}function u(t){let n=(0,r.c)(8),a,o;n[0]===t?(a=n[1],o=n[2]):({className:a,...o}=t,n[0]=t,n[1]=a,n[2]=o);let s;n[3]===a?s=n[4]:(s=e(`flex items-center rounded-b-lg px-4 group-data-[size=sm]/card:px-3 [.border-t]:pt-4 group-data-[size=sm]/card:[.border-t]:pt-3`,a),n[3]=a,n[4]=s);let c;return n[5]!==o||n[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-footer`,...o}),n[5]=o,n[6]=s,n[7]=c):c=n[7],c}export{o as a,u as i,c as n,s as o,l as r,a as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{w as n}from"./SignatureV4.js";var r=t(),i=e();function a(e){let t=(0,r.c)(10),a,o,s;t[0]===e?(a=t[1],o=t[2],s=t[3]):({className:a,size:s,...o}=e,t[0]=e,t[1]=a,t[2]=o,t[3]=s);let c=s===void 0?`default`:s,l;t[4]===a?l=t[5]:(l=n(`group/card flex flex-col gap-4 overflow-hidden rounded-lg bg-card py-4 text-xs/relaxed text-card-foreground ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg`,a),t[4]=a,t[5]=l);let u;return t[6]!==o||t[7]!==c||t[8]!==l?(u=(0,i.jsx)(`div`,{className:l,"data-size":c,"data-slot":`card`,...o}),t[6]=o,t[7]=c,t[8]=l,t[9]=u):u=t[9],u}function o(e){let t=(0,r.c)(8),a,o;t[0]===e?(a=t[1],o=t[2]):({className:a,...o}=e,t[0]=e,t[1]=a,t[2]=o);let s;t[3]===a?s=t[4]:(s=n(`group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-lg px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3`,a),t[3]=a,t[4]=s);let c;return t[5]!==o||t[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-header`,...o}),t[5]=o,t[6]=s,t[7]=c):c=t[7],c}function s(e){let t=(0,r.c)(8),a,o;t[0]===e?(a=t[1],o=t[2]):({className:a,...o}=e,t[0]=e,t[1]=a,t[2]=o);let s;t[3]===a?s=t[4]:(s=n(`text-sm font-medium`,a),t[3]=a,t[4]=s);let c;return t[5]!==o||t[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-title`,...o}),t[5]=o,t[6]=s,t[7]=c):c=t[7],c}function c(e){let t=(0,r.c)(8),a,o;t[0]===e?(a=t[1],o=t[2]):({className:a,...o}=e,t[0]=e,t[1]=a,t[2]=o);let s;t[3]===a?s=t[4]:(s=n(`col-start-2 row-span-2 row-start-1 self-start justify-self-end`,a),t[3]=a,t[4]=s);let c;return t[5]!==o||t[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-action`,...o}),t[5]=o,t[6]=s,t[7]=c):c=t[7],c}function l(e){let t=(0,r.c)(8),a,o;t[0]===e?(a=t[1],o=t[2]):({className:a,...o}=e,t[0]=e,t[1]=a,t[2]=o);let s;t[3]===a?s=t[4]:(s=n(`px-4 group-data-[size=sm]/card:px-3`,a),t[3]=a,t[4]=s);let c;return t[5]!==o||t[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-content`,...o}),t[5]=o,t[6]=s,t[7]=c):c=t[7],c}function u(e){let t=(0,r.c)(8),a,o;t[0]===e?(a=t[1],o=t[2]):({className:a,...o}=e,t[0]=e,t[1]=a,t[2]=o);let s;t[3]===a?s=t[4]:(s=n(`flex items-center rounded-b-lg px-4 group-data-[size=sm]/card:px-3 [.border-t]:pt-4 group-data-[size=sm]/card:[.border-t]:pt-3`,a),t[3]=a,t[4]=s);let c;return t[5]!==o||t[6]!==s?(c=(0,i.jsx)(`div`,{className:s,"data-slot":`card-footer`,...o}),t[5]=o,t[6]=s,t[7]=c):c=t[7],c}export{o as a,u as i,c as n,s as o,l as r,a as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/certificate-import-dialog.js b/spinifex/services/spinifexui/frontend/dist/assets/certificate-import-dialog.js new file mode 100644 index 000000000..b0a0fec2d --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/certificate-import-dialog.js @@ -0,0 +1 @@ +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{n as i,t as a}from"./error-banner.js";import{Cr as o,Tr as s,_r as c,hr as l,n as u}from"./awsClient.js";import{Lr as d}from"./index.js";import{a as f,t as p}from"./field.js";import{a as m,i as h,l as g,n as _,o as v,r as y,s as ee,t as te}from"./alert-dialog.js";import{t as b}from"./textarea.js";var x=class extends s.classBuilder().ep(c).m(function(e,t,n,r){return[o(n,e.getEndpointParameterInstructions())]}).s(`CertificateManager`,`ImportCertificate`,{}).n(`ACMClient`,`ImportCertificateCommand`).sc(l).build(){},S=t(n(),1),C=r(),w=new TextEncoder;function T(){let e=(0,C.c)(2),t=d(),n;return e[0]===t?n=e[1]:(n={mutationFn:E,onSuccess:()=>{t.invalidateQueries({queryKey:[`acm`,`certificates`]})}},e[0]=t,e[1]=n),i(n)}async function E(e){let t=new x({Certificate:w.encode(e.certificate),PrivateKey:w.encode(e.privateKey),CertificateChain:e.certificateChain&&e.certificateChain.length>0?w.encode(e.certificateChain):void 0});return await u().send(t)}var D=e();function O(e){let t=(0,C.c)(54),{open:n,onOpenChange:r,onImported:i}=e,[o,s]=(0,S.useState)(``),[c,l]=(0,S.useState)(``),[u,d]=(0,S.useState)(``),x=T(),w;t[0]===x?w=t[1]:(w=()=>{s(``),l(``),d(``),x.reset()},t[0]=x,t[1]=w);let E=w,O;t[2]!==r||t[3]!==E?(O=e=>{e||E(),r(e)},t[2]=r,t[3]=E,t[4]=O):O=t[4];let k=O,A;t[5]!==o||t[6]!==c?(A=o.trim().length>0&&c.trim().length>0,t[5]=o,t[6]=c,t[7]=A):A=t[7];let j=A,M;t[8]!==j||t[9]!==o||t[10]!==u||t[11]!==x||t[12]!==i||t[13]!==r||t[14]!==c||t[15]!==E?(M=async()=>{if(j)try{let e=await x.mutateAsync({certificate:o,privateKey:c,certificateChain:u});e.CertificateArn&&i(e.CertificateArn),E(),r(!1)}catch{}},t[8]=j,t[9]=o,t[10]=u,t[11]=x,t[12]=i,t[13]=r,t[14]=c,t[15]=E,t[16]=M):M=t[16];let N=M,P;t[17]===Symbol.for(`react.memo_cache_sentinel`)?(P={maxWidth:`36rem`,width:`calc(100vw - 2rem)`},t[17]=P):P=t[17];let F;t[18]===Symbol.for(`react.memo_cache_sentinel`)?(F=(0,D.jsxs)(ee,{children:[(0,D.jsx)(g,{children:`Import certificate`}),(0,D.jsx)(m,{children:`Paste a PEM-encoded certificate and its private key. The optional chain is appended for intermediate CAs.`})]}),t[18]=F):F=t[18];let I;t[19]===x.error?I=t[20]:(I=x.error&&(0,D.jsx)(a,{error:x.error,msg:`Failed to import certificate`}),t[19]=x.error,t[20]=I);let L;t[21]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,D.jsx)(f,{children:(0,D.jsx)(`label`,{htmlFor:`cert-body`,children:`Certificate body`})}),t[21]=L):L=t[21];let R;t[22]===Symbol.for(`react.memo_cache_sentinel`)?(R=e=>s(e.target.value),t[22]=R):R=t[22];let z;t[23]===o?z=t[24]:(z=(0,D.jsxs)(p,{children:[L,(0,D.jsx)(b,{className:`font-mono text-xs`,id:`cert-body`,onChange:R,placeholder:`-----BEGIN CERTIFICATE-----`,rows:5,value:o})]}),t[23]=o,t[24]=z);let B;t[25]===Symbol.for(`react.memo_cache_sentinel`)?(B=(0,D.jsx)(f,{children:(0,D.jsx)(`label`,{htmlFor:`cert-key`,children:`Private key`})}),t[25]=B):B=t[25];let V;t[26]===Symbol.for(`react.memo_cache_sentinel`)?(V=e=>l(e.target.value),t[26]=V):V=t[26];let H;t[27]===c?H=t[28]:(H=(0,D.jsxs)(p,{children:[B,(0,D.jsx)(b,{className:`font-mono text-xs`,id:`cert-key`,onChange:V,placeholder:`-----BEGIN PRIVATE KEY-----`,rows:5,value:c})]}),t[27]=c,t[28]=H);let U;t[29]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,D.jsx)(f,{children:(0,D.jsx)(`label`,{htmlFor:`cert-chain`,children:`Certificate chain (optional)`})}),t[29]=U):U=t[29];let W;t[30]===Symbol.for(`react.memo_cache_sentinel`)?(W=e=>d(e.target.value),t[30]=W):W=t[30];let G;t[31]===u?G=t[32]:(G=(0,D.jsxs)(p,{children:[U,(0,D.jsx)(b,{className:`font-mono text-xs`,id:`cert-chain`,onChange:W,placeholder:`-----BEGIN CERTIFICATE-----`,rows:4,value:u})]}),t[31]=u,t[32]=G);let K;t[33]!==z||t[34]!==H||t[35]!==G?(K=(0,D.jsxs)(`div`,{className:`min-w-0 space-y-4`,children:[z,H,G]}),t[33]=z,t[34]=H,t[35]=G,t[36]=K):K=t[36];let q;t[37]===x.isPending?q=t[38]:(q=(0,D.jsx)(y,{disabled:x.isPending,children:`Cancel`}),t[37]=x.isPending,t[38]=q);let J=!j||x.isPending,Y=x.isPending?`Importing…`:`Import`,X;t[39]!==N||t[40]!==J||t[41]!==Y?(X=(0,D.jsx)(_,{disabled:J,onClick:N,children:Y}),t[39]=N,t[40]=J,t[41]=Y,t[42]=X):X=t[42];let Z;t[43]!==q||t[44]!==X?(Z=(0,D.jsxs)(v,{children:[q,X]}),t[43]=q,t[44]=X,t[45]=Z):Z=t[45];let Q;t[46]!==K||t[47]!==Z||t[48]!==I?(Q=(0,D.jsxs)(h,{className:`grid-cols-[minmax(0,1fr)]`,style:P,children:[F,I,K,Z]}),t[46]=K,t[47]=Z,t[48]=I,t[49]=Q):Q=t[49];let $;return t[50]!==k||t[51]!==n||t[52]!==Q?($=(0,D.jsx)(te,{onOpenChange:k,open:n,children:Q}),t[50]=k,t[51]=n,t[52]=Q,t[53]=$):$=t[53],$}export{O as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/check.js b/spinifex/services/spinifexui/frontend/dist/assets/check.js index 9411d3eb2..7ff6da16f 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/check.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/check.js @@ -1 +1 @@ -import{wo as e}from"./index.js";var t=e(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]);export{t}; \ No newline at end of file +import{Nr as e}from"./index.js";var t=e(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/chevron-down.js b/spinifex/services/spinifexui/frontend/dist/assets/chevron-down.js index 3d9142764..76200db22 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/chevron-down.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/chevron-down.js @@ -1 +1 @@ -import{wo as e}from"./index.js";var t=e(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]);export{t}; \ No newline at end of file +import{Nr as e}from"./index.js";var t=e(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/chevron-right.js b/spinifex/services/spinifexui/frontend/dist/assets/chevron-right.js index 9977653e5..c14ab0b4f 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/chevron-right.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/chevron-right.js @@ -1 +1 @@ -import{wo as e}from"./index.js";var t=e(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]);export{t}; \ No newline at end of file +import{Nr as e}from"./index.js";var t=e(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/cli-command-panel.js b/spinifex/services/spinifexui/frontend/dist/assets/cli-command-panel.js index cf848852d..1027541b4 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/cli-command-panel.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/cli-command-panel.js @@ -1,3 +1,3 @@ -import{t as e}from"./check.js";import{t}from"./chevron-down.js";import{t as n}from"./copy.js";import{$a as r,Fo as i,Ho as a,Uo as o,Xa as s,cs as c,wo as l}from"./index.js";import{t as u}from"./use-copy-to-clipboard.js";var d=l(`square-terminal`,[[`path`,{d:`m7 11 2-2-2-2`,key:`1lz0vl`}],[`path`,{d:`M11 13h4`,key:`1p7l4v`}],[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,ry:`2`,key:`1m3agn`}]]),f=i(),p=c(o(),1),m=a(),h={bin:`text-tactical-cyan font-semibold`,flag:`text-tactical-amber`,value:`text-foreground`,comment:`text-muted-foreground italic`,variable:`text-tactical-green font-semibold`};function g(e){return e.map(e=>e.parts.map(e=>e.value).join(``)).join(` +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{w as i}from"./SignatureV4.js";import{t as a}from"./check.js";import{t as o}from"./chevron-down.js";import{t as s}from"./copy.js";import{t as c}from"./button.js";import{Nr as l}from"./index.js";import{t as u}from"./use-copy-to-clipboard.js";var d=l(`square-terminal`,[[`path`,{d:`m7 11 2-2-2-2`,key:`1lz0vl`}],[`path`,{d:`M11 13h4`,key:`1p7l4v`}],[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,ry:`2`,key:`1m3agn`}]]),f=r(),p=t(n(),1),m=e(),h={bin:`text-tactical-cyan font-semibold`,flag:`text-tactical-amber`,value:`text-foreground`,comment:`text-muted-foreground italic`,variable:`text-tactical-green font-semibold`};function g(e){return e.map(e=>e.parts.map(e=>e.value).join(``)).join(` -`)}function _(i){let a=(0,f.c)(18),{commands:o}=i,[c,l]=(0,p.useState)(!1),{copied:h,copy:_}=u();if(o.length===0)return null;let b;a[0]===Symbol.for(`react.memo_cache_sentinel`)?(b=()=>l(y),a[0]=b):b=a[0];let x,S;a[1]===Symbol.for(`react.memo_cache_sentinel`)?(x=(0,m.jsx)(d,{className:`size-3.5`}),S=(0,m.jsx)(`span`,{children:`AWS CLI`}),a[1]=x,a[2]=S):(x=a[1],S=a[2]);let C=c&&`rotate-180`,w;a[3]===C?w=a[4]:(w=r(`ml-auto size-3.5 transition-transform`,C),a[3]=C,a[4]=w);let T;a[5]===w?T=a[6]:(T=(0,m.jsx)(t,{className:w}),a[5]=w,a[6]=T);let E;a[7]!==c||a[8]!==T?(E=(0,m.jsxs)(`button`,{type:`button`,onClick:b,className:`flex w-full items-center gap-2 px-3 py-2 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground`,"aria-expanded":c,children:[x,S,T]}),a[7]=c,a[8]=T,a[9]=E):E=a[9];let D;a[10]!==o||a[11]!==h||a[12]!==_||a[13]!==c?(D=c&&(0,m.jsxs)(`div`,{className:`border-t border-border`,children:[(0,m.jsxs)(`div`,{className:`flex items-center justify-between border-b border-border px-3 py-1.5`,children:[(0,m.jsx)(`span`,{className:`text-[0.625rem] text-muted-foreground`,children:o.length>1?`${o.length} commands`:`command`}),(0,m.jsxs)(s,{type:`button`,variant:`ghost`,size:`xs`,onClick:async()=>await _(g(o)),children:[h?(0,m.jsx)(e,{className:`size-2.5`}):(0,m.jsx)(n,{className:`size-2.5`}),h?`Copied`:`Copy`]})]}),(0,m.jsx)(`div`,{className:`overflow-x-auto p-3`,children:o.map((e,t)=>(0,m.jsxs)(`div`,{children:[o.length>1&&(0,m.jsx)(`div`,{className:`mb-1 text-[0.625rem] font-medium text-muted-foreground`,children:e.label}),(0,m.jsx)(`pre`,{className:`font-mono text-xs/relaxed`,children:(0,m.jsx)(`code`,{children:e.parts.map(v)})}),tl(y),t[0]=b):b=t[0];let x,S;t[1]===Symbol.for(`react.memo_cache_sentinel`)?(x=(0,m.jsx)(d,{className:`size-3.5`}),S=(0,m.jsx)(`span`,{children:`AWS CLI`}),t[1]=x,t[2]=S):(x=t[1],S=t[2]);let C=r&&`rotate-180`,w;t[3]===C?w=t[4]:(w=i(`ml-auto size-3.5 transition-transform`,C),t[3]=C,t[4]=w);let T;t[5]===w?T=t[6]:(T=(0,m.jsx)(o,{className:w}),t[5]=w,t[6]=T);let E;t[7]!==r||t[8]!==T?(E=(0,m.jsxs)(`button`,{type:`button`,onClick:b,className:`flex w-full items-center gap-2 px-3 py-2 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground`,"aria-expanded":r,children:[x,S,T]}),t[7]=r,t[8]=T,t[9]=E):E=t[9];let D;t[10]!==n||t[11]!==h||t[12]!==_||t[13]!==r?(D=r&&(0,m.jsxs)(`div`,{className:`border-t border-border`,children:[(0,m.jsxs)(`div`,{className:`flex items-center justify-between border-b border-border px-3 py-1.5`,children:[(0,m.jsx)(`span`,{className:`text-[0.625rem] text-muted-foreground`,children:n.length>1?`${n.length} commands`:`command`}),(0,m.jsxs)(c,{type:`button`,variant:`ghost`,size:`xs`,onClick:async()=>await _(g(n)),children:[h?(0,m.jsx)(a,{className:`size-2.5`}):(0,m.jsx)(s,{className:`size-2.5`}),h?`Copied`:`Copy`]})]}),(0,m.jsx)(`div`,{className:`overflow-x-auto p-3`,children:n.map((e,t)=>(0,m.jsxs)(`div`,{children:[n.length>1&&(0,m.jsx)(`div`,{className:`mb-1 text-[0.625rem] font-medium text-muted-foreground`,children:e.label}),(0,m.jsx)(`pre`,{className:`font-mono text-xs/relaxed`,children:(0,m.jsx)(`code`,{children:e.parts.map(v)})}),t{let t=!c,n=o(p,e.nativeEvent);if(r(t,n),n.isCanceled)return;let i=I.current;P.current===`css-animation`&&i!=null&&i.style.removeProperty(`animation-name`),!k&&!j&&(P.current!=null&&P.current!==`css-animation`&&!d&&t&&f(!0),P.current===`css-animation`&&(!h&&t&&g(!0),!d&&t&&f(!0))),l(t),P.current===`none`&&d&&!t&&f(!1)});return C(()=>{a&&P.current===`none`&&!c&&f(!1)},[a,c,t,f]),w.useMemo(()=>({abortControllerRef:N,animationTypeRef:P,disabled:i,handleTrigger:R,height:_,mounted:d,open:c,panelId:O,panelRef:I,runOnceAnimationsFinish:L,setDimensions:x,setHiddenUntilFound:A,setKeepMounted:M,setMounted:f,setOpen:l,setPanelIdState:D,setVisible:g,transitionDimensionRef:F,transitionStatus:m,visible:h,width:v}),[N,P,i,R,_,d,c,O,I,L,x,A,M,f,l,g,F,m,h,v])}var E=w.createContext(void 0);function D(){let e=w.useContext(E);if(e===void 0)throw Error(i(15));return e}var O=function(e){return e.open=`data-open`,e.closed=`data-closed`,e[e.startingStyle=l.startingStyle]=`startingStyle`,e[e.endingStyle=l.endingStyle]=`endingStyle`,e}({}),k=function(e){return e.panelOpen=`data-panel-open`,e}({}),A={[O.open]:``},j={[O.closed]:``},M={open(e){return e?{[k.panelOpen]:``}:null}},N={open(e){return e?A:j},...d},P=r(),F=w.forwardRef(function(e,n){let{render:r,className:i,defaultOpen:a=!1,disabled:o=!1,onOpenChange:s,open:c,style:l,...d}=e,f=u(s),p=T({open:c,defaultOpen:a,onOpenChange:f,disabled:o}),m=w.useMemo(()=>({open:p.open,disabled:p.disabled,transitionStatus:p.transitionStatus}),[p.open,p.disabled,p.transitionStatus]),h=w.useMemo(()=>({...p,onOpenChange:f,state:m}),[p,f,m]),g=t(`div`,e,{state:m,ref:n,props:d,stateAttributesMapping:N});return(0,P.jsx)(E.Provider,{value:h,children:g})}),I={...M,...d},L=w.forwardRef(function(e,n){let{panelId:r,open:i,handleTrigger:a,state:o,disabled:s}=D(),{className:c,disabled:l=s,id:u,render:d,nativeButton:f=!0,style:p,...m}=e,{getButtonProps:h,buttonRef:g}=x({disabled:l,focusableWhenDisabled:!0,native:f}),_=w.useMemo(()=>({"aria-controls":i?r:void 0,"aria-expanded":i,onClick:a}),[r,i,a]);return t(`button`,e,{state:o,ref:[n,g],props:[_,m,h],stateAttributesMapping:I})}),R=function(e){return e.disabled=`data-disabled`,e.orientation=`data-orientation`,e}({});function z(t){let{abortControllerRef:n,animationTypeRef:r,externalRef:i,height:a,hiddenUntilFound:s,keepMounted:c,id:l,mounted:d,onOpenChange:p,open:h,panelRef:y,runOnceAnimationsFinish:b,setDimensions:x,setMounted:S,setOpen:T,setVisible:E,transitionDimensionRef:D,visible:k,width:A}=t,j=w.useRef(!1),M=w.useRef(null),N=w.useRef(h),P=w.useRef(h),F=g(),I=w.useMemo(()=>r.current===`css-animation`?!k:!h&&!d,[h,d,k,r]),L=m(i,y,u(e=>{if(!e)return;if(r.current==null||D.current==null){let t=getComputedStyle(e),n=t.animationName!==`none`&&t.animationName!==``,i=t.transitionDuration!==`0s`&&t.transitionDuration!==``;n&&i||(t.animationName===`none`&&t.transitionDuration!==`0s`?r.current=`css-transition`:t.animationName!==`none`&&t.transitionDuration===`0s`?r.current=`css-animation`:r.current=`none`),e.getAttribute(R.orientation)===`horizontal`||t.transitionProperty.indexOf(`width`)>-1?D.current=`width`:D.current=`height`}if(r.current!==`css-transition`)return;(a===void 0||A===void 0)&&(x({height:e.scrollHeight,width:e.scrollWidth}),P.current&&e.style.setProperty(`transition-duration`,`0s`));let t=-1,n=-1;return t=v.request(()=>{P.current=!1,n=v.request(()=>{setTimeout(()=>{e.style.removeProperty(`transition-duration`)})})}),()=>{v.cancel(t),v.cancel(n)}}));return C(()=>{if(r.current!==`css-transition`)return;let e=y.current;if(!e)return;let t=-1;if(n.current!=null&&(n.current.abort(),n.current=null),h){let n={"justify-content":e.style.justifyContent,"align-items":e.style.alignItems,"align-content":e.style.alignContent,"justify-items":e.style.justifyItems};Object.keys(n).forEach(t=>{e.style.setProperty(t,`initial`,`important`)}),!P.current&&!c&&e.setAttribute(O.startingStyle,``),x({height:e.scrollHeight,width:e.scrollWidth}),t=v.request(()=>{Object.entries(n).forEach(([t,n])=>{n===``?e.style.removeProperty(t):e.style.setProperty(t,n)})})}else{if(e.scrollHeight===0&&e.scrollWidth===0)return;x({height:e.scrollHeight,width:e.scrollWidth});let t=new AbortController;n.current=t;let r=t.signal,i=null,a=O.endingStyle;return i=new MutationObserver(o=>{o.some(e=>e.type===`attributes`&&e.attributeName===a)&&(i?.disconnect(),i=null,b(()=>{x({height:0,width:0}),e.style.removeProperty(`content-visibility`),S(!1),n.current===t&&(n.current=null)},r))}),i.observe(e,{attributes:!0,attributeFilter:[a]}),()=>{i?.disconnect(),F.cancel(),n.current===t&&(t.abort(),n.current=null)}}return()=>{v.cancel(t)}},[n,r,F,s,c,d,h,y,b,x,S]),C(()=>{if(r.current!==`css-animation`)return;let e=y.current;e&&(M.current=e.style.animationName||M.current,e.style.setProperty(`animation-name`,`none`),x({height:e.scrollHeight,width:e.scrollWidth}),!N.current&&!j.current&&e.style.removeProperty(`animation-name`),h?(n.current!=null&&(n.current.abort(),n.current=null),S(!0),E(!0)):(n.current=new AbortController,b(()=>{S(!1),E(!1),n.current=null},n.current.signal)))},[n,r,h,y,b,x,S,E,k]),f(()=>{let e=v.request(()=>{N.current=!1});return()=>v.cancel(e)}),C(()=>{if(!s)return;let e=y.current;if(!e)return;let t=-1,n=-1;return h&&j.current&&(e.style.transitionDuration=`0s`,x({height:e.scrollHeight,width:e.scrollWidth}),t=v.request(()=>{j.current=!1,n=v.request(()=>{setTimeout(()=>{e.style.removeProperty(`transition-duration`)})})})),()=>{v.cancel(t),v.cancel(n)}},[s,h,y,x]),C(()=>{let e=y.current;e&&s&&I&&(e.setAttribute(`hidden`,`until-found`),r.current===`css-transition`&&e.setAttribute(O.startingStyle,``))},[s,I,r,y]),w.useEffect(function(){let t=y.current;if(!t)return;function n(e){j.current=!0,T(!0),p(!0,o(_,e))}return e(t,`beforematch`,n)},[p,y,T]),w.useMemo(()=>({props:{hidden:I,id:l,ref:L}}),[I,l,L])}var B=function(e){return e.collapsiblePanelHeight=`--collapsible-panel-height`,e.collapsiblePanelWidth=`--collapsible-panel-width`,e}({}),V=w.forwardRef(function(e,n){let{className:r,hiddenUntilFound:i,keepMounted:a,render:o,id:s,style:c,...l}=e,{abortControllerRef:u,animationTypeRef:d,height:f,mounted:p,onOpenChange:m,open:g,panelId:_,panelRef:v,runOnceAnimationsFinish:y,setDimensions:b,setHiddenUntilFound:x,setKeepMounted:S,setMounted:T,setPanelIdState:E,setOpen:O,setVisible:k,state:A,transitionDimensionRef:j,visible:M,width:P,transitionStatus:F}=D(),I=i??!1,L=a??!1;C(()=>{if(s)return E(s),()=>{E(void 0)}},[s,E]),C(()=>{x(I)},[x,I]),C(()=>{S(L)},[S,L]);let{props:R}=z({abortControllerRef:u,animationTypeRef:d,externalRef:n,height:f,hiddenUntilFound:I,id:_,keepMounted:L,mounted:p,onOpenChange:m,open:g,panelRef:v,runOnceAnimationsFinish:y,setDimensions:b,setMounted:T,setOpen:O,setVisible:k,transitionDimensionRef:j,visible:M,width:P});h({open:g&&F===`idle`,ref:v,onComplete(){g&&b({height:void 0,width:void 0})}});let V=t(`div`,e,{state:w.useMemo(()=>({...A,transitionStatus:F}),[A,F]),ref:[n,v],props:[R,{style:{[B.collapsiblePanelHeight]:f===void 0?`auto`:`${f}px`,[B.collapsiblePanelWidth]:P===void 0?`auto`:`${P}px`}},l],stateAttributesMapping:N});return L||I||p?V:null}),H=n();function U(e){let t=(0,H.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,P.jsx)(F,{"data-slot":`collapsible`,...n}),t[2]=n,t[3]=r),r}function W(e){let t=(0,H.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,P.jsx)(L,{"data-slot":`collapsible-trigger`,...n}),t[2]=n,t[3]=r),r}function G(e){let t=(0,H.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,P.jsx)(V,{"data-slot":`collapsible-content`,...n}),t[2]=n,t[3]=r),r}export{G as n,W as r,U as t}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{c as i,n as a,r as o,u as s}from"./dist.js";import{_ as c,o as l,r as u,s as d}from"./button.js";import{S as f,f as p,g as m,t as h}from"./createBaseUIEventDetails.js";import{An as g,Bn as _,Gn as v,Hn as y,Kn as b,Un as x,Vn as S,Wn as C,kn as w}from"./index.js";var T=t(n());function E(e){let{open:t,defaultOpen:n,onOpenChange:r,disabled:i}=e,[a,o]=_({controlled:t,default:n,name:`Collapsible`,state:`open`}),{mounted:s,setMounted:c,transitionStatus:l}=S(a,!0,!0),u=f(),[p,g]=T.useState(),v=p??u,y=d(e=>{let t=!a,n=h(m,e.nativeEvent);r(t,n),!n.isCanceled&&o(t)});return T.useMemo(()=>({disabled:i,handleTrigger:y,mounted:s,open:a,panelId:v,setMounted:c,setOpen:o,setPanelIdState:g,transitionStatus:l}),[i,y,s,a,v,c,o,g,l])}var D=T.createContext(void 0);function O(){let e=T.useContext(D);if(e===void 0)throw Error(s(15));return e}var k=function(e){return e.open=`data-open`,e.closed=`data-closed`,e[e.startingStyle=C.startingStyle]=`startingStyle`,e[e.endingStyle=C.endingStyle]=`endingStyle`,e}({}),A=function(e){return e.panelOpen=`data-panel-open`,e}({}),j={[k.open]:``},M={[k.closed]:``},N={open(e){return e?{[A.panelOpen]:``}:null}},P={open(e){return e?j:M},...v},F=e(),I=T.forwardRef(function(e,t){let{render:n,className:r,defaultOpen:i=!1,disabled:o=!1,onOpenChange:s,open:c,style:l,...u}=e,f=d(s),p=E({open:c,defaultOpen:i,onOpenChange:f,disabled:o}),m=T.useMemo(()=>({open:p.open,disabled:p.disabled,transitionStatus:p.transitionStatus}),[p.open,p.disabled,p.transitionStatus]),h=T.useMemo(()=>({...p,onOpenChange:f,state:m}),[p,f,m]),g=a(`div`,e,{state:m,ref:t,props:u,stateAttributesMapping:P});return(0,F.jsx)(D.Provider,{value:h,children:g})}),L={...N,...v},R=T.forwardRef(function(e,t){let{panelId:n,open:r,handleTrigger:i,state:o,disabled:s}=O(),{className:c,disabled:l=s,id:d,render:f,nativeButton:p=!0,style:m,...h}=e,{getButtonProps:g,buttonRef:_}=u({disabled:l,focusableWhenDisabled:!0,native:p});return a(`button`,e,{state:o,ref:[t,_],props:[{"aria-controls":r?n:void 0,"aria-expanded":r,onClick:i},h,g],stateAttributesMapping:L})}),z={height:void 0,width:void 0};function B(e){let{externalRef:t,hiddenUntilFound:n,id:r,keepMounted:a,mounted:o,onOpenChange:s,open:c,setMounted:u,setOpen:f,transitionStatus:m}=e,_=T.useRef(null),v=T.useRef(null),[S,C]=T.useState(z),E=T.useRef(z),D=T.useRef(!1),O=T.useRef(c),A=T.useRef(!1),[j,M]=T.useState(!1),N=T.useRef(null),P=i(t,_),F=w({mounted:o,open:c}),I=x(_,!1,!1),L=!c&&!o,R=j?`idle`:m,B=c&&(O.current||A.current),U=!c&&o&&v.current===`css-animation`&&S.height===void 0&&S.width===void 0?E.current:S,K=n&&L&&v.current!==`css-animation`,q=d((e,t=!0)=>{t&&(E.current=e),C(e)}),J=d(()=>{N.current?.(),N.current=null}),Y=d(e=>{J(),N.current=()=>{N.current=null,e()}}),X=d(()=>{c&&o&&v.current===`css-animation`&&(A.current=!0)});l(()=>{!j||m===`starting`||M(!1)},[j,m]),T.useEffect(()=>()=>{X(),J()},[X,J]),l(()=>{let e=_.current;if(!e)return;!c&&N.current&&J();let t=H(e,B);if(v.current=t,c&&m===`idle`&&O.current&&t===`css-animation`){E.current=V(e);return}if(c&&m===`starting`){let n=D.current;if(D.current=!1,t===`none`){q(V(e)),M(!0);return}if(t===`css-transition`){let t=G(e);return q(V(e)),n?(Y(W(e,`transition-duration`,`0s`)),M(!0),t):t}if(t===`css-animation`){if(q(V(e)),!n){W(e,`animation-name`,`none`)();return}let t=W(e,`animation-name`,`none`),r=W(e,`animation-duration`,`0s`);t(),Y(r),M(!0);return}}if(!c&&o&&(m===`idle`||m===`starting`)){if(t===`none`){q(z,!1),u(!1);return}t===`css-animation`&&(O.current=!1,A.current=!1),q(V(e));return}if(m!==`ending`)return;if(t===`none`){u(!1);return}let n=V(e);if(!((n.height??0)>0||(n.width??0)>0)){u(!1);return}q(n),t===`css-animation`&&W(e,`animation-name`,`none`)()},[o,c,J,q,u,Y,B,m]),y({enabled:c&&o&&R===`idle`,open:!0,ref:_,onComplete(){c&&q(z,!1)}}),T.useEffect(()=>{if(c||!o||R!==`ending`||!_.current)return;let e=new AbortController,t=-1;function n(){F.current.open||(u(!1),q(z,!1))}return t=b.request(()=>{e.signal.aborted||I(n,e.signal)}),()=>{b.cancel(t),e.abort()}},[F,o,c,R,I,q,u]),l(()=>{let e=_.current;!e||!n||!L||e.setAttribute(`hidden`,`until-found`)},[L,n]),T.useEffect(function(){let e=_.current;if(!e)return;function t(e){D.current=!0,f(!0),s(!0,h(p,e))}return g(e,`beforematch`,t)},[s,f]);let Z=a||n||o||c;return{height:U.height,props:{...K?{[k.startingStyle]:``}:void 0,hidden:L,id:r},ref:P,shouldPreventOpenAnimation:B,shouldRender:Z,transitionStatus:R,width:U.width}}function V(e){return{height:e.scrollHeight,width:e.scrollWidth}}function H(e,t=!1){let n=c(e).getComputedStyle(e),r=(n.animationName.split(`,`).map(e=>e.trim()).some(e=>e!==``&&e!==`none`)||t)&&U(n.animationDuration),i=U(n.transitionDuration);return r&&i||i?`css-transition`:r?`css-animation`:`none`}function U(e){return e.split(`,`).map(e=>e.trim()).some(e=>e!==``&&Number.parseFloat(e)>0)}function W(e,t,n){let r=e.style.getPropertyValue(t),i=e.style.getPropertyPriority(t);return e.style.setProperty(t,n),()=>{if(r===``){e.style.removeProperty(t);return}e.style.setProperty(t,r,i)}}function G(e){let t={"justify-content":e.style.justifyContent,"align-items":e.style.alignItems,"align-content":e.style.alignContent,"justify-items":e.style.justifyItems};Object.keys(t).forEach(t=>{e.style.setProperty(t,`initial`,`important`)});function n(){Object.entries(t).forEach(([t,n])=>{if(n===``){e.style.removeProperty(t);return}e.style.setProperty(t,n)})}let r=b.request(n);return()=>{b.cancel(r),n()}}var K=function(e){return e.collapsiblePanelHeight=`--collapsible-panel-height`,e.collapsiblePanelWidth=`--collapsible-panel-width`,e}({}),q=T.forwardRef(function(e,t){let{className:n,hiddenUntilFound:r,keepMounted:i,render:s,id:c,style:u,...d}=e,{mounted:f,onOpenChange:p,open:m,panelId:h,setMounted:g,setPanelIdState:_,setOpen:v,state:y,transitionStatus:b}=O(),x=r??!1,S=i??!1;l(()=>{if(c)return _(c),()=>{_(void 0)}},[c,_]);let{height:C,props:w,ref:T,shouldPreventOpenAnimation:E,shouldRender:D,transitionStatus:k,width:A}=B({externalRef:t,hiddenUntilFound:x,id:h,keepMounted:S,mounted:f,onOpenChange:p,open:m,setMounted:g,setOpen:v,transitionStatus:b}),j={...y,transitionStatus:k},M=o(u,j),N=a(`div`,{...e,style:void 0},{state:j,ref:T,props:[w,{style:{[K.collapsiblePanelHeight]:C===void 0?`auto`:`${C}px`,[K.collapsiblePanelWidth]:A===void 0?`auto`:`${A}px`}},d,M?{style:M}:void 0,E?{style:{animationName:`none`}}:void 0],stateAttributesMapping:P});return D?N:null}),J=r();function Y(e){let t=(0,J.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,F.jsx)(I,{"data-slot":`collapsible`,...n}),t[2]=n,t[3]=r),r}function X(e){let t=(0,J.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,F.jsx)(R,{"data-slot":`collapsible-trigger`,...n}),t[2]=n,t[3]=r),r}function Z(e){let t=(0,J.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,F.jsx)(q,{"data-slot":`collapsible-content`,...n}),t[2]=n,t[3]=r),r}export{Z as n,X as r,Y as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/compiler-runtime.js b/spinifex/services/spinifexui/frontend/dist/assets/compiler-runtime.js new file mode 100644 index 000000000..3a985cd6b --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/compiler-runtime.js @@ -0,0 +1 @@ +var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,a)=>(a=n==null?{}:e(i(n)),c(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),u=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},T=Object.prototype.hasOwnProperty;function E(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function D(e,t){return E(e.type,t,e.props)}function O(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function k(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var A=/\/+/g;function j(e,t){return typeof e==`object`&&e&&e.key!=null?k(``+e.key):t.toString(36)}function M(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function N(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,N(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+j(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(A,`$&/`)+`/`),N(o,r,i,``,function(e){return e})):o!=null&&(O(o)&&(o=D(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(A,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=u()})),f=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),p=o(((e,t)=>{t.exports=f()})),m=o((e=>{var t=d().__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;e.c=function(e){return t.H.useMemoCache(e)}})),h=o(((e,t)=>{t.exports=m()}));export{s as a,o as i,p as n,l as o,d as r,h as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/copy.js b/spinifex/services/spinifexui/frontend/dist/assets/copy.js index 8077f5021..a61ff1fb8 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/copy.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/copy.js @@ -1 +1 @@ -import{wo as e}from"./index.js";var t=e(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]);export{t}; \ No newline at end of file +import{Nr as e}from"./index.js";var t=e(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-bucket.js b/spinifex/services/spinifexui/frontend/dist/assets/create-bucket.js index 8ac14ada0..29123ee6c 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-bucket.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-bucket.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{t as n}from"./cli-command-panel.js";import{t as r}from"./s3.js";import{Fo as i,Ho as a,Ro as o,Ur as s,Vr as c,Xi as l}from"./index.js";import{i as u,t as d}from"./zod.js";import{t as f}from"./form-actions.js";import{t as p}from"./page-heading.js";import{a as m,r as h,t as g}from"./field.js";var _=i(),v=c({bucketName:s().min(3,`Bucket name must be at least 3 characters`).max(63,`Bucket name must be at most 63 characters`).regex(/^[a-z0-9][a-z0-9.-]*[a-z0-9]$/,`Must start and end with a lowercase letter or number`).regex(/^[a-z0-9.-]+$/,`Only lowercase letters, numbers, hyphens, and periods allowed`).refine(e=>!e.includes(`..`),`Must not contain consecutive periods`).refine(e=>!(e.includes(`.-`)||e.includes(`-.`)),`Must not contain adjacent period and hyphen`)}),y=a();function b(){let i=(0,_.c)(40),a=o(),s=r(),c;i[0]===Symbol.for(`react.memo_cache_sentinel`)?(c={resolver:d(v)},i[0]=c):c=i[0];let{handleSubmit:b,register:S,watch:C,formState:w}=u(c),{errors:T,isSubmitting:E}=w,D;i[1]!==s||i[2]!==a?(D=async e=>{await s.mutateAsync(e),a({to:`/s3/ls`})},i[1]=s,i[2]=a,i[3]=D):D=i[3];let O=D,k,A;i[4]===Symbol.for(`react.memo_cache_sentinel`)?(k=(0,y.jsx)(t,{to:`/s3/ls`,children:`Back to buckets`}),A=(0,y.jsx)(p,{title:`Create Bucket`}),i[4]=k,i[5]=A):(k=i[4],A=i[5]);let j;i[6]===s.error?j=i[7]:(j=s.error&&(0,y.jsx)(e,{error:s.error,msg:`Failed to create bucket`}),i[6]=s.error,i[7]=j);let M;i[8]!==b||i[9]!==O?(M=b(O),i[8]=b,i[9]=O,i[10]=M):M=i[10];let N;i[11]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,y.jsx)(m,{children:(0,y.jsx)(`label`,{htmlFor:`bucketName`,children:`Bucket Name`})}),i[11]=N):N=i[11];let P=!!T.bucketName,F;i[12]===S?F=i[13]:(F=S(`bucketName`),i[12]=S,i[13]=F);let I;i[14]!==P||i[15]!==F?(I=(0,y.jsx)(l,{"aria-invalid":P,id:`bucketName`,placeholder:`my-bucket`,type:`text`,...F}),i[14]=P,i[15]=F,i[16]=I):I=i[16];let L;i[17]===T.bucketName?L=i[18]:(L=(0,y.jsx)(h,{errors:[T.bucketName]}),i[17]=T.bucketName,i[18]=L);let R;i[19]!==I||i[20]!==L?(R=(0,y.jsxs)(g,{children:[N,I,L]}),i[19]=I,i[20]=L,i[21]=R):R=i[21];let z;i[22]===C?z=i[23]:(z=x(C),i[22]=C,i[23]=z);let B;i[24]===z?B=i[25]:(B=(0,y.jsx)(n,{commands:z}),i[24]=z,i[25]=B);let V;i[26]===a?V=i[27]:(V=async()=>await a({to:`/s3/ls`}),i[26]=a,i[27]=V);let H;i[28]!==s.isPending||i[29]!==E||i[30]!==V?(H=(0,y.jsx)(f,{isPending:s.isPending,isSubmitting:E,onCancel:V,pendingLabel:`Creating…`,submitLabel:`Create Bucket`}),i[28]=s.isPending,i[29]=E,i[30]=V,i[31]=H):H=i[31];let U;i[32]!==R||i[33]!==B||i[34]!==H||i[35]!==M?(U=(0,y.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:M,children:[R,B,H]}),i[32]=R,i[33]=B,i[34]=H,i[35]=M,i[36]=U):U=i[36];let W;return i[37]!==U||i[38]!==j?(W=(0,y.jsxs)(y.Fragment,{children:[k,A,j,U]}),i[37]=U,i[38]=j,i[39]=W):W=i[39],W}function x(e){let t=e(`bucketName`);return[{label:`Create Bucket`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws s3api create-bucket`},{type:`flag`,value:` --bucket`},{type:`value`,value:` ${(typeof t==`string`?t:``)||``}`}]}]}export{b as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{_ as r,y as i}from"./SignatureV4.js";import{t as a}from"./back-link.js";import{t as o}from"./cli-command-panel.js";import{t as s}from"./s3.js";import{Ir as c,Rn as l}from"./index.js";import{a as u,i as d,t as f}from"./zod.js";import{t as p}from"./form-actions.js";import{t as m}from"./page-heading.js";import{a as h,r as g,t as _}from"./field.js";var v=t(),y=r({bucketName:i().min(3,`Bucket name must be at least 3 characters`).max(63,`Bucket name must be at most 63 characters`).regex(/^[a-z0-9][a-z0-9.-]*[a-z0-9]$/,`Must start and end with a lowercase letter or number`).regex(/^[a-z0-9.-]+$/,`Only lowercase letters, numbers, hyphens, and periods allowed`).refine(e=>!e.includes(`..`),`Must not contain consecutive periods`).refine(e=>!(e.includes(`.-`)||e.includes(`-.`)),`Must not contain adjacent period and hyphen`)}),b=e();function x(){let e=(0,v.c)(44),t=c(),r=s(),i;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(i={resolver:f(y)},e[0]=i):i=e[0];let{handleSubmit:x,register:C,control:w,formState:T}=d(i),{errors:E,isSubmitting:D}=T,O;e[1]===w?O=e[2]:(O={control:w},e[1]=w,e[2]=O);let k=u(O),A;e[3]===k?A=e[4]:(A=e=>e?k[e]:void 0,e[3]=k,e[4]=A);let j=A,M;e[5]!==r||e[6]!==t?(M=async e=>{await r.mutateAsync(e),t({to:`/s3/ls`})},e[5]=r,e[6]=t,e[7]=M):M=e[7];let N=M,P,F;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,b.jsx)(a,{to:`/s3/ls`,children:`Back to buckets`}),F=(0,b.jsx)(m,{title:`Create Bucket`}),e[8]=P,e[9]=F):(P=e[8],F=e[9]);let I;e[10]===r.error?I=e[11]:(I=r.error&&(0,b.jsx)(n,{error:r.error,msg:`Failed to create bucket`}),e[10]=r.error,e[11]=I);let L;e[12]!==x||e[13]!==N?(L=x(N),e[12]=x,e[13]=N,e[14]=L):L=e[14];let R;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,b.jsx)(h,{children:(0,b.jsx)(`label`,{htmlFor:`bucketName`,children:`Bucket Name`})}),e[15]=R):R=e[15];let z=!!E.bucketName,B;e[16]===C?B=e[17]:(B=C(`bucketName`),e[16]=C,e[17]=B);let V;e[18]!==z||e[19]!==B?(V=(0,b.jsx)(l,{"aria-invalid":z,id:`bucketName`,placeholder:`my-bucket`,type:`text`,...B}),e[18]=z,e[19]=B,e[20]=V):V=e[20];let H;e[21]===E.bucketName?H=e[22]:(H=(0,b.jsx)(g,{errors:[E.bucketName]}),e[21]=E.bucketName,e[22]=H);let U;e[23]!==V||e[24]!==H?(U=(0,b.jsxs)(_,{children:[R,V,H]}),e[23]=V,e[24]=H,e[25]=U):U=e[25];let W;e[26]===j?W=e[27]:(W=S(j),e[26]=j,e[27]=W);let G;e[28]===W?G=e[29]:(G=(0,b.jsx)(o,{commands:W}),e[28]=W,e[29]=G);let K;e[30]===t?K=e[31]:(K=async()=>await t({to:`/s3/ls`}),e[30]=t,e[31]=K);let q;e[32]!==r.isPending||e[33]!==D||e[34]!==K?(q=(0,b.jsx)(p,{isPending:r.isPending,isSubmitting:D,onCancel:K,pendingLabel:`Creating…`,submitLabel:`Create Bucket`}),e[32]=r.isPending,e[33]=D,e[34]=K,e[35]=q):q=e[35];let J;e[36]!==U||e[37]!==G||e[38]!==q||e[39]!==L?(J=(0,b.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:L,children:[U,G,q]}),e[36]=U,e[37]=G,e[38]=q,e[39]=L,e[40]=J):J=e[40];let Y;return e[41]!==J||e[42]!==I?(Y=(0,b.jsxs)(b.Fragment,{children:[P,F,I,J]}),e[41]=J,e[42]=I,e[43]=Y):Y=e[43],Y}function S(e){let t=e(`bucketName`);return[{label:`Create Bucket`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws s3api create-bucket`},{type:`flag`,value:` --bucket`},{type:`value`,value:` ${(typeof t==`string`?t:``)||``}`}]}]}export{x as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-cluster.js b/spinifex/services/spinifexui/frontend/dist/assets/create-cluster.js new file mode 100644 index 000000000..54ac7ca29 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-cluster.js @@ -0,0 +1 @@ +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{t as a}from"./useSuspenseQuery.js";import{A as o}from"./SignatureV4.js";import{t as s}from"./back-link.js";import{t as c}from"./check.js";import{c as l,i as u,n as d,s as f,t as p}from"./select.js";import{t as m}from"./copy.js";import{t as h}from"./rotate-cw.js";import{t as g}from"./triangle-alert.js";import{t as _}from"./button.js";import{c as v,i as y,p as b}from"./eks.js";import{Ir as x,Lr as S,Lt as C,Nt as w,Rn as T,pt as E,rt as D}from"./index.js";import{i as O,n as k,t as ee}from"./zod.js";import{t as A}from"./use-copy-to-clipboard.js";import{t as te}from"./form-actions.js";import{t as j}from"./page-heading.js";import{a as M,r as N,t as P}from"./field.js";import{t as F}from"./system-managed.js";var I=t(n(),1),L=r(),R=e(),z=`make import-eks-node-image`;function B(e){let t=(0,L.c)(21),{isRechecking:n,onRecheck:r}=e,{copied:i,copy:a}=A(),o;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(o=(0,R.jsx)(g,{className:`mt-0.5 size-5 shrink-0 text-tactical-amber`}),t[0]=o):o=t[0];let s;t[1]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,R.jsxs)(`div`,{className:`space-y-1`,children:[(0,R.jsx)(`h2`,{className:`text-sm font-semibold text-foreground`,children:`EKS system image not found`}),(0,R.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`EKS clusters boot a Spinifex-managed node image (K3s) that is not shipped with the platform. Install it on the host before creating a cluster.`})]}),t[1]=s):s=t[1];let l,u;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,R.jsx)(`span`,{className:`text-muted-foreground select-none`,children:`$`}),u=(0,R.jsx)(`span`,{className:`flex-1 text-foreground`,children:z}),t[2]=l,t[3]=u):(l=t[2],u=t[3]);let d;t[4]===a?d=t[5]:(d=()=>void a(z),t[4]=a,t[5]=d);let f;t[6]===i?f=t[7]:(f=i?(0,R.jsx)(c,{className:`size-3.5 text-tactical-green`}):(0,R.jsx)(m,{className:`size-3.5`}),t[6]=i,t[7]=f);let p;t[8]!==d||t[9]!==f?(p=(0,R.jsxs)(`div`,{className:`flex items-center gap-2 rounded border border-border bg-background px-3 py-2 font-mono text-xs`,children:[l,u,(0,R.jsx)(`button`,{"aria-label":`Copy command`,className:`text-muted-foreground transition-colors hover:text-foreground`,onClick:d,type:`button`,children:f})]}),t[8]=d,t[9]=f,t[10]=p):p=t[10];let v;t[11]===Symbol.for(`react.memo_cache_sentinel`)?(v=(0,R.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Run from the Spinifex repo root. The command builds and registers the node image, then click Recheck.`}),t[11]=v):v=t[11];let y=n?`size-4 animate-spin`:`size-4`,b;t[12]===y?b=t[13]:(b=(0,R.jsx)(h,{className:y}),t[12]=y,t[13]=b);let x;t[14]!==n||t[15]!==r||t[16]!==b?(x=(0,R.jsxs)(_,{disabled:n,onClick:r,size:`sm`,variant:`outline`,children:[b,`Recheck`]}),t[14]=n,t[15]=r,t[16]=b,t[17]=x):x=t[17];let S;return t[18]!==x||t[19]!==p?(S=(0,R.jsx)(`div`,{className:`max-w-2xl rounded-md border border-tactical-amber/40 bg-tactical-amber/5 p-6`,children:(0,R.jsxs)(`div`,{className:`flex items-start gap-3`,children:[o,(0,R.jsxs)(`div`,{className:`space-y-4`,children:[s,p,v,x]})]})}),t[18]=x,t[19]=p,t[20]=S):S=t[20],S}function V(e){let t=o(e.Tags);return t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`}function H(e){let t=o(e.Tags);return`${t?`${e.SubnetId} (${t})`:e.SubnetId} · ${e.CidrBlock}`}function U(){let e=x(),t=S(),[n,r]=(0,I.useState)(!1),{data:o}=a(C),{data:c}=a(w),{data:m}=a(D),{data:h}=a(E),g=b(),A=(h.Images??[]).some(F),L=async()=>{r(!0);try{await t.invalidateQueries({queryKey:E.queryKey})}finally{r(!1)}},z=o.Vpcs??[],U=c.Subnets??[],W=m.Roles??[],{control:G,formState:{errors:K,isSubmitting:q},handleSubmit:J,register:ne,setValue:Y,watch:X}=O({resolver:ee(v),defaultValues:{name:``,version:y[0],roleArn:``,vpcId:``,subnetIds:[],bootstrapClusterCreatorAdminPermissions:!0,endpointPublicAccess:!0,endpointPrivateAccess:!1,publicAccessCidrs:[`0.0.0.0/0`]}}),re=X(`vpcId`),Z=X(`subnetIds`),ie=X(`endpointPublicAccess`),Q=X(`publicAccessCidrs`),$=U.filter(e=>e.VpcId===re),ae=(e=``)=>{Y(`vpcId`,e??``,{shouldValidate:!0}),Y(`subnetIds`,[])},oe=e=>{Y(`subnetIds`,Z.includes(e)?Z.filter(t=>t!==e):[...Z,e],{shouldValidate:!0})},se=(e,t)=>{let n=[...Q];n[e]=t,Y(`publicAccessCidrs`,n,{shouldValidate:!0})},ce=()=>Y(`publicAccessCidrs`,[...Q,``],{shouldValidate:!0}),le=e=>Y(`publicAccessCidrs`,Q.filter((t,n)=>n!==e),{shouldValidate:!0});return A?(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(s,{to:`/eks/list-clusters`,children:`Clusters`}),(0,R.jsx)(j,{subtitle:`EKS`,title:`Create Cluster`}),g.error&&(0,R.jsx)(i,{error:g.error,msg:`Failed to create cluster`}),(0,R.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:J(async t=>{await g.mutateAsync(t),await e({to:`/eks/list-clusters/$clusterName`,params:{clusterName:t.name}})}),children:[(0,R.jsxs)(P,{children:[(0,R.jsx)(M,{children:(0,R.jsx)(`label`,{htmlFor:`cluster-name`,children:`Name`})}),(0,R.jsx)(T,{"aria-invalid":!!K.name,id:`cluster-name`,placeholder:`my-cluster`,...ne(`name`)}),(0,R.jsx)(N,{errors:[K.name]})]}),(0,R.jsxs)(P,{children:[(0,R.jsx)(M,{children:(0,R.jsx)(`label`,{htmlFor:`cluster-version`,children:`Kubernetes version`})}),(0,R.jsx)(k,{control:G,name:`version`,render:({field:e})=>(0,R.jsxs)(p,{onValueChange:e.onChange,value:e.value,children:[(0,R.jsx)(f,{className:`w-full`,id:`cluster-version`,children:(0,R.jsx)(l,{placeholder:`Select version`})}),(0,R.jsx)(d,{children:y.map(e=>(0,R.jsx)(u,{value:e,children:e},e))})]})}),(0,R.jsx)(N,{errors:[K.version]})]}),(0,R.jsxs)(P,{children:[(0,R.jsx)(M,{children:(0,R.jsx)(`label`,{htmlFor:`cluster-role`,children:`Cluster IAM role`})}),(0,R.jsx)(k,{control:G,name:`roleArn`,render:({field:e})=>(0,R.jsxs)(p,{onValueChange:e.onChange,value:e.value,children:[(0,R.jsx)(f,{"aria-invalid":!!K.roleArn,className:`w-full`,id:`cluster-role`,children:(0,R.jsx)(l,{placeholder:`Select role`})}),(0,R.jsx)(d,{children:W.map(e=>(0,R.jsx)(u,{value:e.Arn??``,children:e.RoleName},e.Arn))})]})}),(0,R.jsx)(N,{errors:[K.roleArn]})]}),(0,R.jsxs)(P,{children:[(0,R.jsx)(M,{children:(0,R.jsx)(`label`,{htmlFor:`cluster-vpc`,children:`VPC`})}),(0,R.jsx)(k,{control:G,name:`vpcId`,render:({field:e})=>(0,R.jsxs)(p,{onValueChange:e=>ae(e),value:e.value,children:[(0,R.jsx)(f,{"aria-invalid":!!K.vpcId,className:`w-full`,id:`cluster-vpc`,children:(0,R.jsx)(l,{placeholder:`Select VPC`})}),(0,R.jsx)(d,{children:z.map(e=>(0,R.jsx)(u,{value:e.VpcId??``,children:V(e)},e.VpcId))})]})}),(0,R.jsx)(N,{errors:[K.vpcId]})]}),(0,R.jsxs)(P,{children:[(0,R.jsx)(M,{children:`Subnets (select at least 1)`}),$.length===0?(0,R.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No subnets in the selected VPC.`}):(0,R.jsx)(`div`,{className:`space-y-1`,children:$.map(e=>(0,R.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,R.jsx)(`input`,{"aria-label":`Subnet ${H(e)}`,checked:Z.includes(e.SubnetId??``),onChange:()=>oe(e.SubnetId??``),type:`checkbox`}),(0,R.jsx)(`span`,{className:`font-mono`,children:H(e)})]},e.SubnetId))}),(0,R.jsx)(N,{errors:[K.subnetIds]})]}),(0,R.jsxs)(P,{children:[(0,R.jsx)(M,{children:`Endpoint access`}),(0,R.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Public access exposes the API server on an internet-facing endpoint; private access keeps it reachable only from within the VPC. At least one must be enabled.`}),(0,R.jsx)(k,{control:G,name:`endpointPublicAccess`,render:({field:e})=>(0,R.jsxs)(`label`,{className:`mt-2 flex items-center gap-2 text-xs`,children:[(0,R.jsx)(`input`,{"aria-label":`Enable public access`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Public`]})}),(0,R.jsx)(k,{control:G,name:`endpointPrivateAccess`,render:({field:e})=>(0,R.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,R.jsx)(`input`,{"aria-label":`Enable private access`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Private`]})}),(0,R.jsx)(N,{errors:[K.endpointPublicAccess]}),ie&&(0,R.jsxs)(`div`,{className:`mt-2 space-y-2`,children:[(0,R.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Public access source ranges (CIDR). Defaults to`,` `,(0,R.jsx)(`span`,{className:`font-mono`,children:`0.0.0.0/0`}),`.`]}),Q.map((e,t)=>(0,R.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,R.jsx)(T,{"aria-label":`Public access CIDR ${t+1}`,className:`font-mono`,onChange:e=>se(t,e.target.value),placeholder:`203.0.113.0/24`,value:e}),(0,R.jsx)(_,{onClick:()=>le(t),size:`sm`,type:`button`,variant:`ghost`,children:`Remove`})]},t)),(0,R.jsx)(_,{onClick:ce,size:`sm`,type:`button`,variant:`outline`,children:`Add CIDR`}),(0,R.jsx)(N,{errors:[K.publicAccessCidrs]})]})]}),(0,R.jsxs)(P,{children:[(0,R.jsx)(M,{children:`Access`}),(0,R.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Authentication mode is `,(0,R.jsx)(`span`,{className:`font-mono`,children:`API`}),` (IAM access entries). The legacy aws-auth ConfigMap is not supported.`]}),(0,R.jsx)(k,{control:G,name:`bootstrapClusterCreatorAdminPermissions`,render:({field:e})=>(0,R.jsxs)(`label`,{className:`mt-2 flex items-center gap-2 text-xs`,children:[(0,R.jsx)(`input`,{"aria-label":`Grant the cluster creator admin permissions`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Grant the cluster creator admin permissions`]})})]}),(0,R.jsx)(te,{isPending:g.isPending,isSubmitting:q,onCancel:async()=>await e({to:`/eks/list-clusters`}),pendingLabel:`Creating…`,submitLabel:`Create Cluster`})]})]}):(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(s,{to:`/eks/list-clusters`,children:`Clusters`}),(0,R.jsx)(j,{subtitle:`EKS`,title:`Create Cluster`}),(0,R.jsx)(B,{isRechecking:n,onRecheck:L})]})}var W=U;export{W as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-instance-profile.js b/spinifex/services/spinifexui/frontend/dist/assets/create-instance-profile.js new file mode 100644 index 000000000..ca13eeb63 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-instance-profile.js @@ -0,0 +1 @@ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{a}from"./iam.js";import{Ir as o,Rn as s}from"./index.js";import{a as c,i as l,t as u}from"./zod.js";import{t as d}from"./form-actions.js";import{t as f}from"./page-heading.js";import{a as p,r as m,t as h}from"./field.js";import{n as g}from"./iam2.js";var _=t(),v=e();function y(){let e=(0,_.c)(55),t=o(),y=a(),b;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(b={resolver:u(g)},e[0]=b):b=e[0];let{handleSubmit:x,register:S,control:C,formState:w}=l(b),{errors:T,isSubmitting:E}=w,D;e[1]===C?D=e[2]:(D={control:C},e[1]=C,e[2]=D);let O=c(D),k;e[3]===O?k=e[4]:(k=e=>e?O[e]:void 0,e[3]=O,e[4]=k);let A=k,j;e[5]!==y||e[6]!==t?(j=async e=>{await y.mutateAsync(e),t({to:`/iam/list-instance-profiles`})},e[5]=y,e[6]=t,e[7]=j):j=e[7];let M=j,N,P;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,v.jsx)(r,{to:`/iam/list-instance-profiles`,children:`Back to instance profiles`}),P=(0,v.jsx)(f,{title:`Create Instance Profile`}),e[8]=N,e[9]=P):(N=e[8],P=e[9]);let F;e[10]===y.error?F=e[11]:(F=y.error&&(0,v.jsx)(n,{error:y.error,msg:`Failed to create instance profile`}),e[10]=y.error,e[11]=F);let I;e[12]!==x||e[13]!==M?(I=x(M),e[12]=x,e[13]=M,e[14]=I):I=e[14];let L;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,v.jsx)(p,{children:(0,v.jsx)(`label`,{htmlFor:`instanceProfileName`,children:`Instance Profile Name`})}),e[15]=L):L=e[15];let R=!!T.instanceProfileName,z;e[16]===S?z=e[17]:(z=S(`instanceProfileName`),e[16]=S,e[17]=z);let B;e[18]!==R||e[19]!==z?(B=(0,v.jsx)(s,{"aria-invalid":R,id:`instanceProfileName`,placeholder:`my-instance-profile...`,...z}),e[18]=R,e[19]=z,e[20]=B):B=e[20];let V;e[21]===T.instanceProfileName?V=e[22]:(V=(0,v.jsx)(m,{errors:[T.instanceProfileName]}),e[21]=T.instanceProfileName,e[22]=V);let H;e[23]!==B||e[24]!==V?(H=(0,v.jsxs)(h,{children:[L,B,V]}),e[23]=B,e[24]=V,e[25]=H):H=e[25];let U;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,v.jsx)(p,{children:(0,v.jsx)(`label`,{htmlFor:`path`,children:`Path (optional)`})}),e[26]=U):U=e[26];let W;e[27]===S?W=e[28]:(W=S(`path`),e[27]=S,e[28]=W);let G;e[29]===W?G=e[30]:(G=(0,v.jsx)(s,{id:`path`,placeholder:`/`,...W}),e[29]=W,e[30]=G);let K;e[31]===T.path?K=e[32]:(K=(0,v.jsx)(m,{errors:[T.path]}),e[31]=T.path,e[32]=K);let q;e[33]!==G||e[34]!==K?(q=(0,v.jsxs)(h,{children:[U,G,K]}),e[33]=G,e[34]=K,e[35]=q):q=e[35];let J;e[36]===A?J=e[37]:(J=ee(A),e[36]=A,e[37]=J);let Y;e[38]===J?Y=e[39]:(Y=(0,v.jsx)(i,{commands:J}),e[38]=J,e[39]=Y);let X;e[40]===t?X=e[41]:(X=async()=>await t({to:`/iam/list-instance-profiles`}),e[40]=t,e[41]=X);let Z;e[42]!==y.isPending||e[43]!==E||e[44]!==X?(Z=(0,v.jsx)(d,{isPending:y.isPending,isSubmitting:E,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create Instance Profile`}),e[42]=y.isPending,e[43]=E,e[44]=X,e[45]=Z):Z=e[45];let Q;e[46]!==H||e[47]!==q||e[48]!==Y||e[49]!==Z||e[50]!==I?(Q=(0,v.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:I,children:[H,q,Y,Z]}),e[46]=H,e[47]=q,e[48]=Y,e[49]=Z,e[50]=I,e[51]=Q):Q=e[51];let $;return e[52]!==Q||e[53]!==F?($=(0,v.jsxs)(v.Fragment,{children:[N,P,F,Q]}),e[52]=Q,e[53]=F,e[54]=$):$=e[54],$}function ee(e){let t=e(`instanceProfileName`),n=typeof t==`string`?t:``,r=e(`path`),i=typeof r==`string`?r:``,a=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-instance-profile`},{type:`flag`,value:` --instance-profile-name`},{type:`value`,value:` ${n||``}`}];return i&&i!==`/`&&a.push({type:`flag`,value:` --path`},{type:`value`,value:` ${i}`}),[{label:`Create Instance Profile`,parts:a}]}export{y as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-internet-gateway.js b/spinifex/services/spinifexui/frontend/dist/assets/create-internet-gateway.js index aade829a6..df93cc994 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-internet-gateway.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-internet-gateway.js @@ -1,2 +1,2 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{t as n}from"./cli-command-panel.js";import{d as r}from"./ec2.js";import{Fo as i,Ho as a,Ro as o,Xi as s}from"./index.js";import{a as c,i as l,t as u}from"./zod.js";import{t as d}from"./form-actions.js";import{t as f}from"./page-heading.js";import{a as p,t as m}from"./field.js";import{a as h}from"./ec22.js";var g=i(),_=a();function v(){let i=(0,g.c)(36),a=o(),v=r(),b;i[0]===Symbol.for(`react.memo_cache_sentinel`)?(b={resolver:u(h),defaultValues:{name:``}},i[0]=b):b=i[0];let{control:x,handleSubmit:S,register:C,formState:w}=l(b),{isSubmitting:T}=w,E;i[1]===x?E=i[2]:(E={control:x},i[1]=x,i[2]=E);let D=c(E),O;i[3]!==v||i[4]!==a?(O=async e=>{let t=(await v.mutateAsync({name:e.name?.trim()??void 0})).InternetGateway?.InternetGatewayId;a(t?{to:`/ec2/describe-internet-gateways/$id`,params:{id:t}}:{to:`/ec2/describe-internet-gateways`})},i[3]=v,i[4]=a,i[5]=O):O=i[5];let k=O,A,j;i[6]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,_.jsx)(t,{to:`/ec2/describe-internet-gateways`,children:`Back to Internet Gateways`}),j=(0,_.jsx)(f,{title:`Create Internet Gateway`}),i[6]=A,i[7]=j):(A=i[6],j=i[7]);let M;i[8]===v.error?M=i[9]:(M=v.error&&(0,_.jsx)(e,{error:v.error,msg:`Failed to create Internet Gateway`}),i[8]=v.error,i[9]=M);let N;i[10]!==S||i[11]!==k?(N=S(k),i[10]=S,i[11]=k,i[12]=N):N=i[12];let P;i[13]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,_.jsx)(p,{children:(0,_.jsx)(`label`,{htmlFor:`name`,children:`Name (optional)`})}),i[13]=P):P=i[13];let F;i[14]===C?F=i[15]:(F=C(`name`),i[14]=C,i[15]=F);let I;i[16]===F?I=i[17]:(I=(0,_.jsxs)(m,{children:[P,(0,_.jsx)(s,{id:`name`,placeholder:`my-internet-gateway`,...F})]}),i[16]=F,i[17]=I);let L=D.name??``,R;i[18]===L?R=i[19]:(R=y(L),i[18]=L,i[19]=R);let z;i[20]===R?z=i[21]:(z=(0,_.jsx)(n,{commands:R}),i[20]=R,i[21]=z);let B;i[22]===a?B=i[23]:(B=async()=>await a({to:`/ec2/describe-internet-gateways`}),i[22]=a,i[23]=B);let V;i[24]!==v.isPending||i[25]!==T||i[26]!==B?(V=(0,_.jsx)(d,{isPending:v.isPending,isSubmitting:T,onCancel:B,pendingLabel:`Creating…`,submitLabel:`Create`}),i[24]=v.isPending,i[25]=T,i[26]=B,i[27]=V):V=i[27];let H;i[28]!==I||i[29]!==z||i[30]!==V||i[31]!==N?(H=(0,_.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:N,children:[I,z,V]}),i[28]=I,i[29]=z,i[30]=V,i[31]=N,i[32]=H):H=i[32];let U;return i[33]!==H||i[34]!==M?(U=(0,_.jsxs)(_.Fragment,{children:[A,j,M,H]}),i[33]=H,i[34]=M,i[35]=U):U=i[35],U}function y(e){let t=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-internet-gateway`}];return e.trim()&&t.push({type:`flag`,value:` \\ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{f as a}from"./ec2.js";import{Ir as o,Rn as s}from"./index.js";import{a as c,i as l,t as u}from"./zod.js";import{t as d}from"./form-actions.js";import{t as f}from"./page-heading.js";import{a as p,t as m}from"./field.js";import{a as h}from"./ec22.js";var g=t(),_=e();function v(){let e=(0,g.c)(36),t=o(),v=a(),b;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(b={resolver:u(h),defaultValues:{name:``}},e[0]=b):b=e[0];let{control:x,handleSubmit:S,register:C,formState:w}=l(b),{isSubmitting:T}=w,E;e[1]===x?E=e[2]:(E={control:x},e[1]=x,e[2]=E);let D=c(E),O;e[3]!==v||e[4]!==t?(O=async e=>{let n=(await v.mutateAsync({name:e.name?.trim()??void 0})).InternetGateway?.InternetGatewayId;t(n?{to:`/ec2/describe-internet-gateways/$id`,params:{id:n}}:{to:`/ec2/describe-internet-gateways`})},e[3]=v,e[4]=t,e[5]=O):O=e[5];let k=O,A,j;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,_.jsx)(r,{to:`/ec2/describe-internet-gateways`,children:`Back to Internet Gateways`}),j=(0,_.jsx)(f,{title:`Create Internet Gateway`}),e[6]=A,e[7]=j):(A=e[6],j=e[7]);let M;e[8]===v.error?M=e[9]:(M=v.error&&(0,_.jsx)(n,{error:v.error,msg:`Failed to create Internet Gateway`}),e[8]=v.error,e[9]=M);let N;e[10]!==S||e[11]!==k?(N=S(k),e[10]=S,e[11]=k,e[12]=N):N=e[12];let P;e[13]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,_.jsx)(p,{children:(0,_.jsx)(`label`,{htmlFor:`name`,children:`Name (optional)`})}),e[13]=P):P=e[13];let F;e[14]===C?F=e[15]:(F=C(`name`),e[14]=C,e[15]=F);let I;e[16]===F?I=e[17]:(I=(0,_.jsxs)(m,{children:[P,(0,_.jsx)(s,{id:`name`,placeholder:`my-internet-gateway`,...F})]}),e[16]=F,e[17]=I);let L=D.name??``,R;e[18]===L?R=e[19]:(R=y(L),e[18]=L,e[19]=R);let z;e[20]===R?z=e[21]:(z=(0,_.jsx)(i,{commands:R}),e[20]=R,e[21]=z);let B;e[22]===t?B=e[23]:(B=async()=>await t({to:`/ec2/describe-internet-gateways`}),e[22]=t,e[23]=B);let V;e[24]!==v.isPending||e[25]!==T||e[26]!==B?(V=(0,_.jsx)(d,{isPending:v.isPending,isSubmitting:T,onCancel:B,pendingLabel:`Creating…`,submitLabel:`Create`}),e[24]=v.isPending,e[25]=T,e[26]=B,e[27]=V):V=e[27];let H;e[28]!==I||e[29]!==z||e[30]!==V||e[31]!==N?(H=(0,_.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:N,children:[I,z,V]}),e[28]=I,e[29]=z,e[30]=V,e[31]=N,e[32]=H):H=e[32];let U;return e[33]!==H||e[34]!==M?(U=(0,_.jsxs)(_.Fragment,{children:[A,j,M,H]}),e[33]=H,e[34]=M,e[35]=U):U=e[35],U}function y(e){let t=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-internet-gateway`}];return e.trim()&&t.push({type:`flag`,value:` \\ --tag-specifications`},{type:`value`,value:` 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=${e.trim()}}]'`}),[{label:`Create Internet Gateway`,parts:t}]}export{v as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-key-pair.js b/spinifex/services/spinifexui/frontend/dist/assets/create-key-pair.js index 834ccbda7..5b713973b 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-key-pair.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-key-pair.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{t as n}from"./check.js";import{t as r}from"./download.js";import{t as i}from"./cli-command-panel.js";import{t as a}from"./triangle-alert.js";import{f as o}from"./ec2.js";import{Fo as s,Ho as c,Ro as l,Uo as u,Xa as d,Xi as f,cs as p}from"./index.js";import{i as m,t as h}from"./zod.js";import{t as g}from"./use-copy-to-clipboard.js";import{t as _}from"./form-actions.js";import{t as v}from"./page-heading.js";import{a as y,r as b,t as x}from"./field.js";import{o as S}from"./ec22.js";import{a as C,c as w,i as T,l as E,o as D,r as O,s as k,t as A}from"./alert-dialog.js";var j=p(u()),M=s(),N=c();function P(e){let t=(0,M.c)(38),{open:i,keyName:o,keyMaterial:s}=e,c=l(),{copied:u,copy:f}=g(),p;t[0]!==s||t[1]!==o?(p=()=>{let e=new Blob([s],{type:`text/plain`}),t=URL.createObjectURL(e),n=document.createElement(`a`);n.href=t,n.download=`${o}.pem`,n.click(),URL.revokeObjectURL(t)},t[0]=s,t[1]=o,t[2]=p):p=t[2];let m=p,h;t[3]===c?h=t[4]:(h=()=>{c({to:`/ec2/describe-key-pairs`})},t[3]=c,t[4]=h);let _=h,v,y;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(v=(0,N.jsx)(w,{children:(0,N.jsx)(a,{className:`text-destructive`})}),y=(0,N.jsx)(E,{children:`Save Your Private Key`}),t[5]=v,t[6]=y):(v=t[5],y=t[6]);let b;t[7]===o?b=t[8]:(b=(0,N.jsxs)(k,{children:[v,y,(0,N.jsxs)(C,{children:[`This is your only chance to save the private key file for`,` `,(0,N.jsx)(`strong`,{children:o}),`. Please download or copy it now. You won't be able to retrieve it again.`]})]}),t[7]=o,t[8]=b);let x=`Private key material for ${o}`,S;t[9]!==s||t[10]!==x?(S=(0,N.jsx)(`textarea`,{"aria-label":x,className:`w-full resize-none overflow-y-auto rounded-md border border-input bg-input/20 px-2 py-2 font-mono text-xs`,readOnly:!0,rows:4,value:s}),t[9]=s,t[10]=x,t[11]=S):S=t[11];let j;t[12]!==f||t[13]!==s?(j=async()=>await f(s),t[12]=f,t[13]=s,t[14]=j):j=t[14];let P;t[15]===u?P=t[16]:(P=u?(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(n,{className:`mr-2 size-4`}),`Copied!`]}):`Copy to Clipboard`,t[15]=u,t[16]=P);let F;t[17]!==j||t[18]!==P?(F=(0,N.jsx)(d,{className:`flex-1`,onClick:j,type:`button`,variant:`outline`,children:P}),t[17]=j,t[18]=P,t[19]=F):F=t[19];let I;t[20]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,N.jsx)(r,{className:`mr-2 size-4`}),t[20]=I):I=t[20];let L;t[21]===m?L=t[22]:(L=(0,N.jsxs)(d,{className:`flex-1`,onClick:m,type:`button`,variant:`outline`,children:[I,`Download .pem File`]}),t[21]=m,t[22]=L);let R;t[23]!==F||t[24]!==L?(R=(0,N.jsxs)(`div`,{className:`flex gap-2`,children:[F,L]}),t[23]=F,t[24]=L,t[25]=R):R=t[25];let z;t[26]!==R||t[27]!==S?(z=(0,N.jsxs)(`div`,{className:`space-y-3`,children:[S,R]}),t[26]=R,t[27]=S,t[28]=z):z=t[28];let B;t[29]===_?B=t[30]:(B=(0,N.jsx)(D,{children:(0,N.jsx)(O,{onClick:_,children:`Close and Continue`})}),t[29]=_,t[30]=B);let V;t[31]!==z||t[32]!==B||t[33]!==b?(V=(0,N.jsxs)(T,{className:`max-w-2xl`,children:[b,z,B]}),t[31]=z,t[32]=B,t[33]=b,t[34]=V):V=t[34];let H;return t[35]!==i||t[36]!==V?(H=(0,N.jsx)(A,{open:i,children:V}),t[35]=i,t[36]=V,t[37]=H):H=t[37],H}function F(){let n=(0,M.c)(42),r=l(),a=o(),[s,c]=(0,j.useState)(null),u;n[0]===Symbol.for(`react.memo_cache_sentinel`)?(u={resolver:h(S)},n[0]=u):u=n[0];let{handleSubmit:d,register:p,watch:g,formState:C}=m(u),{errors:w,isSubmitting:T}=C,E;n[1]===a?E=n[2]:(E=async e=>{let t=await a.mutateAsync(e);t.KeyMaterial&&c({keyName:e.keyName,material:t.KeyMaterial})},n[1]=a,n[2]=E);let D=E,O,k;n[3]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,N.jsx)(t,{to:`/ec2/describe-key-pairs`,children:`Back to key pairs`}),k=(0,N.jsx)(v,{title:`Create Key Pair`}),n[3]=O,n[4]=k):(O=n[3],k=n[4]);let A;n[5]===a.error?A=n[6]:(A=a.error&&(0,N.jsx)(e,{error:a.error,msg:`Failed to create key pair`}),n[5]=a.error,n[6]=A);let F;n[7]!==d||n[8]!==D?(F=d(D),n[7]=d,n[8]=D,n[9]=F):F=n[9];let L;n[10]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,N.jsx)(y,{children:(0,N.jsx)(`label`,{htmlFor:`keyName`,children:`Key Pair Name`})}),n[10]=L):L=n[10];let R=!!w.keyName,z;n[11]===p?z=n[12]:(z=p(`keyName`),n[11]=p,n[12]=z);let B;n[13]!==R||n[14]!==z?(B=(0,N.jsx)(f,{"aria-invalid":R,id:`keyName`,placeholder:`my-key-pair…`,...z}),n[13]=R,n[14]=z,n[15]=B):B=n[15];let V;n[16]===w.keyName?V=n[17]:(V=(0,N.jsx)(b,{errors:[w.keyName]}),n[16]=w.keyName,n[17]=V);let H;n[18]!==B||n[19]!==V?(H=(0,N.jsxs)(x,{children:[L,B,V]}),n[18]=B,n[19]=V,n[20]=H):H=n[20];let U;n[21]===g?U=n[22]:(U=I(g),n[21]=g,n[22]=U);let W;n[23]===U?W=n[24]:(W=(0,N.jsx)(i,{commands:U}),n[23]=U,n[24]=W);let G;n[25]===r?G=n[26]:(G=async()=>await r({to:`/ec2/describe-key-pairs`}),n[25]=r,n[26]=G);let K;n[27]!==a.isPending||n[28]!==T||n[29]!==G?(K=(0,N.jsx)(_,{isPending:a.isPending,isSubmitting:T,onCancel:G,pendingLabel:`Creating…`,submitLabel:`Create Key Pair`}),n[27]=a.isPending,n[28]=T,n[29]=G,n[30]=K):K=n[30];let q;n[31]!==H||n[32]!==W||n[33]!==K||n[34]!==F?(q=(0,N.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:F,children:[H,W,K]}),n[31]=H,n[32]=W,n[33]=K,n[34]=F,n[35]=q):q=n[35];let J;n[36]===s?J=n[37]:(J=s&&(0,N.jsx)(P,{keyMaterial:s.material,keyName:s.keyName,open:!!s}),n[36]=s,n[37]=J);let Y;return n[38]!==q||n[39]!==J||n[40]!==A?(Y=(0,N.jsxs)(N.Fragment,{children:[O,k,A,q,J]}),n[38]=q,n[39]=J,n[40]=A,n[41]=Y):Y=n[41],Y}function I(e){let t=e(`keyName`);return[{label:`Create Key Pair`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-key-pair`},{type:`flag`,value:` --key-name`},{type:`value`,value:` ${(typeof t==`string`?t:``)||``}`}]}]}export{F as component}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{t as a}from"./back-link.js";import{t as o}from"./check.js";import{t as s}from"./download.js";import{t as c}from"./cli-command-panel.js";import{t as l}from"./triangle-alert.js";import{t as u}from"./button.js";import{p as d}from"./ec2.js";import{Ir as f,Rn as p}from"./index.js";import{a as m,i as h,t as g}from"./zod.js";import{t as _}from"./use-copy-to-clipboard.js";import{t as v}from"./form-actions.js";import{t as y}from"./page-heading.js";import{a as b,r as x,t as S}from"./field.js";import{o as C}from"./ec22.js";import{a as ee,c as w,i as T,l as E,o as D,r as O,s as k,t as A}from"./alert-dialog.js";var j=t(n()),M=r(),N=e();function P(e){let t=(0,M.c)(38),{open:n,keyName:r,keyMaterial:i}=e,a=f(),{copied:c,copy:d}=_(),p;t[0]!==i||t[1]!==r?(p=()=>{let e=new Blob([i],{type:`text/plain`}),t=URL.createObjectURL(e),n=document.createElement(`a`);n.href=t,n.download=`${r}.pem`,n.click(),URL.revokeObjectURL(t)},t[0]=i,t[1]=r,t[2]=p):p=t[2];let m=p,h;t[3]===a?h=t[4]:(h=()=>{a({to:`/ec2/describe-key-pairs`})},t[3]=a,t[4]=h);let g=h,v,y;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(v=(0,N.jsx)(w,{children:(0,N.jsx)(l,{className:`text-destructive`})}),y=(0,N.jsx)(E,{children:`Save Your Private Key`}),t[5]=v,t[6]=y):(v=t[5],y=t[6]);let b;t[7]===r?b=t[8]:(b=(0,N.jsxs)(k,{children:[v,y,(0,N.jsxs)(ee,{children:[`This is your only chance to save the private key file for`,` `,(0,N.jsx)(`strong`,{children:r}),`. Please download or copy it now. You won't be able to retrieve it again.`]})]}),t[7]=r,t[8]=b);let x=`Private key material for ${r}`,S;t[9]!==i||t[10]!==x?(S=(0,N.jsx)(`textarea`,{"aria-label":x,className:`w-full resize-none overflow-y-auto rounded-md border border-input bg-input/20 px-2 py-2 font-mono text-xs`,readOnly:!0,rows:4,value:i}),t[9]=i,t[10]=x,t[11]=S):S=t[11];let C;t[12]!==d||t[13]!==i?(C=async()=>await d(i),t[12]=d,t[13]=i,t[14]=C):C=t[14];let j;t[15]===c?j=t[16]:(j=c?(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(o,{className:`mr-2 size-4`}),`Copied!`]}):`Copy to Clipboard`,t[15]=c,t[16]=j);let P;t[17]!==C||t[18]!==j?(P=(0,N.jsx)(u,{className:`flex-1`,onClick:C,type:`button`,variant:`outline`,children:j}),t[17]=C,t[18]=j,t[19]=P):P=t[19];let F;t[20]===Symbol.for(`react.memo_cache_sentinel`)?(F=(0,N.jsx)(s,{className:`mr-2 size-4`}),t[20]=F):F=t[20];let I;t[21]===m?I=t[22]:(I=(0,N.jsxs)(u,{className:`flex-1`,onClick:m,type:`button`,variant:`outline`,children:[F,`Download .pem File`]}),t[21]=m,t[22]=I);let L;t[23]!==P||t[24]!==I?(L=(0,N.jsxs)(`div`,{className:`flex gap-2`,children:[P,I]}),t[23]=P,t[24]=I,t[25]=L):L=t[25];let R;t[26]!==L||t[27]!==S?(R=(0,N.jsxs)(`div`,{className:`space-y-3`,children:[S,L]}),t[26]=L,t[27]=S,t[28]=R):R=t[28];let z;t[29]===g?z=t[30]:(z=(0,N.jsx)(D,{children:(0,N.jsx)(O,{onClick:g,children:`Close and Continue`})}),t[29]=g,t[30]=z);let B;t[31]!==R||t[32]!==z||t[33]!==b?(B=(0,N.jsxs)(T,{className:`max-w-2xl`,children:[b,R,z]}),t[31]=R,t[32]=z,t[33]=b,t[34]=B):B=t[34];let V;return t[35]!==n||t[36]!==B?(V=(0,N.jsx)(A,{open:n,children:B}),t[35]=n,t[36]=B,t[37]=V):V=t[37],V}function F(){let e=(0,M.c)(46),t=f(),n=d(),[r,o]=(0,j.useState)(null),s;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(s={resolver:g(C)},e[0]=s):s=e[0];let{handleSubmit:l,register:u,control:_,formState:ee}=h(s),{errors:w,isSubmitting:T}=ee,E;e[1]===_?E=e[2]:(E={control:_},e[1]=_,e[2]=E);let D=m(E),O;e[3]===D?O=e[4]:(O=e=>e?D[e]:void 0,e[3]=D,e[4]=O);let k=O,A;e[5]===n?A=e[6]:(A=async e=>{let t=await n.mutateAsync(e);t.KeyMaterial&&o({keyName:e.keyName,material:t.KeyMaterial})},e[5]=n,e[6]=A);let F=A,L,R;e[7]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,N.jsx)(a,{to:`/ec2/describe-key-pairs`,children:`Back to key pairs`}),R=(0,N.jsx)(y,{title:`Create Key Pair`}),e[7]=L,e[8]=R):(L=e[7],R=e[8]);let z;e[9]===n.error?z=e[10]:(z=n.error&&(0,N.jsx)(i,{error:n.error,msg:`Failed to create key pair`}),e[9]=n.error,e[10]=z);let B;e[11]!==l||e[12]!==F?(B=l(F),e[11]=l,e[12]=F,e[13]=B):B=e[13];let V;e[14]===Symbol.for(`react.memo_cache_sentinel`)?(V=(0,N.jsx)(b,{children:(0,N.jsx)(`label`,{htmlFor:`keyName`,children:`Key Pair Name`})}),e[14]=V):V=e[14];let H=!!w.keyName,U;e[15]===u?U=e[16]:(U=u(`keyName`),e[15]=u,e[16]=U);let W;e[17]!==H||e[18]!==U?(W=(0,N.jsx)(p,{"aria-invalid":H,id:`keyName`,placeholder:`my-key-pair…`,...U}),e[17]=H,e[18]=U,e[19]=W):W=e[19];let G;e[20]===w.keyName?G=e[21]:(G=(0,N.jsx)(x,{errors:[w.keyName]}),e[20]=w.keyName,e[21]=G);let K;e[22]!==W||e[23]!==G?(K=(0,N.jsxs)(S,{children:[V,W,G]}),e[22]=W,e[23]=G,e[24]=K):K=e[24];let q;e[25]===k?q=e[26]:(q=I(k),e[25]=k,e[26]=q);let J;e[27]===q?J=e[28]:(J=(0,N.jsx)(c,{commands:q}),e[27]=q,e[28]=J);let Y;e[29]===t?Y=e[30]:(Y=async()=>await t({to:`/ec2/describe-key-pairs`}),e[29]=t,e[30]=Y);let X;e[31]!==n.isPending||e[32]!==T||e[33]!==Y?(X=(0,N.jsx)(v,{isPending:n.isPending,isSubmitting:T,onCancel:Y,pendingLabel:`Creating…`,submitLabel:`Create Key Pair`}),e[31]=n.isPending,e[32]=T,e[33]=Y,e[34]=X):X=e[34];let Z;e[35]!==K||e[36]!==J||e[37]!==X||e[38]!==B?(Z=(0,N.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:B,children:[K,J,X]}),e[35]=K,e[36]=J,e[37]=X,e[38]=B,e[39]=Z):Z=e[39];let Q;e[40]===r?Q=e[41]:(Q=r&&(0,N.jsx)(P,{keyMaterial:r.material,keyName:r.keyName,open:!!r}),e[40]=r,e[41]=Q);let $;return e[42]!==Z||e[43]!==Q||e[44]!==z?($=(0,N.jsxs)(N.Fragment,{children:[L,R,z,Z,Q]}),e[42]=Z,e[43]=Q,e[44]=z,e[45]=$):$=e[45],$}function I(e){let t=e(`keyName`);return[{label:`Create Key Pair`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-key-pair`},{type:`flag`,value:` --key-name`},{type:`value`,value:` ${(typeof t==`string`?t:``)||``}`}]}]}export{F as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-load-balancer.js b/spinifex/services/spinifexui/frontend/dist/assets/create-load-balancer.js index e2dfc7de5..dda7d0de3 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-load-balancer.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-load-balancer.js @@ -1,17 +1,21 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as a,s as o,t as s}from"./select.js";import{t as c}from"./plus.js";import{t as l}from"./cli-command-panel.js";import{t as u}from"./trash-2.js";import{n as d}from"./elbv2.js";import{Ho as f,I as p,Ro as m,Uo as h,Xa as g,Xi as _,ao as v,cs as y,dt as ee,ht as te,st as ne}from"./index.js";import{i as b,n as x,t as S}from"./zod.js";import{t as C}from"./form-actions.js";import{t as re}from"./page-heading.js";import{a as w,r as T,t as E}from"./field.js";import{t as ie}from"./target-group-form.js";import{i as ae,n as D}from"./elbv22.js";var O=y(h(),1),k=f();function A(e){let t=v(e.Tags);return t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`}function j(e){let t=v(e.Tags);return`${t?`${e.SubnetId} (${t})`:e.SubnetId} · ${e.CidrBlock}`}function oe(e){let t=new Map;for(let n of e){let e=n.AvailabilityZone??`unknown`,r=t.get(e)??[];r.push(n),t.set(e,r)}return[...t.entries()].toSorted((e,t)=>e[0].localeCompare(t[0])).map(([e,t])=>({az:e,subnets:t}))}function M(){let f=m(),{data:h}=t(te),{data:v}=t(ee),{data:y}=t(ne),{data:M}=t(p),N=d(),[P,F]=(0,O.useState)(null),I=h.Vpcs??[],ce=v.Subnets??[],L=y.SecurityGroups??[],R=M.TargetGroups??[],z=b({resolver:S(D),defaultValues:{name:``,scheme:`internet-facing`,vpcId:I[0]?.VpcId??``,subnetIds:[],securityGroupIds:[],tags:[],listener:{protocol:`HTTP`,port:80,targetGroupMode:`new`,existingTargetGroupArn:``}}}),B=b({resolver:S(ae),defaultValues:{name:``,protocol:`HTTP`,port:80,vpcId:I[0]?.VpcId??``,healthCheck:{protocol:`HTTP`,path:`/`,port:`traffic-port`,intervalSeconds:30,timeoutSeconds:5,healthyThresholdCount:5,unhealthyThresholdCount:2,matcher:`200`},tags:[]}}),{handleSubmit:V,register:H,watch:U,control:W,setValue:G,getValues:K,formState:{errors:q,isSubmitting:le}}=z,J=U(`vpcId`),ue=U(`subnetIds`),de=U(`securityGroupIds`),Y=U(`listener.targetGroupMode`),fe=U(`tags`),X=ce.filter(e=>e.VpcId===J),Z=L.filter(e=>e.VpcId===J),Q=R.filter(e=>e.VpcId===J),pe=(e=``)=>{let t=e??``;G(`vpcId`,t,{shouldValidate:!0}),G(`subnetIds`,[],{shouldValidate:!0}),G(`securityGroupIds`,[]),G(`listener.existingTargetGroupArn`,``),B.setValue(`vpcId`,t)},me=e=>{let t=K(`subnetIds`);G(`subnetIds`,t.includes(e)?t.filter(t=>t!==e):[...t,e],{shouldValidate:!0})},he=e=>{let t=K(`securityGroupIds`);G(`securityGroupIds`,t.includes(e)?t.filter(t=>t!==e):[...t,e])},ge=async e=>{F(null);let t;if(e.listener.targetGroupMode===`new`){if(!await B.trigger())return;t=B.getValues()}let n=await N.mutateAsync({lb:{name:e.name,scheme:e.scheme,vpcId:e.vpcId,subnetIds:e.subnetIds,securityGroupIds:e.securityGroupIds,tags:e.tags},listener:{protocol:e.listener.protocol,port:e.listener.port,targetGroupMode:e.listener.targetGroupMode,existingTargetGroupArn:e.listener.existingTargetGroupArn,newTargetGroup:t}});F(n),!n.error&&n.loadBalancerArn&&f({to:`/ec2/describe-load-balancers/$id`,params:{id:encodeURIComponent(n.loadBalancerArn)}})},$=oe(X);return(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(n,{to:`/ec2/describe-load-balancers`,children:`Back to load balancers`}),(0,k.jsx)(re,{title:`Create Load Balancer`}),N.error&&(0,k.jsx)(e,{error:N.error,msg:`Failed to create load balancer`}),P?.error&&(0,k.jsxs)(`div`,{className:`mb-6 max-w-4xl rounded-md border border-destructive bg-destructive/10 p-4`,children:[(0,k.jsxs)(`h2`,{className:`text-sm font-medium text-destructive`,children:[`Wizard failed: `,P.failedStep]}),(0,k.jsx)(`p`,{className:`mt-1 text-sm text-destructive`,children:P.error.message}),P.created.length>0&&(0,k.jsxs)(`div`,{className:`mt-3`,children:[(0,k.jsx)(`p`,{className:`text-xs font-medium text-destructive`,children:`Successfully created resources (clean up manually if needed):`}),(0,k.jsx)(`ul`,{className:`mt-1 list-inside list-disc text-xs text-destructive`,children:P.created.map((e,t)=>(0,k.jsxs)(`li`,{children:[e.type,`: `,e.id??`(created)`]},t))})]})]}),(0,k.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:V(ge),children:[(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:(0,k.jsx)(`label`,{htmlFor:`lb-name`,children:`Name`})}),(0,k.jsx)(_,{"aria-invalid":!!q.name,id:`lb-name`,placeholder:`my-load-balancer`,...H(`name`)}),(0,k.jsx)(T,{errors:[q.name]})]}),(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:`Scheme`}),(0,k.jsx)(x,{control:W,name:`scheme`,render:({field:e})=>(0,k.jsxs)(`div`,{className:`flex gap-4`,children:[(0,k.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,k.jsx)(`input`,{"aria-label":`Internet-facing`,checked:e.value===`internet-facing`,name:`scheme`,onChange:()=>e.onChange(`internet-facing`),type:`radio`}),`Internet-facing`]}),(0,k.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,k.jsx)(`input`,{"aria-label":`Internal`,checked:e.value===`internal`,name:`scheme`,onChange:()=>e.onChange(`internal`),type:`radio`}),`Internal`]})]})})]}),(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:(0,k.jsx)(`label`,{htmlFor:`lb-vpc`,children:`VPC`})}),(0,k.jsx)(x,{control:W,name:`vpcId`,render:({field:e})=>(0,k.jsxs)(s,{onValueChange:e=>pe(e),value:e.value??``,children:[(0,k.jsx)(o,{"aria-invalid":!!q.vpcId,className:`w-full`,id:`lb-vpc`,children:(0,k.jsx)(r,{placeholder:`Select VPC`})}),(0,k.jsx)(a,{children:I.map(e=>(0,k.jsx)(i,{value:e.VpcId??``,children:A(e)},e.VpcId))})]})}),(0,k.jsx)(T,{errors:[q.vpcId]})]}),(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:`Subnets (select at least 2)`}),$.length===0?(0,k.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No subnets in the selected VPC.`}):(0,k.jsx)(`div`,{className:`space-y-3`,children:$.map(e=>(0,k.jsxs)(`div`,{children:[(0,k.jsx)(`p`,{className:`mb-1 text-xs font-medium text-muted-foreground`,children:e.az}),(0,k.jsx)(`div`,{className:`space-y-1`,children:e.subnets.map(e=>(0,k.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,k.jsx)(`input`,{"aria-label":`Subnet ${j(e)}`,checked:ue.includes(e.SubnetId??``),onChange:()=>me(e.SubnetId??``),type:`checkbox`}),(0,k.jsx)(`span`,{className:`font-mono`,children:j(e)})]},e.SubnetId))})]},e.az))}),(0,k.jsx)(T,{errors:[q.subnetIds]})]}),(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:`Security groups`}),Z.length===0?(0,k.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No security groups in the selected VPC.`}):(0,k.jsx)(`div`,{className:`space-y-1`,children:Z.map(e=>(0,k.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,k.jsx)(`input`,{"aria-label":`Security group ${e.GroupId} (${e.GroupName})`,checked:de.includes(e.GroupId??``),onChange:()=>he(e.GroupId??``),type:`checkbox`}),(0,k.jsxs)(`span`,{className:`font-mono`,children:[e.GroupId,` (`,e.GroupName,`)`]})]},e.GroupId))})]}),(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:`Tags`}),(0,k.jsxs)(`div`,{className:`space-y-2`,children:[fe.map((e,t)=>(0,k.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,k.jsx)(_,{placeholder:`Key`,...H(`tags.${t}.key`)}),(0,k.jsx)(_,{placeholder:`Value`,...H(`tags.${t}.value`)}),(0,k.jsx)(g,{onClick:()=>G(`tags`,K(`tags`).filter((e,n)=>n!==t)),size:`icon`,type:`button`,variant:`ghost`,children:(0,k.jsx)(u,{className:`size-3.5`})})]},t)),(0,k.jsxs)(g,{onClick:()=>G(`tags`,[...K(`tags`),{key:``,value:``}]),size:`sm`,type:`button`,variant:`outline`,children:[(0,k.jsx)(c,{className:`size-3.5`}),`Add tag`]})]})]}),(0,k.jsxs)(`div`,{className:`space-y-4 rounded-md border bg-card p-4`,children:[(0,k.jsx)(`h2`,{className:`text-sm font-semibold`,children:`First listener & default target group`}),(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:(0,k.jsx)(`label`,{htmlFor:`listener-protocol`,children:`Protocol`})}),(0,k.jsx)(x,{control:W,name:`listener.protocol`,render:({field:e})=>(0,k.jsxs)(s,{onValueChange:e.onChange,value:e.value,children:[(0,k.jsx)(o,{className:`w-full`,id:`listener-protocol`,children:(0,k.jsx)(r,{})}),(0,k.jsx)(a,{children:(0,k.jsx)(i,{value:`HTTP`,children:`HTTP`})})]})})]}),(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:(0,k.jsx)(`label`,{htmlFor:`listener-port`,children:`Port`})}),(0,k.jsx)(_,{"aria-invalid":!!q.listener?.port,id:`listener-port`,inputMode:`numeric`,type:`number`,...H(`listener.port`,{valueAsNumber:!0})}),(0,k.jsx)(T,{errors:[q.listener?.port]})]}),(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:`Default target group`}),(0,k.jsx)(x,{control:W,name:`listener.targetGroupMode`,render:({field:e})=>(0,k.jsxs)(`div`,{className:`flex gap-4`,children:[(0,k.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,k.jsx)(`input`,{"aria-label":`Create new target group`,checked:e.value===`new`,name:`tg-mode`,onChange:()=>e.onChange(`new`),type:`radio`}),`Create new`]}),(0,k.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,k.jsx)(`input`,{"aria-label":`Use existing target group`,checked:e.value===`existing`,name:`tg-mode`,onChange:()=>e.onChange(`existing`),type:`radio`}),`Use existing`]})]})})]}),Y===`existing`&&(0,k.jsxs)(E,{children:[(0,k.jsx)(w,{children:(0,k.jsx)(`label`,{htmlFor:`tg-existing`,children:`Target group`})}),(0,k.jsx)(x,{control:W,name:`listener.existingTargetGroupArn`,render:({field:e})=>(0,k.jsxs)(s,{onValueChange:e.onChange,value:e.value??``,children:[(0,k.jsx)(o,{"aria-invalid":!!q.listener?.existingTargetGroupArn,className:`w-full`,id:`tg-existing`,children:(0,k.jsx)(r,{placeholder:`Select target group`})}),(0,k.jsx)(a,{children:Q.map(e=>(0,k.jsxs)(i,{value:e.TargetGroupArn??``,children:[e.TargetGroupName,` · `,e.Protocol,`:`,e.Port]},e.TargetGroupArn))})]})}),(0,k.jsx)(T,{errors:[q.listener?.existingTargetGroupArn]}),Q.length===0&&(0,k.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`No target groups in this VPC. Choose “Create new” instead.`})]}),Y===`new`&&(0,k.jsx)(`div`,{className:`space-y-4 border-l-2 border-muted pl-4`,children:(0,k.jsx)(ie,{form:B,vpcs:I})})]}),(0,k.jsx)(l,{commands:se(U,B.watch)}),(0,k.jsx)(C,{isPending:N.isPending,isSubmitting:le,onCancel:async()=>await f({to:`/ec2/describe-load-balancers`}),pendingLabel:`Creating…`,submitLabel:`Create Load Balancer`})]})]})}function se(e,t){let n=t=>{let n=e(t);return typeof n==`string`?n:``},r=t=>{let n=e(t);return Array.isArray(n)?n.filter(e=>typeof e==`string`):[]},i=t=>{let n=e(t);return typeof n==`number`&&Number.isFinite(n)?n:void 0},a=n(`name`)||``,o=n(`scheme`)||`internet-facing`,s=r(`subnetIds`),c=r(`securityGroupIds`),l=n(`listener.protocol`)||`HTTP`,u=i(`listener.port`)??80,d=n(`listener.targetGroupMode`),f=n(`listener.existingTargetGroupArn`),p=[],m={type:`comment`,value:`# Create ALB with listener and default target group +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{A as a}from"./SignatureV4.js";import{t as o}from"./back-link.js";import{c as s,i as c,n as l,s as u,t as d}from"./select.js";import{t as f}from"./plus.js";import{t as p}from"./cli-command-panel.js";import{t as ee}from"./trash-2.js";import{t as m}from"./button.js";import{t as h}from"./certificate-import-dialog.js";import{n as g}from"./elbv2.js";import{G as _,Ir as v,Lt as y,Nt as b,Rn as x,U as te,kt as ne,z as re}from"./index.js";import{i as S,n as C,t as w}from"./zod.js";import{t as ie}from"./form-actions.js";import{t as ae}from"./page-heading.js";import{a as T,r as E,t as D}from"./field.js";import{t as oe}from"./target-group-form.js";import{a as se,c as ce,o as le,r as ue,s as O,t as de}from"./elbv22.js";var k=t(n(),1),A=e();function fe(e){let t=a(e.Tags);return t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`}function j(e){let t=a(e.Tags);return`${t?`${e.SubnetId} (${t})`:e.SubnetId} · ${e.CidrBlock}`}function pe(e){return e===`TLS`||e===`TCP`?`TCP`:e===`UDP`?`UDP`:e===`TCP_UDP`?`TCP_UDP`:`HTTP`}function me(e){let t=new Map;for(let n of e){let e=n.AvailabilityZone??`unknown`,r=t.get(e)??[];r.push(n),t.set(e,r)}return[...t.entries()].toSorted((e,t)=>e[0].localeCompare(t[0])).map(([e,t])=>({az:e,subnets:t}))}function M(){let e=v(),{data:t}=i(y),{data:n}=i(b),{data:a}=i(ne),{data:M}=i(te),{data:N}=i(_),{data:ge}=i(re),P=g(),[F,I]=(0,k.useState)(null),[_e,L]=(0,k.useState)(!1),R=t.Vpcs??[],ve=n.Subnets??[],ye=a.SecurityGroups??[],be=M.TargetGroups??[],z=N.CertificateSummaryList??[],xe=ge.SslPolicies??[],Se=S({resolver:w(ue),defaultValues:{name:``,type:`application`,scheme:`internet-facing`,vpcId:R[0]?.VpcId??``,subnetIds:[],securityGroupIds:[],tags:[],listener:{protocol:`HTTP`,port:80,targetGroupMode:`new`,existingTargetGroupArn:``,certificateArn:void 0,sslPolicy:void 0}}}),B=S({resolver:w(se),defaultValues:{name:``,protocol:`HTTP`,port:80,vpcId:R[0]?.VpcId??``,healthCheck:{protocol:`HTTP`,path:`/`,port:`traffic-port`,intervalSeconds:30,timeoutSeconds:5,healthyThresholdCount:5,unhealthyThresholdCount:2,matcher:`200`},tags:[]}}),{handleSubmit:Ce,register:V,watch:H,control:U,setValue:W,getValues:G,formState:{errors:K,isSubmitting:we}}=Se,q=H(`vpcId`),Te=H(`subnetIds`),Ee=H(`securityGroupIds`),J=H(`type`),Y=H(`listener.protocol`),X=H(`listener.targetGroupMode`),De=H(`tags`),Z=J===`network`,Oe=O(Y),ke=ce(J),Ae=Z?[`TCP`,`UDP`,`TLS`,`TCP_UDP`]:[`HTTP`,`HTTPS`],je=ve.filter(e=>e.VpcId===q),Q=ye.filter(e=>e.VpcId===q),$=be.filter(e=>e.VpcId===q),Me=e=>{let t=pe(e);B.setValue(`protocol`,t),B.setValue(`healthCheck.protocol`,t===`HTTP`?`HTTP`:`TCP`)},Ne=e=>{W(`type`,e);let t=le(e);W(`listener.protocol`,t),W(`listener.port`,80),W(`listener.certificateArn`,void 0),W(`listener.sslPolicy`,void 0),e===`network`&&W(`securityGroupIds`,[]),Me(t)},Pe=e=>{W(`listener.protocol`,e);let t=G(`listener.port`);O(e)?(t===80&&W(`listener.port`,443),e===`HTTPS`&&!G(`listener.sslPolicy`)&&W(`listener.sslPolicy`,de)):(t===443&&W(`listener.port`,80),W(`listener.certificateArn`,void 0),W(`listener.sslPolicy`,void 0)),Me(e)},Fe=(e=``)=>{let t=e??``;W(`vpcId`,t,{shouldValidate:!0}),W(`subnetIds`,[],{shouldValidate:!0}),W(`securityGroupIds`,[]),W(`listener.existingTargetGroupArn`,``),B.setValue(`vpcId`,t)},Ie=e=>{let t=G(`subnetIds`);W(`subnetIds`,t.includes(e)?t.filter(t=>t!==e):[...t,e],{shouldValidate:!0})},Le=e=>{let t=G(`securityGroupIds`);W(`securityGroupIds`,t.includes(e)?t.filter(t=>t!==e):[...t,e])},Re=async t=>{I(null);let n;if(t.listener.targetGroupMode===`new`){if(!await B.trigger())return;n=B.getValues()}let r=await P.mutateAsync({lb:{name:t.name,type:t.type,scheme:t.scheme,vpcId:t.vpcId,subnetIds:t.subnetIds,securityGroupIds:t.securityGroupIds,tags:t.tags},listener:{protocol:t.listener.protocol,port:t.listener.port,targetGroupMode:t.listener.targetGroupMode,existingTargetGroupArn:t.listener.existingTargetGroupArn,certificateArn:t.listener.certificateArn,sslPolicy:t.listener.sslPolicy,newTargetGroup:n}});I(r),!r.error&&r.loadBalancerArn&&e({to:`/ec2/describe-load-balancers/$id`,params:{id:encodeURIComponent(r.loadBalancerArn)}})},ze=me(je);return(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(o,{to:`/ec2/describe-load-balancers`,children:`Back to load balancers`}),(0,A.jsx)(ae,{title:`Create Load Balancer`}),P.error&&(0,A.jsx)(r,{error:P.error,msg:`Failed to create load balancer`}),F?.error&&(0,A.jsxs)(`div`,{className:`mb-6 max-w-4xl rounded-md border border-destructive bg-destructive/10 p-4`,children:[(0,A.jsxs)(`h2`,{className:`text-sm font-medium text-destructive`,children:[`Wizard failed: `,F.failedStep]}),(0,A.jsx)(`p`,{className:`mt-1 text-sm text-destructive`,children:F.error.message}),F.created.length>0&&(0,A.jsxs)(`div`,{className:`mt-3`,children:[(0,A.jsx)(`p`,{className:`text-xs font-medium text-destructive`,children:`Successfully created resources (clean up manually if needed):`}),(0,A.jsx)(`ul`,{className:`mt-1 list-inside list-disc text-xs text-destructive`,children:F.created.map((e,t)=>(0,A.jsxs)(`li`,{children:[e.type,`: `,e.id??`(created)`]},t))})]})]}),(0,A.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:Ce(Re),children:[(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:(0,A.jsx)(`label`,{htmlFor:`lb-name`,children:`Name`})}),(0,A.jsx)(x,{"aria-invalid":!!K.name,id:`lb-name`,placeholder:`my-load-balancer`,...V(`name`)}),(0,A.jsx)(E,{errors:[K.name]})]}),(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:`Type`}),(0,A.jsx)(C,{control:U,name:`type`,render:({field:e})=>(0,A.jsxs)(`div`,{className:`flex gap-4`,children:[(0,A.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,A.jsx)(`input`,{"aria-label":`Application Load Balancer`,checked:e.value===`application`,name:`lb-type`,onChange:()=>Ne(`application`),type:`radio`}),`Application (ALB)`]}),(0,A.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,A.jsx)(`input`,{"aria-label":`Network Load Balancer`,checked:e.value===`network`,name:`lb-type`,onChange:()=>Ne(`network`),type:`radio`}),`Network (NLB)`]})]})})]}),(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:`Scheme`}),(0,A.jsx)(C,{control:U,name:`scheme`,render:({field:e})=>(0,A.jsxs)(`div`,{className:`flex gap-4`,children:[(0,A.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,A.jsx)(`input`,{"aria-label":`Internet-facing`,checked:e.value===`internet-facing`,name:`scheme`,onChange:()=>e.onChange(`internet-facing`),type:`radio`}),`Internet-facing`]}),(0,A.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,A.jsx)(`input`,{"aria-label":`Internal`,checked:e.value===`internal`,name:`scheme`,onChange:()=>e.onChange(`internal`),type:`radio`}),`Internal`]})]})})]}),(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:(0,A.jsx)(`label`,{htmlFor:`lb-vpc`,children:`VPC`})}),(0,A.jsx)(C,{control:U,name:`vpcId`,render:({field:e})=>(0,A.jsxs)(d,{onValueChange:e=>Fe(e),value:e.value??``,children:[(0,A.jsx)(u,{"aria-invalid":!!K.vpcId,className:`w-full`,id:`lb-vpc`,children:(0,A.jsx)(s,{placeholder:`Select VPC`})}),(0,A.jsx)(l,{children:R.map(e=>(0,A.jsx)(c,{value:e.VpcId??``,children:fe(e)},e.VpcId))})]})}),(0,A.jsx)(E,{errors:[K.vpcId]})]}),(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:`Subnets (select at least 1)`}),ze.length===0?(0,A.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No subnets in the selected VPC.`}):(0,A.jsx)(`div`,{className:`space-y-3`,children:ze.map(e=>(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`p`,{className:`mb-1 text-xs font-medium text-muted-foreground`,children:e.az}),(0,A.jsx)(`div`,{className:`space-y-1`,children:e.subnets.map(e=>(0,A.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,A.jsx)(`input`,{"aria-label":`Subnet ${j(e)}`,checked:Te.includes(e.SubnetId??``),onChange:()=>Ie(e.SubnetId??``),type:`checkbox`}),(0,A.jsx)(`span`,{className:`font-mono`,children:j(e)})]},e.SubnetId))})]},e.az))}),(0,A.jsx)(E,{errors:[K.subnetIds]})]}),!Z&&(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:`Security groups`}),Q.length===0?(0,A.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No security groups in the selected VPC.`}):(0,A.jsx)(`div`,{className:`space-y-1`,children:Q.map(e=>(0,A.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,A.jsx)(`input`,{"aria-label":`Security group ${e.GroupId} (${e.GroupName})`,checked:Ee.includes(e.GroupId??``),onChange:()=>Le(e.GroupId??``),type:`checkbox`}),(0,A.jsxs)(`span`,{className:`font-mono`,children:[e.GroupId,` (`,e.GroupName,`)`]})]},e.GroupId))})]}),(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:`Tags`}),(0,A.jsxs)(`div`,{className:`space-y-2`,children:[De.map((e,t)=>(0,A.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,A.jsx)(x,{placeholder:`Key`,...V(`tags.${t}.key`)}),(0,A.jsx)(x,{placeholder:`Value`,...V(`tags.${t}.value`)}),(0,A.jsx)(m,{onClick:()=>W(`tags`,G(`tags`).filter((e,n)=>n!==t)),size:`icon`,type:`button`,variant:`ghost`,children:(0,A.jsx)(ee,{className:`size-3.5`})})]},t)),(0,A.jsxs)(m,{onClick:()=>W(`tags`,[...G(`tags`),{key:``,value:``}]),size:`sm`,type:`button`,variant:`outline`,children:[(0,A.jsx)(f,{className:`size-3.5`}),`Add tag`]})]})]}),(0,A.jsxs)(`div`,{className:`space-y-4 rounded-md border bg-card p-4`,children:[(0,A.jsx)(`h2`,{className:`text-sm font-semibold`,children:`First listener & default target group`}),(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:(0,A.jsx)(`label`,{htmlFor:`listener-protocol`,children:`Protocol`})}),(0,A.jsx)(C,{control:U,name:`listener.protocol`,render:({field:e})=>(0,A.jsxs)(d,{onValueChange:e=>{e&&Pe(e)},value:e.value,children:[(0,A.jsx)(u,{className:`w-full`,id:`listener-protocol`,children:(0,A.jsx)(s,{})}),(0,A.jsx)(l,{children:ke.map(e=>(0,A.jsx)(c,{value:e,children:e},e))})]})})]}),(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:(0,A.jsx)(`label`,{htmlFor:`listener-port`,children:`Port`})}),(0,A.jsx)(x,{"aria-invalid":!!K.listener?.port,id:`listener-port`,inputMode:`numeric`,type:`number`,...V(`listener.port`,{valueAsNumber:!0})}),(0,A.jsx)(E,{errors:[K.listener?.port]})]}),Oe&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:(0,A.jsx)(`label`,{htmlFor:`listener-certificate`,children:`Certificate`})}),(0,A.jsx)(C,{control:U,name:`listener.certificateArn`,render:({field:e})=>(0,A.jsxs)(d,{onValueChange:e.onChange,value:e.value??``,children:[(0,A.jsx)(u,{"aria-invalid":!!K.listener?.certificateArn,className:`w-full`,id:`listener-certificate`,children:(0,A.jsx)(s,{placeholder:`Select certificate`})}),(0,A.jsx)(l,{children:z.map(e=>(0,A.jsx)(c,{value:e.CertificateArn??``,children:e.DomainName??e.CertificateArn?.split(`/`).pop()},e.CertificateArn))})]})}),(0,A.jsx)(E,{errors:[K.listener?.certificateArn]}),(0,A.jsxs)(`div`,{className:`mt-1 flex items-center justify-between`,children:[z.length===0&&(0,A.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No certificates imported yet.`}),(0,A.jsx)(m,{className:`ml-auto`,onClick:()=>L(!0),size:`sm`,type:`button`,variant:`ghost`,children:`Import certificate…`})]})]}),Y===`HTTPS`&&(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:(0,A.jsx)(`label`,{htmlFor:`listener-ssl-policy`,children:`Security policy`})}),(0,A.jsx)(C,{control:U,name:`listener.sslPolicy`,render:({field:e})=>(0,A.jsxs)(d,{onValueChange:e.onChange,value:e.value??`ELBSecurityPolicy-2016-08`,children:[(0,A.jsx)(u,{className:`w-full`,id:`listener-ssl-policy`,children:(0,A.jsx)(s,{})}),(0,A.jsx)(l,{children:xe.map(e=>(0,A.jsx)(c,{value:e.Name??``,children:e.Name},e.Name))})]})})]})]}),(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:`Default target group`}),(0,A.jsx)(C,{control:U,name:`listener.targetGroupMode`,render:({field:e})=>(0,A.jsxs)(`div`,{className:`flex gap-4`,children:[(0,A.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,A.jsx)(`input`,{"aria-label":`Create new target group`,checked:e.value===`new`,name:`tg-mode`,onChange:()=>e.onChange(`new`),type:`radio`}),`Create new`]}),(0,A.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,A.jsx)(`input`,{"aria-label":`Use existing target group`,checked:e.value===`existing`,name:`tg-mode`,onChange:()=>e.onChange(`existing`),type:`radio`}),`Use existing`]})]})})]}),X===`existing`&&(0,A.jsxs)(D,{children:[(0,A.jsx)(T,{children:(0,A.jsx)(`label`,{htmlFor:`tg-existing`,children:`Target group`})}),(0,A.jsx)(C,{control:U,name:`listener.existingTargetGroupArn`,render:({field:e})=>(0,A.jsxs)(d,{onValueChange:e.onChange,value:e.value??``,children:[(0,A.jsx)(u,{"aria-invalid":!!K.listener?.existingTargetGroupArn,className:`w-full`,id:`tg-existing`,children:(0,A.jsx)(s,{placeholder:`Select target group`})}),(0,A.jsx)(l,{children:$.map(e=>(0,A.jsxs)(c,{value:e.TargetGroupArn??``,children:[e.TargetGroupName,` · `,e.Protocol,`:`,e.Port]},e.TargetGroupArn))})]})}),(0,A.jsx)(E,{errors:[K.listener?.existingTargetGroupArn]}),$.length===0&&(0,A.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`No target groups in this VPC. Choose “Create new” instead.`})]}),X===`new`&&(0,A.jsx)(`div`,{className:`space-y-4 border-l-2 border-muted pl-4`,children:(0,A.jsx)(oe,{allowedProtocols:Ae,form:B,vpcs:R})})]}),(0,A.jsx)(h,{onImported:e=>W(`listener.certificateArn`,e,{shouldValidate:!0}),onOpenChange:L,open:_e}),(0,A.jsx)(p,{commands:he(H,B.watch)}),(0,A.jsx)(ie,{isPending:P.isPending,isSubmitting:we,onCancel:async()=>await e({to:`/ec2/describe-load-balancers`}),pendingLabel:`Creating…`,submitLabel:`Create Load Balancer`})]})]})}function he(e,t){let n=t=>{let n=e(t);return typeof n==`string`?n:``},r=t=>{let n=e(t);return Array.isArray(n)?n.filter(e=>typeof e==`string`):[]},i=t=>{let n=e(t);return typeof n==`number`&&Number.isFinite(n)?n:void 0},a=n(`name`)||``,o=n(`type`)||`application`,s=n(`scheme`)||`internet-facing`,c=r(`subnetIds`),l=r(`securityGroupIds`),u=n(`listener.protocol`)||`HTTP`,d=i(`listener.port`)??80,f=n(`listener.certificateArn`),p=n(`listener.sslPolicy`),ee=n(`listener.targetGroupMode`),m=n(`listener.existingTargetGroupArn`),h=[],g={type:`comment`,value:o===`network`?`# Create NLB with listener and default target group -`},h=e=>{let n=t(e);return typeof n==`string`?n:``},g=e=>{let n=t(e);return typeof n==`number`&&Number.isFinite(n)?n:void 0};if(d===`new`){let e=h(`name`)||``,t=g(`port`)??80,n=h(`vpcId`);p.push({label:`Create Target Group`,parts:[m,{type:`variable`,value:`TG_ARN=`},{type:`bin`,value:`$(AWS_PROFILE=spinifex aws elbv2 create-target-group`},{type:`flag`,value:` \\ +`:`# Create ALB with listener and default target group + +`},_=e=>{let n=t(e);return typeof n==`string`?n:``},v=e=>{let n=t(e);return typeof n==`number`&&Number.isFinite(n)?n:void 0};if(ee===`new`){let e=_(`name`)||``,t=v(`port`)??80,n=_(`vpcId`),r=_(`protocol`)||`HTTP`;h.push({label:`Create Target Group`,parts:[g,{type:`variable`,value:`TG_ARN=`},{type:`bin`,value:`$(AWS_PROFILE=spinifex aws elbv2 create-target-group`},{type:`flag`,value:` \\ --name`},{type:`value`,value:` ${e}`},{type:`flag`,value:` \\ - --protocol HTTP --port`},{type:`value`,value:` ${t}`},{type:`flag`,value:` \\ + --protocol`},{type:`value`,value:` ${r}`},{type:`flag`,value:` --port`},{type:`value`,value:` ${t}`},{type:`flag`,value:` \\ --target-type instance --vpc-id`},{type:`value`,value:` ${n||``}`},{type:`flag`,value:` \\ - --query`},{type:`value`,value:` 'TargetGroups[0].TargetGroupArn'`},{type:`flag`,value:` --output`},{type:`value`,value:` text)`}]})}else p.push({label:`Use Existing Target Group`,parts:[m,{type:`variable`,value:`TG_ARN=`},{type:`value`,value:f||``}]});let _=[{type:`variable`,value:`LB_ARN=`},{type:`bin`,value:`$(AWS_PROFILE=spinifex aws elbv2 create-load-balancer`},{type:`flag`,value:` \\ + --query`},{type:`value`,value:` 'TargetGroups[0].TargetGroupArn'`},{type:`flag`,value:` --output`},{type:`value`,value:` text)`}]})}else h.push({label:`Use Existing Target Group`,parts:[g,{type:`variable`,value:`TG_ARN=`},{type:`value`,value:m||``}]});let y=[{type:`variable`,value:`LB_ARN=`},{type:`bin`,value:`$(AWS_PROFILE=spinifex aws elbv2 create-load-balancer`},{type:`flag`,value:` \\ --name`},{type:`value`,value:` ${a}`},{type:`flag`,value:` \\ - --scheme`},{type:`value`,value:` ${o}`},{type:`flag`,value:` \\ - --type application`}];return s.length>0&&_.push({type:`flag`,value:` \\ - --subnets`},{type:`value`,value:` ${s.join(` `)}`}),c.length>0&&_.push({type:`flag`,value:` \\ - --security-groups`},{type:`value`,value:` ${c.join(` `)}`}),_.push({type:`flag`,value:` \\ - --query`},{type:`value`,value:` 'LoadBalancers[0].LoadBalancerArn'`},{type:`flag`,value:` --output`},{type:`value`,value:` text)`}),p.push({label:`Create Load Balancer`,parts:_}),p.push({label:`Create Listener`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws elbv2 create-listener`},{type:`flag`,value:` \\ + --scheme`},{type:`value`,value:` ${s}`},{type:`flag`,value:` \\ + --type`},{type:`value`,value:` ${o}`}];c.length>0&&y.push({type:`flag`,value:` \\ + --subnets`},{type:`value`,value:` ${c.join(` `)}`}),l.length>0&&y.push({type:`flag`,value:` \\ + --security-groups`},{type:`value`,value:` ${l.join(` `)}`}),y.push({type:`flag`,value:` \\ + --query`},{type:`value`,value:` 'LoadBalancers[0].LoadBalancerArn'`},{type:`flag`,value:` --output`},{type:`value`,value:` text)`}),h.push({label:`Create Load Balancer`,parts:y});let b=[{type:`bin`,value:`AWS_PROFILE=spinifex aws elbv2 create-listener`},{type:`flag`,value:` \\ --load-balancer-arn`},{type:`variable`,value:` "$LB_ARN"`},{type:`flag`,value:` \\ - --protocol`},{type:`value`,value:` ${l}`},{type:`flag`,value:` \\ - --port`},{type:`value`,value:` ${u}`},{type:`flag`,value:` \\ - --default-actions`},{type:`value`,value:` Type=forward,TargetGroupArn="$TG_ARN"`}]}),p}var N=M;export{N as component}; \ No newline at end of file + --protocol`},{type:`value`,value:` ${u}`},{type:`flag`,value:` \\ + --port`},{type:`value`,value:` ${d}`}];return(u===`HTTPS`||u===`TLS`)&&f&&(b.push({type:`flag`,value:` \\ + --certificates`},{type:`value`,value:` CertificateArn=${f}`}),u===`HTTPS`&&p&&b.push({type:`flag`,value:` \\ + --ssl-policy`},{type:`value`,value:` ${p}`})),b.push({type:`flag`,value:` \\ + --default-actions`},{type:`value`,value:` Type=forward,TargetGroupArn="$TG_ARN"`}),h.push({label:`Create Listener`,parts:b}),h}var N=M;export{N as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-nat-gateway.js b/spinifex/services/spinifexui/frontend/dist/assets/create-nat-gateway.js index 0d567e5d4..0b89d11b2 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-nat-gateway.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-nat-gateway.js @@ -1,4 +1,4 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as ee}from"./back-link.js";import{c as n,i as r,n as i,s as a,t as o}from"./select.js";import{t as te}from"./cli-command-panel.js";import{p as ne}from"./ec2.js";import{Fo as s,H as re,Ho as c,Lo as ie,Ro as ae,Xi as oe,ao as l,dt as se}from"./index.js";import{a as ce,i as le,n as ue,t as de}from"./zod.js";import{t as fe}from"./form-actions.js";import{t as pe}from"./page-heading.js";import{a as u,r as me,t as d}from"./field.js";import{s as he}from"./ec22.js";var ge=s(),f=c();function p(){let r=(0,ge.c)(70),s=ae(),{data:c}=t(se),{data:l}=t(re),p=ne(),m;r[0]===c.Subnets?m=r[1]:(m=c.Subnets??[],r[0]=c.Subnets,r[1]=m);let h=m,g;r[2]===h?g=r[3]:(g=h.filter(be),r[2]=h,r[3]=g);let Se=g,_=Se.length>0?Se:h,v;r[4]===l.Addresses?v=r[5]:(v=l.Addresses??[],r[4]=l.Addresses,r[5]=v);let y;r[6]===v?y=r[7]:(y=v.filter(ye),r[6]=v,r[7]=y);let b=y,x;r[8]===Symbol.for(`react.memo_cache_sentinel`)?(x={resolver:de(he),defaultValues:{subnetId:``,allocationId:``,name:``}},r[8]=x):x=r[8];let{control:S,handleSubmit:C,register:Ce,formState:we}=le(x),{errors:w,isSubmitting:T}=we,E;r[9]===S?E=r[10]:(E={control:S},r[9]=S,r[10]=E);let D=ce(E),O;r[11]===D?O=r[12]:(O=e=>e?D[e]:void 0,r[11]=D,r[12]=O);let k=O,A;r[13]!==p||r[14]!==s?(A=async e=>{let t=(await p.mutateAsync({subnetId:e.subnetId,allocationId:e.allocationId,name:e.name?.trim()??void 0})).NatGateway?.NatGatewayId;s(t?{to:`/ec2/describe-nat-gateways/$id`,params:{id:t}}:{to:`/ec2/describe-nat-gateways`})},r[13]=p,r[14]=s,r[15]=A):A=r[15];let j=A,M,N;r[16]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,f.jsx)(ee,{to:`/ec2/describe-nat-gateways`,children:`Back to NAT Gateways`}),M=(0,f.jsx)(pe,{title:`Create NAT Gateway`}),r[16]=M,r[17]=N):(M=r[16],N=r[17]);let P;r[18]===p.error?P=r[19]:(P=p.error&&(0,f.jsx)(e,{error:p.error,msg:`Failed to create NAT Gateway`}),r[18]=p.error,r[19]=P);let F;r[20]!==C||r[21]!==j?(F=C(j),r[20]=C,r[21]=j,r[22]=F):F=r[22];let I;r[23]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,f.jsx)(u,{children:(0,f.jsx)(`label`,{htmlFor:`name`,children:`Name (optional)`})}),r[23]=I):I=r[23];let L;r[24]===Ce?L=r[25]:(L=Ce(`name`),r[24]=Ce,r[25]=L);let R;r[26]===L?R=r[27]:(R=(0,f.jsxs)(d,{children:[I,(0,f.jsx)(oe,{id:`name`,placeholder:`my-nat-gateway`,...L})]}),r[26]=L,r[27]=R);let z;r[28]===Symbol.for(`react.memo_cache_sentinel`)?(z=(0,f.jsx)(u,{children:(0,f.jsx)(`label`,{htmlFor:`subnetId`,children:`Public subnet`})}),r[28]=z):z=r[28];let B;r[29]!==w.subnetId||r[30]!==_?(B=e=>{let{field:t}=e;return(0,f.jsxs)(o,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,f.jsx)(a,{"aria-invalid":!!w.subnetId,className:`w-full`,id:`subnetId`,children:(0,f.jsx)(n,{placeholder:`Select a public subnet`})}),(0,f.jsx)(i,{children:_.map(ve)})]})},r[29]=w.subnetId,r[30]=_,r[31]=B):B=r[31];let V;r[32]!==S||r[33]!==B?(V=(0,f.jsx)(ue,{control:S,name:`subnetId`,render:B}),r[32]=S,r[33]=B,r[34]=V):V=r[34];let H;r[35]===w.subnetId?H=r[36]:(H=(0,f.jsx)(me,{errors:[w.subnetId]}),r[35]=w.subnetId,r[36]=H);let U;r[37]!==V||r[38]!==H?(U=(0,f.jsxs)(d,{children:[z,V,H]}),r[37]=V,r[38]=H,r[39]=U):U=r[39];let W;r[40]===Symbol.for(`react.memo_cache_sentinel`)?(W=(0,f.jsx)(u,{children:(0,f.jsx)(`label`,{htmlFor:`allocationId`,children:`Elastic IP`})}),r[40]=W):W=r[40];let G;r[41]!==b||r[42]!==S||r[43]!==w.allocationId?(G=b.length>0?(0,f.jsx)(ue,{control:S,name:`allocationId`,render:e=>{let{field:t}=e;return(0,f.jsxs)(o,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,f.jsx)(a,{"aria-invalid":!!w.allocationId,className:`w-full`,id:`allocationId`,children:(0,f.jsx)(n,{placeholder:`Select an Elastic IP`})}),(0,f.jsx)(i,{children:b.map(_e)})]})}}):(0,f.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[`No available Elastic IPs.`,` `,(0,f.jsx)(ie,{className:`text-primary hover:underline`,to:`/ec2/allocate-address`,children:`Allocate one`}),` `,`first.`]}),r[41]=b,r[42]=S,r[43]=w.allocationId,r[44]=G):G=r[44];let K;r[45]===w.allocationId?K=r[46]:(K=(0,f.jsx)(me,{errors:[w.allocationId]}),r[45]=w.allocationId,r[46]=K);let q;r[47]!==G||r[48]!==K?(q=(0,f.jsxs)(d,{children:[W,G,K]}),r[47]=G,r[48]=K,r[49]=q):q=r[49];let J;r[50]===k?J=r[51]:(J=xe(k),r[50]=k,r[51]=J);let Y;r[52]===J?Y=r[53]:(Y=(0,f.jsx)(te,{commands:J}),r[52]=J,r[53]=Y);let X;r[54]===s?X=r[55]:(X=async()=>await s({to:`/ec2/describe-nat-gateways`}),r[54]=s,r[55]=X);let Z;r[56]!==p.isPending||r[57]!==T||r[58]!==X?(Z=(0,f.jsx)(fe,{isPending:p.isPending,isSubmitting:T,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create NAT Gateway`}),r[56]=p.isPending,r[57]=T,r[58]=X,r[59]=Z):Z=r[59];let Q;r[60]!==F||r[61]!==R||r[62]!==U||r[63]!==q||r[64]!==Y||r[65]!==Z?(Q=(0,f.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:F,children:[R,U,q,Y,Z]}),r[60]=F,r[61]=R,r[62]=U,r[63]=q,r[64]=Y,r[65]=Z,r[66]=Q):Q=r[66];let $;return r[67]!==P||r[68]!==Q?($=(0,f.jsxs)(f.Fragment,{children:[N,M,P,Q]}),r[67]=P,r[68]=Q,r[69]=$):$=r[69],$}function _e(e){return(0,f.jsx)(r,{value:e.AllocationId??``,children:`${e.AllocationId} (${e.PublicIp})`},e.AllocationId)}function ve(e){let t=l(e.Tags);return(0,f.jsx)(r,{value:e.SubnetId??``,children:t?`${e.SubnetId} (${t})`:`${e.SubnetId} (${e.CidrBlock})`},e.SubnetId)}function ye(e){return!e.AssociationId}function be(e){return e.MapPublicIpOnLaunch}function xe(e){let t=e(`subnetId`),ee=typeof t==`string`?t:``,n=e(`allocationId`),r=typeof n==`string`?n:``,i=e(`name`),a=typeof i==`string`?i:``,o=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-nat-gateway`},{type:`flag`,value:` \\ - --subnet-id`},{type:`value`,value:` ${ee||``}`},{type:`flag`,value:` \\ - --allocation-id`},{type:`value`,value:` ${r||``}`}];return a.trim()&&o.push({type:`flag`,value:` \\ - --tag-specifications`},{type:`value`,value:` 'ResourceType=natgateway,Tags=[{Key=Name,Value=${a.trim()}}]'`}),[{label:`Create NAT Gateway`,parts:o}]}export{p as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{Or as ee}from"./awsClient.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./back-link.js";import{c as o,i as s,n as te,s as ne,t as c}from"./select.js";import{t as re}from"./cli-command-panel.js";import{m as ie}from"./ec2.js";import{Ir as ae,Nt as oe,Rn as se,lt as ce}from"./index.js";import{a as le,i as ue,n as de,t as fe}from"./zod.js";import{t as pe}from"./form-actions.js";import{t as me}from"./page-heading.js";import{a as l,r as he,t as u}from"./field.js";import{s as ge}from"./ec22.js";var _e=t(),d=e();function f(){let e=(0,_e.c)(70),t=ae(),{data:i}=r(oe),{data:s}=r(ce),f=ie(),p;e[0]===i.Subnets?p=e[1]:(p=i.Subnets??[],e[0]=i.Subnets,e[1]=p);let m=p,h;e[2]===m?h=e[3]:(h=m.filter(xe),e[2]=m,e[3]=h);let Ce=h,g=Ce.length>0?Ce:m,_;e[4]===s.Addresses?_=e[5]:(_=s.Addresses??[],e[4]=s.Addresses,e[5]=_);let v;e[6]===_?v=e[7]:(v=_.filter(be),e[6]=_,e[7]=v);let y=v,b;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(b={resolver:fe(ge),defaultValues:{subnetId:``,allocationId:``,name:``}},e[8]=b):b=e[8];let{control:x,handleSubmit:S,register:C,formState:we}=ue(b),{errors:w,isSubmitting:T}=we,E;e[9]===x?E=e[10]:(E={control:x},e[9]=x,e[10]=E);let D=le(E),O;e[11]===D?O=e[12]:(O=e=>e?D[e]:void 0,e[11]=D,e[12]=O);let k=O,A;e[13]!==f||e[14]!==t?(A=async e=>{let n=(await f.mutateAsync({subnetId:e.subnetId,allocationId:e.allocationId,name:e.name?.trim()??void 0})).NatGateway?.NatGatewayId;t(n?{to:`/ec2/describe-nat-gateways/$id`,params:{id:n}}:{to:`/ec2/describe-nat-gateways`})},e[13]=f,e[14]=t,e[15]=A):A=e[15];let j=A,M,N;e[16]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,d.jsx)(a,{to:`/ec2/describe-nat-gateways`,children:`Back to NAT Gateways`}),M=(0,d.jsx)(me,{title:`Create NAT Gateway`}),e[16]=M,e[17]=N):(M=e[16],N=e[17]);let P;e[18]===f.error?P=e[19]:(P=f.error&&(0,d.jsx)(n,{error:f.error,msg:`Failed to create NAT Gateway`}),e[18]=f.error,e[19]=P);let F;e[20]!==S||e[21]!==j?(F=S(j),e[20]=S,e[21]=j,e[22]=F):F=e[22];let I;e[23]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,d.jsx)(l,{children:(0,d.jsx)(`label`,{htmlFor:`name`,children:`Name (optional)`})}),e[23]=I):I=e[23];let L;e[24]===C?L=e[25]:(L=C(`name`),e[24]=C,e[25]=L);let R;e[26]===L?R=e[27]:(R=(0,d.jsxs)(u,{children:[I,(0,d.jsx)(se,{id:`name`,placeholder:`my-nat-gateway`,...L})]}),e[26]=L,e[27]=R);let z;e[28]===Symbol.for(`react.memo_cache_sentinel`)?(z=(0,d.jsx)(l,{children:(0,d.jsx)(`label`,{htmlFor:`subnetId`,children:`Public subnet`})}),e[28]=z):z=e[28];let B;e[29]!==w.subnetId||e[30]!==g?(B=e=>{let{field:t}=e;return(0,d.jsxs)(c,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,d.jsx)(ne,{"aria-invalid":!!w.subnetId,className:`w-full`,id:`subnetId`,children:(0,d.jsx)(o,{placeholder:`Select a public subnet`})}),(0,d.jsx)(te,{children:g.map(ye)})]})},e[29]=w.subnetId,e[30]=g,e[31]=B):B=e[31];let V;e[32]!==x||e[33]!==B?(V=(0,d.jsx)(de,{control:x,name:`subnetId`,render:B}),e[32]=x,e[33]=B,e[34]=V):V=e[34];let H;e[35]===w.subnetId?H=e[36]:(H=(0,d.jsx)(he,{errors:[w.subnetId]}),e[35]=w.subnetId,e[36]=H);let U;e[37]!==V||e[38]!==H?(U=(0,d.jsxs)(u,{children:[z,V,H]}),e[37]=V,e[38]=H,e[39]=U):U=e[39];let W;e[40]===Symbol.for(`react.memo_cache_sentinel`)?(W=(0,d.jsx)(l,{children:(0,d.jsx)(`label`,{htmlFor:`allocationId`,children:`Elastic IP`})}),e[40]=W):W=e[40];let G;e[41]!==y||e[42]!==x||e[43]!==w.allocationId?(G=y.length>0?(0,d.jsx)(de,{control:x,name:`allocationId`,render:e=>{let{field:t}=e;return(0,d.jsxs)(c,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,d.jsx)(ne,{"aria-invalid":!!w.allocationId,className:`w-full`,id:`allocationId`,children:(0,d.jsx)(o,{placeholder:`Select an Elastic IP`})}),(0,d.jsx)(te,{children:y.map(ve)})]})}}):(0,d.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[`No available Elastic IPs.`,` `,(0,d.jsx)(ee,{className:`text-primary hover:underline`,to:`/ec2/allocate-address`,children:`Allocate one`}),` `,`first.`]}),e[41]=y,e[42]=x,e[43]=w.allocationId,e[44]=G):G=e[44];let K;e[45]===w.allocationId?K=e[46]:(K=(0,d.jsx)(he,{errors:[w.allocationId]}),e[45]=w.allocationId,e[46]=K);let q;e[47]!==G||e[48]!==K?(q=(0,d.jsxs)(u,{children:[W,G,K]}),e[47]=G,e[48]=K,e[49]=q):q=e[49];let J;e[50]===k?J=e[51]:(J=Se(k),e[50]=k,e[51]=J);let Y;e[52]===J?Y=e[53]:(Y=(0,d.jsx)(re,{commands:J}),e[52]=J,e[53]=Y);let X;e[54]===t?X=e[55]:(X=async()=>await t({to:`/ec2/describe-nat-gateways`}),e[54]=t,e[55]=X);let Z;e[56]!==f.isPending||e[57]!==T||e[58]!==X?(Z=(0,d.jsx)(pe,{isPending:f.isPending,isSubmitting:T,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create NAT Gateway`}),e[56]=f.isPending,e[57]=T,e[58]=X,e[59]=Z):Z=e[59];let Q;e[60]!==F||e[61]!==R||e[62]!==U||e[63]!==q||e[64]!==Y||e[65]!==Z?(Q=(0,d.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:F,children:[R,U,q,Y,Z]}),e[60]=F,e[61]=R,e[62]=U,e[63]=q,e[64]=Y,e[65]=Z,e[66]=Q):Q=e[66];let $;return e[67]!==P||e[68]!==Q?($=(0,d.jsxs)(d.Fragment,{children:[N,M,P,Q]}),e[67]=P,e[68]=Q,e[69]=$):$=e[69],$}function ve(e){return(0,d.jsx)(s,{value:e.AllocationId??``,children:`${e.AllocationId} (${e.PublicIp})`},e.AllocationId)}function ye(e){let t=i(e.Tags);return(0,d.jsx)(s,{value:e.SubnetId??``,children:t?`${e.SubnetId} (${t})`:`${e.SubnetId} (${e.CidrBlock})`},e.SubnetId)}function be(e){return!e.AssociationId}function xe(e){return e.MapPublicIpOnLaunch}function Se(e){let t=e(`subnetId`),n=typeof t==`string`?t:``,r=e(`allocationId`),ee=typeof r==`string`?r:``,i=e(`name`),a=typeof i==`string`?i:``,o=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-nat-gateway`},{type:`flag`,value:` \\ + --subnet-id`},{type:`value`,value:` ${n||``}`},{type:`flag`,value:` \\ + --allocation-id`},{type:`value`,value:` ${ee||``}`}];return a.trim()&&o.push({type:`flag`,value:` \\ + --tag-specifications`},{type:`value`,value:` 'ResourceType=natgateway,Tags=[{Key=Name,Value=${a.trim()}}]'`}),[{label:`Create NAT Gateway`,parts:o}]}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-placement-group.js b/spinifex/services/spinifexui/frontend/dist/assets/create-placement-group.js index 77e1e3c82..f6b5c3b4d 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-placement-group.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-placement-group.js @@ -1,3 +1,3 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{c as n,i as r,n as i,s as a,t as o}from"./select.js";import{t as s}from"./cli-command-panel.js";import{m as c}from"./ec2.js";import{Fo as l,Ho as u,Ro as ee,Xi as te}from"./index.js";import{i as d,n as f,t as p}from"./zod.js";import{t as m}from"./form-actions.js";import{t as ne}from"./page-heading.js";import{a as h,r as g,t as _}from"./field.js";import{c as v}from"./ec22.js";var y=l(),b=u();function x(){let l=(0,y.c)(52),u=ee(),x=c(),S;l[0]===Symbol.for(`react.memo_cache_sentinel`)?(S={resolver:p(v),defaultValues:{groupName:``,strategy:`spread`}},l[0]=S):S=l[0];let{control:w,handleSubmit:T,register:E,watch:D,formState:O}=d(S),{errors:k,isSubmitting:A}=O,j;l[1]!==x||l[2]!==u?(j=async e=>{u({to:`/ec2/describe-placement-groups/$id`,params:{id:(await x.mutateAsync(e)).PlacementGroup?.GroupId??``}})},l[1]=x,l[2]=u,l[3]=j):j=l[3];let M=j,N,P;l[4]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,b.jsx)(t,{to:`/ec2/describe-placement-groups`,children:`Back to placement groups`}),P=(0,b.jsx)(ne,{title:`Create Placement Group`}),l[4]=N,l[5]=P):(N=l[4],P=l[5]);let F;l[6]===x.error?F=l[7]:(F=x.error&&(0,b.jsx)(e,{error:x.error,msg:`Failed to create placement group`}),l[6]=x.error,l[7]=F);let I;l[8]!==T||l[9]!==M?(I=T(M),l[8]=T,l[9]=M,l[10]=I):I=l[10];let L;l[11]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,b.jsx)(h,{children:(0,b.jsx)(`label`,{htmlFor:`groupName`,children:`Group Name`})}),l[11]=L):L=l[11];let R=!!k.groupName,z;l[12]===E?z=l[13]:(z=E(`groupName`),l[12]=E,l[13]=z);let B;l[14]!==R||l[15]!==z?(B=(0,b.jsx)(te,{"aria-invalid":R,id:`groupName`,placeholder:`my-placement-group`,...z}),l[14]=R,l[15]=z,l[16]=B):B=l[16];let V;l[17]===k.groupName?V=l[18]:(V=(0,b.jsx)(g,{errors:[k.groupName]}),l[17]=k.groupName,l[18]=V);let H;l[19]!==B||l[20]!==V?(H=(0,b.jsxs)(_,{children:[L,B,V]}),l[19]=B,l[20]=V,l[21]=H):H=l[21];let U;l[22]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,b.jsx)(h,{children:(0,b.jsx)(`label`,{htmlFor:`strategy`,children:`Strategy`})}),l[22]=U):U=l[22];let W;l[23]===k.strategy?W=l[24]:(W=e=>{let{field:t}=e;return(0,b.jsxs)(o,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,b.jsx)(a,{"aria-invalid":!!k.strategy,className:`w-full`,id:`strategy`,children:(0,b.jsx)(n,{})}),(0,b.jsxs)(i,{children:[(0,b.jsx)(r,{value:`spread`,children:`spread`}),(0,b.jsx)(r,{value:`cluster`,children:`cluster`})]})]})},l[23]=k.strategy,l[24]=W);let G;l[25]!==w||l[26]!==W?(G=(0,b.jsx)(f,{control:w,name:`strategy`,render:W}),l[25]=w,l[26]=W,l[27]=G):G=l[27];let K;l[28]===k.strategy?K=l[29]:(K=(0,b.jsx)(g,{errors:[k.strategy]}),l[28]=k.strategy,l[29]=K);let q;l[30]!==G||l[31]!==K?(q=(0,b.jsxs)(_,{children:[U,G,K]}),l[30]=G,l[31]=K,l[32]=q):q=l[32];let J;l[33]===D?J=l[34]:(J=C(D),l[33]=D,l[34]=J);let Y;l[35]===J?Y=l[36]:(Y=(0,b.jsx)(s,{commands:J}),l[35]=J,l[36]=Y);let X;l[37]===u?X=l[38]:(X=async()=>await u({to:`/ec2/describe-placement-groups`}),l[37]=u,l[38]=X);let Z;l[39]!==x.isPending||l[40]!==A||l[41]!==X?(Z=(0,b.jsx)(m,{isPending:x.isPending,isSubmitting:A,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Placement Group`}),l[39]=x.isPending,l[40]=A,l[41]=X,l[42]=Z):Z=l[42];let Q;l[43]!==H||l[44]!==q||l[45]!==Y||l[46]!==Z||l[47]!==I?(Q=(0,b.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:I,children:[H,q,Y,Z]}),l[43]=H,l[44]=q,l[45]=Y,l[46]=Z,l[47]=I,l[48]=Q):Q=l[48];let $;return l[49]!==Q||l[50]!==F?($=(0,b.jsxs)(b.Fragment,{children:[N,P,F,Q]}),l[49]=Q,l[50]=F,l[51]=$):$=l[51],$}function S(e){return`'${e.replaceAll(`'`,`'"'"'`)}'`}function C(e){let t=e(`groupName`),n=typeof t==`string`?t:``,r=e(`strategy`),i=typeof r==`string`?r:``;return[{label:`Create Placement Group`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-placement-group`},{type:`flag`,value:` \\ - --group-name`},{type:`value`,value:` ${n?S(n):``}`},{type:`flag`,value:` \\ - --strategy`},{type:`value`,value:` ${i||`spread`}`}]}]}export{x as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{c as i,i as a,n as ee,s as o,t as te}from"./select.js";import{t as ne}from"./cli-command-panel.js";import{h as s}from"./ec2.js";import{Ir as c,Rn as l}from"./index.js";import{a as u,i as d,n as f,t as p}from"./zod.js";import{t as m}from"./form-actions.js";import{t as h}from"./page-heading.js";import{a as g,r as _,t as v}from"./field.js";import{c as re}from"./ec22.js";var ie=t(),y=e();function b(){let e=(0,ie.c)(56),t=c(),b=s(),x;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(x={resolver:p(re),defaultValues:{groupName:``,strategy:`spread`}},e[0]=x):x=e[0];let{control:S,handleSubmit:C,register:w,formState:oe}=d(x),{errors:T,isSubmitting:E}=oe,D;e[1]===S?D=e[2]:(D={control:S},e[1]=S,e[2]=D);let O=u(D),k;e[3]===O?k=e[4]:(k=e=>e?O[e]:void 0,e[3]=O,e[4]=k);let A=k,j;e[5]!==b||e[6]!==t?(j=async e=>{t({to:`/ec2/describe-placement-groups/$id`,params:{id:(await b.mutateAsync(e)).PlacementGroup?.GroupId??``}})},e[5]=b,e[6]=t,e[7]=j):j=e[7];let M=j,N,P;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,y.jsx)(r,{to:`/ec2/describe-placement-groups`,children:`Back to placement groups`}),P=(0,y.jsx)(h,{title:`Create Placement Group`}),e[8]=N,e[9]=P):(N=e[8],P=e[9]);let F;e[10]===b.error?F=e[11]:(F=b.error&&(0,y.jsx)(n,{error:b.error,msg:`Failed to create placement group`}),e[10]=b.error,e[11]=F);let I;e[12]!==C||e[13]!==M?(I=C(M),e[12]=C,e[13]=M,e[14]=I):I=e[14];let L;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,y.jsx)(g,{children:(0,y.jsx)(`label`,{htmlFor:`groupName`,children:`Group Name`})}),e[15]=L):L=e[15];let R=!!T.groupName,z;e[16]===w?z=e[17]:(z=w(`groupName`),e[16]=w,e[17]=z);let B;e[18]!==R||e[19]!==z?(B=(0,y.jsx)(l,{"aria-invalid":R,id:`groupName`,placeholder:`my-placement-group`,...z}),e[18]=R,e[19]=z,e[20]=B):B=e[20];let V;e[21]===T.groupName?V=e[22]:(V=(0,y.jsx)(_,{errors:[T.groupName]}),e[21]=T.groupName,e[22]=V);let H;e[23]!==B||e[24]!==V?(H=(0,y.jsxs)(v,{children:[L,B,V]}),e[23]=B,e[24]=V,e[25]=H):H=e[25];let U;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,y.jsx)(g,{children:(0,y.jsx)(`label`,{htmlFor:`strategy`,children:`Strategy`})}),e[26]=U):U=e[26];let W;e[27]===T.strategy?W=e[28]:(W=e=>{let{field:t}=e;return(0,y.jsxs)(te,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,y.jsx)(o,{"aria-invalid":!!T.strategy,className:`w-full`,id:`strategy`,children:(0,y.jsx)(i,{})}),(0,y.jsxs)(ee,{children:[(0,y.jsx)(a,{value:`spread`,children:`spread`}),(0,y.jsx)(a,{value:`cluster`,children:`cluster`})]})]})},e[27]=T.strategy,e[28]=W);let G;e[29]!==S||e[30]!==W?(G=(0,y.jsx)(f,{control:S,name:`strategy`,render:W}),e[29]=S,e[30]=W,e[31]=G):G=e[31];let K;e[32]===T.strategy?K=e[33]:(K=(0,y.jsx)(_,{errors:[T.strategy]}),e[32]=T.strategy,e[33]=K);let q;e[34]!==G||e[35]!==K?(q=(0,y.jsxs)(v,{children:[U,G,K]}),e[34]=G,e[35]=K,e[36]=q):q=e[36];let J;e[37]===A?J=e[38]:(J=ae(A),e[37]=A,e[38]=J);let Y;e[39]===J?Y=e[40]:(Y=(0,y.jsx)(ne,{commands:J}),e[39]=J,e[40]=Y);let X;e[41]===t?X=e[42]:(X=async()=>await t({to:`/ec2/describe-placement-groups`}),e[41]=t,e[42]=X);let Z;e[43]!==b.isPending||e[44]!==E||e[45]!==X?(Z=(0,y.jsx)(m,{isPending:b.isPending,isSubmitting:E,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Placement Group`}),e[43]=b.isPending,e[44]=E,e[45]=X,e[46]=Z):Z=e[46];let Q;e[47]!==H||e[48]!==q||e[49]!==Y||e[50]!==Z||e[51]!==I?(Q=(0,y.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:I,children:[H,q,Y,Z]}),e[47]=H,e[48]=q,e[49]=Y,e[50]=Z,e[51]=I,e[52]=Q):Q=e[52];let $;return e[53]!==Q||e[54]!==F?($=(0,y.jsxs)(y.Fragment,{children:[N,P,F,Q]}),e[53]=Q,e[54]=F,e[55]=$):$=e[55],$}function x(e){return`'${e.replaceAll(`'`,`'"'"'`)}'`}function ae(e){let t=e(`groupName`),n=typeof t==`string`?t:``,r=e(`strategy`),i=typeof r==`string`?r:``;return[{label:`Create Placement Group`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-placement-group`},{type:`flag`,value:` \\ + --group-name`},{type:`value`,value:` ${n?x(n):``}`},{type:`flag`,value:` \\ + --strategy`},{type:`value`,value:` ${i||`spread`}`}]}]}export{b as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-policy.js b/spinifex/services/spinifexui/frontend/dist/assets/create-policy.js index 968f23657..dacd1cfae 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-policy.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-policy.js @@ -1,4 +1,4 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{t as n}from"./cli-command-panel.js";import{r}from"./iam.js";import{Fo as i,Ho as a,Ro as o,Xi as s}from"./index.js";import{i as c,t as l}from"./zod.js";import{t as u}from"./form-actions.js";import{t as d}from"./page-heading.js";import{a as f,r as p,t as m}from"./field.js";import{t as h}from"./textarea.js";import{t as g}from"./iam2.js";var ee=i(),_=a(),te=JSON.stringify({Version:`2012-10-17`,Statement:[{Effect:`Allow`,Action:`*`,Resource:`*`}]},null,2);function v(){let i=(0,ee.c)(63),a=o(),v=r(),y;i[0]===Symbol.for(`react.memo_cache_sentinel`)?(y={resolver:l(g),defaultValues:{policyDocument:te}},i[0]=y):y=i[0];let{handleSubmit:b,register:x,watch:S,formState:re}=c(y),{errors:C,isSubmitting:w}=re,T;i[1]!==v||i[2]!==a?(T=async e=>{await v.mutateAsync(e),a({to:`/iam/list-policies`})},i[1]=v,i[2]=a,i[3]=T):T=i[3];let E=T,D,O;i[4]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,_.jsx)(t,{to:`/iam/list-policies`,children:`Back to policies`}),O=(0,_.jsx)(d,{title:`Create Policy`}),i[4]=D,i[5]=O):(D=i[4],O=i[5]);let k;i[6]===v.error?k=i[7]:(k=v.error&&(0,_.jsx)(e,{error:v.error,msg:`Failed to create policy`}),i[6]=v.error,i[7]=k);let A;i[8]!==b||i[9]!==E?(A=b(E),i[8]=b,i[9]=E,i[10]=A):A=i[10];let j;i[11]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,_.jsx)(f,{children:(0,_.jsx)(`label`,{htmlFor:`policyName`,children:`Policy Name`})}),i[11]=j):j=i[11];let M=!!C.policyName,N;i[12]===x?N=i[13]:(N=x(`policyName`),i[12]=x,i[13]=N);let P;i[14]!==M||i[15]!==N?(P=(0,_.jsx)(s,{"aria-invalid":M,id:`policyName`,placeholder:`my-policy...`,...N}),i[14]=M,i[15]=N,i[16]=P):P=i[16];let F;i[17]===C.policyName?F=i[18]:(F=(0,_.jsx)(p,{errors:[C.policyName]}),i[17]=C.policyName,i[18]=F);let I;i[19]!==P||i[20]!==F?(I=(0,_.jsxs)(m,{children:[j,P,F]}),i[19]=P,i[20]=F,i[21]=I):I=i[21];let L;i[22]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,_.jsx)(f,{children:(0,_.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),i[22]=L):L=i[22];let R;i[23]===x?R=i[24]:(R=x(`description`),i[23]=x,i[24]=R);let z;i[25]===R?z=i[26]:(z=(0,_.jsx)(s,{id:`description`,placeholder:`A brief description of this policy...`,...R}),i[25]=R,i[26]=z);let B;i[27]===C.description?B=i[28]:(B=(0,_.jsx)(p,{errors:[C.description]}),i[27]=C.description,i[28]=B);let V;i[29]!==z||i[30]!==B?(V=(0,_.jsxs)(m,{children:[L,z,B]}),i[29]=z,i[30]=B,i[31]=V):V=i[31];let H;i[32]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,_.jsx)(f,{children:(0,_.jsx)(`label`,{htmlFor:`policyDocument`,children:`Policy Document (JSON)`})}),i[32]=H):H=i[32];let U=!!C.policyDocument,W;i[33]===x?W=i[34]:(W=x(`policyDocument`),i[33]=x,i[34]=W);let G;i[35]!==U||i[36]!==W?(G=(0,_.jsx)(h,{"aria-invalid":U,className:`font-mono text-sm`,id:`policyDocument`,rows:15,...W}),i[35]=U,i[36]=W,i[37]=G):G=i[37];let K;i[38]===C.policyDocument?K=i[39]:(K=(0,_.jsx)(p,{errors:[C.policyDocument]}),i[38]=C.policyDocument,i[39]=K);let q;i[40]!==G||i[41]!==K?(q=(0,_.jsxs)(m,{children:[H,G,K]}),i[40]=G,i[41]=K,i[42]=q):q=i[42];let J;i[43]===S?J=i[44]:(J=ne(S),i[43]=S,i[44]=J);let Y;i[45]===J?Y=i[46]:(Y=(0,_.jsx)(n,{commands:J}),i[45]=J,i[46]=Y);let X;i[47]===a?X=i[48]:(X=async()=>await a({to:`/iam/list-policies`}),i[47]=a,i[48]=X);let Z;i[49]!==v.isPending||i[50]!==w||i[51]!==X?(Z=(0,_.jsx)(u,{isPending:v.isPending,isSubmitting:w,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create Policy`}),i[49]=v.isPending,i[50]=w,i[51]=X,i[52]=Z):Z=i[52];let Q;i[53]!==I||i[54]!==V||i[55]!==q||i[56]!==Y||i[57]!==Z||i[58]!==A?(Q=(0,_.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:A,children:[I,V,q,Y,Z]}),i[53]=I,i[54]=V,i[55]=q,i[56]=Y,i[57]=Z,i[58]=A,i[59]=Q):Q=i[59];let $;return i[60]!==Q||i[61]!==k?($=(0,_.jsxs)(_.Fragment,{children:[D,O,k,Q]}),i[60]=Q,i[61]=k,i[62]=$):$=i[62],$}function ne(e){let t=e(`policyName`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=e(`policyDocument`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-policy`},{type:`flag`,value:` \\ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{o as a}from"./iam.js";import{Ir as o,Rn as s}from"./index.js";import{a as ee,i as te,t as ne}from"./zod.js";import{t as re}from"./form-actions.js";import{t as ie}from"./page-heading.js";import{a as c,r as l,t as u}from"./field.js";import{t as d}from"./textarea.js";import{r as f}from"./iam2.js";var ae=t(),p=e(),oe=JSON.stringify({Version:`2012-10-17`,Statement:[{Effect:`Allow`,Action:`*`,Resource:`*`}]},null,2);function m(){let e=(0,ae.c)(67),t=o(),m=a(),h;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(h={resolver:ne(f),defaultValues:{policyDocument:oe}},e[0]=h):h=e[0];let{handleSubmit:g,register:_,control:v,formState:ce}=te(h),{errors:y,isSubmitting:b}=ce,x;e[1]===v?x=e[2]:(x={control:v},e[1]=v,e[2]=x);let S=ee(x),C;e[3]===S?C=e[4]:(C=e=>e?S[e]:void 0,e[3]=S,e[4]=C);let w=C,T;e[5]!==m||e[6]!==t?(T=async e=>{await m.mutateAsync(e),t({to:`/iam/list-policies`})},e[5]=m,e[6]=t,e[7]=T):T=e[7];let E=T,D,O;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,p.jsx)(r,{to:`/iam/list-policies`,children:`Back to policies`}),O=(0,p.jsx)(ie,{title:`Create Policy`}),e[8]=D,e[9]=O):(D=e[8],O=e[9]);let k;e[10]===m.error?k=e[11]:(k=m.error&&(0,p.jsx)(n,{error:m.error,msg:`Failed to create policy`}),e[10]=m.error,e[11]=k);let A;e[12]!==g||e[13]!==E?(A=g(E),e[12]=g,e[13]=E,e[14]=A):A=e[14];let j;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`policyName`,children:`Policy Name`})}),e[15]=j):j=e[15];let M=!!y.policyName,N;e[16]===_?N=e[17]:(N=_(`policyName`),e[16]=_,e[17]=N);let P;e[18]!==M||e[19]!==N?(P=(0,p.jsx)(s,{"aria-invalid":M,id:`policyName`,placeholder:`my-policy...`,...N}),e[18]=M,e[19]=N,e[20]=P):P=e[20];let F;e[21]===y.policyName?F=e[22]:(F=(0,p.jsx)(l,{errors:[y.policyName]}),e[21]=y.policyName,e[22]=F);let I;e[23]!==P||e[24]!==F?(I=(0,p.jsxs)(u,{children:[j,P,F]}),e[23]=P,e[24]=F,e[25]=I):I=e[25];let L;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),e[26]=L):L=e[26];let R;e[27]===_?R=e[28]:(R=_(`description`),e[27]=_,e[28]=R);let z;e[29]===R?z=e[30]:(z=(0,p.jsx)(s,{id:`description`,placeholder:`A brief description of this policy...`,...R}),e[29]=R,e[30]=z);let B;e[31]===y.description?B=e[32]:(B=(0,p.jsx)(l,{errors:[y.description]}),e[31]=y.description,e[32]=B);let V;e[33]!==z||e[34]!==B?(V=(0,p.jsxs)(u,{children:[L,z,B]}),e[33]=z,e[34]=B,e[35]=V):V=e[35];let H;e[36]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`policyDocument`,children:`Policy Document (JSON)`})}),e[36]=H):H=e[36];let U=!!y.policyDocument,W;e[37]===_?W=e[38]:(W=_(`policyDocument`),e[37]=_,e[38]=W);let G;e[39]!==U||e[40]!==W?(G=(0,p.jsx)(d,{"aria-invalid":U,className:`font-mono text-sm`,id:`policyDocument`,rows:15,...W}),e[39]=U,e[40]=W,e[41]=G):G=e[41];let K;e[42]===y.policyDocument?K=e[43]:(K=(0,p.jsx)(l,{errors:[y.policyDocument]}),e[42]=y.policyDocument,e[43]=K);let q;e[44]!==G||e[45]!==K?(q=(0,p.jsxs)(u,{children:[H,G,K]}),e[44]=G,e[45]=K,e[46]=q):q=e[46];let J;e[47]===w?J=e[48]:(J=se(w),e[47]=w,e[48]=J);let Y;e[49]===J?Y=e[50]:(Y=(0,p.jsx)(i,{commands:J}),e[49]=J,e[50]=Y);let X;e[51]===t?X=e[52]:(X=async()=>await t({to:`/iam/list-policies`}),e[51]=t,e[52]=X);let Z;e[53]!==m.isPending||e[54]!==b||e[55]!==X?(Z=(0,p.jsx)(re,{isPending:m.isPending,isSubmitting:b,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create Policy`}),e[53]=m.isPending,e[54]=b,e[55]=X,e[56]=Z):Z=e[56];let Q;e[57]!==I||e[58]!==V||e[59]!==q||e[60]!==Y||e[61]!==Z||e[62]!==A?(Q=(0,p.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:A,children:[I,V,q,Y,Z]}),e[57]=I,e[58]=V,e[59]=q,e[60]=Y,e[61]=Z,e[62]=A,e[63]=Q):Q=e[63];let $;return e[64]!==Q||e[65]!==k?($=(0,p.jsxs)(p.Fragment,{children:[D,O,k,Q]}),e[64]=Q,e[65]=k,e[66]=$):$=e[66],$}function se(e){let t=e(`policyName`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=e(`policyDocument`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-policy`},{type:`flag`,value:` \\ --policy-name`},{type:`value`,value:` ${n||``}`}];return i&&s.push({type:`flag`,value:` \\ --description`},{type:`value`,value:` "${i}"`}),s.push({type:`flag`,value:` \\ - --policy-document`},{type:`value`,value:` '${o||``}'`}),[{label:`Create Policy`,parts:s}]}export{v as component}; \ No newline at end of file + --policy-document`},{type:`value`,value:` '${o||``}'`}),[{label:`Create Policy`,parts:s}]}export{m as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-role.js b/spinifex/services/spinifexui/frontend/dist/assets/create-role.js new file mode 100644 index 000000000..775a39d7a --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-role.js @@ -0,0 +1,4 @@ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{s as a}from"./iam.js";import{Ir as o,Rn as s}from"./index.js";import{a as ee,i as te,t as ne}from"./zod.js";import{t as re}from"./form-actions.js";import{t as ie}from"./page-heading.js";import{a as c,r as l,t as u}from"./field.js";import{t as d}from"./textarea.js";import{i as f,t as ae}from"./iam2.js";var oe=t(),p=e();function m(){let e=(0,oe.c)(67),t=o(),m=a(),h;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(h={resolver:ne(f),defaultValues:{assumeRolePolicyDocument:ae}},e[0]=h):h=e[0];let{handleSubmit:g,register:_,control:v,formState:ce}=te(h),{errors:y,isSubmitting:b}=ce,x;e[1]===v?x=e[2]:(x={control:v},e[1]=v,e[2]=x);let S=ee(x),C;e[3]===S?C=e[4]:(C=e=>e?S[e]:void 0,e[3]=S,e[4]=C);let w=C,T;e[5]!==m||e[6]!==t?(T=async e=>{await m.mutateAsync(e),t({to:`/iam/list-roles`})},e[5]=m,e[6]=t,e[7]=T):T=e[7];let E=T,D,O;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,p.jsx)(r,{to:`/iam/list-roles`,children:`Back to roles`}),O=(0,p.jsx)(ie,{title:`Create Role`}),e[8]=D,e[9]=O):(D=e[8],O=e[9]);let k;e[10]===m.error?k=e[11]:(k=m.error&&(0,p.jsx)(n,{error:m.error,msg:`Failed to create role`}),e[10]=m.error,e[11]=k);let A;e[12]!==g||e[13]!==E?(A=g(E),e[12]=g,e[13]=E,e[14]=A):A=e[14];let j;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`roleName`,children:`Role Name`})}),e[15]=j):j=e[15];let M=!!y.roleName,N;e[16]===_?N=e[17]:(N=_(`roleName`),e[16]=_,e[17]=N);let P;e[18]!==M||e[19]!==N?(P=(0,p.jsx)(s,{"aria-invalid":M,id:`roleName`,placeholder:`my-role...`,...N}),e[18]=M,e[19]=N,e[20]=P):P=e[20];let F;e[21]===y.roleName?F=e[22]:(F=(0,p.jsx)(l,{errors:[y.roleName]}),e[21]=y.roleName,e[22]=F);let I;e[23]!==P||e[24]!==F?(I=(0,p.jsxs)(u,{children:[j,P,F]}),e[23]=P,e[24]=F,e[25]=I):I=e[25];let L;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),e[26]=L):L=e[26];let R;e[27]===_?R=e[28]:(R=_(`description`),e[27]=_,e[28]=R);let z;e[29]===R?z=e[30]:(z=(0,p.jsx)(s,{id:`description`,placeholder:`A brief description of this role...`,...R}),e[29]=R,e[30]=z);let B;e[31]===y.description?B=e[32]:(B=(0,p.jsx)(l,{errors:[y.description]}),e[31]=y.description,e[32]=B);let V;e[33]!==z||e[34]!==B?(V=(0,p.jsxs)(u,{children:[L,z,B]}),e[33]=z,e[34]=B,e[35]=V):V=e[35];let H;e[36]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`assumeRolePolicyDocument`,children:`Trust Policy (JSON)`})}),e[36]=H):H=e[36];let U=!!y.assumeRolePolicyDocument,W;e[37]===_?W=e[38]:(W=_(`assumeRolePolicyDocument`),e[37]=_,e[38]=W);let G;e[39]!==U||e[40]!==W?(G=(0,p.jsx)(d,{"aria-invalid":U,className:`font-mono text-sm`,id:`assumeRolePolicyDocument`,rows:15,...W}),e[39]=U,e[40]=W,e[41]=G):G=e[41];let K;e[42]===y.assumeRolePolicyDocument?K=e[43]:(K=(0,p.jsx)(l,{errors:[y.assumeRolePolicyDocument]}),e[42]=y.assumeRolePolicyDocument,e[43]=K);let q;e[44]!==G||e[45]!==K?(q=(0,p.jsxs)(u,{children:[H,G,K]}),e[44]=G,e[45]=K,e[46]=q):q=e[46];let J;e[47]===w?J=e[48]:(J=se(w),e[47]=w,e[48]=J);let Y;e[49]===J?Y=e[50]:(Y=(0,p.jsx)(i,{commands:J}),e[49]=J,e[50]=Y);let X;e[51]===t?X=e[52]:(X=async()=>await t({to:`/iam/list-roles`}),e[51]=t,e[52]=X);let Z;e[53]!==m.isPending||e[54]!==b||e[55]!==X?(Z=(0,p.jsx)(re,{isPending:m.isPending,isSubmitting:b,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create Role`}),e[53]=m.isPending,e[54]=b,e[55]=X,e[56]=Z):Z=e[56];let Q;e[57]!==I||e[58]!==V||e[59]!==q||e[60]!==Y||e[61]!==Z||e[62]!==A?(Q=(0,p.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:A,children:[I,V,q,Y,Z]}),e[57]=I,e[58]=V,e[59]=q,e[60]=Y,e[61]=Z,e[62]=A,e[63]=Q):Q=e[63];let $;return e[64]!==Q||e[65]!==k?($=(0,p.jsxs)(p.Fragment,{children:[D,O,k,Q]}),e[64]=Q,e[65]=k,e[66]=$):$=e[66],$}function se(e){let t=e(`roleName`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=e(`assumeRolePolicyDocument`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-role`},{type:`flag`,value:` \\ + --role-name`},{type:`value`,value:` ${n||``}`}];return i&&s.push({type:`flag`,value:` \\ + --description`},{type:`value`,value:` "${i}"`}),s.push({type:`flag`,value:` \\ + --assume-role-policy-document`},{type:`value`,value:` '${o||``}'`}),[{label:`Create Role`,parts:s}]}export{m as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-route-table.js b/spinifex/services/spinifexui/frontend/dist/assets/create-route-table.js index 35346712d..de6c04793 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-route-table.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-route-table.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as a,s as o,t as s}from"./select.js";import{g as c}from"./ec2.js";import{Fo as l,Ho as u,Ro as d,Xi as f,ao as p,ht as m}from"./index.js";import{i as h,n as g,t as _}from"./zod.js";import{t as v}from"./form-actions.js";import{t as y}from"./page-heading.js";import{a as b,r as x,t as S}from"./field.js";import{u as C}from"./ec22.js";var w=l(),T=u();function E(){let i=(0,w.c)(44),l=d(),{data:u}=t(m),p=c(),E;i[0]===u.Vpcs?E=i[1]:(E=u.Vpcs??[],i[0]=u.Vpcs,i[1]=E);let O=E,k;i[2]===Symbol.for(`react.memo_cache_sentinel`)?(k={resolver:_(C),defaultValues:{vpcId:``,name:``}},i[2]=k):k=i[2];let{control:A,handleSubmit:j,register:M,formState:N}=h(k),{errors:P,isSubmitting:F}=N,I;i[3]!==p||i[4]!==l?(I=async e=>{let t=(await p.mutateAsync({vpcId:e.vpcId,name:e.name?.trim()?e.name.trim():void 0})).RouteTable?.RouteTableId;l(t?{to:`/ec2/describe-route-tables/$id`,params:{id:t}}:{to:`/ec2/describe-route-tables`})},i[3]=p,i[4]=l,i[5]=I):I=i[5];let L=I,R,z;i[6]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,T.jsx)(n,{to:`/ec2/describe-route-tables`,children:`Back to Route Tables`}),z=(0,T.jsx)(y,{title:`Create Route Table`}),i[6]=R,i[7]=z):(R=i[6],z=i[7]);let B;i[8]===p.error?B=i[9]:(B=p.error&&(0,T.jsx)(e,{error:p.error,msg:`Failed to create Route Table`}),i[8]=p.error,i[9]=B);let V;i[10]!==j||i[11]!==L?(V=j(L),i[10]=j,i[11]=L,i[12]=V):V=i[12];let H;i[13]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,T.jsx)(b,{children:(0,T.jsx)(`label`,{htmlFor:`name`,children:`Name (optional)`})}),i[13]=H):H=i[13];let U;i[14]===M?U=i[15]:(U=M(`name`),i[14]=M,i[15]=U);let W;i[16]===U?W=i[17]:(W=(0,T.jsxs)(S,{children:[H,(0,T.jsx)(f,{id:`name`,placeholder:`my-route-table`,...U})]}),i[16]=U,i[17]=W);let G;i[18]===Symbol.for(`react.memo_cache_sentinel`)?(G=(0,T.jsx)(b,{children:(0,T.jsx)(`label`,{htmlFor:`vpcId`,children:`VPC`})}),i[18]=G):G=i[18];let K;i[19]!==P.vpcId||i[20]!==O?(K=e=>{let{field:t}=e;return(0,T.jsxs)(s,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,T.jsx)(o,{"aria-invalid":!!P.vpcId,className:`w-full`,id:`vpcId`,children:(0,T.jsx)(r,{placeholder:`Select a VPC`})}),(0,T.jsx)(a,{children:O.map(D)})]})},i[19]=P.vpcId,i[20]=O,i[21]=K):K=i[21];let q;i[22]!==A||i[23]!==K?(q=(0,T.jsx)(g,{control:A,name:`vpcId`,render:K}),i[22]=A,i[23]=K,i[24]=q):q=i[24];let J;i[25]===P.vpcId?J=i[26]:(J=(0,T.jsx)(x,{errors:[P.vpcId]}),i[25]=P.vpcId,i[26]=J);let Y;i[27]!==q||i[28]!==J?(Y=(0,T.jsxs)(S,{children:[G,q,J]}),i[27]=q,i[28]=J,i[29]=Y):Y=i[29];let X;i[30]===l?X=i[31]:(X=async()=>await l({to:`/ec2/describe-route-tables`}),i[30]=l,i[31]=X);let Z;i[32]!==p.isPending||i[33]!==F||i[34]!==X?(Z=(0,T.jsx)(v,{isPending:p.isPending,isSubmitting:F,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Route Table`}),i[32]=p.isPending,i[33]=F,i[34]=X,i[35]=Z):Z=i[35];let Q;i[36]!==W||i[37]!==Y||i[38]!==Z||i[39]!==V?(Q=(0,T.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:V,children:[W,Y,Z]}),i[36]=W,i[37]=Y,i[38]=Z,i[39]=V,i[40]=Q):Q=i[40];let $;return i[41]!==Q||i[42]!==B?($=(0,T.jsxs)(T.Fragment,{children:[R,z,B,Q]}),i[41]=Q,i[42]=B,i[43]=$):$=i[43],$}function D(e){let t=p(e.Tags);return(0,T.jsx)(i,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)}export{E as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./back-link.js";import{c as o,i as s,n as c,s as l,t as u}from"./select.js";import{_ as d}from"./ec2.js";import{Ir as f,Lt as p,Rn as m}from"./index.js";import{i as h,n as g,t as _}from"./zod.js";import{t as v}from"./form-actions.js";import{t as y}from"./page-heading.js";import{a as b,r as x,t as S}from"./field.js";import{u as C}from"./ec22.js";var w=t(),T=e();function E(){let e=(0,w.c)(44),t=f(),{data:i}=r(p),s=d(),E;e[0]===i.Vpcs?E=e[1]:(E=i.Vpcs??[],e[0]=i.Vpcs,e[1]=E);let O=E,k;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(k={resolver:_(C),defaultValues:{vpcId:``,name:``}},e[2]=k):k=e[2];let{control:A,handleSubmit:j,register:M,formState:N}=h(k),{errors:P,isSubmitting:F}=N,I;e[3]!==s||e[4]!==t?(I=async e=>{let n=(await s.mutateAsync({vpcId:e.vpcId,name:e.name?.trim()?e.name.trim():void 0})).RouteTable?.RouteTableId;t(n?{to:`/ec2/describe-route-tables/$id`,params:{id:n}}:{to:`/ec2/describe-route-tables`})},e[3]=s,e[4]=t,e[5]=I):I=e[5];let L=I,R,z;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,T.jsx)(a,{to:`/ec2/describe-route-tables`,children:`Back to Route Tables`}),z=(0,T.jsx)(y,{title:`Create Route Table`}),e[6]=R,e[7]=z):(R=e[6],z=e[7]);let B;e[8]===s.error?B=e[9]:(B=s.error&&(0,T.jsx)(n,{error:s.error,msg:`Failed to create Route Table`}),e[8]=s.error,e[9]=B);let V;e[10]!==j||e[11]!==L?(V=j(L),e[10]=j,e[11]=L,e[12]=V):V=e[12];let H;e[13]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,T.jsx)(b,{children:(0,T.jsx)(`label`,{htmlFor:`name`,children:`Name (optional)`})}),e[13]=H):H=e[13];let U;e[14]===M?U=e[15]:(U=M(`name`),e[14]=M,e[15]=U);let W;e[16]===U?W=e[17]:(W=(0,T.jsxs)(S,{children:[H,(0,T.jsx)(m,{id:`name`,placeholder:`my-route-table`,...U})]}),e[16]=U,e[17]=W);let G;e[18]===Symbol.for(`react.memo_cache_sentinel`)?(G=(0,T.jsx)(b,{children:(0,T.jsx)(`label`,{htmlFor:`vpcId`,children:`VPC`})}),e[18]=G):G=e[18];let K;e[19]!==P.vpcId||e[20]!==O?(K=e=>{let{field:t}=e;return(0,T.jsxs)(u,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,T.jsx)(l,{"aria-invalid":!!P.vpcId,className:`w-full`,id:`vpcId`,children:(0,T.jsx)(o,{placeholder:`Select a VPC`})}),(0,T.jsx)(c,{children:O.map(D)})]})},e[19]=P.vpcId,e[20]=O,e[21]=K):K=e[21];let q;e[22]!==A||e[23]!==K?(q=(0,T.jsx)(g,{control:A,name:`vpcId`,render:K}),e[22]=A,e[23]=K,e[24]=q):q=e[24];let J;e[25]===P.vpcId?J=e[26]:(J=(0,T.jsx)(x,{errors:[P.vpcId]}),e[25]=P.vpcId,e[26]=J);let Y;e[27]!==q||e[28]!==J?(Y=(0,T.jsxs)(S,{children:[G,q,J]}),e[27]=q,e[28]=J,e[29]=Y):Y=e[29];let X;e[30]===t?X=e[31]:(X=async()=>await t({to:`/ec2/describe-route-tables`}),e[30]=t,e[31]=X);let Z;e[32]!==s.isPending||e[33]!==F||e[34]!==X?(Z=(0,T.jsx)(v,{isPending:s.isPending,isSubmitting:F,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Route Table`}),e[32]=s.isPending,e[33]=F,e[34]=X,e[35]=Z):Z=e[35];let Q;e[36]!==W||e[37]!==Y||e[38]!==Z||e[39]!==V?(Q=(0,T.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:V,children:[W,Y,Z]}),e[36]=W,e[37]=Y,e[38]=Z,e[39]=V,e[40]=Q):Q=e[40];let $;return e[41]!==Q||e[42]!==B?($=(0,T.jsxs)(T.Fragment,{children:[R,z,B,Q]}),e[41]=Q,e[42]=B,e[43]=$):$=e[43],$}function D(e){let t=i(e.Tags);return(0,T.jsx)(s,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)}export{E as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-security-group.js b/spinifex/services/spinifexui/frontend/dist/assets/create-security-group.js index 2f07aeab8..8cee6f577 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-security-group.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-security-group.js @@ -1,4 +1,4 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as ee}from"./back-link.js";import{c as n,i as r,n as i,s as a,t as o}from"./select.js";import{t as te}from"./cli-command-panel.js";import{_ as ne}from"./ec2.js";import{Fo as s,Ho as c,Ro as re,Xi as ie,ao as l,ht as ae}from"./index.js";import{i as oe,n as se,t as ce}from"./zod.js";import{t as le}from"./form-actions.js";import{t as ue}from"./page-heading.js";import{a as u,r as d,t as f}from"./field.js";import{d as de}from"./ec22.js";var fe=s(),p=c();function m(){let r=(0,fe.c)(69),s=re(),{data:c}=t(ae),l=ne(),m;r[0]===c.Vpcs?m=r[1]:(m=c.Vpcs??[],r[0]=c.Vpcs,r[1]=m);let h=m,g;r[2]===Symbol.for(`react.memo_cache_sentinel`)?(g=ce(de),r[2]=g):g=r[2];let _=h[0]?.VpcId??``,v;r[3]===_?v=r[4]:(v={resolver:g,defaultValues:{groupName:``,description:``,vpcId:_}},r[3]=_,r[4]=v);let{control:y,handleSubmit:b,register:x,watch:S,formState:he}=oe(v),{errors:C,isSubmitting:w}=he,T;r[5]!==l||r[6]!==s?(T=async e=>{let t=(await l.mutateAsync(e)).GroupId;s(t?{to:`/ec2/describe-security-groups/$id`,params:{id:t}}:{to:`/ec2/describe-security-groups`})},r[5]=l,r[6]=s,r[7]=T):T=r[7];let E=T,D,O;r[8]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,p.jsx)(ee,{to:`/ec2/describe-security-groups`,children:`Back to Security Groups`}),O=(0,p.jsx)(ue,{title:`Create Security Group`}),r[8]=D,r[9]=O):(D=r[8],O=r[9]);let k;r[10]===l.error?k=r[11]:(k=l.error&&(0,p.jsx)(e,{error:l.error,msg:`Failed to create security group`}),r[10]=l.error,r[11]=k);let A;r[12]!==b||r[13]!==E?(A=b(E),r[12]=b,r[13]=E,r[14]=A):A=r[14];let j;r[15]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,p.jsx)(u,{children:(0,p.jsx)(`label`,{htmlFor:`groupName`,children:`Group Name`})}),r[15]=j):j=r[15];let M=!!C.groupName,N;r[16]===x?N=r[17]:(N=x(`groupName`),r[16]=x,r[17]=N);let P;r[18]!==M||r[19]!==N?(P=(0,p.jsx)(ie,{"aria-invalid":M,id:`groupName`,placeholder:`my-security-group`,...N}),r[18]=M,r[19]=N,r[20]=P):P=r[20];let F;r[21]===C.groupName?F=r[22]:(F=(0,p.jsx)(d,{errors:[C.groupName]}),r[21]=C.groupName,r[22]=F);let I;r[23]!==P||r[24]!==F?(I=(0,p.jsxs)(f,{children:[j,P,F]}),r[23]=P,r[24]=F,r[25]=I):I=r[25];let L;r[26]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,p.jsx)(u,{children:(0,p.jsx)(`label`,{htmlFor:`description`,children:`Description`})}),r[26]=L):L=r[26];let R=!!C.description,z;r[27]===x?z=r[28]:(z=x(`description`),r[27]=x,r[28]=z);let B;r[29]!==R||r[30]!==z?(B=(0,p.jsx)(ie,{"aria-invalid":R,id:`description`,placeholder:`My security group description`,...z}),r[29]=R,r[30]=z,r[31]=B):B=r[31];let V;r[32]===C.description?V=r[33]:(V=(0,p.jsx)(d,{errors:[C.description]}),r[32]=C.description,r[33]=V);let H;r[34]!==B||r[35]!==V?(H=(0,p.jsxs)(f,{children:[L,B,V]}),r[34]=B,r[35]=V,r[36]=H):H=r[36];let U;r[37]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,p.jsx)(u,{children:(0,p.jsx)(`label`,{htmlFor:`vpcId`,children:`VPC`})}),r[37]=U):U=r[37];let W;r[38]!==C.vpcId||r[39]!==h?(W=e=>{let{field:t}=e;return(0,p.jsxs)(o,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,p.jsx)(a,{"aria-invalid":!!C.vpcId,className:`w-full`,id:`vpcId`,children:(0,p.jsx)(n,{})}),(0,p.jsx)(i,{children:h.map(pe)})]})},r[38]=C.vpcId,r[39]=h,r[40]=W):W=r[40];let G;r[41]!==y||r[42]!==W?(G=(0,p.jsx)(se,{control:y,name:`vpcId`,render:W}),r[41]=y,r[42]=W,r[43]=G):G=r[43];let K;r[44]===C.vpcId?K=r[45]:(K=(0,p.jsx)(d,{errors:[C.vpcId]}),r[44]=C.vpcId,r[45]=K);let q;r[46]!==G||r[47]!==K?(q=(0,p.jsxs)(f,{children:[U,G,K]}),r[46]=G,r[47]=K,r[48]=q):q=r[48];let J;r[49]===S?J=r[50]:(J=me(S),r[49]=S,r[50]=J);let Y;r[51]===J?Y=r[52]:(Y=(0,p.jsx)(te,{commands:J}),r[51]=J,r[52]=Y);let X;r[53]===s?X=r[54]:(X=async()=>await s({to:`/ec2/describe-security-groups`}),r[53]=s,r[54]=X);let Z;r[55]!==l.isPending||r[56]!==w||r[57]!==X?(Z=(0,p.jsx)(le,{isPending:l.isPending,isSubmitting:w,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Security Group`}),r[55]=l.isPending,r[56]=w,r[57]=X,r[58]=Z):Z=r[58];let Q;r[59]!==I||r[60]!==H||r[61]!==q||r[62]!==Y||r[63]!==Z||r[64]!==A?(Q=(0,p.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:A,children:[I,H,q,Y,Z]}),r[59]=I,r[60]=H,r[61]=q,r[62]=Y,r[63]=Z,r[64]=A,r[65]=Q):Q=r[65];let $;return r[66]!==Q||r[67]!==k?($=(0,p.jsxs)(p.Fragment,{children:[D,O,k,Q]}),r[66]=Q,r[67]=k,r[68]=$):$=r[68],$}function pe(e){let t=l(e.Tags);return(0,p.jsx)(r,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)}function me(e){let t=e(`groupName`),ee=typeof t==`string`?t:``,n=e(`description`),r=typeof n==`string`?n:``,i=e(`vpcId`),a=typeof i==`string`?i:``,o=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-security-group`},{type:`flag`,value:` \\ - --group-name`},{type:`value`,value:` ${ee||``}`},{type:`flag`,value:` \\ - --description`},{type:`value`,value:` "${r||``}"`}];return a&&o.push({type:`flag`,value:` \\ - --vpc-id`},{type:`value`,value:` ${a}`}),[{label:`Create Security Group`,parts:o}]}export{m as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./back-link.js";import{c as o,i as s,n as ee,s as te,t as ne}from"./select.js";import{t as re}from"./cli-command-panel.js";import{v as ie}from"./ec2.js";import{Ir as ae,Lt as oe,Rn as se}from"./index.js";import{a as ce,i as le,n as ue,t as de}from"./zod.js";import{t as fe}from"./form-actions.js";import{t as pe}from"./page-heading.js";import{a as c,r as l,t as u}from"./field.js";import{d as me}from"./ec22.js";var he=t(),d=e();function f(){let e=(0,he.c)(73),t=ae(),{data:i}=r(oe),s=ie(),f;e[0]===i.Vpcs?f=e[1]:(f=i.Vpcs??[],e[0]=i.Vpcs,e[1]=f);let p=f,m;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(m=de(me),e[2]=m):m=e[2];let h=p[0]?.VpcId??``,g;e[3]===h?g=e[4]:(g={resolver:m,defaultValues:{groupName:``,description:``,vpcId:h}},e[3]=h,e[4]=g);let{control:_,handleSubmit:v,register:y,formState:ve}=le(g),{errors:b,isSubmitting:ye}=ve,x;e[5]===_?x=e[6]:(x={control:_},e[5]=_,e[6]=x);let S=ce(x),C;e[7]===S?C=e[8]:(C=e=>e?S[e]:void 0,e[7]=S,e[8]=C);let w=C,T;e[9]!==s||e[10]!==t?(T=async e=>{let n=(await s.mutateAsync(e)).GroupId;t(n?{to:`/ec2/describe-security-groups/$id`,params:{id:n}}:{to:`/ec2/describe-security-groups`})},e[9]=s,e[10]=t,e[11]=T):T=e[11];let E=T,D,O;e[12]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,d.jsx)(a,{to:`/ec2/describe-security-groups`,children:`Back to Security Groups`}),O=(0,d.jsx)(pe,{title:`Create Security Group`}),e[12]=D,e[13]=O):(D=e[12],O=e[13]);let k;e[14]===s.error?k=e[15]:(k=s.error&&(0,d.jsx)(n,{error:s.error,msg:`Failed to create security group`}),e[14]=s.error,e[15]=k);let A;e[16]!==v||e[17]!==E?(A=v(E),e[16]=v,e[17]=E,e[18]=A):A=e[18];let j;e[19]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,d.jsx)(c,{children:(0,d.jsx)(`label`,{htmlFor:`groupName`,children:`Group Name`})}),e[19]=j):j=e[19];let M=!!b.groupName,N;e[20]===y?N=e[21]:(N=y(`groupName`),e[20]=y,e[21]=N);let P;e[22]!==M||e[23]!==N?(P=(0,d.jsx)(se,{"aria-invalid":M,id:`groupName`,placeholder:`my-security-group`,...N}),e[22]=M,e[23]=N,e[24]=P):P=e[24];let F;e[25]===b.groupName?F=e[26]:(F=(0,d.jsx)(l,{errors:[b.groupName]}),e[25]=b.groupName,e[26]=F);let I;e[27]!==P||e[28]!==F?(I=(0,d.jsxs)(u,{children:[j,P,F]}),e[27]=P,e[28]=F,e[29]=I):I=e[29];let L;e[30]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,d.jsx)(c,{children:(0,d.jsx)(`label`,{htmlFor:`description`,children:`Description`})}),e[30]=L):L=e[30];let R=!!b.description,z;e[31]===y?z=e[32]:(z=y(`description`),e[31]=y,e[32]=z);let B;e[33]!==R||e[34]!==z?(B=(0,d.jsx)(se,{"aria-invalid":R,id:`description`,placeholder:`My security group description`,...z}),e[33]=R,e[34]=z,e[35]=B):B=e[35];let V;e[36]===b.description?V=e[37]:(V=(0,d.jsx)(l,{errors:[b.description]}),e[36]=b.description,e[37]=V);let H;e[38]!==B||e[39]!==V?(H=(0,d.jsxs)(u,{children:[L,B,V]}),e[38]=B,e[39]=V,e[40]=H):H=e[40];let U;e[41]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,d.jsx)(c,{children:(0,d.jsx)(`label`,{htmlFor:`vpcId`,children:`VPC`})}),e[41]=U):U=e[41];let W;e[42]!==b.vpcId||e[43]!==p?(W=e=>{let{field:t}=e;return(0,d.jsxs)(ne,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,d.jsx)(te,{"aria-invalid":!!b.vpcId,className:`w-full`,id:`vpcId`,children:(0,d.jsx)(o,{})}),(0,d.jsx)(ee,{children:p.map(ge)})]})},e[42]=b.vpcId,e[43]=p,e[44]=W):W=e[44];let G;e[45]!==_||e[46]!==W?(G=(0,d.jsx)(ue,{control:_,name:`vpcId`,render:W}),e[45]=_,e[46]=W,e[47]=G):G=e[47];let K;e[48]===b.vpcId?K=e[49]:(K=(0,d.jsx)(l,{errors:[b.vpcId]}),e[48]=b.vpcId,e[49]=K);let q;e[50]!==G||e[51]!==K?(q=(0,d.jsxs)(u,{children:[U,G,K]}),e[50]=G,e[51]=K,e[52]=q):q=e[52];let J;e[53]===w?J=e[54]:(J=_e(w),e[53]=w,e[54]=J);let Y;e[55]===J?Y=e[56]:(Y=(0,d.jsx)(re,{commands:J}),e[55]=J,e[56]=Y);let X;e[57]===t?X=e[58]:(X=async()=>await t({to:`/ec2/describe-security-groups`}),e[57]=t,e[58]=X);let Z;e[59]!==s.isPending||e[60]!==ye||e[61]!==X?(Z=(0,d.jsx)(fe,{isPending:s.isPending,isSubmitting:ye,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Security Group`}),e[59]=s.isPending,e[60]=ye,e[61]=X,e[62]=Z):Z=e[62];let Q;e[63]!==A||e[64]!==I||e[65]!==H||e[66]!==q||e[67]!==Y||e[68]!==Z?(Q=(0,d.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:A,children:[I,H,q,Y,Z]}),e[63]=A,e[64]=I,e[65]=H,e[66]=q,e[67]=Y,e[68]=Z,e[69]=Q):Q=e[69];let $;return e[70]!==k||e[71]!==Q?($=(0,d.jsxs)(d.Fragment,{children:[D,O,k,Q]}),e[70]=k,e[71]=Q,e[72]=$):$=e[72],$}function ge(e){let t=i(e.Tags);return(0,d.jsx)(s,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)}function _e(e){let t=e(`groupName`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=e(`vpcId`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-security-group`},{type:`flag`,value:` \\ + --group-name`},{type:`value`,value:` ${n||``}`},{type:`flag`,value:` \\ + --description`},{type:`value`,value:` "${i||``}"`}];return o&&s.push({type:`flag`,value:` \\ + --vpc-id`},{type:`value`,value:` ${o}`}),[{label:`Create Security Group`,parts:s}]}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-snapshot.js b/spinifex/services/spinifexui/frontend/dist/assets/create-snapshot.js index 1db8ed568..8b408aed5 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-snapshot.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-snapshot.js @@ -1,3 +1,3 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as a,s as o,t as s}from"./select.js";import{t as c}from"./cli-command-panel.js";import{v as l}from"./ec2.js";import{Fo as u,Ho as d,R as f,Ro as p,pt as ee}from"./index.js";import{i as te,n as ne,t as re}from"./zod.js";import{t as ie}from"./form-actions.js";import{t as ae}from"./page-heading.js";import{a as m,r as oe,t as h}from"./field.js";import{f as g}from"./ec22.js";import{t as _}from"./textarea.js";var v=u(),y=d();function b(){let i=(0,v.c)(51),u=p(),{data:d}=t(ee),b=l(),x;i[0]===d.Volumes?x=i[1]:(x=d.Volumes??[],i[0]=d.Volumes,i[1]=x);let S=x,{volumeId:C}=f.useSearch(),w=C??S[0]?.VolumeId??``,T;i[2]===Symbol.for(`react.memo_cache_sentinel`)?(T=re(g),i[2]=T):T=i[2];let E;i[3]===w?E=i[4]:(E={resolver:T,defaultValues:{volumeId:w,description:``}},i[3]=w,i[4]=E);let{control:D,handleSubmit:O,register:k,watch:A,formState:j}=te(E),{errors:M,isSubmitting:N}=j,P;i[5]!==b||i[6]!==u?(P=async e=>{await b.mutateAsync(e),u({to:`/ec2/describe-snapshots`})},i[5]=b,i[6]=u,i[7]=P):P=i[7];let F=P,I,L;i[8]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,y.jsx)(n,{to:`/ec2/describe-snapshots`,children:`Back to snapshots`}),L=(0,y.jsx)(ae,{title:`Create Snapshot`}),i[8]=I,i[9]=L):(I=i[8],L=i[9]);let R;i[10]===b.error?R=i[11]:(R=b.error&&(0,y.jsx)(e,{error:b.error,msg:`Failed to create snapshot`}),i[10]=b.error,i[11]=R);let z;i[12]!==O||i[13]!==F?(z=O(F),i[12]=O,i[13]=F,i[14]=z):z=i[14];let B;i[15]===Symbol.for(`react.memo_cache_sentinel`)?(B=(0,y.jsx)(m,{children:(0,y.jsx)(`label`,{htmlFor:`volumeId`,children:`Volume`})}),i[15]=B):B=i[15];let V;i[16]!==M.volumeId||i[17]!==S?(V=e=>{let{field:t}=e;return(0,y.jsxs)(s,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,y.jsx)(o,{"aria-invalid":!!M.volumeId,className:`w-full`,id:`volumeId`,children:(0,y.jsx)(r,{})}),(0,y.jsx)(a,{children:S.map(se)})]})},i[16]=M.volumeId,i[17]=S,i[18]=V):V=i[18];let H;i[19]!==D||i[20]!==V?(H=(0,y.jsx)(ne,{control:D,name:`volumeId`,render:V}),i[19]=D,i[20]=V,i[21]=H):H=i[21];let U;i[22]===M.volumeId?U=i[23]:(U=(0,y.jsx)(oe,{errors:[M.volumeId]}),i[22]=M.volumeId,i[23]=U);let W;i[24]!==H||i[25]!==U?(W=(0,y.jsxs)(h,{children:[B,H,U]}),i[24]=H,i[25]=U,i[26]=W):W=i[26];let G;i[27]===Symbol.for(`react.memo_cache_sentinel`)?(G=(0,y.jsx)(m,{children:(0,y.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),i[27]=G):G=i[27];let K;i[28]===k?K=i[29]:(K=k(`description`),i[28]=k,i[29]=K);let q;i[30]===K?q=i[31]:(q=(0,y.jsxs)(h,{children:[G,(0,y.jsx)(_,{id:`description`,placeholder:`Snapshot description`,...K})]}),i[30]=K,i[31]=q);let J;i[32]===A?J=i[33]:(J=ce(A),i[32]=A,i[33]=J);let Y;i[34]===J?Y=i[35]:(Y=(0,y.jsx)(c,{commands:J}),i[34]=J,i[35]=Y);let X;i[36]===u?X=i[37]:(X=async()=>await u({to:`/ec2/describe-snapshots`}),i[36]=u,i[37]=X);let Z;i[38]!==b.isPending||i[39]!==N||i[40]!==X?(Z=(0,y.jsx)(ie,{isPending:b.isPending,isSubmitting:N,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Snapshot`}),i[38]=b.isPending,i[39]=N,i[40]=X,i[41]=Z):Z=i[41];let Q;i[42]!==W||i[43]!==q||i[44]!==Y||i[45]!==Z||i[46]!==z?(Q=(0,y.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:z,children:[W,q,Y,Z]}),i[42]=W,i[43]=q,i[44]=Y,i[45]=Z,i[46]=z,i[47]=Q):Q=i[47];let $;return i[48]!==Q||i[49]!==R?($=(0,y.jsxs)(y.Fragment,{children:[I,L,R,Q]}),i[48]=Q,i[49]=R,i[50]=$):$=i[50],$}function se(e){return(0,y.jsxs)(i,{value:e.VolumeId??``,children:[e.VolumeId,` (`,e.Size,` GiB)`]},e.VolumeId)}function ce(e){let t=e(`volumeId`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-snapshot`},{type:`flag`,value:` \\ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{t as i}from"./back-link.js";import{c as a,i as o,n as s,s as c,t as ee}from"./select.js";import{t as te}from"./cli-command-panel.js";import{y as ne}from"./ec2.js";import{Ft as re,Ir as ie,K as ae}from"./index.js";import{a as oe,i as se,n as ce,t as le}from"./zod.js";import{t as ue}from"./form-actions.js";import{t as l}from"./page-heading.js";import{a as u,r as d,t as f}from"./field.js";import{f as p}from"./ec22.js";import{t as m}from"./textarea.js";var h=t(),g=e();function _(){let e=(0,h.c)(55),t=ie(),{data:o}=r(re),_=ne(),v;e[0]===o.Volumes?v=e[1]:(v=o.Volumes??[],e[0]=o.Volumes,e[1]=v);let y=v,{volumeId:b}=ae.useSearch(),x=b??y[0]?.VolumeId??``,S;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(S=le(p),e[2]=S):S=e[2];let C;e[3]===x?C=e[4]:(C={resolver:S,defaultValues:{volumeId:x,description:``}},e[3]=x,e[4]=C);let{control:w,handleSubmit:T,register:E,formState:D}=se(C),{errors:O,isSubmitting:k}=D,A;e[5]===w?A=e[6]:(A={control:w},e[5]=w,e[6]=A);let j=oe(A),M;e[7]===j?M=e[8]:(M=e=>e?j[e]:void 0,e[7]=j,e[8]=M);let N=M,P;e[9]!==_||e[10]!==t?(P=async e=>{await _.mutateAsync(e),t({to:`/ec2/describe-snapshots`})},e[9]=_,e[10]=t,e[11]=P):P=e[11];let F=P,I,L;e[12]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,g.jsx)(i,{to:`/ec2/describe-snapshots`,children:`Back to snapshots`}),L=(0,g.jsx)(l,{title:`Create Snapshot`}),e[12]=I,e[13]=L):(I=e[12],L=e[13]);let R;e[14]===_.error?R=e[15]:(R=_.error&&(0,g.jsx)(n,{error:_.error,msg:`Failed to create snapshot`}),e[14]=_.error,e[15]=R);let z;e[16]!==T||e[17]!==F?(z=T(F),e[16]=T,e[17]=F,e[18]=z):z=e[18];let B;e[19]===Symbol.for(`react.memo_cache_sentinel`)?(B=(0,g.jsx)(u,{children:(0,g.jsx)(`label`,{htmlFor:`volumeId`,children:`Volume`})}),e[19]=B):B=e[19];let V;e[20]!==O.volumeId||e[21]!==y?(V=e=>{let{field:t}=e;return(0,g.jsxs)(ee,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,g.jsx)(c,{"aria-invalid":!!O.volumeId,className:`w-full`,id:`volumeId`,children:(0,g.jsx)(a,{})}),(0,g.jsx)(s,{children:y.map(de)})]})},e[20]=O.volumeId,e[21]=y,e[22]=V):V=e[22];let H;e[23]!==w||e[24]!==V?(H=(0,g.jsx)(ce,{control:w,name:`volumeId`,render:V}),e[23]=w,e[24]=V,e[25]=H):H=e[25];let U;e[26]===O.volumeId?U=e[27]:(U=(0,g.jsx)(d,{errors:[O.volumeId]}),e[26]=O.volumeId,e[27]=U);let W;e[28]!==H||e[29]!==U?(W=(0,g.jsxs)(f,{children:[B,H,U]}),e[28]=H,e[29]=U,e[30]=W):W=e[30];let G;e[31]===Symbol.for(`react.memo_cache_sentinel`)?(G=(0,g.jsx)(u,{children:(0,g.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),e[31]=G):G=e[31];let K;e[32]===E?K=e[33]:(K=E(`description`),e[32]=E,e[33]=K);let q;e[34]===K?q=e[35]:(q=(0,g.jsxs)(f,{children:[G,(0,g.jsx)(m,{id:`description`,placeholder:`Snapshot description`,...K})]}),e[34]=K,e[35]=q);let J;e[36]===N?J=e[37]:(J=fe(N),e[36]=N,e[37]=J);let Y;e[38]===J?Y=e[39]:(Y=(0,g.jsx)(te,{commands:J}),e[38]=J,e[39]=Y);let X;e[40]===t?X=e[41]:(X=async()=>await t({to:`/ec2/describe-snapshots`}),e[40]=t,e[41]=X);let Z;e[42]!==_.isPending||e[43]!==k||e[44]!==X?(Z=(0,g.jsx)(ue,{isPending:_.isPending,isSubmitting:k,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Snapshot`}),e[42]=_.isPending,e[43]=k,e[44]=X,e[45]=Z):Z=e[45];let Q;e[46]!==z||e[47]!==W||e[48]!==q||e[49]!==Y||e[50]!==Z?(Q=(0,g.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:z,children:[W,q,Y,Z]}),e[46]=z,e[47]=W,e[48]=q,e[49]=Y,e[50]=Z,e[51]=Q):Q=e[51];let $;return e[52]!==Q||e[53]!==R?($=(0,g.jsxs)(g.Fragment,{children:[I,L,R,Q]}),e[52]=Q,e[53]=R,e[54]=$):$=e[54],$}function de(e){return(0,g.jsxs)(o,{value:e.VolumeId??``,children:[e.VolumeId,` (`,e.Size,` GiB)`]},e.VolumeId)}function fe(e){let t=e(`volumeId`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-snapshot`},{type:`flag`,value:` \\ --volume-id`},{type:`value`,value:` ${n||``}`}];return i&&a.push({type:`flag`,value:` \\ - --description`},{type:`value`,value:` "${i}"`}),[{label:`Create Snapshot`,parts:a}]}export{b as component}; \ No newline at end of file + --description`},{type:`value`,value:` "${i}"`}),[{label:`Create Snapshot`,parts:a}]}export{_ as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-subnet.js b/spinifex/services/spinifexui/frontend/dist/assets/create-subnet.js index 5b0c3778b..8fa0b6bd7 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-subnet.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-subnet.js @@ -1,6 +1,6 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as ee}from"./back-link.js";import{c as n,i as r,n as i,s as a,t as o}from"./select.js";import{t as te}from"./cli-command-panel.js";import{y as ne}from"./ec2.js";import{Fo as s,Ho as c,Ro as re,U as ie,Xi as ae,ao as l,ht as oe}from"./index.js";import{i as se,n as u,t as ce}from"./zod.js";import{t as le}from"./form-actions.js";import{t as ue}from"./page-heading.js";import{a as d,r as de,t as f}from"./field.js";import{p as fe}from"./ec22.js";var pe=s(),p=c();function m(){let s=(0,pe.c)(66),c=re(),{data:l}=t(oe),{data:m}=t(ie),h=ne(),g;s[0]===l.Vpcs?g=s[1]:(g=l.Vpcs??[],s[0]=l.Vpcs,s[1]=g);let _=g,v;s[2]===m.AvailabilityZones?v=s[3]:(v=m.AvailabilityZones??[],s[2]=m.AvailabilityZones,s[3]=v);let y=v,b;s[4]===Symbol.for(`react.memo_cache_sentinel`)?(b=ce(fe),s[4]=b):b=s[4];let x=_[0]?.VpcId??``,S;s[5]===x?S=s[6]:(S={resolver:b,defaultValues:{vpcId:x,cidrBlock:`10.0.1.0/24`,availabilityZone:void 0,mapPublicIpOnLaunch:!1}},s[5]=x,s[6]=S);let{control:C,handleSubmit:w,register:T,formState:ve}=se(S),{errors:E,isSubmitting:ye}=ve,D;s[7]!==h||s[8]!==c?(D=async e=>{let t=(await h.mutateAsync(e)).Subnet?.SubnetId;c(t?{to:`/ec2/describe-subnets/$id`,params:{id:t}}:{to:`/ec2/describe-subnets`})},s[7]=h,s[8]=c,s[9]=D):D=s[9];let O=D,k,A;s[10]===Symbol.for(`react.memo_cache_sentinel`)?(k=(0,p.jsx)(ee,{to:`/ec2/describe-subnets`,children:`Back to Subnets`}),A=(0,p.jsx)(ue,{title:`Create Subnet`}),s[10]=k,s[11]=A):(k=s[10],A=s[11]);let j;s[12]===h.error?j=s[13]:(j=h.error&&(0,p.jsx)(e,{error:h.error,msg:`Failed to create subnet`}),s[12]=h.error,s[13]=j);let M;s[14]!==w||s[15]!==O?(M=w(O),s[14]=w,s[15]=O,s[16]=M):M=s[16];let N;s[17]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,p.jsx)(d,{children:(0,p.jsx)(`label`,{htmlFor:`vpcId`,children:`VPC`})}),s[17]=N):N=s[17];let P;s[18]!==E.vpcId||s[19]!==_?(P=e=>{let{field:t}=e;return(0,p.jsxs)(o,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,p.jsx)(a,{"aria-invalid":!!E.vpcId,className:`w-full`,id:`vpcId`,children:(0,p.jsx)(n,{})}),(0,p.jsx)(i,{children:_.map(ge)})]})},s[18]=E.vpcId,s[19]=_,s[20]=P):P=s[20];let F;s[21]!==C||s[22]!==P?(F=(0,p.jsx)(u,{control:C,name:`vpcId`,render:P}),s[21]=C,s[22]=P,s[23]=F):F=s[23];let I;s[24]===E.vpcId?I=s[25]:(I=(0,p.jsx)(de,{errors:[E.vpcId]}),s[24]=E.vpcId,s[25]=I);let L;s[26]!==F||s[27]!==I?(L=(0,p.jsxs)(f,{children:[N,F,I]}),s[26]=F,s[27]=I,s[28]=L):L=s[28];let R;s[29]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,p.jsx)(d,{children:(0,p.jsx)(`label`,{htmlFor:`cidrBlock`,children:`CIDR Block`})}),s[29]=R):R=s[29];let z=!!E.cidrBlock,B;s[30]===T?B=s[31]:(B=T(`cidrBlock`),s[30]=T,s[31]=B);let V;s[32]!==z||s[33]!==B?(V=(0,p.jsx)(ae,{"aria-invalid":z,id:`cidrBlock`,placeholder:`10.0.1.0/24`,...B}),s[32]=z,s[33]=B,s[34]=V):V=s[34];let H;s[35]===E.cidrBlock?H=s[36]:(H=(0,p.jsx)(de,{errors:[E.cidrBlock]}),s[35]=E.cidrBlock,s[36]=H);let U;s[37]!==V||s[38]!==H?(U=(0,p.jsxs)(f,{children:[R,V,H]}),s[37]=V,s[38]=H,s[39]=U):U=s[39];let W;s[40]===Symbol.for(`react.memo_cache_sentinel`)?(W=(0,p.jsx)(d,{children:(0,p.jsx)(`label`,{htmlFor:`availabilityZone`,children:`Availability Zone (optional)`})}),s[40]=W):W=s[40];let G;s[41]===y?G=s[42]:(G=e=>{let{field:t}=e;return(0,p.jsxs)(o,{onValueChange:e=>t.onChange(e===`none`?void 0:e),value:t.value??`none`,children:[(0,p.jsx)(a,{className:`w-full`,id:`availabilityZone`,children:(0,p.jsx)(n,{})}),(0,p.jsxs)(i,{children:[(0,p.jsx)(r,{value:`none`,children:`none`}),y.map(he)]})]})},s[41]=y,s[42]=G);let K;s[43]!==C||s[44]!==G?(K=(0,p.jsxs)(f,{children:[W,(0,p.jsx)(u,{control:C,name:`availabilityZone`,render:G})]}),s[43]=C,s[44]=G,s[45]=K):K=s[45];let q;s[46]===Symbol.for(`react.memo_cache_sentinel`)?(q=(0,p.jsx)(d,{children:`Auto-assign public IPv4`}),s[46]=q):q=s[46];let J;s[47]===C?J=s[48]:(J=(0,p.jsxs)(f,{children:[q,(0,p.jsx)(u,{control:C,name:`mapPublicIpOnLaunch`,render:me})]}),s[47]=C,s[48]=J);let Y;s[49]===Symbol.for(`react.memo_cache_sentinel`)?(Y=(0,p.jsx)(te,{commands:_e(cliWatch)}),s[49]=Y):Y=s[49];let X;s[50]===c?X=s[51]:(X=async()=>await c({to:`/ec2/describe-subnets`}),s[50]=c,s[51]=X);let Z;s[52]!==h.isPending||s[53]!==ye||s[54]!==X?(Z=(0,p.jsx)(le,{isPending:h.isPending,isSubmitting:ye,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Subnet`}),s[52]=h.isPending,s[53]=ye,s[54]=X,s[55]=Z):Z=s[55];let Q;s[56]!==M||s[57]!==L||s[58]!==U||s[59]!==K||s[60]!==J||s[61]!==Z?(Q=(0,p.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:M,children:[L,U,K,J,Y,Z]}),s[56]=M,s[57]=L,s[58]=U,s[59]=K,s[60]=J,s[61]=Z,s[62]=Q):Q=s[62];let $;return s[63]!==Q||s[64]!==j?($=(0,p.jsxs)(p.Fragment,{children:[k,A,j,Q]}),s[63]=Q,s[64]=j,s[65]=$):$=s[65],$}function me(e){let{field:t}=e;return(0,p.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,p.jsx)(`input`,{"aria-label":`Enable auto-assign public IPv4 address`,checked:t.value??!1,onChange:e=>t.onChange(e.target.checked),type:`checkbox`}),`Enable auto-assign public IPv4 address`]})}function he(e){return(0,p.jsx)(r,{value:e.ZoneName??``,children:e.ZoneName},e.ZoneName)}function ge(e){let t=l(e.Tags);return(0,p.jsx)(r,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)}function _e(e){let t=e(`vpcId`),ee=typeof t==`string`?t:``,n=e(`cidrBlock`),r=typeof n==`string`?n:``,i=e(`availabilityZone`),a=typeof i==`string`?i:``,o=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-subnet`},{type:`flag`,value:` \\ - --vpc-id`},{type:`value`,value:` ${ee||``}`},{type:`flag`,value:` \\ - --cidr-block`},{type:`value`,value:` ${r||``}`}];a&&o.push({type:`flag`,value:` \\ - --availability-zone`},{type:`value`,value:` ${a}`});let te=[{label:`Create Subnet`,parts:o}];return e(`mapPublicIpOnLaunch`)===!0&&te.push({label:`Enable auto-assign public IPv4`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 modify-subnet-attribute`},{type:`flag`,value:` \\ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./back-link.js";import{c as o,i as s,n as c,s as ee,t as te}from"./select.js";import{t as ne}from"./cli-command-panel.js";import{b as re}from"./ec2.js";import{Ir as ie,Lt as ae,Rn as oe,ut as se}from"./index.js";import{a as ce,i as le,n as ue,t as de}from"./zod.js";import{t as fe}from"./form-actions.js";import{t as pe}from"./page-heading.js";import{a as l,r as me,t as u}from"./field.js";import{p as he}from"./ec22.js";var ge=t(),d=e();function f(){let e=(0,ge.c)(74),t=ie(),{data:i}=r(ae),{data:f}=r(se),p=re(),m;e[0]===i.Vpcs?m=e[1]:(m=i.Vpcs??[],e[0]=i.Vpcs,e[1]=m);let h=m,g;e[2]===f.AvailabilityZones?g=e[3]:(g=f.AvailabilityZones??[],e[2]=f.AvailabilityZones,e[3]=g);let _=g,v;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(v=de(he),e[4]=v):v=e[4];let y=h[0]?.VpcId??``,b;e[5]===y?b=e[6]:(b={resolver:v,defaultValues:{vpcId:y,cidrBlock:`10.0.1.0/24`,availabilityZone:void 0,mapPublicIpOnLaunch:!1}},e[5]=y,e[6]=b);let{control:x,handleSubmit:xe,register:Se,formState:Ce}=le(b),{errors:S,isSubmitting:we}=Ce,C;e[7]===x?C=e[8]:(C={control:x},e[7]=x,e[8]=C);let w=ce(C),T;e[9]===w?T=e[10]:(T=e=>e?w[e]:void 0,e[9]=w,e[10]=T);let E=T,D;e[11]!==p||e[12]!==t?(D=async e=>{let n=(await p.mutateAsync(e)).Subnet?.SubnetId;t(n?{to:`/ec2/describe-subnets/$id`,params:{id:n}}:{to:`/ec2/describe-subnets`})},e[11]=p,e[12]=t,e[13]=D):D=e[13];let O=D,k,A;e[14]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,d.jsx)(a,{to:`/ec2/describe-subnets`,children:`Back to Subnets`}),k=(0,d.jsx)(pe,{title:`Create Subnet`}),e[14]=k,e[15]=A):(k=e[14],A=e[15]);let j;e[16]===p.error?j=e[17]:(j=p.error&&(0,d.jsx)(n,{error:p.error,msg:`Failed to create subnet`}),e[16]=p.error,e[17]=j);let M;e[18]!==xe||e[19]!==O?(M=xe(O),e[18]=xe,e[19]=O,e[20]=M):M=e[20];let N;e[21]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,d.jsx)(l,{children:(0,d.jsx)(`label`,{htmlFor:`vpcId`,children:`VPC`})}),e[21]=N):N=e[21];let P;e[22]!==S.vpcId||e[23]!==h?(P=e=>{let{field:t}=e;return(0,d.jsxs)(te,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,d.jsx)(ee,{"aria-invalid":!!S.vpcId,className:`w-full`,id:`vpcId`,children:(0,d.jsx)(o,{})}),(0,d.jsx)(c,{children:h.map(ye)})]})},e[22]=S.vpcId,e[23]=h,e[24]=P):P=e[24];let F;e[25]!==x||e[26]!==P?(F=(0,d.jsx)(ue,{control:x,name:`vpcId`,render:P}),e[25]=x,e[26]=P,e[27]=F):F=e[27];let I;e[28]===S.vpcId?I=e[29]:(I=(0,d.jsx)(me,{errors:[S.vpcId]}),e[28]=S.vpcId,e[29]=I);let L;e[30]!==F||e[31]!==I?(L=(0,d.jsxs)(u,{children:[N,F,I]}),e[30]=F,e[31]=I,e[32]=L):L=e[32];let R;e[33]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,d.jsx)(l,{children:(0,d.jsx)(`label`,{htmlFor:`cidrBlock`,children:`CIDR Block`})}),e[33]=R):R=e[33];let Te=!!S.cidrBlock,z;e[34]===Se?z=e[35]:(z=Se(`cidrBlock`),e[34]=Se,e[35]=z);let B;e[36]!==Te||e[37]!==z?(B=(0,d.jsx)(oe,{"aria-invalid":Te,id:`cidrBlock`,placeholder:`10.0.1.0/24`,...z}),e[36]=Te,e[37]=z,e[38]=B):B=e[38];let V;e[39]===S.cidrBlock?V=e[40]:(V=(0,d.jsx)(me,{errors:[S.cidrBlock]}),e[39]=S.cidrBlock,e[40]=V);let H;e[41]!==B||e[42]!==V?(H=(0,d.jsxs)(u,{children:[R,B,V]}),e[41]=B,e[42]=V,e[43]=H):H=e[43];let U;e[44]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,d.jsx)(l,{children:(0,d.jsx)(`label`,{htmlFor:`availabilityZone`,children:`Availability Zone (optional)`})}),e[44]=U):U=e[44];let W;e[45]===_?W=e[46]:(W=e=>{let{field:t}=e;return(0,d.jsxs)(te,{onValueChange:e=>t.onChange(e===`none`?void 0:e),value:t.value??`none`,children:[(0,d.jsx)(ee,{className:`w-full`,id:`availabilityZone`,children:(0,d.jsx)(o,{})}),(0,d.jsxs)(c,{children:[(0,d.jsx)(s,{value:`none`,children:`none`}),_.map(ve)]})]})},e[45]=_,e[46]=W);let G;e[47]!==x||e[48]!==W?(G=(0,d.jsxs)(u,{children:[U,(0,d.jsx)(ue,{control:x,name:`availabilityZone`,render:W})]}),e[47]=x,e[48]=W,e[49]=G):G=e[49];let K;e[50]===Symbol.for(`react.memo_cache_sentinel`)?(K=(0,d.jsx)(l,{children:`Auto-assign public IPv4`}),e[50]=K):K=e[50];let q;e[51]===x?q=e[52]:(q=(0,d.jsxs)(u,{children:[K,(0,d.jsx)(ue,{control:x,name:`mapPublicIpOnLaunch`,render:_e})]}),e[51]=x,e[52]=q);let J;e[53]===E?J=e[54]:(J=be(E),e[53]=E,e[54]=J);let Y;e[55]===J?Y=e[56]:(Y=(0,d.jsx)(ne,{commands:J}),e[55]=J,e[56]=Y);let X;e[57]===t?X=e[58]:(X=async()=>await t({to:`/ec2/describe-subnets`}),e[57]=t,e[58]=X);let Z;e[59]!==p.isPending||e[60]!==we||e[61]!==X?(Z=(0,d.jsx)(fe,{isPending:p.isPending,isSubmitting:we,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Subnet`}),e[59]=p.isPending,e[60]=we,e[61]=X,e[62]=Z):Z=e[62];let Q;e[63]!==M||e[64]!==L||e[65]!==H||e[66]!==G||e[67]!==q||e[68]!==Y||e[69]!==Z?(Q=(0,d.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:M,children:[L,H,G,q,Y,Z]}),e[63]=M,e[64]=L,e[65]=H,e[66]=G,e[67]=q,e[68]=Y,e[69]=Z,e[70]=Q):Q=e[70];let $;return e[71]!==j||e[72]!==Q?($=(0,d.jsxs)(d.Fragment,{children:[A,k,j,Q]}),e[71]=j,e[72]=Q,e[73]=$):$=e[73],$}function _e(e){let{field:t}=e;return(0,d.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,d.jsx)(`input`,{"aria-label":`Enable auto-assign public IPv4 address`,checked:t.value??!1,onChange:e=>t.onChange(e.target.checked),type:`checkbox`}),`Enable auto-assign public IPv4 address`]})}function ve(e){return(0,d.jsx)(s,{value:e.ZoneName??``,children:e.ZoneName},e.ZoneName)}function ye(e){let t=i(e.Tags);return(0,d.jsx)(s,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)}function be(e){let t=e(`vpcId`),n=typeof t==`string`?t:``,r=e(`cidrBlock`),i=typeof r==`string`?r:``,a=e(`availabilityZone`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-subnet`},{type:`flag`,value:` \\ + --vpc-id`},{type:`value`,value:` ${n||``}`},{type:`flag`,value:` \\ + --cidr-block`},{type:`value`,value:` ${i||``}`}];o&&s.push({type:`flag`,value:` \\ + --availability-zone`},{type:`value`,value:` ${o}`});let c=[{label:`Create Subnet`,parts:s}];return e(`mapPublicIpOnLaunch`)===!0&&c.push({label:`Enable auto-assign public IPv4`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 modify-subnet-attribute`},{type:`flag`,value:` \\ --subnet-id`},{type:`value`,value:` `},{type:`flag`,value:` \\ - --map-public-ip-on-launch`}]}),te}export{m as component}; \ No newline at end of file + --map-public-ip-on-launch`}]}),c}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-target-group.js b/spinifex/services/spinifexui/frontend/dist/assets/create-target-group.js index b3611bdc6..9cec08ea4 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-target-group.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-target-group.js @@ -1,14 +1,14 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./cli-command-panel.js";import{i}from"./elbv2.js";import{Ho as a,Ro as o,ht as s}from"./index.js";import{i as c,t as l}from"./zod.js";import{t as u}from"./form-actions.js";import{t as d}from"./page-heading.js";import{t as f}from"./target-group-form.js";import{i as p}from"./elbv22.js";var m=a();function h(){let a=o(),{data:h}=t(s),_=i(),v=h.Vpcs??[],y=c({resolver:l(p),defaultValues:{name:``,protocol:`HTTP`,port:80,vpcId:v[0]?.VpcId??``,healthCheck:{protocol:`HTTP`,path:`/`,port:`traffic-port`,intervalSeconds:30,timeoutSeconds:5,healthyThresholdCount:5,unhealthyThresholdCount:2,matcher:`200`},tags:[]}}),{handleSubmit:b,watch:x,formState:{isSubmitting:S}}=y;return(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(n,{to:`/ec2/describe-target-groups`,children:`Back to target groups`}),(0,m.jsx)(d,{title:`Create Target Group`}),_.error&&(0,m.jsx)(e,{error:_.error,msg:`Failed to create target group`}),(0,m.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:b(async e=>{try{let t=(await _.mutateAsync(e)).TargetGroups?.[0]?.TargetGroupArn;a(t?{to:`/ec2/describe-target-groups/$id`,params:{id:encodeURIComponent(t)}}:{to:`/ec2/describe-target-groups`})}catch{}}),children:[(0,m.jsx)(f,{form:y,vpcs:v}),(0,m.jsx)(r,{commands:g(x)}),(0,m.jsx)(u,{isPending:_.isPending,isSubmitting:S,onCancel:async()=>await a({to:`/ec2/describe-target-groups`}),pendingLabel:`Creating…`,submitLabel:`Create Target Group`})]})]})}function g(e){let t=t=>{let n=e(t);return typeof n==`string`?n:``},n=t=>{let n=e(t);return typeof n==`number`&&Number.isFinite(n)?n:void 0},r=t(`name`),i=t(`protocol`)||`HTTP`,a=n(`port`),o=t(`vpcId`),s=t(`healthCheck.protocol`)||`HTTP`,c=t(`healthCheck.path`)||`/`,l=t(`healthCheck.port`)||`traffic-port`,u=n(`healthCheck.intervalSeconds`),d=n(`healthCheck.timeoutSeconds`),f=n(`healthCheck.healthyThresholdCount`),p=n(`healthCheck.unhealthyThresholdCount`),m=t(`healthCheck.matcher`)||`200`,h=[{type:`bin`,value:`AWS_PROFILE=spinifex aws elbv2 create-target-group`},{type:`flag`,value:` \\ +import{n as e}from"./compiler-runtime.js";import{t}from"./error-banner.js";import{t as n}from"./useSuspenseQuery.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{i as a}from"./elbv2.js";import{Ir as o,Lt as s}from"./index.js";import{i as c,t as l}from"./zod.js";import{t as u}from"./form-actions.js";import{t as d}from"./page-heading.js";import{t as f}from"./target-group-form.js";import{a as p}from"./elbv22.js";var m=e();function h(){let e=o(),{data:h}=n(s),_=a(),v=h.Vpcs??[],y=c({resolver:l(p),defaultValues:{name:``,protocol:`HTTP`,port:80,vpcId:v[0]?.VpcId??``,healthCheck:{protocol:`HTTP`,path:`/`,port:`traffic-port`,intervalSeconds:30,timeoutSeconds:5,healthyThresholdCount:5,unhealthyThresholdCount:2,matcher:`200`},tags:[]}}),{handleSubmit:b,watch:x,formState:{isSubmitting:S}}=y;return(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(r,{to:`/ec2/describe-target-groups`,children:`Back to target groups`}),(0,m.jsx)(d,{title:`Create Target Group`}),_.error&&(0,m.jsx)(t,{error:_.error,msg:`Failed to create target group`}),(0,m.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:b(async t=>{try{let n=(await _.mutateAsync(t)).TargetGroups?.[0]?.TargetGroupArn;e(n?{to:`/ec2/describe-target-groups/$id`,params:{id:encodeURIComponent(n)}}:{to:`/ec2/describe-target-groups`})}catch{}}),children:[(0,m.jsx)(f,{form:y,vpcs:v}),(0,m.jsx)(i,{commands:g(x)}),(0,m.jsx)(u,{isPending:_.isPending,isSubmitting:S,onCancel:async()=>await e({to:`/ec2/describe-target-groups`}),pendingLabel:`Creating…`,submitLabel:`Create Target Group`})]})]})}function g(e){let t=t=>{let n=e(t);return typeof n==`string`?n:``},n=t=>{let n=e(t);return typeof n==`number`&&Number.isFinite(n)?n:void 0},r=t(`name`),i=t(`protocol`)||`HTTP`,a=n(`port`),o=t(`vpcId`),s=t(`healthCheck.protocol`)||`HTTP`,c=t(`healthCheck.path`)||`/`,l=t(`healthCheck.port`)||`traffic-port`,u=n(`healthCheck.intervalSeconds`),d=n(`healthCheck.timeoutSeconds`),f=n(`healthCheck.healthyThresholdCount`),p=n(`healthCheck.unhealthyThresholdCount`),m=t(`healthCheck.matcher`)||`200`,h=[{type:`bin`,value:`AWS_PROFILE=spinifex aws elbv2 create-target-group`},{type:`flag`,value:` \\ --name`},{type:`value`,value:` ${r||``}`},{type:`flag`,value:` \\ --protocol`},{type:`value`,value:` ${i}`},{type:`flag`,value:` \\ --port`},{type:`value`,value:` ${a??80}`},{type:`flag`,value:` \\ - --target-type`},{type:`value`,value:` instance`}];return o&&h.push({type:`flag`,value:` \\ - --vpc-id`},{type:`value`,value:` ${o}`}),h.push({type:`flag`,value:` \\ - --health-check-protocol`},{type:`value`,value:` ${s}`},{type:`flag`,value:` \\ - --health-check-path`},{type:`value`,value:` ${c}`},{type:`flag`,value:` \\ + --target-type`},{type:`value`,value:` instance`}];o&&h.push({type:`flag`,value:` \\ + --vpc-id`},{type:`value`,value:` ${o}`});let g=s===`HTTP`;return h.push({type:`flag`,value:` \\ + --health-check-protocol`},{type:`value`,value:` ${s}`}),g&&h.push({type:`flag`,value:` \\ + --health-check-path`},{type:`value`,value:` ${c}`}),h.push({type:`flag`,value:` \\ --health-check-port`},{type:`value`,value:` ${l}`},{type:`flag`,value:` \\ --health-check-interval-seconds`},{type:`value`,value:` ${u??30}`},{type:`flag`,value:` \\ --health-check-timeout-seconds`},{type:`value`,value:` ${d??5}`},{type:`flag`,value:` \\ --healthy-threshold-count`},{type:`value`,value:` ${f??5}`},{type:`flag`,value:` \\ - --unhealthy-threshold-count`},{type:`value`,value:` ${p??2}`},{type:`flag`,value:` \\ + --unhealthy-threshold-count`},{type:`value`,value:` ${p??2}`}),g&&h.push({type:`flag`,value:` \\ --matcher`},{type:`value`,value:` HttpCode=${m}`}),[{label:`Create Target Group`,parts:h}]}var _=h;export{_ as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-user.js b/spinifex/services/spinifexui/frontend/dist/assets/create-user.js index 17929f61c..c247d0468 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-user.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-user.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{t as n}from"./cli-command-panel.js";import{i as r}from"./iam.js";import{Fo as i,Ho as a,Ro as o,Xi as s}from"./index.js";import{i as c,t as l}from"./zod.js";import{t as u}from"./form-actions.js";import{t as d}from"./page-heading.js";import{a as f,r as p,t as m}from"./field.js";import{n as h}from"./iam2.js";var g=i(),_=a();function v(){let i=(0,g.c)(51),a=o(),v=r(),b;i[0]===Symbol.for(`react.memo_cache_sentinel`)?(b={resolver:l(h)},i[0]=b):b=i[0];let{handleSubmit:x,register:S,watch:C,formState:w}=c(b),{errors:T,isSubmitting:E}=w,D;i[1]!==v||i[2]!==a?(D=async e=>{await v.mutateAsync(e),a({to:`/iam/list-users`})},i[1]=v,i[2]=a,i[3]=D):D=i[3];let O=D,k,A;i[4]===Symbol.for(`react.memo_cache_sentinel`)?(k=(0,_.jsx)(t,{to:`/iam/list-users`,children:`Back to users`}),A=(0,_.jsx)(d,{title:`Create User`}),i[4]=k,i[5]=A):(k=i[4],A=i[5]);let j;i[6]===v.error?j=i[7]:(j=v.error&&(0,_.jsx)(e,{error:v.error,msg:`Failed to create user`}),i[6]=v.error,i[7]=j);let M;i[8]!==x||i[9]!==O?(M=x(O),i[8]=x,i[9]=O,i[10]=M):M=i[10];let N;i[11]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,_.jsx)(f,{children:(0,_.jsx)(`label`,{htmlFor:`userName`,children:`User Name`})}),i[11]=N):N=i[11];let P=!!T.userName,F;i[12]===S?F=i[13]:(F=S(`userName`),i[12]=S,i[13]=F);let I;i[14]!==P||i[15]!==F?(I=(0,_.jsx)(s,{"aria-invalid":P,id:`userName`,placeholder:`my-user...`,...F}),i[14]=P,i[15]=F,i[16]=I):I=i[16];let L;i[17]===T.userName?L=i[18]:(L=(0,_.jsx)(p,{errors:[T.userName]}),i[17]=T.userName,i[18]=L);let R;i[19]!==I||i[20]!==L?(R=(0,_.jsxs)(m,{children:[N,I,L]}),i[19]=I,i[20]=L,i[21]=R):R=i[21];let z;i[22]===Symbol.for(`react.memo_cache_sentinel`)?(z=(0,_.jsx)(f,{children:(0,_.jsx)(`label`,{htmlFor:`path`,children:`Path (optional)`})}),i[22]=z):z=i[22];let B;i[23]===S?B=i[24]:(B=S(`path`),i[23]=S,i[24]=B);let V;i[25]===B?V=i[26]:(V=(0,_.jsx)(s,{id:`path`,placeholder:`/`,...B}),i[25]=B,i[26]=V);let H;i[27]===T.path?H=i[28]:(H=(0,_.jsx)(p,{errors:[T.path]}),i[27]=T.path,i[28]=H);let U;i[29]!==V||i[30]!==H?(U=(0,_.jsxs)(m,{children:[z,V,H]}),i[29]=V,i[30]=H,i[31]=U):U=i[31];let W;i[32]===C?W=i[33]:(W=y(C),i[32]=C,i[33]=W);let G;i[34]===W?G=i[35]:(G=(0,_.jsx)(n,{commands:W}),i[34]=W,i[35]=G);let K;i[36]===a?K=i[37]:(K=async()=>await a({to:`/iam/list-users`}),i[36]=a,i[37]=K);let q;i[38]!==v.isPending||i[39]!==E||i[40]!==K?(q=(0,_.jsx)(u,{isPending:v.isPending,isSubmitting:E,onCancel:K,pendingLabel:`Creating...`,submitLabel:`Create User`}),i[38]=v.isPending,i[39]=E,i[40]=K,i[41]=q):q=i[41];let J;i[42]!==R||i[43]!==U||i[44]!==G||i[45]!==q||i[46]!==M?(J=(0,_.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:M,children:[R,U,G,q]}),i[42]=R,i[43]=U,i[44]=G,i[45]=q,i[46]=M,i[47]=J):J=i[47];let Y;return i[48]!==J||i[49]!==j?(Y=(0,_.jsxs)(_.Fragment,{children:[k,A,j,J]}),i[48]=J,i[49]=j,i[50]=Y):Y=i[50],Y}function y(e){let t=e(`userName`),n=typeof t==`string`?t:``,r=e(`path`),i=typeof r==`string`?r:``,a=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-user`},{type:`flag`,value:` --user-name`},{type:`value`,value:` ${n||``}`}];return i&&i!==`/`&&a.push({type:`flag`,value:` --path`},{type:`value`,value:` ${i}`}),[{label:`Create User`,parts:a}]}export{v as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{c as a}from"./iam.js";import{Ir as o,Rn as s}from"./index.js";import{a as c,i as l,t as u}from"./zod.js";import{t as d}from"./form-actions.js";import{t as f}from"./page-heading.js";import{a as p,r as m,t as h}from"./field.js";import{a as g}from"./iam2.js";var _=t(),v=e();function y(){let e=(0,_.c)(55),t=o(),y=a(),b;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(b={resolver:u(g)},e[0]=b):b=e[0];let{handleSubmit:x,register:S,control:C,formState:w}=l(b),{errors:T,isSubmitting:E}=w,D;e[1]===C?D=e[2]:(D={control:C},e[1]=C,e[2]=D);let O=c(D),k;e[3]===O?k=e[4]:(k=e=>e?O[e]:void 0,e[3]=O,e[4]=k);let A=k,j;e[5]!==y||e[6]!==t?(j=async e=>{await y.mutateAsync(e),t({to:`/iam/list-users`})},e[5]=y,e[6]=t,e[7]=j):j=e[7];let M=j,N,P;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,v.jsx)(r,{to:`/iam/list-users`,children:`Back to users`}),P=(0,v.jsx)(f,{title:`Create User`}),e[8]=N,e[9]=P):(N=e[8],P=e[9]);let F;e[10]===y.error?F=e[11]:(F=y.error&&(0,v.jsx)(n,{error:y.error,msg:`Failed to create user`}),e[10]=y.error,e[11]=F);let I;e[12]!==x||e[13]!==M?(I=x(M),e[12]=x,e[13]=M,e[14]=I):I=e[14];let L;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,v.jsx)(p,{children:(0,v.jsx)(`label`,{htmlFor:`userName`,children:`User Name`})}),e[15]=L):L=e[15];let R=!!T.userName,z;e[16]===S?z=e[17]:(z=S(`userName`),e[16]=S,e[17]=z);let B;e[18]!==R||e[19]!==z?(B=(0,v.jsx)(s,{"aria-invalid":R,id:`userName`,placeholder:`my-user...`,...z}),e[18]=R,e[19]=z,e[20]=B):B=e[20];let V;e[21]===T.userName?V=e[22]:(V=(0,v.jsx)(m,{errors:[T.userName]}),e[21]=T.userName,e[22]=V);let H;e[23]!==B||e[24]!==V?(H=(0,v.jsxs)(h,{children:[L,B,V]}),e[23]=B,e[24]=V,e[25]=H):H=e[25];let U;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,v.jsx)(p,{children:(0,v.jsx)(`label`,{htmlFor:`path`,children:`Path (optional)`})}),e[26]=U):U=e[26];let W;e[27]===S?W=e[28]:(W=S(`path`),e[27]=S,e[28]=W);let G;e[29]===W?G=e[30]:(G=(0,v.jsx)(s,{id:`path`,placeholder:`/`,...W}),e[29]=W,e[30]=G);let K;e[31]===T.path?K=e[32]:(K=(0,v.jsx)(m,{errors:[T.path]}),e[31]=T.path,e[32]=K);let q;e[33]!==G||e[34]!==K?(q=(0,v.jsxs)(h,{children:[U,G,K]}),e[33]=G,e[34]=K,e[35]=q):q=e[35];let J;e[36]===A?J=e[37]:(J=ee(A),e[36]=A,e[37]=J);let Y;e[38]===J?Y=e[39]:(Y=(0,v.jsx)(i,{commands:J}),e[38]=J,e[39]=Y);let X;e[40]===t?X=e[41]:(X=async()=>await t({to:`/iam/list-users`}),e[40]=t,e[41]=X);let Z;e[42]!==y.isPending||e[43]!==E||e[44]!==X?(Z=(0,v.jsx)(d,{isPending:y.isPending,isSubmitting:E,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create User`}),e[42]=y.isPending,e[43]=E,e[44]=X,e[45]=Z):Z=e[45];let Q;e[46]!==H||e[47]!==q||e[48]!==Y||e[49]!==Z||e[50]!==I?(Q=(0,v.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:I,children:[H,q,Y,Z]}),e[46]=H,e[47]=q,e[48]=Y,e[49]=Z,e[50]=I,e[51]=Q):Q=e[51];let $;return e[52]!==Q||e[53]!==F?($=(0,v.jsxs)(v.Fragment,{children:[N,P,F,Q]}),e[52]=Q,e[53]=F,e[54]=$):$=e[54],$}function ee(e){let t=e(`userName`),n=typeof t==`string`?t:``,r=e(`path`),i=typeof r==`string`?r:``,a=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-user`},{type:`flag`,value:` --user-name`},{type:`value`,value:` ${n||``}`}];return i&&i!==`/`&&a.push({type:`flag`,value:` --path`},{type:`value`,value:` ${i}`}),[{label:`Create User`,parts:a}]}export{y as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-volume.js b/spinifex/services/spinifexui/frontend/dist/assets/create-volume.js index 10bab65e6..c500787f9 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-volume.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-volume.js @@ -1,3 +1,3 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{c as r,i,n as ee,s as a,t as te}from"./select.js";import{t as o}from"./cli-command-panel.js";import{b as ne}from"./ec2.js";import{Fo as s,Ho as c,Ro as re,U as ie,Xi as l}from"./index.js";import{i as u,n as d,t as f}from"./zod.js";import{t as p}from"./form-actions.js";import{t as ae}from"./page-heading.js";import{a as m,r as h,t as g}from"./field.js";import{m as oe}from"./ec22.js";var se=s(),_=c();function v(){let i=(0,se.c)(57),s=re(),{data:c}=t(ie),v=ne(),y;i[0]===c.AvailabilityZones?y=i[1]:(y=c.AvailabilityZones??[],i[0]=c.AvailabilityZones,i[1]=y);let b=y,x=b[0]?.ZoneName??``,S;i[2]===Symbol.for(`react.memo_cache_sentinel`)?(S=f(oe),i[2]=S):S=i[2];let C;i[3]===x?C=i[4]:(C={resolver:S,defaultValues:{size:1,availabilityZone:x}},i[3]=x,i[4]=C);let{control:w,handleSubmit:T,register:E,watch:D,formState:O}=u(C),{errors:k,isSubmitting:A}=O,j;i[5]!==v||i[6]!==s?(j=async e=>{await v.mutateAsync(e),s({to:`/ec2/describe-volumes`})},i[5]=v,i[6]=s,i[7]=j):j=i[7];let M=j,N,P;i[8]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,_.jsx)(n,{to:`/ec2/describe-volumes`,children:`Back to volumes`}),P=(0,_.jsx)(ae,{title:`Create Volume`}),i[8]=N,i[9]=P):(N=i[8],P=i[9]);let F;i[10]===v.error?F=i[11]:(F=v.error&&(0,_.jsx)(e,{error:v.error,msg:`Failed to create volume`}),i[10]=v.error,i[11]=F);let I;i[12]!==T||i[13]!==M?(I=T(M),i[12]=T,i[13]=M,i[14]=I):I=i[14];let L;i[15]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,_.jsx)(m,{children:(0,_.jsx)(`label`,{htmlFor:`size`,children:`Size (GiB)`})}),i[15]=L):L=i[15];let R=!!k.size,z;i[16]===E?z=i[17]:(z=E(`size`,{valueAsNumber:!0}),i[16]=E,i[17]=z);let B;i[18]!==R||i[19]!==z?(B=(0,_.jsx)(l,{"aria-invalid":R,id:`size`,max:16384,min:1,type:`number`,...z}),i[18]=R,i[19]=z,i[20]=B):B=i[20];let V;i[21]===k.size?V=i[22]:(V=(0,_.jsx)(h,{errors:[k.size]}),i[21]=k.size,i[22]=V);let H;i[23]!==B||i[24]!==V?(H=(0,_.jsxs)(g,{children:[L,B,V]}),i[23]=B,i[24]=V,i[25]=H):H=i[25];let U;i[26]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,_.jsx)(m,{children:(0,_.jsx)(`label`,{htmlFor:`availabilityZone`,children:`Availability Zone`})}),i[26]=U):U=i[26];let W;i[27]!==b||i[28]!==k.availabilityZone?(W=e=>{let{field:t}=e;return(0,_.jsxs)(te,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,_.jsx)(a,{"aria-invalid":!!k.availabilityZone,className:`w-full`,id:`availabilityZone`,children:(0,_.jsx)(r,{})}),(0,_.jsx)(ee,{children:b.map(ce)})]})},i[27]=b,i[28]=k.availabilityZone,i[29]=W):W=i[29];let G;i[30]!==w||i[31]!==W?(G=(0,_.jsx)(d,{control:w,name:`availabilityZone`,render:W}),i[30]=w,i[31]=W,i[32]=G):G=i[32];let K;i[33]===k.availabilityZone?K=i[34]:(K=(0,_.jsx)(h,{errors:[k.availabilityZone]}),i[33]=k.availabilityZone,i[34]=K);let q;i[35]!==G||i[36]!==K?(q=(0,_.jsxs)(g,{children:[U,G,K]}),i[35]=G,i[36]=K,i[37]=q):q=i[37];let J;i[38]===D?J=i[39]:(J=le(D),i[38]=D,i[39]=J);let Y;i[40]===J?Y=i[41]:(Y=(0,_.jsx)(o,{commands:J}),i[40]=J,i[41]=Y);let X;i[42]===s?X=i[43]:(X=async()=>await s({to:`/ec2/describe-volumes`}),i[42]=s,i[43]=X);let Z;i[44]!==v.isPending||i[45]!==A||i[46]!==X?(Z=(0,_.jsx)(p,{isPending:v.isPending,isSubmitting:A,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Volume`}),i[44]=v.isPending,i[45]=A,i[46]=X,i[47]=Z):Z=i[47];let Q;i[48]!==H||i[49]!==q||i[50]!==Y||i[51]!==Z||i[52]!==I?(Q=(0,_.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:I,children:[H,q,Y,Z]}),i[48]=H,i[49]=q,i[50]=Y,i[51]=Z,i[52]=I,i[53]=Q):Q=i[53];let $;return i[54]!==Q||i[55]!==F?($=(0,_.jsxs)(_.Fragment,{children:[N,P,F,Q]}),i[54]=Q,i[55]=F,i[56]=$):$=i[56],$}function ce(e){return(0,_.jsx)(i,{value:e.ZoneName??``,children:e.ZoneName},e.ZoneName)}function le(e){let t=e(`size`),n=typeof t==`number`?t:0,r=e(`availabilityZone`),i=typeof r==`string`?r:``;return[{label:`Create Volume`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-volume`},{type:`flag`,value:` \\ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./useSuspenseQuery.js";import{t as i}from"./back-link.js";import{c as a,i as o,n as s,s as c,t as l}from"./select.js";import{t as u}from"./cli-command-panel.js";import{x as ee}from"./ec2.js";import{Ir as te,Rn as ne,ut as re}from"./index.js";import{i as ie,n as ae,t as oe}from"./zod.js";import{t as se}from"./form-actions.js";import{t as ce}from"./page-heading.js";import{a as d,r as f,t as p}from"./field.js";import{m}from"./ec22.js";var h=t(),g=e();function _(){let e=(0,h.c)(57),t=te(),{data:o}=r(re),_=ee(),y;e[0]===o.AvailabilityZones?y=e[1]:(y=o.AvailabilityZones??[],e[0]=o.AvailabilityZones,e[1]=y);let b=y,x=b[0]?.ZoneName??``,S;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(S=oe(m),e[2]=S):S=e[2];let C;e[3]===x?C=e[4]:(C={resolver:S,defaultValues:{size:1,availabilityZone:x}},e[3]=x,e[4]=C);let{control:w,handleSubmit:T,register:E,watch:D,formState:O}=ie(C),{errors:k,isSubmitting:A}=O,j;e[5]!==_||e[6]!==t?(j=async e=>{await _.mutateAsync(e),t({to:`/ec2/describe-volumes`})},e[5]=_,e[6]=t,e[7]=j):j=e[7];let M=j,N,P;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,g.jsx)(i,{to:`/ec2/describe-volumes`,children:`Back to volumes`}),P=(0,g.jsx)(ce,{title:`Create Volume`}),e[8]=N,e[9]=P):(N=e[8],P=e[9]);let F;e[10]===_.error?F=e[11]:(F=_.error&&(0,g.jsx)(n,{error:_.error,msg:`Failed to create volume`}),e[10]=_.error,e[11]=F);let I;e[12]!==T||e[13]!==M?(I=T(M),e[12]=T,e[13]=M,e[14]=I):I=e[14];let L;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,g.jsx)(d,{children:(0,g.jsx)(`label`,{htmlFor:`size`,children:`Size (GiB)`})}),e[15]=L):L=e[15];let R=!!k.size,z;e[16]===E?z=e[17]:(z=E(`size`,{valueAsNumber:!0}),e[16]=E,e[17]=z);let B;e[18]!==R||e[19]!==z?(B=(0,g.jsx)(ne,{"aria-invalid":R,id:`size`,max:16384,min:1,type:`number`,...z}),e[18]=R,e[19]=z,e[20]=B):B=e[20];let V;e[21]===k.size?V=e[22]:(V=(0,g.jsx)(f,{errors:[k.size]}),e[21]=k.size,e[22]=V);let H;e[23]!==B||e[24]!==V?(H=(0,g.jsxs)(p,{children:[L,B,V]}),e[23]=B,e[24]=V,e[25]=H):H=e[25];let U;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,g.jsx)(d,{children:(0,g.jsx)(`label`,{htmlFor:`availabilityZone`,children:`Availability Zone`})}),e[26]=U):U=e[26];let W;e[27]!==b||e[28]!==k.availabilityZone?(W=e=>{let{field:t}=e;return(0,g.jsxs)(l,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,g.jsx)(c,{"aria-invalid":!!k.availabilityZone,className:`w-full`,id:`availabilityZone`,children:(0,g.jsx)(a,{})}),(0,g.jsx)(s,{children:b.map(le)})]})},e[27]=b,e[28]=k.availabilityZone,e[29]=W):W=e[29];let G;e[30]!==w||e[31]!==W?(G=(0,g.jsx)(ae,{control:w,name:`availabilityZone`,render:W}),e[30]=w,e[31]=W,e[32]=G):G=e[32];let K;e[33]===k.availabilityZone?K=e[34]:(K=(0,g.jsx)(f,{errors:[k.availabilityZone]}),e[33]=k.availabilityZone,e[34]=K);let q;e[35]!==G||e[36]!==K?(q=(0,g.jsxs)(p,{children:[U,G,K]}),e[35]=G,e[36]=K,e[37]=q):q=e[37];let J;e[38]===D?J=e[39]:(J=v(D),e[38]=D,e[39]=J);let Y;e[40]===J?Y=e[41]:(Y=(0,g.jsx)(u,{commands:J}),e[40]=J,e[41]=Y);let X;e[42]===t?X=e[43]:(X=async()=>await t({to:`/ec2/describe-volumes`}),e[42]=t,e[43]=X);let Z;e[44]!==_.isPending||e[45]!==A||e[46]!==X?(Z=(0,g.jsx)(se,{isPending:_.isPending,isSubmitting:A,onCancel:X,pendingLabel:`Creating…`,submitLabel:`Create Volume`}),e[44]=_.isPending,e[45]=A,e[46]=X,e[47]=Z):Z=e[47];let Q;e[48]!==H||e[49]!==q||e[50]!==Y||e[51]!==Z||e[52]!==I?(Q=(0,g.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:I,children:[H,q,Y,Z]}),e[48]=H,e[49]=q,e[50]=Y,e[51]=Z,e[52]=I,e[53]=Q):Q=e[53];let $;return e[54]!==Q||e[55]!==F?($=(0,g.jsxs)(g.Fragment,{children:[N,P,F,Q]}),e[54]=Q,e[55]=F,e[56]=$):$=e[56],$}function le(e){return(0,g.jsx)(o,{value:e.ZoneName??``,children:e.ZoneName},e.ZoneName)}function v(e){let t=e(`size`),n=typeof t==`number`?t:0,r=e(`availabilityZone`),i=typeof r==`string`?r:``;return[{label:`Create Volume`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-volume`},{type:`flag`,value:` \\ --size`},{type:`value`,value:` ${n||1}`},{type:`flag`,value:` \\ - --availability-zone`},{type:`value`,value:` ${i||``}`}]}]}export{v as component}; \ No newline at end of file + --availability-zone`},{type:`value`,value:` ${i||``}`}]}]}export{_ as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/create-vpc.js b/spinifex/services/spinifexui/frontend/dist/assets/create-vpc.js index 6fd562bcd..fa4cc4db3 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/create-vpc.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/create-vpc.js @@ -1,5 +1,5 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{t as n}from"./chevron-down.js";import{t as r}from"./chevron-right.js";import{c as i,i as a,n as o,s,t as c}from"./select.js";import{t as l}from"./cli-command-panel.js";import{S as u,Y as d,x as f}from"./ec2.js";import{Fo as p,Ho as m,Ro as h,Uo as g,Xa as _,Xi as v,cs as y}from"./index.js";import{i as b,n as x,t as S}from"./zod.js";import{t as C}from"./form-actions.js";import{t as w}from"./page-heading.js";import{a as T,r as E,t as D}from"./field.js";import{h as O}from"./ec22.js";var k=p(),A=y(g(),1),j=m();function M(e){let t=(0,k.c)(18),{form:i,type:a,count:o,defaults:s}=e,[c,l]=(0,A.useState)(!1),u=a===`public`?`publicSubnetCidrs`:`privateSubnetCidrs`;if(o===0)return null;let d;t[0]===c?d=t[1]:(d=()=>l(!c),t[0]=c,t[1]=d);let f;t[2]===c?f=t[3]:(f=c?(0,j.jsx)(n,{className:`size-3`}):(0,j.jsx)(r,{className:`size-3`}),t[2]=c,t[3]=f);let p;t[4]!==d||t[5]!==f||t[6]!==a?(p=(0,j.jsxs)(_,{className:`gap-1 px-0 text-xs text-muted-foreground`,onClick:d,type:`button`,variant:`link`,children:[f,`Customize `,a,` subnet CIDR blocks`]}),t[4]=d,t[5]=f,t[6]=a,t[7]=p):p=t[7];let m;t[8]!==o||t[9]!==s||t[10]!==c||t[11]!==u||t[12]!==i||t[13]!==a?(m=c&&(0,j.jsx)(`div`,{className:`mt-2 space-y-2`,children:Array.from({length:o},(e,t)=>(0,j.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,j.jsxs)(`span`,{className:`w-32 text-xs text-muted-foreground`,children:[a===`public`?`Public`:`Private`,` subnet `,t+1]}),(0,j.jsx)(v,{defaultValue:s[t],placeholder:s[t],...i.register(`${u}.${t}`)})]},t))}),t[8]=o,t[9]=s,t[10]=c,t[11]=u,t[12]=i,t[13]=a,t[14]=m):m=t[14];let h;return t[15]!==p||t[16]!==m?(h=(0,j.jsxs)(`div`,{className:`mt-2`,children:[p,m]}),t[15]=p,t[16]=m,t[17]=h):h=t[17],h}function N(e){let t=(0,k.c)(8),{label:n,cidr:r,variant:i}=e,a=`rounded-md border px-3 py-2 text-xs ${i===`public`?`border-emerald-500/30 bg-emerald-500/10`:`border-blue-500/30 bg-blue-500/10`}`,o;t[0]===n?o=t[1]:(o=(0,j.jsx)(`div`,{className:`font-medium`,children:n}),t[0]=n,t[1]=o);let s;t[2]===r?s=t[3]:(s=(0,j.jsx)(`div`,{className:`text-muted-foreground`,children:r}),t[2]=r,t[3]=s);let c;return t[4]!==a||t[5]!==o||t[6]!==s?(c=(0,j.jsxs)(`div`,{className:a,children:[o,s]}),t[4]=a,t[5]=o,t[6]=s,t[7]=c):c=t[7],c}function P(e){let t=(0,k.c)(14),{mode:n,vpcCidr:r,publicSubnets:i,privateSubnets:a,hasIgw:o}=e,s;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,j.jsx)(`h3`,{className:`mb-3 text-xs font-semibold tracking-wider text-muted-foreground uppercase`,children:`Preview`}),t[0]=s):s=t[0];let c;t[1]===r?c=t[2]:(c=(0,j.jsxs)(`div`,{className:`mb-3 text-xs font-medium`,children:[`VPC `,(0,j.jsxs)(`span`,{className:`text-muted-foreground`,children:[`(`,r,`)`]})]}),t[1]=r,t[2]=c);let l;t[3]!==o||t[4]!==n||t[5]!==a||t[6]!==i?(l=n===`vpc-and-more`&&(0,j.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-3`,children:[(0,j.jsxs)(`div`,{className:`space-y-2`,children:[(0,j.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:`Subnets`}),i.map(I),a.map(F),i.length===0&&a.length===0&&(0,j.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`No subnets`})]}),(0,j.jsxs)(`div`,{className:`space-y-2`,children:[(0,j.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:`Route Tables`}),i.length>0&&(0,j.jsxs)(`div`,{className:`rounded-md border border-foreground/10 bg-background px-3 py-2 text-xs`,children:[(0,j.jsx)(`div`,{className:`font-medium`,children:`Public Route Table`}),(0,j.jsx)(`div`,{className:`mt-1 text-muted-foreground`,children:`0.0.0.0/0 → IGW`}),(0,j.jsx)(`div`,{className:`text-muted-foreground`,children:`local`})]}),a.length>0&&(0,j.jsxs)(`div`,{className:`rounded-md border border-foreground/10 bg-background px-3 py-2 text-xs`,children:[(0,j.jsx)(`div`,{className:`font-medium`,children:`Private Route Table`}),(0,j.jsx)(`div`,{className:`mt-1 text-muted-foreground`,children:`local`})]})]}),(0,j.jsxs)(`div`,{className:`space-y-2`,children:[(0,j.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:`Network Connections`}),o&&(0,j.jsxs)(`div`,{className:`rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs`,children:[(0,j.jsx)(`div`,{className:`font-medium`,children:`Internet Gateway`}),(0,j.jsx)(`div`,{className:`text-muted-foreground`,children:`Attached to VPC`})]}),!o&&(0,j.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`No internet gateway`})]})]}),t[3]=o,t[4]=n,t[5]=a,t[6]=i,t[7]=l):l=t[7];let u;t[8]===n?u=t[9]:(u=n===`vpc-only`&&(0,j.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`VPC only — no additional resources will be created.`}),t[8]=n,t[9]=u);let d;return t[10]!==c||t[11]!==l||t[12]!==u?(d=(0,j.jsxs)(`div`,{className:`rounded-lg border bg-muted/30 p-4`,children:[s,(0,j.jsxs)(`div`,{className:`rounded-lg border-2 border-dashed border-foreground/20 p-4`,children:[c,l,u]})]}),t[10]=c,t[11]=l,t[12]=u,t[13]=d):d=t[13],d}function F(e){return(0,j.jsx)(N,{cidr:e.cidr,label:e.label,variant:`private`},e.cidr)}function I(e){return(0,j.jsx)(N,{cidr:e.cidr,label:e.label,variant:`public`},e.cidr)}var L=[0,1],R=[0,1,2];function z(){let n=h(),r=f(),p=u(),[m,g]=(0,A.useState)(null),y=b({resolver:S(O),defaultValues:{mode:`vpc-only`,namePrefix:``,autoGenerateNames:!0,cidrBlock:`10.0.0.0/16`,tenancy:`default`,natGateway:`none`,publicSubnetCount:1,privateSubnetCount:1,publicSubnetCidrs:[],privateSubnetCidrs:[],tags:[]}}),{handleSubmit:k,register:N,watch:F,control:I,formState:{errors:z,isSubmitting:V}}=y,H=F(`mode`),U=F(`cidrBlock`),W=F(`publicSubnetCount`),G=F(`privateSubnetCount`),K=d(U||`10.0.0.0/16`,H===`vpc-and-more`?W:0,H===`vpc-and-more`?G:0),[q,J]=(0,A.useState)(``),Y=async e=>{if(g(null),e.mode===`vpc-only`){let t=e.autoGenerateNames&&e.namePrefix?`${e.namePrefix}-vpc`:e.namePrefix,i=(await r.mutateAsync({cidrBlock:e.cidrBlock,name:t||void 0})).Vpc?.VpcId;n(i?{to:`/ec2/describe-vpcs/$id`,params:{id:i}}:{to:`/ec2/describe-vpcs`});return}J(`Creating VPC and resources...`);let t=await p.mutateAsync(e);g(t),!t.error&&t.vpcId&&n({to:`/ec2/describe-vpcs/$id`,params:{id:t.vpcId}}),J(``)},X=r.error??p.error,Z=r.isPending||p.isPending;return(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(t,{to:`/ec2/describe-vpcs`,children:`Back to VPCs`}),(0,j.jsx)(w,{title:`Create VPC`}),X&&(0,j.jsx)(e,{error:X,msg:`Failed to create VPC`}),m?.error&&(0,j.jsxs)(`div`,{className:`mb-6 max-w-4xl rounded-md border border-destructive bg-destructive/10 p-4`,children:[(0,j.jsxs)(`h2`,{className:`text-sm font-medium text-destructive`,children:[`Wizard failed: `,m.failedStep]}),(0,j.jsx)(`p`,{className:`mt-1 text-sm text-destructive`,children:m.error.message}),m.created.length>0&&(0,j.jsxs)(`div`,{className:`mt-3`,children:[(0,j.jsx)(`p`,{className:`text-xs font-medium text-destructive`,children:`Successfully created resources:`}),(0,j.jsx)(`ul`,{className:`mt-1 list-inside list-disc text-xs text-destructive`,children:m.created.map((e,t)=>(0,j.jsxs)(`li`,{children:[e.type,`: `,e.id]},t))})]})]}),q&&(0,j.jsx)(`div`,{className:`mb-4 max-w-4xl text-sm text-muted-foreground`,children:q}),(0,j.jsxs)(`div`,{className:`flex max-w-6xl gap-8`,children:[(0,j.jsxs)(`form`,{className:`min-w-0 flex-1 space-y-6`,onSubmit:k(Y),children:[(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:`Resources to create`}),(0,j.jsx)(x,{control:I,name:`mode`,render:({field:e})=>(0,j.jsxs)(`div`,{className:`flex gap-4`,children:[(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,j.jsx)(`input`,{"aria-label":`VPC only`,checked:e.value===`vpc-only`,name:`mode`,onChange:()=>e.onChange(`vpc-only`),type:`radio`}),`VPC only`]}),(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,j.jsx)(`input`,{"aria-label":`VPC and more`,checked:e.value===`vpc-and-more`,name:`mode`,onChange:()=>e.onChange(`vpc-and-more`),type:`radio`}),`VPC and more`]})]})})]}),(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:(0,j.jsx)(`label`,{htmlFor:`namePrefix`,children:`Name tag auto-generation`})}),(0,j.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,j.jsx)(v,{id:`namePrefix`,placeholder:`project`,...N(`namePrefix`)}),(0,j.jsx)(x,{control:I,name:`autoGenerateNames`,render:({field:e})=>(0,j.jsxs)(`label`,{className:`flex shrink-0 items-center gap-2 text-xs`,children:[(0,j.jsx)(`input`,{"aria-label":`Auto-generate name tags`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Auto-generate`]})})]})]}),(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:(0,j.jsx)(`label`,{htmlFor:`cidrBlock`,children:`IPv4 CIDR block`})}),(0,j.jsx)(v,{"aria-invalid":!!z.cidrBlock,id:`cidrBlock`,placeholder:`10.0.0.0/16`,...N(`cidrBlock`)}),(0,j.jsx)(E,{errors:[z.cidrBlock]})]}),(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:`Tenancy`}),(0,j.jsx)(x,{control:I,name:`tenancy`,render:({field:e})=>(0,j.jsxs)(c,{onValueChange:e.onChange,value:e.value,children:[(0,j.jsx)(s,{id:`tenancy`,children:(0,j.jsx)(i,{})}),(0,j.jsxs)(o,{children:[(0,j.jsx)(a,{value:`default`,children:`default`}),(0,j.jsx)(a,{value:`dedicated`,children:`dedicated`})]})]})})]}),H===`vpc-and-more`&&(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:`Number of public subnets`}),(0,j.jsx)(x,{control:I,name:`publicSubnetCount`,render:({field:e})=>(0,j.jsx)(`div`,{className:`flex gap-1`,children:L.map(t=>(0,j.jsx)(_,{onClick:()=>e.onChange(t),size:`sm`,type:`button`,variant:e.value===t?`default`:`outline`,children:t},t))})}),(0,j.jsx)(M,{count:W,defaults:K.publicSubnets.map(e=>e.cidr),form:y,type:`public`})]}),(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:`Number of private subnets`}),(0,j.jsx)(x,{control:I,name:`privateSubnetCount`,render:({field:e})=>(0,j.jsx)(`div`,{className:`flex gap-1`,children:R.map(t=>(0,j.jsx)(_,{onClick:()=>e.onChange(t),size:`sm`,type:`button`,variant:e.value===t?`default`:`outline`,children:t},t))})}),(0,j.jsx)(M,{count:G,defaults:K.privateSubnets.map(e=>e.cidr),form:y,type:`private`})]}),W>0&&G>0&&(0,j.jsxs)(D,{children:[(0,j.jsx)(T,{children:`NAT gateways`}),(0,j.jsx)(x,{control:I,name:`natGateway`,render:({field:e})=>(0,j.jsxs)(c,{onValueChange:e.onChange,value:e.value??`none`,children:[(0,j.jsx)(s,{id:`natGateway`,children:(0,j.jsx)(i,{})}),(0,j.jsxs)(o,{children:[(0,j.jsx)(a,{value:`none`,children:`None`}),(0,j.jsx)(a,{value:`single`,children:`In 1 AZ`})]})]})}),(0,j.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Creates an Elastic IP + NAT gateway in the public subnet and routes private subnet egress (0.0.0.0/0) through it.`})]})]}),(0,j.jsx)(l,{commands:B(F,K)}),(0,j.jsx)(C,{isPending:Z,isSubmitting:V,onCancel:async()=>await n({to:`/ec2/describe-vpcs`}),pendingLabel:`Creating…`,submitLabel:`Create VPC`})]}),(0,j.jsx)(`div`,{className:`hidden min-w-0 flex-1 lg:block`,children:(0,j.jsx)(P,{hasIgw:H===`vpc-and-more`&&W>0,mode:H,privateSubnets:K.privateSubnets,publicSubnets:K.publicSubnets,vpcCidr:U||`10.0.0.0/16`})})]})]})}function B(e,t){let n=e(`mode`),r=typeof n==`string`?n:``,i=e(`cidrBlock`),a=typeof i==`string`?i:``,o=e(`tenancy`),s=typeof o==`string`?o:``,c=e(`natGateway`),l=typeof c==`string`?c:``;if(r===`vpc-only`){let e=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-vpc`},{type:`flag`,value:` \\ - --cidr-block`},{type:`value`,value:` ${a||`10.0.0.0/16`}`}];return s&&s!==`default`&&e.push({type:`flag`,value:` \\ +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{t as a}from"./back-link.js";import{t as o}from"./chevron-down.js";import{t as s}from"./chevron-right.js";import{c,i as l,n as u,s as d,t as f}from"./select.js";import{t as p}from"./cli-command-panel.js";import{t as m}from"./button.js";import{C as h,S as g}from"./ec2.js";import{Ir as _,Rn as v}from"./index.js";import{i as y,n as b,t as x}from"./zod.js";import{t as S}from"./form-actions.js";import{t as C}from"./page-heading.js";import{a as w,r as T,t as E}from"./field.js";import{t as D}from"./subnet-calculator.js";import{h as O}from"./ec22.js";var k=r(),A=t(n(),1),j=e();function M(e){let t=(0,k.c)(18),{form:n,type:r,count:i,defaults:a}=e,[c,l]=(0,A.useState)(!1),u=r===`public`?`publicSubnetCidrs`:`privateSubnetCidrs`;if(i===0)return null;let d;t[0]===c?d=t[1]:(d=()=>l(!c),t[0]=c,t[1]=d);let f;t[2]===c?f=t[3]:(f=c?(0,j.jsx)(o,{className:`size-3`}):(0,j.jsx)(s,{className:`size-3`}),t[2]=c,t[3]=f);let p;t[4]!==d||t[5]!==f||t[6]!==r?(p=(0,j.jsxs)(m,{className:`gap-1 px-0 text-xs text-muted-foreground`,onClick:d,type:`button`,variant:`link`,children:[f,`Customize `,r,` subnet CIDR blocks`]}),t[4]=d,t[5]=f,t[6]=r,t[7]=p):p=t[7];let h;t[8]!==i||t[9]!==a||t[10]!==c||t[11]!==u||t[12]!==n||t[13]!==r?(h=c&&(0,j.jsx)(`div`,{className:`mt-2 space-y-2`,children:Array.from({length:i},(e,t)=>(0,j.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,j.jsxs)(`span`,{className:`w-32 text-xs text-muted-foreground`,children:[r===`public`?`Public`:`Private`,` subnet `,t+1]}),(0,j.jsx)(v,{defaultValue:a[t],placeholder:a[t],...n.register(`${u}.${t}`)})]},t))}),t[8]=i,t[9]=a,t[10]=c,t[11]=u,t[12]=n,t[13]=r,t[14]=h):h=t[14];let g;return t[15]!==p||t[16]!==h?(g=(0,j.jsxs)(`div`,{className:`mt-2`,children:[p,h]}),t[15]=p,t[16]=h,t[17]=g):g=t[17],g}function N(e){let t=(0,k.c)(8),{label:n,cidr:r,variant:i}=e,a=`rounded-md border px-3 py-2 text-xs ${i===`public`?`border-emerald-500/30 bg-emerald-500/10`:`border-blue-500/30 bg-blue-500/10`}`,o;t[0]===n?o=t[1]:(o=(0,j.jsx)(`div`,{className:`font-medium`,children:n}),t[0]=n,t[1]=o);let s;t[2]===r?s=t[3]:(s=(0,j.jsx)(`div`,{className:`text-muted-foreground`,children:r}),t[2]=r,t[3]=s);let c;return t[4]!==a||t[5]!==o||t[6]!==s?(c=(0,j.jsxs)(`div`,{className:a,children:[o,s]}),t[4]=a,t[5]=o,t[6]=s,t[7]=c):c=t[7],c}function P(e){let t=(0,k.c)(14),{mode:n,vpcCidr:r,publicSubnets:i,privateSubnets:a,hasIgw:o}=e,s;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,j.jsx)(`h3`,{className:`mb-3 text-xs font-semibold tracking-wider text-muted-foreground uppercase`,children:`Preview`}),t[0]=s):s=t[0];let c;t[1]===r?c=t[2]:(c=(0,j.jsxs)(`div`,{className:`mb-3 text-xs font-medium`,children:[`VPC `,(0,j.jsxs)(`span`,{className:`text-muted-foreground`,children:[`(`,r,`)`]})]}),t[1]=r,t[2]=c);let l;t[3]!==o||t[4]!==n||t[5]!==a||t[6]!==i?(l=n===`vpc-and-more`&&(0,j.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-3`,children:[(0,j.jsxs)(`div`,{className:`space-y-2`,children:[(0,j.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:`Subnets`}),i.map(I),a.map(F),i.length===0&&a.length===0&&(0,j.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`No subnets`})]}),(0,j.jsxs)(`div`,{className:`space-y-2`,children:[(0,j.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:`Route Tables`}),i.length>0&&(0,j.jsxs)(`div`,{className:`rounded-md border border-foreground/10 bg-background px-3 py-2 text-xs`,children:[(0,j.jsx)(`div`,{className:`font-medium`,children:`Public Route Table`}),(0,j.jsx)(`div`,{className:`mt-1 text-muted-foreground`,children:`0.0.0.0/0 → IGW`}),(0,j.jsx)(`div`,{className:`text-muted-foreground`,children:`local`})]}),a.length>0&&(0,j.jsxs)(`div`,{className:`rounded-md border border-foreground/10 bg-background px-3 py-2 text-xs`,children:[(0,j.jsx)(`div`,{className:`font-medium`,children:`Private Route Table`}),(0,j.jsx)(`div`,{className:`mt-1 text-muted-foreground`,children:`local`})]})]}),(0,j.jsxs)(`div`,{className:`space-y-2`,children:[(0,j.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:`Network Connections`}),o&&(0,j.jsxs)(`div`,{className:`rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs`,children:[(0,j.jsx)(`div`,{className:`font-medium`,children:`Internet Gateway`}),(0,j.jsx)(`div`,{className:`text-muted-foreground`,children:`Attached to VPC`})]}),!o&&(0,j.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`No internet gateway`})]})]}),t[3]=o,t[4]=n,t[5]=a,t[6]=i,t[7]=l):l=t[7];let u;t[8]===n?u=t[9]:(u=n===`vpc-only`&&(0,j.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`VPC only — no additional resources will be created.`}),t[8]=n,t[9]=u);let d;return t[10]!==c||t[11]!==l||t[12]!==u?(d=(0,j.jsxs)(`div`,{className:`rounded-lg border bg-muted/30 p-4`,children:[s,(0,j.jsxs)(`div`,{className:`rounded-lg border-2 border-dashed border-foreground/20 p-4`,children:[c,l,u]})]}),t[10]=c,t[11]=l,t[12]=u,t[13]=d):d=t[13],d}function F(e){return(0,j.jsx)(N,{cidr:e.cidr,label:e.label,variant:`private`},e.cidr)}function I(e){return(0,j.jsx)(N,{cidr:e.cidr,label:e.label,variant:`public`},e.cidr)}var L=[0,1],R=[0,1,2];function z(){let e=_(),t=g(),n=h(),[r,o]=(0,A.useState)(null),s=y({resolver:x(O),defaultValues:{mode:`vpc-only`,namePrefix:``,autoGenerateNames:!0,cidrBlock:`10.0.0.0/16`,tenancy:`default`,natGateway:`none`,publicSubnetCount:1,privateSubnetCount:1,publicSubnetCidrs:[],privateSubnetCidrs:[],tags:[]}}),{handleSubmit:k,register:N,watch:F,control:I,formState:{errors:z,isSubmitting:V}}=s,H=F(`mode`),U=F(`cidrBlock`),W=F(`publicSubnetCount`),G=F(`privateSubnetCount`),K=D(U||`10.0.0.0/16`,H===`vpc-and-more`?W:0,H===`vpc-and-more`?G:0),[q,J]=(0,A.useState)(``),Y=async r=>{if(o(null),r.mode===`vpc-only`){let n=r.autoGenerateNames&&r.namePrefix?`${r.namePrefix}-vpc`:r.namePrefix,i=(await t.mutateAsync({cidrBlock:r.cidrBlock,name:n||void 0})).Vpc?.VpcId;e(i?{to:`/ec2/describe-vpcs/$id`,params:{id:i}}:{to:`/ec2/describe-vpcs`});return}J(`Creating VPC and resources...`);let i=await n.mutateAsync(r);o(i),!i.error&&i.vpcId&&e({to:`/ec2/describe-vpcs/$id`,params:{id:i.vpcId}}),J(``)},X=t.error??n.error,Z=t.isPending||n.isPending;return(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(a,{to:`/ec2/describe-vpcs`,children:`Back to VPCs`}),(0,j.jsx)(C,{title:`Create VPC`}),X&&(0,j.jsx)(i,{error:X,msg:`Failed to create VPC`}),r?.error&&(0,j.jsxs)(`div`,{className:`mb-6 max-w-4xl rounded-md border border-destructive bg-destructive/10 p-4`,children:[(0,j.jsxs)(`h2`,{className:`text-sm font-medium text-destructive`,children:[`Wizard failed: `,r.failedStep]}),(0,j.jsx)(`p`,{className:`mt-1 text-sm text-destructive`,children:r.error.message}),r.created.length>0&&(0,j.jsxs)(`div`,{className:`mt-3`,children:[(0,j.jsx)(`p`,{className:`text-xs font-medium text-destructive`,children:`Successfully created resources:`}),(0,j.jsx)(`ul`,{className:`mt-1 list-inside list-disc text-xs text-destructive`,children:r.created.map((e,t)=>(0,j.jsxs)(`li`,{children:[e.type,`: `,e.id]},t))})]})]}),q&&(0,j.jsx)(`div`,{className:`mb-4 max-w-4xl text-sm text-muted-foreground`,children:q}),(0,j.jsxs)(`div`,{className:`flex max-w-6xl gap-8`,children:[(0,j.jsxs)(`form`,{className:`min-w-0 flex-1 space-y-6`,onSubmit:k(Y),children:[(0,j.jsxs)(E,{children:[(0,j.jsx)(w,{children:`Resources to create`}),(0,j.jsx)(b,{control:I,name:`mode`,render:({field:e})=>(0,j.jsxs)(`div`,{className:`flex gap-4`,children:[(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,j.jsx)(`input`,{"aria-label":`VPC only`,checked:e.value===`vpc-only`,name:`mode`,onChange:()=>e.onChange(`vpc-only`),type:`radio`}),`VPC only`]}),(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,j.jsx)(`input`,{"aria-label":`VPC and more`,checked:e.value===`vpc-and-more`,name:`mode`,onChange:()=>e.onChange(`vpc-and-more`),type:`radio`}),`VPC and more`]})]})})]}),(0,j.jsxs)(E,{children:[(0,j.jsx)(w,{children:(0,j.jsx)(`label`,{htmlFor:`namePrefix`,children:`Name tag auto-generation`})}),(0,j.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,j.jsx)(v,{id:`namePrefix`,placeholder:`project`,...N(`namePrefix`)}),(0,j.jsx)(b,{control:I,name:`autoGenerateNames`,render:({field:e})=>(0,j.jsxs)(`label`,{className:`flex shrink-0 items-center gap-2 text-xs`,children:[(0,j.jsx)(`input`,{"aria-label":`Auto-generate name tags`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Auto-generate`]})})]})]}),(0,j.jsxs)(E,{children:[(0,j.jsx)(w,{children:(0,j.jsx)(`label`,{htmlFor:`cidrBlock`,children:`IPv4 CIDR block`})}),(0,j.jsx)(v,{"aria-invalid":!!z.cidrBlock,id:`cidrBlock`,placeholder:`10.0.0.0/16`,...N(`cidrBlock`)}),(0,j.jsx)(T,{errors:[z.cidrBlock]})]}),(0,j.jsxs)(E,{children:[(0,j.jsx)(w,{children:`Tenancy`}),(0,j.jsx)(b,{control:I,name:`tenancy`,render:({field:e})=>(0,j.jsxs)(f,{onValueChange:e.onChange,value:e.value,children:[(0,j.jsx)(d,{id:`tenancy`,children:(0,j.jsx)(c,{})}),(0,j.jsxs)(u,{children:[(0,j.jsx)(l,{value:`default`,children:`default`}),(0,j.jsx)(l,{value:`dedicated`,children:`dedicated`})]})]})})]}),H===`vpc-and-more`&&(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(E,{children:[(0,j.jsx)(w,{children:`Number of public subnets`}),(0,j.jsx)(b,{control:I,name:`publicSubnetCount`,render:({field:e})=>(0,j.jsx)(`div`,{className:`flex gap-1`,children:L.map(t=>(0,j.jsx)(m,{onClick:()=>e.onChange(t),size:`sm`,type:`button`,variant:e.value===t?`default`:`outline`,children:t},t))})}),(0,j.jsx)(M,{count:W,defaults:K.publicSubnets.map(e=>e.cidr),form:s,type:`public`})]}),(0,j.jsxs)(E,{children:[(0,j.jsx)(w,{children:`Number of private subnets`}),(0,j.jsx)(b,{control:I,name:`privateSubnetCount`,render:({field:e})=>(0,j.jsx)(`div`,{className:`flex gap-1`,children:R.map(t=>(0,j.jsx)(m,{onClick:()=>e.onChange(t),size:`sm`,type:`button`,variant:e.value===t?`default`:`outline`,children:t},t))})}),(0,j.jsx)(M,{count:G,defaults:K.privateSubnets.map(e=>e.cidr),form:s,type:`private`})]}),W>0&&G>0&&(0,j.jsxs)(E,{children:[(0,j.jsx)(w,{children:`NAT gateways`}),(0,j.jsx)(b,{control:I,name:`natGateway`,render:({field:e})=>(0,j.jsxs)(f,{onValueChange:e.onChange,value:e.value??`none`,children:[(0,j.jsx)(d,{id:`natGateway`,children:(0,j.jsx)(c,{})}),(0,j.jsxs)(u,{children:[(0,j.jsx)(l,{value:`none`,children:`None`}),(0,j.jsx)(l,{value:`single`,children:`In 1 AZ`})]})]})}),(0,j.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Creates an Elastic IP + NAT gateway in the public subnet and routes private subnet egress (0.0.0.0/0) through it.`})]})]}),(0,j.jsx)(p,{commands:B(F,K)}),(0,j.jsx)(S,{isPending:Z,isSubmitting:V,onCancel:async()=>await e({to:`/ec2/describe-vpcs`}),pendingLabel:`Creating…`,submitLabel:`Create VPC`})]}),(0,j.jsx)(`div`,{className:`hidden min-w-0 flex-1 lg:block`,children:(0,j.jsx)(P,{hasIgw:H===`vpc-and-more`&&W>0,mode:H,privateSubnets:K.privateSubnets,publicSubnets:K.publicSubnets,vpcCidr:U||`10.0.0.0/16`})})]})]})}function B(e,t){let n=e(`mode`),r=typeof n==`string`?n:``,i=e(`cidrBlock`),a=typeof i==`string`?i:``,o=e(`tenancy`),s=typeof o==`string`?o:``,c=e(`natGateway`),l=typeof c==`string`?c:``;if(r===`vpc-only`){let e=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 create-vpc`},{type:`flag`,value:` \\ + --cidr-block`},{type:`value`,value:` ${a||`10.0.0.0/16`}`}];return s&&s!=="default"&&e.push({type:`flag`,value:` \\ --instance-tenancy`},{type:`value`,value:` ${s}`}),[{label:`Create VPC`,parts:e}]}let u=[],d=[...t.publicSubnets.map((e,t)=>({...e,name:`PUBLIC_SUBNET_${t+1}`})),...t.privateSubnets.map((e,t)=>({...e,name:`PRIVATE_SUBNET_${t+1}`}))];u.push({label:`Create VPC`,parts:[{type:`comment`,value:`# Create VPC with subnets, internet gateway, and routing `},{type:`variable`,value:`VPC_ID=`},{type:`bin`,value:`$(AWS_PROFILE=spinifex aws ec2 create-vpc`},{type:`flag`,value:` \\ diff --git a/spinifex/services/spinifexui/frontend/dist/assets/createBaseUIEventDetails.js b/spinifex/services/spinifexui/frontend/dist/assets/createBaseUIEventDetails.js new file mode 100644 index 000000000..397e004d6 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/createBaseUIEventDetails.js @@ -0,0 +1 @@ +import{o as e,r as t}from"./compiler-runtime.js";import{a as n}from"./dist.js";import{c as r,w as i}from"./button.js";var a=e(t()),o=0;function s(e,t=`mui`){let[n,r]=a.useState(e),i=e||n;return a.useEffect(()=>{n??(o+=1,r(`${t}-${o}`))},[n,t]),i}var c=r.useId;function l(e,t){if(c!==void 0){let n=c();return e??(t?`${t}-${n}`:n)}return s(e,t)}function u(e){return l(e,`base-ui`)}function d(e){return e?.ownerDocument||document}function f(e){let t=e.activeElement;for(;t?.shadowRoot?.activeElement!=null;)t=t.shadowRoot.activeElement;return t}function p(e,t){if(!e||!t)return!1;let n=t.getRootNode?.();if(e.contains(t))return!0;if(n&&i(n)){let n=t;for(;n;){if(e===n)return!0;n=n.parentNode||n.host}}return!1}function m(e){return`composedPath`in e?e.composedPath()[0]:e.target}var h=`none`,g=`trigger-press`,_=`trigger-hover`,v=`trigger-focus`,y=`outside-press`,b=`item-press`,x=`close-press`,S=`focus-out`,C=`escape-key`,w=`list-navigation`,T=`cancel-open`,E=`disabled`,D=`missing`,O=`initial`,k=`imperative-action`,A=`window-resize`;function j(e,t,r,i){let a=!1,o=!1,s=i??n;return{reason:e,event:t??new Event(`base-ui`),cancel(){a=!0},allowPropagation(){o=!0},get isCanceled(){return a},get isPropagationAllowed(){return o},trigger:r,...s}}export{l as C,u as S,A as _,C as a,m as b,O as c,D as d,h as f,g,_ as h,E as i,b as l,v as m,T as n,S as o,y as p,x as r,k as s,j as t,w as u,f as v,d as x,p as y}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/delete-confirmation-dialog.js b/spinifex/services/spinifexui/frontend/dist/assets/delete-confirmation-dialog.js index 35fd39eca..947a6e4d9 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/delete-confirmation-dialog.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/delete-confirmation-dialog.js @@ -1 +1 @@ -import{Fo as e,Ho as t}from"./index.js";import{a as n,i as r,l as i,n as a,o,r as s,s as c,t as l}from"./alert-dialog.js";var u=e(),d=t();function f(e){let t=(0,u.c)(19),{open:f,onOpenChange:p,title:m,description:h,isPending:g,onConfirm:_,confirmLabel:v,pendingLabel:y}=e,b=v===void 0?`Delete`:v,x=y===void 0?`Deleting…`:y,S;t[0]===m?S=t[1]:(S=(0,d.jsx)(i,{children:m}),t[0]=m,t[1]=S);let C;t[2]===h?C=t[3]:(C=(0,d.jsx)(n,{children:h}),t[2]=h,t[3]=C);let w;t[4]!==S||t[5]!==C?(w=(0,d.jsxs)(c,{children:[S,C]}),t[4]=S,t[5]=C,t[6]=w):w=t[6];let T;t[7]===Symbol.for(`react.memo_cache_sentinel`)?(T=(0,d.jsx)(s,{children:`Cancel`}),t[7]=T):T=t[7];let E=g?x:b,D;t[8]!==g||t[9]!==_||t[10]!==E?(D=(0,d.jsxs)(o,{children:[T,(0,d.jsx)(a,{disabled:g,onClick:_,children:E})]}),t[8]=g,t[9]=_,t[10]=E,t[11]=D):D=t[11];let O;t[12]!==w||t[13]!==D?(O=(0,d.jsxs)(r,{children:[w,D]}),t[12]=w,t[13]=D,t[14]=O):O=t[14];let k;return t[15]!==p||t[16]!==f||t[17]!==O?(k=(0,d.jsx)(l,{onOpenChange:p,open:f,children:O}),t[15]=p,t[16]=f,t[17]=O,t[18]=k):k=t[18],k}export{f as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{a as n,i as r,l as i,n as a,o,r as s,s as c,t as l}from"./alert-dialog.js";var u=t(),d=e();function f(e){let t=(0,u.c)(19),{open:f,onOpenChange:p,title:m,description:h,isPending:g,onConfirm:_,confirmLabel:v,pendingLabel:y}=e,b=v===void 0?`Delete`:v,x=y===void 0?`Deleting…`:y,S;t[0]===m?S=t[1]:(S=(0,d.jsx)(i,{children:m}),t[0]=m,t[1]=S);let C;t[2]===h?C=t[3]:(C=(0,d.jsx)(n,{children:h}),t[2]=h,t[3]=C);let w;t[4]!==S||t[5]!==C?(w=(0,d.jsxs)(c,{children:[S,C]}),t[4]=S,t[5]=C,t[6]=w):w=t[6];let T;t[7]===Symbol.for(`react.memo_cache_sentinel`)?(T=(0,d.jsx)(s,{children:`Cancel`}),t[7]=T):T=t[7];let E=g?x:b,D;t[8]!==g||t[9]!==_||t[10]!==E?(D=(0,d.jsxs)(o,{children:[T,(0,d.jsx)(a,{disabled:g,onClick:_,children:E})]}),t[8]=g,t[9]=_,t[10]=E,t[11]=D):D=t[11];let O;t[12]!==w||t[13]!==D?(O=(0,d.jsxs)(r,{children:[w,D]}),t[12]=w,t[13]=D,t[14]=O):O=t[14];let k;return t[15]!==p||t[16]!==f||t[17]!==O?(k=(0,d.jsx)(l,{onOpenChange:p,open:f,children:O}),t[15]=p,t[16]=f,t[17]=O,t[18]=k):k=t[18],k}export{f as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-addresses.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-addresses.js index 3bd943efc..6f4d61101 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-addresses.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-addresses.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,H as n,Ho as r,Lo as i,Xa as a,ao as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";var l=t(),u=r();function d(){let t=(0,l.c)(5),{data:r}=e(n),o;t[0]===r.Addresses?o=t[1]:(o=r.Addresses??[],t[0]=r.Addresses,t[1]=o);let c=o,d;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(s,{actions:(0,u.jsx)(i,{to:`/ec2/allocate-address`,children:(0,u.jsx)(a,{children:`Allocate Elastic IP`})}),title:`Elastic IPs`}),t[2]=d):d=t[2];let p;return t[3]===c?p=t[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No Elastic IPs found.`})]}),t[3]=c,t[4]=p),p}function f(e){if(!e.AllocationId)return null;let t=o(e.Tags),n=e.AssociationId?`${e.PublicIp} • associated`:`${e.PublicIp} • available`;return(0,u.jsx)(c,{params:{id:e.AllocationId},subtitle:n,title:t?`${e.AllocationId} (${t})`:e.AllocationId??``,to:`/ec2/describe-addresses/$id`},e.AllocationId)}export{d as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./button.js";import{lt as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";var l=t(),u=e();function d(){let e=(0,l.c)(5),{data:t}=n(o),i;e[0]===t.Addresses?i=e[1]:(i=t.Addresses??[],e[0]=t.Addresses,e[1]=i);let c=i,d;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(s,{actions:(0,u.jsx)(r,{to:`/ec2/allocate-address`,children:(0,u.jsx)(a,{children:`Allocate Elastic IP`})}),title:`Elastic IPs`}),e[2]=d):d=e[2];let p;return e[3]===c?p=e[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No Elastic IPs found.`})]}),e[3]=c,e[4]=p),p}function f(e){if(!e.AllocationId)return null;let t=i(e.Tags),n=e.AssociationId?`${e.PublicIp} • associated`:`${e.PublicIp} • available`;return(0,u.jsx)(c,{params:{id:e.AllocationId},subtitle:n,title:t?`${e.AllocationId} (${t})`:e.AllocationId??``,to:`/ec2/describe-addresses/$id`},e.AllocationId)}export{d as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-images.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-images.js index 67527c711..0a25bee42 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-images.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-images.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,G as n,Ho as r}from"./index.js";import{t as i}from"./page-heading.js";import{t as a}from"./list-card.js";import{t as o}from"./state-badge.js";import{t as s}from"./system-managed.js";var c=t(),l=r();function u(){let t=(0,c.c)(3),{data:r}=e(n),a;if(t[0]!==r.Images){let e=(r.Images??[]).filter(f),n;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(n=(0,l.jsx)(i,{title:`Images`}),t[2]=n):n=t[2],a=(0,l.jsxs)(l.Fragment,{children:[n,e.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:e.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No images found.`})]}),t[0]=r.Images,t[1]=a}else a=t[1];return a}function d(e){return e.ImageId?(0,l.jsx)(a,{badge:(0,l.jsx)(o,{state:e.State}),params:{id:e.ImageId},subtitle:e.ImageId??``,title:e.Name??e.ImageId??``,to:`/ec2/describe-images/$id`},e.ImageId):null}function f(e){return!s(e)}export{u as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{pt as r}from"./index.js";import{t as i}from"./page-heading.js";import{t as a}from"./list-card.js";import{t as o}from"./state-badge.js";import{n as s}from"./system-managed.js";var c=t(),l=e();function u(){let e=(0,c.c)(3),{data:t}=n(r),a;if(e[0]!==t.Images){let n=(t.Images??[]).filter(f),r;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,l.jsx)(i,{title:`Images`}),e[2]=r):r=e[2],a=(0,l.jsxs)(l.Fragment,{children:[r,n.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:n.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No images found.`})]}),e[0]=t.Images,e[1]=a}else a=e[1];return a}function d(e){return e.ImageId?(0,l.jsx)(a,{badge:(0,l.jsx)(o,{state:e.State}),params:{id:e.ImageId},subtitle:e.ImageId??``,title:e.Name??e.ImageId??``,to:`/ec2/describe-images/$id`},e.ImageId):null}function f(e){return!s(e)}export{u as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-instances.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-instances.js index 5a4addc9a..3ffa51af6 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-instances.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-instances.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,J as r,Lo as i,Xa as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";import{t as c}from"./state-badge.js";var l=t(),u=n(),d={running:0,pending:1,stopping:2,"shutting-down":3,stopped:4,terminated:5};function f(){let t=(0,l.c)(3),{data:n}=e(r),s;if(t[0]!==n.Reservations){let e=(n.Reservations?.flatMap(h)??[]).toSorted(m),r;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,u.jsx)(o,{actions:(0,u.jsx)(i,{to:`/ec2/run-instances`,children:(0,u.jsx)(a,{children:`Run Instances`})}),title:`Instances`}),t[2]=r):r=t[2],s=(0,u.jsxs)(u.Fragment,{children:[r,e.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:e.map(p)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No instances found.`})]}),t[0]=n.Reservations,t[1]=s}else s=t[1];return s}function p(e){return e.InstanceId?(0,u.jsx)(s,{badge:(0,u.jsx)(c,{state:e.State?.Name}),params:{id:e.InstanceId},subtitle:`${e.InstanceType}`,title:e.InstanceId,to:`/ec2/describe-instances/$id`},e.InstanceId):null}function m(e,t){let n=d[e.State?.Name??``]??6,r=d[t.State?.Name??``]??6;return n===r?(e.InstanceId??``).localeCompare(t.InstanceId??``):n-r}function h(e){return e.Instances??[]}export{f as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{gt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";import{t as c}from"./state-badge.js";var l=t(),u=e(),d={running:0,pending:1,stopping:2,"shutting-down":3,stopped:4,terminated:5};function f(){let e=(0,l.c)(3),{data:t}=n(a),s;if(e[0]!==t.Reservations){let n=(t.Reservations?.flatMap(h)??[]).toSorted(m),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,u.jsx)(o,{actions:(0,u.jsx)(r,{to:`/ec2/run-instances`,children:(0,u.jsx)(i,{children:`Run Instances`})}),title:`Instances`}),e[2]=a):a=e[2],s=(0,u.jsxs)(u.Fragment,{children:[a,n.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:n.map(p)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No instances found.`})]}),e[0]=t.Reservations,e[1]=s}else s=e[1];return s}function p(e){return e.InstanceId?(0,u.jsx)(s,{badge:(0,u.jsx)(c,{state:e.State?.Name}),params:{id:e.InstanceId},subtitle:`${e.InstanceType}`,title:e.InstanceId,to:`/ec2/describe-instances/$id`},e.InstanceId):null}function m(e,t){let n=d[e.State?.Name??``]??6,r=d[t.State?.Name??``]??6;return n===r?(e.InstanceId??``).localeCompare(t.InstanceId??``):n-r}function h(e){return e.Instances??[]}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-internet-gateways.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-internet-gateways.js index c50a77b00..da57dc6fb 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-internet-gateways.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-internet-gateways.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,X as i,Xa as a,ao as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";var l=t(),u=n();function d(){let t=(0,l.c)(5),{data:n}=e(i),o;t[0]===n.InternetGateways?o=t[1]:(o=n.InternetGateways??[],t[0]=n.InternetGateways,t[1]=o);let c=o,d;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(s,{actions:(0,u.jsx)(r,{to:`/ec2/create-internet-gateway`,children:(0,u.jsx)(a,{children:`Create Internet Gateway`})}),title:`Internet Gateways`}),t[2]=d):d=t[2];let p;return t[3]===c?p=t[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No Internet Gateways found.`})]}),t[3]=c,t[4]=p),p}function f(e){if(!e.InternetGatewayId)return null;let t=o(e.Tags),n=e.Attachments?.[0]?.VpcId;return(0,u.jsx)(c,{params:{id:e.InternetGatewayId},subtitle:n?`attached to ${n}`:`detached`,title:t?`${e.InternetGatewayId} (${t})`:e.InternetGatewayId,to:`/ec2/describe-internet-gateways/$id`},e.InternetGatewayId)}export{d as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./button.js";import{vt as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";var l=t(),u=e();function d(){let e=(0,l.c)(5),{data:t}=n(o),i;e[0]===t.InternetGateways?i=e[1]:(i=t.InternetGateways??[],e[0]=t.InternetGateways,e[1]=i);let c=i,d;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(s,{actions:(0,u.jsx)(r,{to:`/ec2/create-internet-gateway`,children:(0,u.jsx)(a,{children:`Create Internet Gateway`})}),title:`Internet Gateways`}),e[2]=d):d=e[2];let p;return e[3]===c?p=e[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No Internet Gateways found.`})]}),e[3]=c,e[4]=p),p}function f(e){if(!e.InternetGatewayId)return null;let t=i(e.Tags),n=e.Attachments?.[0]?.VpcId;return(0,u.jsx)(c,{params:{id:e.InternetGatewayId},subtitle:n?`attached to ${n}`:`detached`,title:t?`${e.InternetGatewayId} (${t})`:e.InternetGatewayId,to:`/ec2/describe-internet-gateways/$id`},e.InternetGatewayId)}export{d as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-key-pairs.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-key-pairs.js index c31782aff..59fd709ec 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-key-pairs.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-key-pairs.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Q as i,Xa as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=n();function u(){let t=(0,c.c)(4),{data:n}=e(i),s;if(t[0]!==n.KeyPairs){let e=(n.KeyPairs??[]).toSorted(f),i;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(i=(0,l.jsx)(r,{to:`/ec2/import-key-pair`,children:(0,l.jsx)(a,{variant:`outline`,children:`Import Key Pair`})}),t[2]=i):i=t[2];let c;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,l.jsx)(o,{actions:(0,l.jsxs)(`div`,{className:`flex gap-2`,children:[i,(0,l.jsx)(r,{to:`/ec2/create-key-pair`,children:(0,l.jsx)(a,{children:`Create Key Pair`})})]}),title:`Key Pairs`}),t[3]=c):c=t[3],s=(0,l.jsxs)(l.Fragment,{children:[c,e.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:e.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No key pairs found.`})]}),t[0]=n.KeyPairs,t[1]=s}else s=t[1];return s}function d(e){return e.KeyPairId?(0,l.jsx)(s,{params:{id:e.KeyPairId},subtitle:e.KeyPairId??``,title:e.KeyName??``,to:`/ec2/describe-key-pairs/$id`},e.KeyPairId):null}function f(e,t){let n=e.KeyName?.toLowerCase()??``,r=t.KeyName?.toLowerCase()??``;return n.localeCompare(r)}export{u as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{bt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=e();function u(){let e=(0,c.c)(4),{data:t}=n(a),s;if(e[0]!==t.KeyPairs){let n=(t.KeyPairs??[]).toSorted(f),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,l.jsx)(r,{to:`/ec2/import-key-pair`,children:(0,l.jsx)(i,{variant:`outline`,children:`Import Key Pair`})}),e[2]=a):a=e[2];let c;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,l.jsx)(o,{actions:(0,l.jsxs)(`div`,{className:`flex gap-2`,children:[a,(0,l.jsx)(r,{to:`/ec2/create-key-pair`,children:(0,l.jsx)(i,{children:`Create Key Pair`})})]}),title:`Key Pairs`}),e[3]=c):c=e[3],s=(0,l.jsxs)(l.Fragment,{children:[c,n.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:n.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No key pairs found.`})]}),e[0]=t.KeyPairs,e[1]=s}else s=e[1];return s}function d(e){return e.KeyPairId?(0,l.jsx)(s,{params:{id:e.KeyPairId},subtitle:e.KeyPairId??``,title:e.KeyName??``,to:`/ec2/describe-key-pairs/$id`},e.KeyPairId):null}function f(e,t){let n=e.KeyName?.toLowerCase()??``,r=t.KeyName?.toLowerCase()??``;return n.localeCompare(r)}export{u as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-load-balancers.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-load-balancers.js index e82fa9461..d1b7831ba 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-load-balancers.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-load-balancers.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,M as i,Ro as a,Xa as o,no as s}from"./index.js";import{t as c}from"./page-heading.js";import{t as l}from"./state-badge.js";var u=t(),d=n();function f(){let t=(0,u.c)(10),n=a(),{data:f}=e(i),m;t[0]===f.LoadBalancers?m=t[1]:(m=f.LoadBalancers??[],t[0]=f.LoadBalancers,t[1]=m);let h=m,g;t[2]===n?g=t[3]:(g=(0,d.jsx)(c,{actions:(0,d.jsx)(o,{onClick:async()=>await n({to:`/ec2/create-load-balancer`}),children:`Create Load Balancer`}),title:`Load Balancers`}),t[2]=n,t[3]=g);let _;t[4]!==h||t[5]!==n?(_=h.length>0?(0,d.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,d.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Name`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`DNS Name`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`State`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Type`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Scheme`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`VPC`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Created`})]})}),(0,d.jsx)(`tbody`,{children:h.map(e=>{if(!e.LoadBalancerArn)return null;let t=e.LoadBalancerArn;return(0,d.jsxs)(`tr`,{className:`cursor-pointer border-b transition-colors last:border-0 hover:bg-accent`,onClick:async()=>await n({to:`/ec2/describe-load-balancers/$id`,params:{id:encodeURIComponent(t)}}),children:[(0,d.jsx)(`td`,{className:`px-4 py-2 font-medium`,children:(0,d.jsx)(r,{className:`text-primary hover:underline`,onClick:p,params:{id:encodeURIComponent(t)},to:`/ec2/describe-load-balancers/$id`,children:e.LoadBalancerName??t})}),(0,d.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:e.DNSName??``}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:(0,d.jsx)(l,{state:e.State?.Code})}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.Type??``}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.Scheme??``}),(0,d.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:e.VpcId??``}),(0,d.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:s(e.CreatedTime)})]},t)})})]})}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No load balancers found.`}),t[4]=h,t[5]=n,t[6]=_):_=t[6];let v;return t[7]!==g||t[8]!==_?(v=(0,d.jsxs)(d.Fragment,{children:[g,_]}),t[7]=g,t[8]=_,t[9]=v):v=t[9],v}function p(e){return e.stopPropagation()}var m=f;export{m as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{D as i}from"./SignatureV4.js";import{t as a}from"./button.js";import{Ir as o,R as s}from"./index.js";import{t as c}from"./page-heading.js";import{t as l}from"./state-badge.js";var u=t(),d=e();function f(){let e=(0,u.c)(10),t=o(),{data:f}=n(s),m;e[0]===f.LoadBalancers?m=e[1]:(m=f.LoadBalancers??[],e[0]=f.LoadBalancers,e[1]=m);let h=m,g;e[2]===t?g=e[3]:(g=(0,d.jsx)(c,{actions:(0,d.jsx)(a,{onClick:async()=>await t({to:`/ec2/create-load-balancer`}),children:`Create Load Balancer`}),title:`Load Balancers`}),e[2]=t,e[3]=g);let _;e[4]!==h||e[5]!==t?(_=h.length>0?(0,d.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,d.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Name`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`DNS Name`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`State`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Type`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Scheme`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`VPC`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Created`})]})}),(0,d.jsx)(`tbody`,{children:h.map(e=>{if(!e.LoadBalancerArn)return null;let n=e.LoadBalancerArn;return(0,d.jsxs)(`tr`,{className:`cursor-pointer border-b transition-colors last:border-0 hover:bg-accent`,onClick:async()=>await t({to:`/ec2/describe-load-balancers/$id`,params:{id:encodeURIComponent(n)}}),children:[(0,d.jsx)(`td`,{className:`px-4 py-2 font-medium`,children:(0,d.jsx)(r,{className:`text-primary hover:underline`,onClick:p,params:{id:encodeURIComponent(n)},to:`/ec2/describe-load-balancers/$id`,children:e.LoadBalancerName??n})}),(0,d.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:e.DNSName??``}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:(0,d.jsx)(l,{state:e.State?.Code})}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.Type??``}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.Scheme??``}),(0,d.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:e.VpcId??``}),(0,d.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:i(e.CreatedTime)})]},n)})})]})}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No load balancers found.`}),e[4]=h,e[5]=t,e[6]=_):_=e[6];let v;return e[7]!==g||e[8]!==_?(v=(0,d.jsxs)(d.Fragment,{children:[g,_]}),e[7]=g,e[8]=_,e[9]=v):v=e[9],v}function p(e){return e.stopPropagation()}var m=f;export{m as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-nat-gateways.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-nat-gateways.js index 9c4ceb0dd..9fb479870 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-nat-gateways.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-nat-gateways.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Xa as i,ao as a,et as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";import{t as l}from"./state-badge.js";var u=t(),d=n();function f(){let t=(0,u.c)(5),{data:n}=e(o),a;t[0]===n.NatGateways?a=t[1]:(a=n.NatGateways??[],t[0]=n.NatGateways,t[1]=a);let c=a,l;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,d.jsx)(s,{actions:(0,d.jsx)(r,{to:`/ec2/create-nat-gateway`,children:(0,d.jsx)(i,{children:`Create NAT Gateway`})}),title:`NAT Gateways`}),t[2]=l):l=t[2];let f;return t[3]===c?f=t[4]:(f=(0,d.jsxs)(d.Fragment,{children:[l,c.length>0?(0,d.jsx)(`div`,{className:`space-y-4`,children:c.map(p)}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No NAT Gateways found.`})]}),t[3]=c,t[4]=f),f}function p(e){if(!e.NatGatewayId)return null;let t=a(e.Tags);return(0,d.jsx)(c,{badge:(0,d.jsx)(l,{state:e.State}),params:{id:e.NatGatewayId},subtitle:e.SubnetId??``,title:t?`${e.NatGatewayId} (${t})`:e.NatGatewayId,to:`/ec2/describe-nat-gateways/$id`},e.NatGatewayId)}export{f as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./button.js";import{St as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";import{t as l}from"./state-badge.js";var u=t(),d=e();function f(){let e=(0,u.c)(5),{data:t}=n(o),i;e[0]===t.NatGateways?i=e[1]:(i=t.NatGateways??[],e[0]=t.NatGateways,e[1]=i);let c=i,l;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,d.jsx)(s,{actions:(0,d.jsx)(r,{to:`/ec2/create-nat-gateway`,children:(0,d.jsx)(a,{children:`Create NAT Gateway`})}),title:`NAT Gateways`}),e[2]=l):l=e[2];let f;return e[3]===c?f=e[4]:(f=(0,d.jsxs)(d.Fragment,{children:[l,c.length>0?(0,d.jsx)(`div`,{className:`space-y-4`,children:c.map(p)}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No NAT Gateways found.`})]}),e[3]=c,e[4]=f),f}function p(e){if(!e.NatGatewayId)return null;let t=i(e.Tags);return(0,d.jsx)(c,{badge:(0,d.jsx)(l,{state:e.State}),params:{id:e.NatGatewayId},subtitle:e.SubnetId??``,title:t?`${e.NatGatewayId} (${t})`:e.NatGatewayId,to:`/ec2/describe-nat-gateways/$id`},e.NatGatewayId)}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-placement-groups.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-placement-groups.js index 30d7320ae..0a290835b 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-placement-groups.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-placement-groups.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Xa as i,nt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";import{t as c}from"./state-badge.js";var l=t(),u=n();function d(){let t=(0,l.c)(5),{data:n}=e(a),s;t[0]===n.PlacementGroups?s=t[1]:(s=n.PlacementGroups??[],t[0]=n.PlacementGroups,t[1]=s);let c=s,d;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(o,{actions:(0,u.jsx)(r,{to:`/ec2/create-placement-group`,children:(0,u.jsx)(i,{children:`Create Placement Group`})}),title:`Placement Groups`}),t[2]=d):d=t[2];let p;return t[3]===c?p=t[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No placement groups found.`})]}),t[3]=c,t[4]=p),p}function f(e){return e.GroupId?(0,u.jsx)(s,{badge:(0,u.jsx)(c,{state:e.State}),params:{id:e.GroupId},subtitle:`${e.Strategy} \u2022 ${e.GroupId}`,title:e.GroupName??e.GroupId,to:`/ec2/describe-placement-groups/$id`},e.GroupId):null}export{d as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{wt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";import{t as c}from"./state-badge.js";var l=t(),u=e();function d(){let e=(0,l.c)(5),{data:t}=n(a),s;e[0]===t.PlacementGroups?s=e[1]:(s=t.PlacementGroups??[],e[0]=t.PlacementGroups,e[1]=s);let c=s,d;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(o,{actions:(0,u.jsx)(r,{to:`/ec2/create-placement-group`,children:(0,u.jsx)(i,{children:`Create Placement Group`})}),title:`Placement Groups`}),e[2]=d):d=e[2];let p;return e[3]===c?p=e[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No placement groups found.`})]}),e[3]=c,e[4]=p),p}function f(e){return e.GroupId?(0,u.jsx)(s,{badge:(0,u.jsx)(c,{state:e.State}),params:{id:e.GroupId},subtitle:`${e.Strategy} \u2022 ${e.GroupId}`,title:e.GroupName??e.GroupId,to:`/ec2/describe-placement-groups/$id`},e.GroupId):null}export{d as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-route-tables.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-route-tables.js index 378784f81..f8187b859 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-route-tables.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-route-tables.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Xa as i,ao as a,at as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";import{t as l}from"./badge.js";var u=t(),d=n();function f(){let t=(0,u.c)(5),{data:n}=e(o),a;t[0]===n.RouteTables?a=t[1]:(a=n.RouteTables??[],t[0]=n.RouteTables,t[1]=a);let c=a,l;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,d.jsx)(s,{actions:(0,d.jsx)(r,{to:`/ec2/create-route-table`,children:(0,d.jsx)(i,{children:`Create Route Table`})}),title:`Route Tables`}),t[2]=l):l=t[2];let f;return t[3]===c?f=t[4]:(f=(0,d.jsxs)(d.Fragment,{children:[l,c.length>0?(0,d.jsx)(`div`,{className:`space-y-4`,children:c.map(p)}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No Route Tables found.`})]}),t[3]=c,t[4]=f),f}function p(e){if(!e.RouteTableId)return null;let t=a(e.Tags),n=e.Associations?.some(h)??!1,r=e.Associations?.filter(m).length??0;return(0,d.jsx)(c,{badge:n?(0,d.jsx)(l,{variant:`secondary`,children:`Main`}):null,params:{id:e.RouteTableId},subtitle:`${e.VpcId??``} · ${r} subnet${r===1?``:`s`}`,title:t?`${e.RouteTableId} (${t})`:e.RouteTableId,to:`/ec2/describe-route-tables/$id`},e.RouteTableId)}function m(e){return e.SubnetId}function h(e){return e.Main}export{f as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./button.js";import{Dt as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";import{t as l}from"./badge.js";var u=t(),d=e();function f(){let e=(0,u.c)(5),{data:t}=n(o),i;e[0]===t.RouteTables?i=e[1]:(i=t.RouteTables??[],e[0]=t.RouteTables,e[1]=i);let c=i,l;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,d.jsx)(s,{actions:(0,d.jsx)(r,{to:`/ec2/create-route-table`,children:(0,d.jsx)(a,{children:`Create Route Table`})}),title:`Route Tables`}),e[2]=l):l=e[2];let f;return e[3]===c?f=e[4]:(f=(0,d.jsxs)(d.Fragment,{children:[l,c.length>0?(0,d.jsx)(`div`,{className:`space-y-4`,children:c.map(p)}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No Route Tables found.`})]}),e[3]=c,e[4]=f),f}function p(e){if(!e.RouteTableId)return null;let t=i(e.Tags),n=e.Associations?.some(h)??!1,r=e.Associations?.filter(m).length??0;return(0,d.jsx)(c,{badge:n?(0,d.jsx)(l,{variant:`secondary`,children:`Main`}):null,params:{id:e.RouteTableId},subtitle:`${e.VpcId??``} · ${r} subnet${r===1?``:`s`}`,title:t?`${e.RouteTableId} (${t})`:e.RouteTableId,to:`/ec2/describe-route-tables/$id`},e.RouteTableId)}function m(e){return e.SubnetId}function h(e){return e.Main}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-security-groups.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-security-groups.js index c7ae4cd81..de4a29294 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-security-groups.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-security-groups.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Xa as i,st as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=n();function u(){let t=(0,c.c)(5),{data:n}=e(a),s;t[0]===n.SecurityGroups?s=t[1]:(s=n.SecurityGroups??[],t[0]=n.SecurityGroups,t[1]=s);let u=s,f;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(f=(0,l.jsx)(o,{actions:(0,l.jsx)(r,{to:`/ec2/create-security-group`,children:(0,l.jsx)(i,{children:`Create Security Group`})}),title:`Security Groups`}),t[2]=f):f=t[2];let p;return t[3]===u?p=t[4]:(p=(0,l.jsxs)(l.Fragment,{children:[f,u.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:u.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No security groups found.`})]}),t[3]=u,t[4]=p),p}function d(e){return e.GroupId?(0,l.jsx)(s,{params:{id:e.GroupId},subtitle:e.VpcId??``,title:e.GroupName?`${e.GroupId} (${e.GroupName})`:e.GroupId,to:`/ec2/describe-security-groups/$id`},e.GroupId):null}export{u as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{kt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=e();function u(){let e=(0,c.c)(5),{data:t}=n(a),s;e[0]===t.SecurityGroups?s=e[1]:(s=t.SecurityGroups??[],e[0]=t.SecurityGroups,e[1]=s);let u=s,f;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(f=(0,l.jsx)(o,{actions:(0,l.jsx)(r,{to:`/ec2/create-security-group`,children:(0,l.jsx)(i,{children:`Create Security Group`})}),title:`Security Groups`}),e[2]=f):f=e[2];let p;return e[3]===u?p=e[4]:(p=(0,l.jsxs)(l.Fragment,{children:[f,u.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:u.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No security groups found.`})]}),e[3]=u,e[4]=p),p}function d(e){return e.GroupId?(0,l.jsx)(s,{params:{id:e.GroupId},subtitle:e.VpcId??``,title:e.GroupName?`${e.GroupId} (${e.GroupName})`:e.GroupId,to:`/ec2/describe-security-groups/$id`},e.GroupId):null}export{u as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-snapshots.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-snapshots.js index 1c5606a44..76e721908 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-snapshots.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-snapshots.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Xa as i,lt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";import{t as c}from"./state-badge.js";var l=t(),u=n();function d(){let t=(0,l.c)(5),{data:n}=e(a),s;t[0]===n.Snapshots?s=t[1]:(s=n.Snapshots??[],t[0]=n.Snapshots,t[1]=s);let c=s,d;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(o,{actions:(0,u.jsx)(r,{to:`/ec2/create-snapshot`,children:(0,u.jsx)(i,{children:`Create Snapshot`})}),title:`Snapshots`}),t[2]=d):d=t[2];let p;return t[3]===c?p=t[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No snapshots found.`})]}),t[3]=c,t[4]=p),p}function f(e){return e.SnapshotId?(0,u.jsx)(s,{badge:(0,u.jsx)(c,{state:e.State}),params:{id:e.SnapshotId},subtitle:`${e.VolumeSize} GiB \u2022 ${e.VolumeId}`,title:e.SnapshotId,to:`/ec2/describe-snapshots/$id`},e.SnapshotId):null}export{d as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{jt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";import{t as c}from"./state-badge.js";var l=t(),u=e();function d(){let e=(0,l.c)(5),{data:t}=n(a),s;e[0]===t.Snapshots?s=e[1]:(s=t.Snapshots??[],e[0]=t.Snapshots,e[1]=s);let c=s,d;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(o,{actions:(0,u.jsx)(r,{to:`/ec2/create-snapshot`,children:(0,u.jsx)(i,{children:`Create Snapshot`})}),title:`Snapshots`}),e[2]=d):d=e[2];let p;return e[3]===c?p=e[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No snapshots found.`})]}),e[3]=c,e[4]=p),p}function f(e){return e.SnapshotId?(0,u.jsx)(s,{badge:(0,u.jsx)(c,{state:e.State}),params:{id:e.SnapshotId},subtitle:`${e.VolumeSize} GiB \u2022 ${e.VolumeId}`,title:e.SnapshotId,to:`/ec2/describe-snapshots/$id`},e.SnapshotId):null}export{d as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-subnets.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-subnets.js index 1bb08537d..e25a0f4d6 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-subnets.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-subnets.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Xa as i,ao as a,dt as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";import{t as l}from"./state-badge.js";var u=t(),d=n();function f(){let t=(0,u.c)(5),{data:n}=e(o),a;t[0]===n.Subnets?a=t[1]:(a=n.Subnets??[],t[0]=n.Subnets,t[1]=a);let c=a,l;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,d.jsx)(s,{actions:(0,d.jsx)(r,{to:`/ec2/create-subnet`,children:(0,d.jsx)(i,{children:`Create Subnet`})}),title:`Subnets`}),t[2]=l):l=t[2];let f;return t[3]===c?f=t[4]:(f=(0,d.jsxs)(d.Fragment,{children:[l,c.length>0?(0,d.jsx)(`div`,{className:`space-y-4`,children:c.map(p)}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No subnets found.`})]}),t[3]=c,t[4]=f),f}function p(e){if(!e.SubnetId)return null;let t=a(e.Tags);return(0,d.jsx)(c,{badge:(0,d.jsx)(l,{state:e.State}),params:{id:e.SubnetId},subtitle:`${e.CidrBlock??``} \u2022 ${e.AvailabilityZone??``}`,title:t?`${e.SubnetId} (${t})`:e.SubnetId,to:`/ec2/describe-subnets/$id`},e.SubnetId)}export{f as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./button.js";import{Nt as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";import{t as l}from"./state-badge.js";var u=t(),d=e();function f(){let e=(0,u.c)(5),{data:t}=n(o),i;e[0]===t.Subnets?i=e[1]:(i=t.Subnets??[],e[0]=t.Subnets,e[1]=i);let c=i,l;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,d.jsx)(s,{actions:(0,d.jsx)(r,{to:`/ec2/create-subnet`,children:(0,d.jsx)(a,{children:`Create Subnet`})}),title:`Subnets`}),e[2]=l):l=e[2];let f;return e[3]===c?f=e[4]:(f=(0,d.jsxs)(d.Fragment,{children:[l,c.length>0?(0,d.jsx)(`div`,{className:`space-y-4`,children:c.map(p)}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No subnets found.`})]}),e[3]=c,e[4]=f),f}function p(e){if(!e.SubnetId)return null;let t=i(e.Tags);return(0,d.jsx)(c,{badge:(0,d.jsx)(l,{state:e.State}),params:{id:e.SubnetId},subtitle:`${e.CidrBlock??``} \u2022 ${e.AvailabilityZone??``}`,title:t?`${e.SubnetId} (${t})`:e.SubnetId,to:`/ec2/describe-subnets/$id`},e.SubnetId)}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-target-groups.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-target-groups.js index eec1b40ce..6e93a5959 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-target-groups.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-target-groups.js @@ -1 +1 @@ -import{t as e}from"./useQuery.js";import{t}from"./useSuspenseQuery.js";import{Fo as n,Ho as r,I as i,L as a,Lo as o,Ro as s,Xa as c}from"./index.js";import{t as l}from"./page-heading.js";var u=n(),d=r();function f(){let e=(0,u.c)(6),n=s(),{data:r}=t(i),a;e[0]===r.TargetGroups?a=e[1]:(a=r.TargetGroups??[],e[0]=r.TargetGroups,e[1]=a);let f=a,h;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,d.jsx)(l,{actions:(0,d.jsx)(o,{to:`/ec2/create-target-group`,children:(0,d.jsx)(c,{children:`Create Target Group`})}),title:`Target Groups`}),e[2]=h):h=e[2];let g;return e[3]!==n||e[4]!==f?(g=(0,d.jsxs)(d.Fragment,{children:[h,f.length>0?(0,d.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,d.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Name`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Protocol`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Port`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`VPC`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Target Type`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Targets`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Health`})]})}),(0,d.jsx)(`tbody`,{children:f.map(e=>{if(!e.TargetGroupArn)return null;let t=e.TargetGroupArn;return(0,d.jsxs)(`tr`,{className:`cursor-pointer border-b transition-colors last:border-0 hover:bg-accent`,onClick:async()=>await n({to:`/ec2/describe-target-groups/$id`,params:{id:encodeURIComponent(t)}}),children:[(0,d.jsx)(`td`,{className:`px-4 py-2 font-medium`,children:(0,d.jsx)(o,{className:`text-primary hover:underline`,onClick:p,params:{id:encodeURIComponent(t)},to:`/ec2/describe-target-groups/$id`,children:e.TargetGroupName??t})}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.Protocol??``}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.Port??``}),(0,d.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:e.VpcId??``}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.TargetType??``}),(0,d.jsx)(m,{arn:t})]},t)})})]})}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No target groups found.`})]}),e[3]=n,e[4]=f,e[5]=g):g=e[5],g}function p(e){return e.stopPropagation()}function m(t){let n=(0,u.c)(13),{arn:r}=t,i;n[0]===r?i=n[1]:(i=a(r),n[0]=r,n[1]=i);let{data:o,isLoading:s}=e(i);if(s){let e;return n[2]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(`td`,{className:`px-4 py-2 text-muted-foreground`,children:`…`}),(0,d.jsx)(`td`,{className:`px-4 py-2 text-muted-foreground`,children:`…`})]}),n[2]=e):e=n[2],e}let c,l;if(n[3]!==o?.TargetHealthDescriptions){c=o?.TargetHealthDescriptions??[];let e={};for(let t of c){let n=t.TargetHealth?.State??`unknown`;e[n]=(e[n]??0)+1}l=Object.entries(e).map(h).join(`, `)||`—`,n[3]=o?.TargetHealthDescriptions,n[4]=c,n[5]=l}else c=n[4],l=n[5];let f=l,p;n[6]===c.length?p=n[7]:(p=(0,d.jsx)(`td`,{className:`px-4 py-2`,children:c.length}),n[6]=c.length,n[7]=p);let m;n[8]===f?m=n[9]:(m=(0,d.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:f}),n[8]=f,n[9]=m);let g;return n[10]!==p||n[11]!==m?(g=(0,d.jsxs)(d.Fragment,{children:[p,m]}),n[10]=p,n[11]=m,n[12]=g):g=n[12],g}function h(e){let[t,n]=e;return`${n} ${t}`}var g=f;export{g as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useQuery.js";import{t as r}from"./useSuspenseQuery.js";import{Or as i}from"./awsClient.js";import{t as a}from"./button.js";import{Ir as o,U as s,W as c}from"./index.js";import{t as l}from"./page-heading.js";var u=t(),d=e();function f(){let e=(0,u.c)(6),t=o(),{data:n}=r(s),c;e[0]===n.TargetGroups?c=e[1]:(c=n.TargetGroups??[],e[0]=n.TargetGroups,e[1]=c);let f=c,h;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,d.jsx)(l,{actions:(0,d.jsx)(i,{to:`/ec2/create-target-group`,children:(0,d.jsx)(a,{children:`Create Target Group`})}),title:`Target Groups`}),e[2]=h):h=e[2];let g;return e[3]!==t||e[4]!==f?(g=(0,d.jsxs)(d.Fragment,{children:[h,f.length>0?(0,d.jsx)(`div`,{className:`overflow-x-auto rounded-lg border bg-card`,children:(0,d.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Name`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Protocol`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Port`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`VPC`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Target Type`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Targets`}),(0,d.jsx)(`th`,{className:`px-4 py-2 font-medium`,children:`Health`})]})}),(0,d.jsx)(`tbody`,{children:f.map(e=>{if(!e.TargetGroupArn)return null;let n=e.TargetGroupArn;return(0,d.jsxs)(`tr`,{className:`cursor-pointer border-b transition-colors last:border-0 hover:bg-accent`,onClick:async()=>await t({to:`/ec2/describe-target-groups/$id`,params:{id:encodeURIComponent(n)}}),children:[(0,d.jsx)(`td`,{className:`px-4 py-2 font-medium`,children:(0,d.jsx)(i,{className:`text-primary hover:underline`,onClick:p,params:{id:encodeURIComponent(n)},to:`/ec2/describe-target-groups/$id`,children:e.TargetGroupName??n})}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.Protocol??``}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.Port??``}),(0,d.jsx)(`td`,{className:`px-4 py-2 font-mono text-xs`,children:e.VpcId??``}),(0,d.jsx)(`td`,{className:`px-4 py-2`,children:e.TargetType??``}),(0,d.jsx)(m,{arn:n})]},n)})})]})}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No target groups found.`})]}),e[3]=t,e[4]=f,e[5]=g):g=e[5],g}function p(e){return e.stopPropagation()}function m(e){let t=(0,u.c)(13),{arn:r}=e,i;t[0]===r?i=t[1]:(i=c(r),t[0]=r,t[1]=i);let{data:a,isLoading:o}=n(i);if(o){let e;return t[2]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(`td`,{className:`px-4 py-2 text-muted-foreground`,children:`…`}),(0,d.jsx)(`td`,{className:`px-4 py-2 text-muted-foreground`,children:`…`})]}),t[2]=e):e=t[2],e}let s,l;if(t[3]!==a?.TargetHealthDescriptions){s=a?.TargetHealthDescriptions??[];let e={};for(let t of s){let n=t.TargetHealth?.State??`unknown`;e[n]=(e[n]??0)+1}l=Object.entries(e).map(h).join(`, `)||`—`,t[3]=a?.TargetHealthDescriptions,t[4]=s,t[5]=l}else s=t[4],l=t[5];let f=l,p;t[6]===s.length?p=t[7]:(p=(0,d.jsx)(`td`,{className:`px-4 py-2`,children:s.length}),t[6]=s.length,t[7]=p);let m;t[8]===f?m=t[9]:(m=(0,d.jsx)(`td`,{className:`px-4 py-2 text-xs`,children:f}),t[8]=f,t[9]=m);let g;return t[10]!==p||t[11]!==m?(g=(0,d.jsxs)(d.Fragment,{children:[p,m]}),t[10]=p,t[11]=m,t[12]=g):g=t[12],g}function h(e){let[t,n]=e;return`${n} ${t}`}var g=f;export{g as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-volumes.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-volumes.js index 7d90520f5..86684fb4e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-volumes.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-volumes.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Xa as i,pt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";import{t as c}from"./state-badge.js";var l=t(),u=n();function d(){let t=(0,l.c)(5),{data:n}=e(a),s;t[0]===n.Volumes?s=t[1]:(s=n.Volumes??[],t[0]=n.Volumes,t[1]=s);let c=s,d;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(o,{actions:(0,u.jsx)(r,{to:`/ec2/create-volume`,children:(0,u.jsx)(i,{children:`Create Volume`})}),title:`Volumes`}),t[2]=d):d=t[2];let p;return t[3]===c?p=t[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No volumes found.`})]}),t[3]=c,t[4]=p),p}function f(e){return e.VolumeId?(0,u.jsx)(s,{badge:(0,u.jsx)(c,{state:e.State}),params:{id:e.VolumeId},subtitle:`${e.Size} GiB`,title:e.VolumeId,to:`/ec2/describe-volumes/$id`},e.VolumeId):null}export{d as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{Ft as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";import{t as c}from"./state-badge.js";var l=t(),u=e();function d(){let e=(0,l.c)(5),{data:t}=n(a),s;e[0]===t.Volumes?s=e[1]:(s=t.Volumes??[],e[0]=t.Volumes,e[1]=s);let c=s,d;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(o,{actions:(0,u.jsx)(r,{to:`/ec2/create-volume`,children:(0,u.jsx)(i,{children:`Create Volume`})}),title:`Volumes`}),e[2]=d):d=e[2];let p;return e[3]===c?p=e[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No volumes found.`})]}),e[3]=c,e[4]=p),p}function f(e){return e.VolumeId?(0,u.jsx)(s,{badge:(0,u.jsx)(c,{state:e.State}),params:{id:e.VolumeId},subtitle:`${e.Size} GiB`,title:e.VolumeId,to:`/ec2/describe-volumes/$id`},e.VolumeId):null}export{d as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/describe-vpcs.js b/spinifex/services/spinifexui/frontend/dist/assets/describe-vpcs.js index 1666d16e1..730f90493 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/describe-vpcs.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/describe-vpcs.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,Xa as i,ao as a,ht as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";import{t as l}from"./state-badge.js";var u=t(),d=n();function f(){let t=(0,u.c)(5),{data:n}=e(o),a;t[0]===n.Vpcs?a=t[1]:(a=n.Vpcs??[],t[0]=n.Vpcs,t[1]=a);let c=a,l;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,d.jsx)(s,{actions:(0,d.jsx)(r,{to:`/ec2/create-vpc`,children:(0,d.jsx)(i,{children:`Create VPC`})}),title:`VPCs`}),t[2]=l):l=t[2];let f;return t[3]===c?f=t[4]:(f=(0,d.jsxs)(d.Fragment,{children:[l,c.length>0?(0,d.jsx)(`div`,{className:`space-y-4`,children:c.map(p)}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No VPCs found.`})]}),t[3]=c,t[4]=f),f}function p(e){if(!e.VpcId)return null;let t=a(e.Tags);return(0,d.jsx)(c,{badge:(0,d.jsx)(l,{state:e.State}),params:{id:e.VpcId},subtitle:e.CidrBlock??``,title:t?`${e.VpcId} (${t})`:e.VpcId,to:`/ec2/describe-vpcs/$id`},e.VpcId)}export{f as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{A as i}from"./SignatureV4.js";import{t as a}from"./button.js";import{Lt as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";import{t as l}from"./state-badge.js";var u=t(),d=e();function f(){let e=(0,u.c)(5),{data:t}=n(o),i;e[0]===t.Vpcs?i=e[1]:(i=t.Vpcs??[],e[0]=t.Vpcs,e[1]=i);let c=i,l;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,d.jsx)(s,{actions:(0,d.jsx)(r,{to:`/ec2/create-vpc`,children:(0,d.jsx)(a,{children:`Create VPC`})}),title:`VPCs`}),e[2]=l):l=e[2];let f;return e[3]===c?f=e[4]:(f=(0,d.jsxs)(d.Fragment,{children:[l,c.length>0?(0,d.jsx)(`div`,{className:`space-y-4`,children:c.map(p)}):(0,d.jsx)(`p`,{className:`text-muted-foreground`,children:`No VPCs found.`})]}),e[3]=c,e[4]=f),f}function p(e){if(!e.VpcId)return null;let t=i(e.Tags);return(0,d.jsx)(c,{badge:(0,d.jsx)(l,{state:e.State}),params:{id:e.VpcId},subtitle:e.CidrBlock??``,title:t?`${e.VpcId} (${t})`:e.VpcId,to:`/ec2/describe-vpcs/$id`},e.VpcId)}export{f as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/detail-row.js b/spinifex/services/spinifexui/frontend/dist/assets/detail-row.js index 005aac22c..942dfe228 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/detail-row.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/detail-row.js @@ -1 +1 @@ -import{Fo as e,Ho as t}from"./index.js";var n=e(),r=t();function i(e){let t=(0,n.c)(2),{children:i}=e,a;return t[0]===i?a=t[1]:(a=(0,r.jsx)(`div`,{className:`rounded-lg border bg-card`,children:i}),t[0]=i,t[1]=a),a}function a(e){let t=(0,n.c)(2),{children:i}=e,a;return t[0]===i?a=t[1]:(a=(0,r.jsx)(`div`,{className:`border-b p-4`,children:(0,r.jsx)(`h2`,{className:`font-semibold`,children:i})}),t[0]=i,t[1]=a),a}function o(e){let t=(0,n.c)(2),{children:i}=e,a;return t[0]===i?a=t[1]:(a=(0,r.jsx)(`div`,{className:`grid gap-4 p-4 sm:grid-cols-2`,children:i}),t[0]=i,t[1]=a),a}var s=Object.assign(i,{Header:a,Content:o});function c(e){let t=(0,n.c)(9),{label:i,value:a}=e,o;t[0]===i?o=t[1]:(o=(0,r.jsx)(`dt`,{className:`text-sm text-muted-foreground`,children:i}),t[0]=i,t[1]=o);let s;t[2]===a?s=t[3]:(s=a||(0,r.jsx)(`span`,{className:`text-muted-foreground`,children:`—`}),t[2]=a,t[3]=s);let c;t[4]===s?c=t[5]:(c=(0,r.jsx)(`dd`,{className:`mt-1 font-mono text-sm`,children:s}),t[4]=s,t[5]=c);let l;return t[6]!==o||t[7]!==c?(l=(0,r.jsxs)(`div`,{children:[o,c]}),t[6]=o,t[7]=c,t[8]=l):l=t[8],l}export{s as n,c as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";var n=t(),r=e();function i(e){let t=(0,n.c)(2),{children:i}=e,a;return t[0]===i?a=t[1]:(a=(0,r.jsx)(`div`,{className:`rounded-lg border bg-card`,children:i}),t[0]=i,t[1]=a),a}function a(e){let t=(0,n.c)(2),{children:i}=e,a;return t[0]===i?a=t[1]:(a=(0,r.jsx)(`div`,{className:`border-b p-4`,children:(0,r.jsx)(`h2`,{className:`font-semibold`,children:i})}),t[0]=i,t[1]=a),a}function o(e){let t=(0,n.c)(2),{children:i}=e,a;return t[0]===i?a=t[1]:(a=(0,r.jsx)(`div`,{className:`grid gap-4 p-4 sm:grid-cols-2`,children:i}),t[0]=i,t[1]=a),a}var s=Object.assign(i,{Header:a,Content:o});function c(e){let t=(0,n.c)(9),{label:i,value:a}=e,o;t[0]===i?o=t[1]:(o=(0,r.jsx)(`dt`,{className:`text-sm text-muted-foreground`,children:i}),t[0]=i,t[1]=o);let s;t[2]===a?s=t[3]:(s=a||(0,r.jsx)(`span`,{className:`text-muted-foreground`,children:`—`}),t[2]=a,t[3]=s);let c;t[4]===s?c=t[5]:(c=(0,r.jsx)(`dd`,{className:`mt-1 font-mono text-sm`,children:s}),t[4]=s,t[5]=c);let l;return t[6]!==o||t[7]!==c?(l=(0,r.jsxs)(`div`,{children:[o,c]}),t[6]=o,t[7]=c,t[8]=l):l=t[8],l}export{s as n,c as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/dist.js b/spinifex/services/spinifexui/frontend/dist/assets/dist.js new file mode 100644 index 000000000..355493ed3 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/dist.js @@ -0,0 +1 @@ +import{o as e,r as t}from"./compiler-runtime.js";import{M as n}from"./SignatureV4.js";function r(e,t){if(e&&!t)return e;if(!e&&t)return t;if(e||t)return{...e,...t}}var i={};function a(e,t,n,r,i){if(!n&&!r&&!i&&!e)return s(t);let a=s(e);return t&&(a=c(a,t)),n&&(a=c(a,n)),r&&(a=c(a,r)),i&&(a=c(a,i)),a}function o(e){if(e.length===0)return i;if(e.length===1)return s(e[0]);let t=s(e[0]);for(let n=1;n=65&&i<=90&&(typeof t==`function`||t===void 0)}function f(e){return typeof e==`function`}function p(e,t){return f(e)?e(t):e??i}function m(e,t){return t?e?(...n)=>{let r=n[0];if(v(r)){let i=r;g(i);let a=t(...n);return i.baseUIHandlerPrevented||e?.(...n),a}let i=t(...n);return e?.(...n),i}:h(t):e}function h(e){return e&&((...t)=>{let n=t[0];return v(n)&&g(n),e(...t)})}function g(e){return e.preventBaseUIHandler=()=>{e.baseUIHandlerPrevented=!0},e}function _(e,t){return t?e?t+` `+e:t:e}function v(e){return typeof e==`object`&&!!e&&`nativeEvent`in e}function y(e,t){return function(n,...r){let i=new URL(e);return i.searchParams.set(`code`,n.toString()),r.forEach(e=>i.searchParams.append(`args[]`,e)),`${t} error #${n}; visit ${i} for the full message.`}}var b=y(`https://base-ui.com/production-error`,`Base UI`),x=e(t()),S={};function C(e,t){let n=x.useRef(S);return n.current===S&&(n.current=e(t)),n}function w(e,t,n,r){let i=C(E).current;return D(i,e,t,n,r)&&k(i,[e,t,n,r]),i.callback}function T(e){let t=C(E).current;return O(t,e)&&k(t,e),t.callback}function E(){return{callback:null,cleanup:null,refs:[]}}function D(e,t,n,r,i){return e.refs[0]!==t||e.refs[1]!==n||e.refs[2]!==r||e.refs[3]!==i}function O(e,t){return e.refs.length!==t.length||e.refs.some((e,n)=>e!==t[n])}function k(e,t){if(e.refs=t,t.every(e=>e==null)){e.callback=null;return}e.callback=n=>{if(e.cleanup&&=(e.cleanup(),null),n!=null){let r=Array(t.length).fill(null);for(let e=0;e{for(let e=0;e=e}function M(e){if(!x.isValidElement(e))return null;let t=e,n=t.props;return(j(19)?n?.ref:t.ref)??null}function N(){}var P=Object.freeze([]),F=Object.freeze({});function I(e,t){let n={};for(let r in e){let i=e[r];if(t?.hasOwnProperty(r)){let e=t[r](i);e!=null&&Object.assign(n,e);continue}i===!0?n[`data-${r.toLowerCase()}`]=``:i&&(n[`data-${r.toLowerCase()}`]=i.toString())}return n}function L(e,t){return typeof e==`function`?e(t):e}function R(e,t){return typeof e==`function`?e(t):e}function z(e,t,n={}){let r=t.render,i=B(t,n);return n.enabled===!1?null:U(e,r,i,n.state??F)}function B(e,t={}){let{className:n,style:i,render:a}=e,{state:o=F,ref:s,props:c,stateAttributesMapping:l,enabled:u=!0}=t,d=u?L(n,o):void 0,f=u?R(i,o):void 0,p=u?I(o,l):F,m=u&&c?V(c):void 0,h=u?r(p,m)??{}:F;return typeof document<`u`&&(u?Array.isArray(s)?h.ref=T([h.ref,M(a),...s]):h.ref=w(h.ref,M(a),s):w(null,null)),u?(d!==void 0&&(h.className=_(h.className,d)),f!==void 0&&(h.style=r(h.style,f)),h):F}function V(e){return Array.isArray(e)?o(e):a(void 0,e)}var H=Symbol.for(`react.lazy`);function U(e,t,n,r){if(t){if(typeof t==`function`)return t(n,r);let e=a(n,t.props);e.ref=n.ref;let i=t;return i?.$$typeof===H&&(i=x.Children.toArray(t)[0]),x.cloneElement(i,e)}if(e&&typeof e==`string`)return W(e,n);throw Error(b(8))}function W(e,t){return e===`button`?(0,x.createElement)(`button`,{type:`button`,...t,key:t.key}):e===`img`?(0,x.createElement)(`img`,{alt:``,...t,key:t.key}):x.createElement(e,t)}var G=e=>typeof e==`boolean`?`${e}`:e===0?`0`:e,K=n,q=(e,t)=>n=>{if(t?.variants==null)return K(e,n?.class,n?.className);let{variants:r,defaultVariants:i}=t,a=Object.keys(r).map(e=>{let t=n?.[e],a=i?.[e];if(t===null)return null;let o=G(t)||G(a);return r[e][o]}),o=n&&Object.entries(n).reduce((e,t)=>{let[n,r]=t;return r===void 0||(e[n]=r),e},{});return K(e,a,t?.compoundVariants?.reduce((e,t)=>{let{class:n,className:r,...a}=t;return Object.entries(a).every(e=>{let[t,n]=e;return Array.isArray(n)?n.includes({...i,...o}[t]):{...i,...o}[t]===n})?[...e,n,r]:e},[]),n?.class,n?.className)};export{F as a,w as c,g as d,a as f,P as i,C as l,z as n,N as o,R as r,j as s,q as t,b as u}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/download.js b/spinifex/services/spinifexui/frontend/dist/assets/download.js index 17a4663cc..d91050ecd 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/download.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/download.js @@ -1 +1 @@ -import{wo as e}from"./index.js";var t=e(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]);export{t}; \ No newline at end of file +import{Nr as e}from"./index.js";var t=e(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/ec2.js b/spinifex/services/spinifexui/frontend/dist/assets/ec2.js index 877bdcea6..86c59621f 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/ec2.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/ec2.js @@ -1 +1 @@ -import{n as e}from"./error-banner.js";import{$n as t,An as n,Bn as r,Cn as i,Dn as a,En as o,Fn as s,Fo as c,Gn as l,Hn as u,In as d,Jn as f,Kn as p,Ln as ee,Mn as te,Nn as ne,On as m,Pn as h,Q as g,Qn as re,Rn as ie,Sn as ae,Tn as oe,Un as se,Vn as ce,Vo as _,Wn as le,Xn as ue,Yn as de,Zn as fe,_n as pe,bn as me,cr as he,dn as ge,er as _e,fn as ve,fr as ye,gn as be,hn as xe,ir as v,jn as Se,kn as Ce,lr as y,mn as b,nr as x,or as S,pn as C,pr as w,qn as T,rr as E,sr as D,tr as O,un as k,vn as A,vr as we,vt as j,wn as Te,wr as M,xn as Ee,yn as De,zn as Oe}from"./index.js";var N=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AllocateAddress`,{}).n(`EC2Client`,`AllocateAddressCommand`).sc(k).build(){},ke=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AssociateAddress`,{}).n(`EC2Client`,`AssociateAddressCommand`).sc(ge).build(){},P=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AssociateRouteTable`,{}).n(`EC2Client`,`AssociateRouteTableCommand`).sc(ve).build(){},F=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AttachInternetGateway`,{}).n(`EC2Client`,`AttachInternetGatewayCommand`).sc(C).build(){},Ae=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AttachVolume`,{}).n(`EC2Client`,`AttachVolumeCommand`).sc(b).build(){},je=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AuthorizeSecurityGroupEgress`,{}).n(`EC2Client`,`AuthorizeSecurityGroupEgressCommand`).sc(xe).build(){},I=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AuthorizeSecurityGroupIngress`,{}).n(`EC2Client`,`AuthorizeSecurityGroupIngressCommand`).sc(be).build(){};function Me(e){let{port:t,query:n}=e,{protocol:r,path:i,hostname:a}=e;r&&r.slice(-1)!==`:`&&(r+=`:`),t&&(a+=`:${t}`),i&&i.charAt(0)!==`/`&&(i=`/${i}`);let o=n?w(n):``;o&&o[0]!==`?`&&(o=`?${o}`);let s=``;(e.username!=null||e.password!=null)&&(s=`${e.username??``}:${e.password??``}@`);let c=``;return e.fragment&&(c=`#${e.fragment}`),`${r}//${s}${a}${i}${o}${c}`}var Ne=`2016-11-15`;function Pe(e){return(t,n)=>async n=>{let{input:r}=n;if(!r.PresignedUrl){let t=await e.region(),i=await he(r,{getEndpointParameterInstructions(){return{UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}}}},{...e,region:r.SourceRegion}),a=typeof e.endpoint==`function`?await e.endpoint():y(i),o=new M({...a,protocol:`https`,headers:{host:a.hostname},query:{...Object.entries(r).reduce((e,[t,n])=>(e[t]=String(n??``),e),{}),Action:`CopySnapshot`,Version:Ne,DestinationRegion:t}}),s=await new we({credentials:e.credentials,region:r.SourceRegion,service:`ec2`,sha256:e.sha256,uriEscapePath:e.signingEscapePath}).presign(o,{expiresIn:3600});if(n={...n,input:{...n.input,DestinationRegion:t,PresignedUrl:Me(s)}},M.isInstance(n.request)){let{request:e}=n;(e.body??``).includes(`DestinationRegion=`)||(e.body+=`&DestinationRegion=${t}`),(e.body??``).includes(`PresignedUrl=`)||(e.body+=`&PresignedUrl=${ye(n.input.PresignedUrl)}`)}}return t(n)}}var Fe={step:`serialize`,tags:[`CROSS_REGION_PRESIGNED_URL`],name:`crossRegionPresignedUrlMiddleware`,override:!0,relation:`after`,toMiddleware:`endpointV2Middleware`},Ie=e=>({applyToStack:t=>{t.add(Pe(e),Fe)}}),Le=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions()),Ie(n)]}).s(`AmazonEC2`,`CopySnapshot`,{}).n(`EC2Client`,`CopySnapshotCommand`).sc(pe).build(){},Re=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateImage`,{}).n(`EC2Client`,`CreateImageCommand`).sc(A).build(){},L=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateInternetGateway`,{}).n(`EC2Client`,`CreateInternetGatewayCommand`).sc(De).build(){},ze=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateKeyPair`,{}).n(`EC2Client`,`CreateKeyPairCommand`).sc(me).build(){},R=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateNatGateway`,{}).n(`EC2Client`,`CreateNatGatewayCommand`).sc(Ee).build(){},Be=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreatePlacementGroup`,{}).n(`EC2Client`,`CreatePlacementGroupCommand`).sc(ae).build(){},z=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateRoute`,{}).n(`EC2Client`,`CreateRouteCommand`).sc(i).build(){},B=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateRouteTable`,{}).n(`EC2Client`,`CreateRouteTableCommand`).sc(Te).build(){},V=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateSecurityGroup`,{}).n(`EC2Client`,`CreateSecurityGroupCommand`).sc(oe).build(){},Ve=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateSnapshot`,{}).n(`EC2Client`,`CreateSnapshotCommand`).sc(o).build(){},H=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateSubnet`,{}).n(`EC2Client`,`CreateSubnetCommand`).sc(a).build(){},He=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateVolume`,{}).n(`EC2Client`,`CreateVolumeCommand`).sc(m).build(){},U=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateVpc`,{}).n(`EC2Client`,`CreateVpcCommand`).sc(Ce).build(){},Ue=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteInternetGateway`,{}).n(`EC2Client`,`DeleteInternetGatewayCommand`).sc(n).build(){},We=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteKeyPair`,{}).n(`EC2Client`,`DeleteKeyPairCommand`).sc(Se).build(){},Ge=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteNatGateway`,{}).n(`EC2Client`,`DeleteNatGatewayCommand`).sc(te).build(){},Ke=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeletePlacementGroup`,{}).n(`EC2Client`,`DeletePlacementGroupCommand`).sc(ne).build(){},qe=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteRoute`,{}).n(`EC2Client`,`DeleteRouteCommand`).sc(h).build(){},Je=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteRouteTable`,{}).n(`EC2Client`,`DeleteRouteTableCommand`).sc(s).build(){},Ye=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteSecurityGroup`,{}).n(`EC2Client`,`DeleteSecurityGroupCommand`).sc(d).build(){},Xe=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteSnapshot`,{}).n(`EC2Client`,`DeleteSnapshotCommand`).sc(ee).build(){},Ze=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteSubnet`,{}).n(`EC2Client`,`DeleteSubnetCommand`).sc(ie).build(){},Qe=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteVolume`,{}).n(`EC2Client`,`DeleteVolumeCommand`).sc(Oe).build(){},$e=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteVpc`,{}).n(`EC2Client`,`DeleteVpcCommand`).sc(r).build(){},et=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DetachInternetGateway`,{}).n(`EC2Client`,`DetachInternetGatewayCommand`).sc(ce).build(){},tt=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DetachVolume`,{}).n(`EC2Client`,`DetachVolumeCommand`).sc(u).build(){},nt=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DisassociateAddress`,{}).n(`EC2Client`,`DisassociateAddressCommand`).sc(se).build(){},rt=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DisassociateRouteTable`,{}).n(`EC2Client`,`DisassociateRouteTableCommand`).sc(le).build(){},it=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`GetConsoleOutput`,{}).n(`EC2Client`,`GetConsoleOutputCommand`).sc(l).build(){},at=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ImportKeyPair`,{}).n(`EC2Client`,`ImportKeyPairCommand`).sc(p).build(){},ot=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ModifyInstanceAttribute`,{}).n(`EC2Client`,`ModifyInstanceAttributeCommand`).sc(T).build(){},W=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ModifySubnetAttribute`,{}).n(`EC2Client`,`ModifySubnetAttributeCommand`).sc(f).build(){},st=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ModifyVolume`,{}).n(`EC2Client`,`ModifyVolumeCommand`).sc(de).build(){},ct=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`RebootInstances`,{}).n(`EC2Client`,`RebootInstancesCommand`).sc(ue).build(){},lt=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ReleaseAddress`,{}).n(`EC2Client`,`ReleaseAddressCommand`).sc(fe).build(){},ut=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`RevokeSecurityGroupEgress`,{}).n(`EC2Client`,`RevokeSecurityGroupEgressCommand`).sc(re).build(){},dt=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`RevokeSecurityGroupIngress`,{}).n(`EC2Client`,`RevokeSecurityGroupIngressCommand`).sc(t).build(){},ft=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`RunInstances`,{}).n(`EC2Client`,`RunInstancesCommand`).sc(_e).build(){},pt=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`StartInstances`,{}).n(`EC2Client`,`StartInstancesCommand`).sc(O).build(){},mt=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`StopInstances`,{}).n(`EC2Client`,`StopInstancesCommand`).sc(x).build(){},ht=class extends S.classBuilder().ep(v).m(function(e,t,n,r){return[D(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`TerminateInstances`,{}).n(`EC2Client`,`TerminateInstancesCommand`).sc(E).build(){},G={capacity_block:`capacity-block`,capacity_manager_data_export:`capacity-manager-data-export`,capacity_reservation:`capacity-reservation`,capacity_reservation_fleet:`capacity-reservation-fleet`,carrier_gateway:`carrier-gateway`,client_vpn_endpoint:`client-vpn-endpoint`,coip_pool:`coip-pool`,customer_gateway:`customer-gateway`,declarative_policies_report:`declarative-policies-report`,dedicated_host:`dedicated-host`,dhcp_options:`dhcp-options`,egress_only_internet_gateway:`egress-only-internet-gateway`,elastic_gpu:`elastic-gpu`,elastic_ip:`elastic-ip`,export_image_task:`export-image-task`,export_instance_task:`export-instance-task`,fleet:`fleet`,fpga_image:`fpga-image`,host_reservation:`host-reservation`,image:`image`,image_usage_report:`image-usage-report`,import_image_task:`import-image-task`,import_snapshot_task:`import-snapshot-task`,instance:`instance`,instance_connect_endpoint:`instance-connect-endpoint`,instance_event_window:`instance-event-window`,internet_gateway:`internet-gateway`,ipam:`ipam`,ipam_external_resource_verification_token:`ipam-external-resource-verification-token`,ipam_policy:`ipam-policy`,ipam_pool:`ipam-pool`,ipam_prefix_list_resolver:`ipam-prefix-list-resolver`,ipam_prefix_list_resolver_target:`ipam-prefix-list-resolver-target`,ipam_resource_discovery:`ipam-resource-discovery`,ipam_resource_discovery_association:`ipam-resource-discovery-association`,ipam_scope:`ipam-scope`,ipv4pool_ec2:`ipv4pool-ec2`,ipv6pool_ec2:`ipv6pool-ec2`,key_pair:`key-pair`,launch_template:`launch-template`,local_gateway:`local-gateway`,local_gateway_route_table:`local-gateway-route-table`,local_gateway_route_table_virtual_interface_group_association:`local-gateway-route-table-virtual-interface-group-association`,local_gateway_route_table_vpc_association:`local-gateway-route-table-vpc-association`,local_gateway_virtual_interface:`local-gateway-virtual-interface`,local_gateway_virtual_interface_group:`local-gateway-virtual-interface-group`,mac_modification_task:`mac-modification-task`,natgateway:`natgateway`,network_acl:`network-acl`,network_insights_access_scope:`network-insights-access-scope`,network_insights_access_scope_analysis:`network-insights-access-scope-analysis`,network_insights_analysis:`network-insights-analysis`,network_insights_path:`network-insights-path`,network_interface:`network-interface`,outpost_lag:`outpost-lag`,placement_group:`placement-group`,prefix_list:`prefix-list`,replace_root_volume_task:`replace-root-volume-task`,reserved_instances:`reserved-instances`,route_server:`route-server`,route_server_endpoint:`route-server-endpoint`,route_server_peer:`route-server-peer`,route_table:`route-table`,secondary_interface:`secondary-interface`,secondary_network:`secondary-network`,secondary_subnet:`secondary-subnet`,security_group:`security-group`,security_group_rule:`security-group-rule`,service_link_virtual_interface:`service-link-virtual-interface`,snapshot:`snapshot`,spot_fleet_request:`spot-fleet-request`,spot_instances_request:`spot-instances-request`,subnet:`subnet`,subnet_cidr_reservation:`subnet-cidr-reservation`,traffic_mirror_filter:`traffic-mirror-filter`,traffic_mirror_filter_rule:`traffic-mirror-filter-rule`,traffic_mirror_session:`traffic-mirror-session`,traffic_mirror_target:`traffic-mirror-target`,transit_gateway:`transit-gateway`,transit_gateway_attachment:`transit-gateway-attachment`,transit_gateway_connect_peer:`transit-gateway-connect-peer`,transit_gateway_metering_policy:`transit-gateway-metering-policy`,transit_gateway_multicast_domain:`transit-gateway-multicast-domain`,transit_gateway_policy_table:`transit-gateway-policy-table`,transit_gateway_route_table:`transit-gateway-route-table`,transit_gateway_route_table_announcement:`transit-gateway-route-table-announcement`,verified_access_endpoint:`verified-access-endpoint`,verified_access_endpoint_target:`verified-access-endpoint-target`,verified_access_group:`verified-access-group`,verified_access_instance:`verified-access-instance`,verified_access_policy:`verified-access-policy`,verified_access_trust_provider:`verified-access-trust-provider`,volume:`volume`,vpc:`vpc`,vpc_block_public_access_exclusion:`vpc-block-public-access-exclusion`,vpc_encryption_control:`vpc-encryption-control`,vpc_endpoint:`vpc-endpoint`,vpc_endpoint_connection:`vpc-endpoint-connection`,vpc_endpoint_connection_device_type:`vpc-endpoint-connection-device-type`,vpc_endpoint_service:`vpc-endpoint-service`,vpc_endpoint_service_permission:`vpc-endpoint-service-permission`,vpc_flow_log:`vpc-flow-log`,vpc_peering_connection:`vpc-peering-connection`,vpn_concentrator:`vpn-concentrator`,vpn_connection:`vpn-connection`,vpn_connection_device_type:`vpn-connection-device-type`,vpn_gateway:`vpn-gateway`},K=c();function q(e){let t=e.split(`/`),n=t[0]??``,r=t[1]??`0`,i=Number.parseInt(r,10),a=n.split(`.`).map(e=>Number.parseInt(e,10)),o=a[0]??0,s=a[1]??0,c=a[2]??0,l=a[3]??0;return{network:((o<<24|s<<16|c<<8|l)>>>0&(i===0?0:4294967295<<32-i>>>0))>>>0,prefix:i}}function J(e){return`${e>>>24&255}.${e>>>16&255}.${e>>>8&255}.${e&255}`}function Y(e){return 2**(32-e)}function gt(e){let t=e+4;return Math.min(t,28)}function X(e,t,n){let{network:r,prefix:i}=q(e),a=gt(i),o=Y(a),s=Y(i)/2,c=[];for(let e=0;e>>0;c.push({cidr:`${J(t)}/${a}`,label:`Public Subnet ${e+1}`})}let l=[];for(let e=0;e>>0;l.push({cidr:`${J(t)}/${a}`,label:`Private Subnet ${e+1}`})}return{publicSubnets:c,privateSubnets:l}}function _t(e,t){let n=q(e),r=q(t),i=n.network+Y(n.prefix)-1>>>0,a=r.network+Y(r.prefix)-1>>>0;return n.network<=a&&r.network<=i}function vt(e,t){let n=q(e),r=q(t),i=n.network+Y(n.prefix)-1>>>0,a=r.network+Y(r.prefix)-1>>>0;return r.network>=n.network&&a<=i}function yt(e,t=16,n=28){let r=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/.exec(e);if(!r||[Number.parseInt(r[1]??`0`,10),Number.parseInt(r[2]??`0`,10),Number.parseInt(r[3]??`0`,10),Number.parseInt(r[4]??`0`,10)].some(e=>e>255))return!1;let i=Number.parseInt(r[5]??`0`,10);return i>=t&&i<=n}var bt=/\s+/;function xt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:St,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function St(e){let t=new pt({InstanceIds:[e]});return await j().send(t)}function Ct(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:wt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function wt(e){let t=new mt({InstanceIds:[e]});return await j().send(t)}function Tt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Et,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function Et(e){let t=new ht({InstanceIds:[e]});return await j().send(t)}function Dt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Ot,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function Ot(e){let t=new ct({InstanceIds:[e]});return await j().send(t)}function kt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:At,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]})}},t[0]=n,t[1]=r),e(r)}async function At(e){let t=j(),n=await jt(t,e),r=new ft({ImageId:e.imageId,InstanceType:e.instanceType,KeyName:e.keyName,MinCount:e.count,MaxCount:e.count,SubnetId:e.subnetId||void 0,SecurityGroupIds:n.length>0?n:void 0,Placement:e.placementGroupName?{GroupName:e.placementGroupName}:void 0,BlockDeviceMappings:Mt(e)});return await t.send(r)}async function jt(e,t){if(t.securityGroupMode!==`create`)return t.securityGroupIds??[];let n=(await e.send(new V({GroupName:t.newSgName,Description:t.newSgDescription||`Created by launch wizard`,VpcId:t.resolvedVpcId}))).GroupId;if(!n)throw Error(`Security group was created but no ID was returned`);let r=[...t.allowSsh?[22]:[],...t.allowHttp?[80]:[],...t.allowHttps?[443]:[]];if(r.length>0){let i=t.ruleSource===`custom`&&t.customCidr?t.customCidr:`0.0.0.0/0`;await e.send(new I({GroupId:n,IpPermissions:r.map(e=>({IpProtocol:`tcp`,FromPort:e,ToPort:e,IpRanges:[{CidrIp:i}]}))}))}return[n]}function Mt(e){let{rootDeviceName:t,rootVolumeSize:n,rootVolumeType:r,rootDeleteOnTermination:i}=e;return n!==void 0||r!==void 0||i!==void 0?[{DeviceName:t,Ebs:{...n!==void 0&&{VolumeSize:n},...r!==void 0&&{VolumeType:r},...i!==void 0&&{DeleteOnTermination:i}}}]:void 0}function Nt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Pt,onSuccess:()=>{n.invalidateQueries(g)}},t[0]=n,t[1]=r),e(r)}async function Pt(e){let t=new ze({KeyName:e.keyName,KeyType:`rsa`}),n=await j().send(t);if(!n.KeyMaterial)throw Error(`Key pair was created but the API returned no private key. The key cannot be recovered — delete it and try again.`);return n}function Ft(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:It,onSuccess:()=>{n.invalidateQueries(g)}},t[0]=n,t[1]=r),e(r)}async function It(e){let t=e.publicKeyMaterial.trim().split(bt).slice(0,2).join(` `),n=new at({KeyName:e.keyName,PublicKeyMaterial:new TextEncoder().encode(t)});return await j().send(n)}function Lt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Rt,onSuccess:()=>{n.invalidateQueries(g)}},t[0]=n,t[1]=r),e(r)}async function Rt(e){let t=new We({KeyPairId:e});return await j().send(t)}function zt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Bt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]})}},t[0]=n,t[1]=r),e(r)}async function Bt(e){let t=new He({Size:e.size,AvailabilityZone:e.availabilityZone,VolumeType:`gp3`});return await j().send(t)}function Vt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Ht,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]})}},t[0]=n,t[1]=r),e(r)}async function Ht(e){let t=new st({VolumeId:e.volumeId,Size:e.size});return await j().send(t)}function Ut(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Wt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]})}},t[0]=n,t[1]=r),e(r)}async function Wt(e){let t=new Qe({VolumeId:e});return await j().send(t)}function Gt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Kt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`snapshots`]})}},t[0]=n,t[1]=r),e(r)}async function Kt(e){let t=new Ve({VolumeId:e.volumeId,Description:e.description||void 0});return await j().send(t)}function qt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Jt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`snapshots`]})}},t[0]=n,t[1]=r),e(r)}async function Jt(e){let t=new Xe({SnapshotId:e});return await j().send(t)}function Yt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Xt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`snapshots`]})}},t[0]=n,t[1]=r),e(r)}async function Xt(e){let t=new Le({SourceSnapshotId:e.sourceSnapshotId,SourceRegion:e.sourceRegion,Description:e.description||void 0});return await j().send(t)}function Zt(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Qt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function Qt(e){let t=new Ae({VolumeId:e.volumeId,InstanceId:e.instanceId,Device:e.device||void 0});return await j().send(t)}function $t(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:en,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function en(e){let t=new tt({VolumeId:e.volumeId,InstanceId:e.instanceId||void 0,Force:e.force});return await j().send(t)}function tn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:nn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function nn(e){let{instanceId:t,...n}=e,r=new ot({InstanceId:t,InstanceType:{Value:n.instanceType}});return await j().send(r)}function rn(){let t=(0,K.c)(1),n;return t[0]===Symbol.for(`react.memo_cache_sentinel`)?(n={mutationFn:an},t[0]=n):n=t[0],e(n)}async function an(e){let t=new it({InstanceId:e});return await j().send(t)}function on(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:sn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`images`]})}},t[0]=n,t[1]=r),e(r)}async function sn(e){let t=new Re({InstanceId:e.instanceId,Name:e.name,Description:e.description||void 0});return await j().send(t)}function cn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:ln,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`vpcs`]})}},t[0]=n,t[1]=r),e(r)}async function ln(e){let t=new U({CidrBlock:e.cidrBlock,TagSpecifications:e.name?[{ResourceType:`vpc`,Tags:[{Key:`Name`,Value:e.name}]}]:void 0});return await j().send(t)}function un(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:dn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`vpcs`]})}},t[0]=n,t[1]=r),e(r)}async function dn(e){let t=new $e({VpcId:e});return await j().send(t)}function fn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:pn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`subnets`]})}},t[0]=n,t[1]=r),e(r)}async function pn(e){let t=j(),n=await t.send(new H({VpcId:e.vpcId,CidrBlock:e.cidrBlock,AvailabilityZone:e.availabilityZone||void 0})),r=n.Subnet?.SubnetId;return e.mapPublicIpOnLaunch&&r&&await t.send(new W({SubnetId:r,MapPublicIpOnLaunch:{Value:!0}})),n}function mn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:hn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`subnets`]})}},t[0]=n,t[1]=r),e(r)}async function hn(e){let t=new Ze({SubnetId:e});return await j().send(t)}function gn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:_n,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`placementGroups`]})}},t[0]=n,t[1]=r),e(r)}async function _n(e){let t=new Be({GroupName:e.groupName,Strategy:e.strategy});return await j().send(t)}function vn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:yn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`placementGroups`]})}},t[0]=n,t[1]=r),e(r)}async function yn(e){let t=new Ke({GroupName:e});return await j().send(t)}function bn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:xn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]})}},t[0]=n,t[1]=r),e(r)}async function xn(e){let t=new V({GroupName:e.groupName,Description:e.description,VpcId:e.vpcId});return await j().send(t)}function Sn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Cn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]})}},t[0]=n,t[1]=r),e(r)}async function Cn(e){let t=new Ye({GroupId:e});return await j().send(t)}function wn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Tn,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`,t.groupId]})}},t[0]=n,t[1]=r),e(r)}async function Tn(e){let t=new I({GroupId:e.groupId,IpPermissions:[{IpProtocol:e.ipProtocol,FromPort:e.fromPort,ToPort:e.toPort,IpRanges:[{CidrIp:e.cidrIp}]}]});return await j().send(t)}function En(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Dn,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`,t.groupId]})}},t[0]=n,t[1]=r),e(r)}async function Dn(e){let t=new je({GroupId:e.groupId,IpPermissions:[{IpProtocol:e.ipProtocol,FromPort:e.fromPort,ToPort:e.toPort,IpRanges:[{CidrIp:e.cidrIp}]}]});return await j().send(t)}function Z(e){return{IpProtocol:e.ipProtocol,FromPort:e.fromPort,ToPort:e.toPort,...e.sourceGroupId?{UserIdGroupPairs:[{GroupId:e.sourceGroupId}]}:{IpRanges:[{CidrIp:e.cidrIp??``}]}}}function On(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:kn,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`,t.groupId]})}},t[0]=n,t[1]=r),e(r)}async function kn(e){let t=new dt({GroupId:e.groupId,IpPermissions:[Z(e)]});return await j().send(t)}function An(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:jn,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`,t.groupId]})}},t[0]=n,t[1]=r),e(r)}async function jn(e){let t=new ut({GroupId:e.groupId,IpPermissions:[Z(e)]});return await j().send(t)}function Mn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Nn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`internetGateways`]})}},t[0]=n,t[1]=r),e(r)}async function Nn(e){let t=new L({TagSpecifications:$(G.internet_gateway,e?.name,[])});return await j().send(t)}function Pn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Fn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`internetGateways`]})}},t[0]=n,t[1]=r),e(r)}async function Fn(e){let t=new Ue({InternetGatewayId:e});return await j().send(t)}function In(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Ln,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`internetGateways`]})}},t[0]=n,t[1]=r),e(r)}async function Ln(e){let t=new F({InternetGatewayId:e.internetGatewayId,VpcId:e.vpcId});return await j().send(t)}function Rn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:zn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`internetGateways`]})}},t[0]=n,t[1]=r),e(r)}async function zn(e){let t=new et({InternetGatewayId:e.internetGatewayId,VpcId:e.vpcId});return await j().send(t)}function Bn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Vn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`addresses`]})}},t[0]=n,t[1]=r),e(r)}async function Vn(e){let t=new N({Domain:`vpc`,TagSpecifications:$(G.elastic_ip,e?.name,[])});return await j().send(t)}function Hn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Un,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`addresses`]})}},t[0]=n,t[1]=r),e(r)}async function Un(e){let t=new lt({AllocationId:e});return await j().send(t)}function Wn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Gn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`addresses`]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function Gn(e){let t=new ke({AllocationId:e.allocationId,InstanceId:e.instanceId,NetworkInterfaceId:e.networkInterfaceId});return await j().send(t)}function Kn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:qn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`addresses`]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},t[0]=n,t[1]=r),e(r)}async function qn(e){let t=new nt({AssociationId:e});return await j().send(t)}function Jn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Q,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`natGateways`]}),n.invalidateQueries({queryKey:[`ec2`,`addresses`]})}},t[0]=n,t[1]=r),e(r)}async function Q(e){let t=new R({SubnetId:e.subnetId,AllocationId:e.allocationId,TagSpecifications:$(G.natgateway,e.name,[])});return await j().send(t)}function Yn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Xn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`natGateways`]})}},t[0]=n,t[1]=r),e(r)}async function Xn(e){let t=new Ge({NatGatewayId:e});return await j().send(t)}function Zn(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:Qn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},t[0]=n,t[1]=r),e(r)}async function Qn(e){let t=new B({VpcId:e.vpcId,TagSpecifications:$(G.route_table,e.name,[])});return await j().send(t)}function $n(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:er,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},t[0]=n,t[1]=r),e(r)}async function er(e){let t=new Je({RouteTableId:e});return await j().send(t)}function tr(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:nr,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},t[0]=n,t[1]=r),e(r)}async function nr(e){let t=new z({RouteTableId:e.routeTableId,DestinationCidrBlock:e.destinationCidrBlock,GatewayId:e.gatewayId,NatGatewayId:e.natGatewayId});return await j().send(t)}function rr(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:ir,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},t[0]=n,t[1]=r),e(r)}async function ir(e){let t=new qe({RouteTableId:e.routeTableId,DestinationCidrBlock:e.destinationCidrBlock});return await j().send(t)}function ar(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:or,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},t[0]=n,t[1]=r),e(r)}async function or(e){let t=new P({RouteTableId:e.routeTableId,SubnetId:e.subnetId});return await j().send(t)}function sr(){let t=(0,K.c)(2),n=_(),r;return t[0]===n?r=t[1]:(r={mutationFn:cr,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},t[0]=n,t[1]=r),e(r)}async function cr(e){let t=new rt({AssociationId:e});return await j().send(t)}function lr(e,t){let n=[];return e&&n.push({Key:`Name`,Value:e}),n.push(...t),n}function $(e,t,n){let r=lr(t,n);if(r.length!==0)return[{ResourceType:e,Tags:r}]}function ur(){let t=_();return e({mutationFn:async e=>{let t=j(),n=[],r=e.autoGenerateNames?e.namePrefix:void 0,i=e.tags.map(e=>({Key:e.key,Value:e.value})),a=`creating VPC`;try{let o=r?`${r}-vpc`:e.namePrefix,s=e.tenancy,c=(await t.send(new U({CidrBlock:e.cidrBlock,InstanceTenancy:s,TagSpecifications:$(G.vpc,o,i)}))).Vpc?.VpcId;if(!c)throw Error(`VPC was created but no VPC ID was returned by the API`);if(n.push({type:`VPC`,id:c}),e.mode===`vpc-only`)return{vpcId:c,created:n};let l=X(e.cidrBlock,e.publicSubnetCount,e.privateSubnetCount),u=e.publicSubnetCidrs.length>0?e.publicSubnetCidrs:l.publicSubnets.map(e=>e.cidr),d=e.privateSubnetCidrs.length>0?e.privateSubnetCidrs:l.privateSubnets.map(e=>e.cidr);a=`creating public subnets`;let f=[];for(let a=0;a0){a=`creating internet gateway`;let e=r?`${r}-igw`:void 0,o=(await t.send(new L({TagSpecifications:$(G.internet_gateway,e,i)}))).InternetGateway?.InternetGatewayId;if(!o)throw Error(`Internet gateway was created but no ID was returned`);n.push({type:`Internet Gateway`,id:o}),a=`attaching internet gateway to VPC`,await t.send(new F({InternetGatewayId:o,VpcId:c})),a=`creating public route table`;let s=r?`${r}-rtb-public`:void 0,l=(await t.send(new B({VpcId:c,TagSpecifications:$(G.route_table,s,i)}))).RouteTable?.RouteTableId;if(!l)throw Error(`Public route table was created but no ID was returned`);n.push({type:`Public Route Table`,id:l}),a=`creating default route to internet gateway`,await t.send(new z({RouteTableId:l,DestinationCidrBlock:`0.0.0.0/0`,GatewayId:o})),a=`associating public subnets with route table`;for(let e of f)await t.send(new P({RouteTableId:l,SubnetId:e}))}if(e.privateSubnetCount>0){a=`creating private route table`;let o=r?`${r}-rtb-private`:void 0,s=(await t.send(new B({VpcId:c,TagSpecifications:$(G.route_table,o,i)}))).RouteTable?.RouteTableId;if(!s)throw Error(`Private route table was created but no ID was returned`);n.push({type:`Private Route Table`,id:s}),a=`associating private subnets with route table`;for(let e of p)await t.send(new P({RouteTableId:s,SubnetId:e}));if(e.natGateway===`single`&&f[0]){a=`allocating Elastic IP for NAT gateway`;let e=(await t.send(new N({Domain:`vpc`,TagSpecifications:$(G.elastic_ip,r?`${r}-eip-nat`:void 0,i)}))).AllocationId;if(!e)throw Error(`Elastic IP was allocated but no allocation ID was returned`);n.push({type:`Elastic IP`,id:e}),a=`creating NAT gateway`;let o=(await t.send(new R({SubnetId:f[0],AllocationId:e,TagSpecifications:$(G.natgateway,r?`${r}-nat`:void 0,i)}))).NatGateway?.NatGatewayId;if(!o)throw Error(`NAT gateway was created but no ID was returned`);n.push({type:`NAT Gateway`,id:o}),a=`creating default route to NAT gateway`,await t.send(new z({RouteTableId:s,DestinationCidrBlock:`0.0.0.0/0`,NatGatewayId:o}))}}return{vpcId:c,created:n}}catch(e){return{vpcId:n.find(e=>e.type===`VPC`)?.id,created:n,error:e instanceof Error?e:Error(String(e)),failedStep:`Failed while ${a}`}}},onSuccess:()=>{t.invalidateQueries({queryKey:[`ec2`,`vpcs`]}),t.invalidateQueries({queryKey:[`ec2`,`subnets`]}),t.invalidateQueries({queryKey:[`ec2`,`internetGateways`]}),t.invalidateQueries({queryKey:[`ec2`,`routeTables`]}),t.invalidateQueries({queryKey:[`ec2`,`natGateways`]}),t.invalidateQueries({queryKey:[`ec2`,`addresses`]})}})}export{qt as A,tn as B,Pn as C,rr as D,vn as E,$t as F,On as G,Dt as H,Kn as I,Tt as J,xt as K,sr as L,Ut as M,un as N,$n as O,Rn as P,yt as Q,rn as R,ur as S,Yn as T,Hn as U,Vt as V,An as W,vt as X,X as Y,_t as Z,bn as _,Zt as a,zt as b,Yt as c,Mn as d,Nt as f,Zn as g,tr as h,In as i,mn as j,Sn as k,on as l,gn as m,Wn as n,En as o,Jn as p,Ct as q,ar as r,wn as s,Bn as t,kt as u,Gt as v,Lt as w,cn as x,fn as y,Ft as z}; \ No newline at end of file +import{t as e}from"./compiler-runtime.js";import{n as t}from"./error-banner.js";import{$n as n,An as r,Cn as i,Cr as a,Dn as o,En as s,Er as c,Mn as l,On as u,Qn as d,Sn as f,Sr as p,Tn as m,Tr as h,Xn as g,Zn as ee,_n as te,an as ne,ar as re,bn as ie,cn as ae,cr as oe,dn as se,dr as ce,er as le,fn as ue,fr as de,gn as fe,hn as pe,in as me,ir as he,jn as ge,kn as _e,ln as ve,lr as ye,mn as be,mr as _,nn as v,nr as y,on as b,or as x,pn as S,pr as C,r as w,rn as T,rr as E,sn as D,sr as O,tn as k,tr as A,un as j,ur as M,vn as N,wn as xe,xn as Se,xr as Ce,yn as we,yr as Te}from"./awsClient.js";import{i as P,t as Ee}from"./SignatureV4.js";import{Lr as F,bt as I}from"./index.js";import{t as De}from"./subnet-calculator.js";var L=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AllocateAddress`,{}).n(`EC2Client`,`AllocateAddressCommand`).sc(k).build(){},Oe=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AssociateAddress`,{}).n(`EC2Client`,`AssociateAddressCommand`).sc(v).build(){},ke=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AssociateIamInstanceProfile`,{}).n(`EC2Client`,`AssociateIamInstanceProfileCommand`).sc(T).build(){},R=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AssociateRouteTable`,{}).n(`EC2Client`,`AssociateRouteTableCommand`).sc(me).build(){},z=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AttachInternetGateway`,{}).n(`EC2Client`,`AttachInternetGatewayCommand`).sc(ne).build(){},Ae=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AttachVolume`,{}).n(`EC2Client`,`AttachVolumeCommand`).sc(b).build(){},je=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AuthorizeSecurityGroupEgress`,{}).n(`EC2Client`,`AuthorizeSecurityGroupEgressCommand`).sc(D).build(){},B=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`AuthorizeSecurityGroupIngress`,{}).n(`EC2Client`,`AuthorizeSecurityGroupIngressCommand`).sc(ae).build(){};function Me(e){let{port:t,query:n}=e,{protocol:r,path:i,hostname:a}=e;r&&r.slice(-1)!==`:`&&(r+=`:`),t&&(a+=`:${t}`),i&&i.charAt(0)!==`/`&&(i=`/${i}`);let o=n?Te(n):``;o&&o[0]!==`?`&&(o=`?${o}`);let s=``;(e.username!=null||e.password!=null)&&(s=`${e.username??``}:${e.password??``}@`);let c=``;return e.fragment&&(c=`#${e.fragment}`),`${r}//${s}${a}${i}${o}${c}`}var Ne=`2016-11-15`;function Pe(e){return(t,n)=>async n=>{let{input:r}=n;if(!r.PresignedUrl){let t=await e.region(),i=await p(r,{getEndpointParameterInstructions(){return{UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}}}},{...e,region:r.SourceRegion}),a=typeof e.endpoint==`function`?await e.endpoint():c(i),o=new P({...a,protocol:`https`,headers:{host:a.hostname},query:{...Object.entries(r).reduce((e,[t,n])=>(e[t]=String(n??``),e),{}),Action:`CopySnapshot`,Version:Ne,DestinationRegion:t}}),s=await new Ee({credentials:e.credentials,region:r.SourceRegion,service:`ec2`,sha256:e.sha256,uriEscapePath:e.signingEscapePath}).presign(o,{expiresIn:3600});if(n={...n,input:{...n.input,DestinationRegion:t,PresignedUrl:Me(s)}},P.isInstance(n.request)){let{request:e}=n;(e.body??``).includes(`DestinationRegion=`)||(e.body+=`&DestinationRegion=${t}`),(e.body??``).includes(`PresignedUrl=`)||(e.body+=`&PresignedUrl=${Ce(n.input.PresignedUrl)}`)}}return t(n)}}var Fe={step:`serialize`,tags:[`CROSS_REGION_PRESIGNED_URL`],name:`crossRegionPresignedUrlMiddleware`,override:!0,relation:`after`,toMiddleware:`endpointV2Middleware`},Ie=e=>({applyToStack:t=>{t.add(Pe(e),Fe)}}),Le=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions()),Ie(n)]}).s(`AmazonEC2`,`CopySnapshot`,{}).n(`EC2Client`,`CopySnapshotCommand`).sc(ve).build(){},Re=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateImage`,{}).n(`EC2Client`,`CreateImageCommand`).sc(j).build(){},V=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateInternetGateway`,{}).n(`EC2Client`,`CreateInternetGatewayCommand`).sc(se).build(){},ze=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateKeyPair`,{}).n(`EC2Client`,`CreateKeyPairCommand`).sc(ue).build(){},H=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateNatGateway`,{}).n(`EC2Client`,`CreateNatGatewayCommand`).sc(S).build(){},Be=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreatePlacementGroup`,{}).n(`EC2Client`,`CreatePlacementGroupCommand`).sc(be).build(){},U=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateRoute`,{}).n(`EC2Client`,`CreateRouteCommand`).sc(pe).build(){},W=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateRouteTable`,{}).n(`EC2Client`,`CreateRouteTableCommand`).sc(fe).build(){},G=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateSecurityGroup`,{}).n(`EC2Client`,`CreateSecurityGroupCommand`).sc(te).build(){},Ve=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateSnapshot`,{}).n(`EC2Client`,`CreateSnapshotCommand`).sc(N).build(){},K=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateSubnet`,{}).n(`EC2Client`,`CreateSubnetCommand`).sc(we).build(){},He=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateVolume`,{}).n(`EC2Client`,`CreateVolumeCommand`).sc(ie).build(){},q=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`CreateVpc`,{}).n(`EC2Client`,`CreateVpcCommand`).sc(Se).build(){},Ue=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteInternetGateway`,{}).n(`EC2Client`,`DeleteInternetGatewayCommand`).sc(f).build(){},We=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteKeyPair`,{}).n(`EC2Client`,`DeleteKeyPairCommand`).sc(i).build(){},Ge=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteNatGateway`,{}).n(`EC2Client`,`DeleteNatGatewayCommand`).sc(xe).build(){},Ke=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeletePlacementGroup`,{}).n(`EC2Client`,`DeletePlacementGroupCommand`).sc(m).build(){},qe=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteRoute`,{}).n(`EC2Client`,`DeleteRouteCommand`).sc(s).build(){},Je=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteRouteTable`,{}).n(`EC2Client`,`DeleteRouteTableCommand`).sc(o).build(){},Ye=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteSecurityGroup`,{}).n(`EC2Client`,`DeleteSecurityGroupCommand`).sc(u).build(){},Xe=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteSnapshot`,{}).n(`EC2Client`,`DeleteSnapshotCommand`).sc(_e).build(){},Ze=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteSubnet`,{}).n(`EC2Client`,`DeleteSubnetCommand`).sc(r).build(){},Qe=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteVolume`,{}).n(`EC2Client`,`DeleteVolumeCommand`).sc(ge).build(){},$e=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DeleteVpc`,{}).n(`EC2Client`,`DeleteVpcCommand`).sc(l).build(){},et=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DetachInternetGateway`,{}).n(`EC2Client`,`DetachInternetGatewayCommand`).sc(g).build(){},tt=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DetachVolume`,{}).n(`EC2Client`,`DetachVolumeCommand`).sc(ee).build(){},nt=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DisassociateAddress`,{}).n(`EC2Client`,`DisassociateAddressCommand`).sc(d).build(){},rt=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DisassociateIamInstanceProfile`,{}).n(`EC2Client`,`DisassociateIamInstanceProfileCommand`).sc(n).build(){},it=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DisassociateRouteTable`,{}).n(`EC2Client`,`DisassociateRouteTableCommand`).sc(le).build(){},at=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`GetConsoleOutput`,{}).n(`EC2Client`,`GetConsoleOutputCommand`).sc(A).build(){},ot=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ImportKeyPair`,{}).n(`EC2Client`,`ImportKeyPairCommand`).sc(y).build(){},st=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ModifyInstanceAttribute`,{}).n(`EC2Client`,`ModifyInstanceAttributeCommand`).sc(E).build(){},J=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ModifySubnetAttribute`,{}).n(`EC2Client`,`ModifySubnetAttributeCommand`).sc(he).build(){},ct=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ModifyVolume`,{}).n(`EC2Client`,`ModifyVolumeCommand`).sc(re).build(){},lt=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`RebootInstances`,{}).n(`EC2Client`,`RebootInstancesCommand`).sc(x).build(){},ut=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`ReleaseAddress`,{}).n(`EC2Client`,`ReleaseAddressCommand`).sc(O).build(){},dt=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`RevokeSecurityGroupEgress`,{}).n(`EC2Client`,`RevokeSecurityGroupEgressCommand`).sc(oe).build(){},ft=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`RevokeSecurityGroupIngress`,{}).n(`EC2Client`,`RevokeSecurityGroupIngressCommand`).sc(ye).build(){},pt=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`RunInstances`,{}).n(`EC2Client`,`RunInstancesCommand`).sc(M).build(){},mt=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`StartInstances`,{}).n(`EC2Client`,`StartInstancesCommand`).sc(ce).build(){},ht=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`StopInstances`,{}).n(`EC2Client`,`StopInstancesCommand`).sc(de).build(){},gt=class extends h.classBuilder().ep(_).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`TerminateInstances`,{}).n(`EC2Client`,`TerminateInstancesCommand`).sc(C).build(){},Y={capacity_block:`capacity-block`,capacity_manager_data_export:`capacity-manager-data-export`,capacity_reservation:`capacity-reservation`,capacity_reservation_cancellation_quote:`capacity-reservation-cancellation-quote`,capacity_reservation_fleet:`capacity-reservation-fleet`,carrier_gateway:`carrier-gateway`,client_vpn_endpoint:`client-vpn-endpoint`,coip_pool:`coip-pool`,customer_gateway:`customer-gateway`,declarative_policies_report:`declarative-policies-report`,dedicated_host:`dedicated-host`,dhcp_options:`dhcp-options`,egress_only_internet_gateway:`egress-only-internet-gateway`,elastic_gpu:`elastic-gpu`,elastic_ip:`elastic-ip`,export_image_task:`export-image-task`,export_instance_task:`export-instance-task`,fleet:`fleet`,fpga_image:`fpga-image`,host_reservation:`host-reservation`,image:`image`,image_usage_report:`image-usage-report`,import_image_task:`import-image-task`,import_snapshot_task:`import-snapshot-task`,instance:`instance`,instance_connect_endpoint:`instance-connect-endpoint`,instance_event_window:`instance-event-window`,internet_gateway:`internet-gateway`,ipam:`ipam`,ipam_external_resource_verification_token:`ipam-external-resource-verification-token`,ipam_policy:`ipam-policy`,ipam_pool:`ipam-pool`,ipam_pool_allocation:`ipam-pool-allocation`,ipam_prefix_list_resolver:`ipam-prefix-list-resolver`,ipam_prefix_list_resolver_target:`ipam-prefix-list-resolver-target`,ipam_resource_discovery:`ipam-resource-discovery`,ipam_resource_discovery_association:`ipam-resource-discovery-association`,ipam_scope:`ipam-scope`,ipv4pool_ec2:`ipv4pool-ec2`,ipv6pool_ec2:`ipv6pool-ec2`,key_pair:`key-pair`,launch_template:`launch-template`,local_gateway:`local-gateway`,local_gateway_route_table:`local-gateway-route-table`,local_gateway_route_table_virtual_interface_group_association:`local-gateway-route-table-virtual-interface-group-association`,local_gateway_route_table_vpc_association:`local-gateway-route-table-vpc-association`,local_gateway_virtual_interface:`local-gateway-virtual-interface`,local_gateway_virtual_interface_group:`local-gateway-virtual-interface-group`,mac_modification_task:`mac-modification-task`,natgateway:`natgateway`,network_acl:`network-acl`,network_insights_access_scope:`network-insights-access-scope`,network_insights_access_scope_analysis:`network-insights-access-scope-analysis`,network_insights_analysis:`network-insights-analysis`,network_insights_path:`network-insights-path`,network_interface:`network-interface`,outpost_lag:`outpost-lag`,placement_group:`placement-group`,prefix_list:`prefix-list`,replace_root_volume_task:`replace-root-volume-task`,reserved_instances:`reserved-instances`,route_server:`route-server`,route_server_endpoint:`route-server-endpoint`,route_server_peer:`route-server-peer`,route_table:`route-table`,secondary_interface:`secondary-interface`,secondary_network:`secondary-network`,secondary_subnet:`secondary-subnet`,security_group:`security-group`,security_group_rule:`security-group-rule`,service_link_virtual_interface:`service-link-virtual-interface`,snapshot:`snapshot`,spot_fleet_request:`spot-fleet-request`,spot_instances_request:`spot-instances-request`,subnet:`subnet`,subnet_cidr_reservation:`subnet-cidr-reservation`,traffic_mirror_filter:`traffic-mirror-filter`,traffic_mirror_filter_rule:`traffic-mirror-filter-rule`,traffic_mirror_session:`traffic-mirror-session`,traffic_mirror_target:`traffic-mirror-target`,transit_gateway:`transit-gateway`,transit_gateway_attachment:`transit-gateway-attachment`,transit_gateway_connect_peer:`transit-gateway-connect-peer`,transit_gateway_metering_policy:`transit-gateway-metering-policy`,transit_gateway_multicast_domain:`transit-gateway-multicast-domain`,transit_gateway_policy_table:`transit-gateway-policy-table`,transit_gateway_route_table:`transit-gateway-route-table`,transit_gateway_route_table_announcement:`transit-gateway-route-table-announcement`,verified_access_endpoint:`verified-access-endpoint`,verified_access_endpoint_target:`verified-access-endpoint-target`,verified_access_group:`verified-access-group`,verified_access_instance:`verified-access-instance`,verified_access_policy:`verified-access-policy`,verified_access_trust_provider:`verified-access-trust-provider`,volume:`volume`,vpc:`vpc`,vpc_block_public_access_exclusion:`vpc-block-public-access-exclusion`,vpc_encryption_control:`vpc-encryption-control`,vpc_endpoint:`vpc-endpoint`,vpc_endpoint_connection:`vpc-endpoint-connection`,vpc_endpoint_connection_device_type:`vpc-endpoint-connection-device-type`,vpc_endpoint_service:`vpc-endpoint-service`,vpc_endpoint_service_permission:`vpc-endpoint-service-permission`,vpc_flow_log:`vpc-flow-log`,vpc_peering_connection:`vpc-peering-connection`,vpn_concentrator:`vpn-concentrator`,vpn_connection:`vpn-connection`,vpn_connection_device_type:`vpn-connection-device-type`,vpn_gateway:`vpn-gateway`},X=e(),_t=/\s+/;function vt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:yt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function yt(e){let t=new mt({InstanceIds:[e]});return await w().send(t)}function bt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:xt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function xt(e){let t=new ht({InstanceIds:[e]});return await w().send(t)}function St(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Ct,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function Ct(e){let t=new gt({InstanceIds:[e]});return await w().send(t)}function wt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Tt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function Tt(e){let t=new lt({InstanceIds:[e]});return await w().send(t)}function Et(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Dt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]})}},e[0]=n,e[1]=r),t(r)}async function Dt(e){let t=w(),n=await Ot(t,e),r=new pt({ImageId:e.imageId,InstanceType:e.instanceType,KeyName:e.keyName,MinCount:e.count,MaxCount:e.count,SubnetId:e.subnetId||void 0,SecurityGroupIds:n.length>0?n:void 0,Placement:e.placementGroupName?{GroupName:e.placementGroupName}:void 0,BlockDeviceMappings:kt(e)});return await t.send(r)}async function Ot(e,t){if(t.securityGroupMode!==`create`)return t.securityGroupIds??[];let n=(await e.send(new G({GroupName:t.newSgName,Description:t.newSgDescription||`Created by launch wizard`,VpcId:t.resolvedVpcId}))).GroupId;if(!n)throw Error(`Security group was created but no ID was returned`);let r=[...t.allowSsh?[22]:[],...t.allowHttp?[80]:[],...t.allowHttps?[443]:[]];if(r.length>0){let i=t.ruleSource===`custom`&&t.customCidr?t.customCidr:`0.0.0.0/0`;await e.send(new B({GroupId:n,IpPermissions:r.map(e=>({IpProtocol:`tcp`,FromPort:e,ToPort:e,IpRanges:[{CidrIp:i}]}))}))}return[n]}function kt(e){let{rootDeviceName:t,rootVolumeSize:n,rootVolumeType:r,rootDeleteOnTermination:i}=e;return n!==void 0||r!==void 0||i!==void 0?[{DeviceName:t,Ebs:{...n!==void 0&&{VolumeSize:n},...r!==void 0&&{VolumeType:r},...i!==void 0&&{DeleteOnTermination:i}}}]:void 0}function At(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:jt,onSuccess:()=>{n.invalidateQueries(I)}},e[0]=n,e[1]=r),t(r)}async function jt(e){let t=new ze({KeyName:e.keyName,KeyType:`rsa`}),n=await w().send(t);if(!n.KeyMaterial)throw Error(`Key pair was created but the API returned no private key. The key cannot be recovered — delete it and try again.`);return n}function Mt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Nt,onSuccess:()=>{n.invalidateQueries(I)}},e[0]=n,e[1]=r),t(r)}async function Nt(e){let t=e.publicKeyMaterial.trim().split(_t).slice(0,2).join(` `),n=new ot({KeyName:e.keyName,PublicKeyMaterial:new TextEncoder().encode(t)});return await w().send(n)}function Pt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Ft,onSuccess:()=>{n.invalidateQueries(I)}},e[0]=n,e[1]=r),t(r)}async function Ft(e){let t=new We({KeyPairId:e});return await w().send(t)}function It(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Lt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]})}},e[0]=n,e[1]=r),t(r)}async function Lt(e){let t=new He({Size:e.size,AvailabilityZone:e.availabilityZone,VolumeType:`gp3`});return await w().send(t)}function Rt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:zt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]})}},e[0]=n,e[1]=r),t(r)}async function zt(e){let t=new ct({VolumeId:e.volumeId,Size:e.size});return await w().send(t)}function Bt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Vt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]})}},e[0]=n,e[1]=r),t(r)}async function Vt(e){let t=new Qe({VolumeId:e});return await w().send(t)}function Ht(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Ut,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`snapshots`]})}},e[0]=n,e[1]=r),t(r)}async function Ut(e){let t=new Ve({VolumeId:e.volumeId,Description:e.description||void 0});return await w().send(t)}function Wt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Gt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`snapshots`]})}},e[0]=n,e[1]=r),t(r)}async function Gt(e){let t=new Xe({SnapshotId:e});return await w().send(t)}function Kt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:qt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`snapshots`]})}},e[0]=n,e[1]=r),t(r)}async function qt(e){let t=new Le({SourceSnapshotId:e.sourceSnapshotId,SourceRegion:e.sourceRegion,Description:e.description||void 0});return await w().send(t)}function Jt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Yt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function Yt(e){let t=new Ae({VolumeId:e.volumeId,InstanceId:e.instanceId,Device:e.device||void 0});return await w().send(t)}function Xt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Zt,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`volumes`]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function Zt(e){let t=new tt({VolumeId:e.volumeId,InstanceId:e.instanceId||void 0,Force:e.force});return await w().send(t)}function Qt(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:$t,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function $t(e){let{instanceId:t,...n}=e,r=new st({InstanceId:t,InstanceType:{Value:n.instanceType}});return await w().send(r)}function en(){let e=(0,X.c)(1),n;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(n={mutationFn:tn},e[0]=n):n=e[0],t(n)}async function tn(e){let t=new at({InstanceId:e});return await w().send(t)}function nn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:rn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`images`]})}},e[0]=n,e[1]=r),t(r)}async function rn(e){let t=new Re({InstanceId:e.instanceId,Name:e.name,Description:e.description||void 0});return await w().send(t)}function an(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:on,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`vpcs`]})}},e[0]=n,e[1]=r),t(r)}async function on(e){let t=new q({CidrBlock:e.cidrBlock,TagSpecifications:e.name?[{ResourceType:`vpc`,Tags:[{Key:`Name`,Value:e.name}]}]:void 0});return await w().send(t)}function sn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:cn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`vpcs`]})}},e[0]=n,e[1]=r),t(r)}async function cn(e){let t=new $e({VpcId:e});return await w().send(t)}function ln(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:un,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`subnets`]})}},e[0]=n,e[1]=r),t(r)}async function un(e){let t=w(),n=await t.send(new K({VpcId:e.vpcId,CidrBlock:e.cidrBlock,AvailabilityZone:e.availabilityZone||void 0})),r=n.Subnet?.SubnetId;return e.mapPublicIpOnLaunch&&r&&await t.send(new J({SubnetId:r,MapPublicIpOnLaunch:{Value:!0}})),n}function dn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:fn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`subnets`]})}},e[0]=n,e[1]=r),t(r)}async function fn(e){let t=new Ze({SubnetId:e});return await w().send(t)}function pn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:mn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`placementGroups`]})}},e[0]=n,e[1]=r),t(r)}async function mn(e){let t=new Be({GroupName:e.groupName,Strategy:e.strategy});return await w().send(t)}function hn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:gn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`placementGroups`]})}},e[0]=n,e[1]=r),t(r)}async function gn(e){let t=new Ke({GroupName:e});return await w().send(t)}function _n(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:vn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]})}},e[0]=n,e[1]=r),t(r)}async function vn(e){let t=new G({GroupName:e.groupName,Description:e.description,VpcId:e.vpcId});return await w().send(t)}function yn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:bn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]})}},e[0]=n,e[1]=r),t(r)}async function bn(e){let t=new Ye({GroupId:e});return await w().send(t)}function xn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Sn,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`,t.groupId]})}},e[0]=n,e[1]=r),t(r)}async function Sn(e){let t=new B({GroupId:e.groupId,IpPermissions:[{IpProtocol:e.ipProtocol,FromPort:e.fromPort,ToPort:e.toPort,IpRanges:[{CidrIp:e.cidrIp}]}]});return await w().send(t)}function Cn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:wn,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`,t.groupId]})}},e[0]=n,e[1]=r),t(r)}async function wn(e){let t=new je({GroupId:e.groupId,IpPermissions:[{IpProtocol:e.ipProtocol,FromPort:e.fromPort,ToPort:e.toPort,IpRanges:[{CidrIp:e.cidrIp}]}]});return await w().send(t)}function Z(e){return{IpProtocol:e.ipProtocol,FromPort:e.fromPort,ToPort:e.toPort,...e.sourceGroupId?{UserIdGroupPairs:[{GroupId:e.sourceGroupId}]}:{IpRanges:[{CidrIp:e.cidrIp??``}]}}}function Tn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:En,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`,t.groupId]})}},e[0]=n,e[1]=r),t(r)}async function En(e){let t=new ft({GroupId:e.groupId,IpPermissions:[Z(e)]});return await w().send(t)}function Dn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:On,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`securityGroups`]}),n.invalidateQueries({queryKey:[`ec2`,`securityGroups`,t.groupId]})}},e[0]=n,e[1]=r),t(r)}async function On(e){let t=new dt({GroupId:e.groupId,IpPermissions:[Z(e)]});return await w().send(t)}function kn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:An,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`internetGateways`]})}},e[0]=n,e[1]=r),t(r)}async function An(e){let t=new V({TagSpecifications:$(Y.internet_gateway,e?.name,[])});return await w().send(t)}function jn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Mn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`internetGateways`]})}},e[0]=n,e[1]=r),t(r)}async function Mn(e){let t=new Ue({InternetGatewayId:e});return await w().send(t)}function Nn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Pn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`internetGateways`]})}},e[0]=n,e[1]=r),t(r)}async function Pn(e){let t=new z({InternetGatewayId:e.internetGatewayId,VpcId:e.vpcId});return await w().send(t)}function Fn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:In,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`internetGateways`]})}},e[0]=n,e[1]=r),t(r)}async function In(e){let t=new et({InternetGatewayId:e.internetGatewayId,VpcId:e.vpcId});return await w().send(t)}function Ln(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Rn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`addresses`]})}},e[0]=n,e[1]=r),t(r)}async function Rn(e){let t=new L({Domain:`vpc`,TagSpecifications:$(Y.elastic_ip,e?.name,[])});return await w().send(t)}function zn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Bn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`addresses`]})}},e[0]=n,e[1]=r),t(r)}async function Bn(e){let t=new ut({AllocationId:e});return await w().send(t)}function Vn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Hn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`addresses`]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function Hn(e){let t=new Oe({AllocationId:e.allocationId,InstanceId:e.instanceId,NetworkInterfaceId:e.networkInterfaceId});return await w().send(t)}function Un(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Wn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`addresses`]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function Wn(e){let t=new nt({AssociationId:e});return await w().send(t)}function Gn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Kn,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`iam-instance-profile-associations`,t.instanceId]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function Kn(e){let t=new ke({InstanceId:e.instanceId,IamInstanceProfile:{Name:e.instanceProfileName}});return await w().send(t)}function qn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Jn,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`ec2`,`iam-instance-profile-associations`,t.instanceId]}),n.invalidateQueries({queryKey:[`ec2`,`instances`]})}},e[0]=n,e[1]=r),t(r)}async function Jn(e){let t=new rt({AssociationId:e.associationId});return await w().send(t)}function Q(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Yn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`natGateways`]}),n.invalidateQueries({queryKey:[`ec2`,`addresses`]})}},e[0]=n,e[1]=r),t(r)}async function Yn(e){let t=new H({SubnetId:e.subnetId,AllocationId:e.allocationId,TagSpecifications:$(Y.natgateway,e.name,[])});return await w().send(t)}function Xn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:Zn,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`natGateways`]})}},e[0]=n,e[1]=r),t(r)}async function Zn(e){let t=new Ge({NatGatewayId:e});return await w().send(t)}function Qn(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:$n,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},e[0]=n,e[1]=r),t(r)}async function $n(e){let t=new W({VpcId:e.vpcId,TagSpecifications:$(Y.route_table,e.name,[])});return await w().send(t)}function er(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:tr,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},e[0]=n,e[1]=r),t(r)}async function tr(e){let t=new Je({RouteTableId:e});return await w().send(t)}function nr(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:rr,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},e[0]=n,e[1]=r),t(r)}async function rr(e){let t=new U({RouteTableId:e.routeTableId,DestinationCidrBlock:e.destinationCidrBlock,GatewayId:e.gatewayId,NatGatewayId:e.natGatewayId});return await w().send(t)}function ir(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:ar,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},e[0]=n,e[1]=r),t(r)}async function ar(e){let t=new qe({RouteTableId:e.routeTableId,DestinationCidrBlock:e.destinationCidrBlock});return await w().send(t)}function or(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:sr,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},e[0]=n,e[1]=r),t(r)}async function sr(e){let t=new R({RouteTableId:e.routeTableId,SubnetId:e.subnetId});return await w().send(t)}function cr(){let e=(0,X.c)(2),n=F(),r;return e[0]===n?r=e[1]:(r={mutationFn:lr,onSuccess:()=>{n.invalidateQueries({queryKey:[`ec2`,`routeTables`]})}},e[0]=n,e[1]=r),t(r)}async function lr(e){let t=new it({AssociationId:e});return await w().send(t)}function ur(e,t){let n=[];return e&&n.push({Key:`Name`,Value:e}),n.push(...t),n}function $(e,t,n){let r=ur(t,n);if(r.length!==0)return[{ResourceType:e,Tags:r}]}function dr(){let e=F();return t({mutationFn:async e=>{let t=w(),n=[],r=e.autoGenerateNames?e.namePrefix:void 0,i=e.tags.map(e=>({Key:e.key,Value:e.value})),a=`creating VPC`;try{let o=r?`${r}-vpc`:e.namePrefix,s=e.tenancy,c=(await t.send(new q({CidrBlock:e.cidrBlock,InstanceTenancy:s,TagSpecifications:$(Y.vpc,o,i)}))).Vpc?.VpcId;if(!c)throw Error(`VPC was created but no VPC ID was returned by the API`);if(n.push({type:`VPC`,id:c}),e.mode===`vpc-only`)return{vpcId:c,created:n};let l=De(e.cidrBlock,e.publicSubnetCount,e.privateSubnetCount),u=e.publicSubnetCidrs.length>0?e.publicSubnetCidrs:l.publicSubnets.map(e=>e.cidr),d=e.privateSubnetCidrs.length>0?e.privateSubnetCidrs:l.privateSubnets.map(e=>e.cidr);a=`creating public subnets`;let f=[];for(let a=0;a0){a=`creating internet gateway`;let e=r?`${r}-igw`:void 0,o=(await t.send(new V({TagSpecifications:$(Y.internet_gateway,e,i)}))).InternetGateway?.InternetGatewayId;if(!o)throw Error(`Internet gateway was created but no ID was returned`);n.push({type:`Internet Gateway`,id:o}),a=`attaching internet gateway to VPC`,await t.send(new z({InternetGatewayId:o,VpcId:c})),a=`creating public route table`;let s=r?`${r}-rtb-public`:void 0,l=(await t.send(new W({VpcId:c,TagSpecifications:$(Y.route_table,s,i)}))).RouteTable?.RouteTableId;if(!l)throw Error(`Public route table was created but no ID was returned`);n.push({type:`Public Route Table`,id:l}),a=`creating default route to internet gateway`,await t.send(new U({RouteTableId:l,DestinationCidrBlock:`0.0.0.0/0`,GatewayId:o})),a=`associating public subnets with route table`;for(let e of f)await t.send(new R({RouteTableId:l,SubnetId:e}))}if(e.privateSubnetCount>0){a=`creating private route table`;let o=r?`${r}-rtb-private`:void 0,s=(await t.send(new W({VpcId:c,TagSpecifications:$(Y.route_table,o,i)}))).RouteTable?.RouteTableId;if(!s)throw Error(`Private route table was created but no ID was returned`);n.push({type:`Private Route Table`,id:s}),a=`associating private subnets with route table`;for(let e of p)await t.send(new R({RouteTableId:s,SubnetId:e}));if(e.natGateway===`single`&&f[0]){a=`allocating Elastic IP for NAT gateway`;let e=(await t.send(new L({Domain:`vpc`,TagSpecifications:$(Y.elastic_ip,r?`${r}-eip-nat`:void 0,i)}))).AllocationId;if(!e)throw Error(`Elastic IP was allocated but no allocation ID was returned`);n.push({type:`Elastic IP`,id:e}),a=`creating NAT gateway`;let o=(await t.send(new H({SubnetId:f[0],AllocationId:e,TagSpecifications:$(Y.natgateway,r?`${r}-nat`:void 0,i)}))).NatGateway?.NatGatewayId;if(!o)throw Error(`NAT gateway was created but no ID was returned`);n.push({type:`NAT Gateway`,id:o}),a=`creating default route to NAT gateway`,await t.send(new U({RouteTableId:s,DestinationCidrBlock:`0.0.0.0/0`,NatGatewayId:o}))}}return{vpcId:c,created:n}}catch(e){return{vpcId:n.find(e=>e.type===`VPC`)?.id,created:n,error:e instanceof Error?e:Error(String(e)),failedStep:`Failed while ${a}`}}},onSuccess:()=>{e.invalidateQueries({queryKey:[`ec2`,`vpcs`]}),e.invalidateQueries({queryKey:[`ec2`,`subnets`]}),e.invalidateQueries({queryKey:[`ec2`,`internetGateways`]}),e.invalidateQueries({queryKey:[`ec2`,`routeTables`]}),e.invalidateQueries({queryKey:[`ec2`,`natGateways`]}),e.invalidateQueries({queryKey:[`ec2`,`addresses`]})}})}export{yn as A,en as B,dr as C,hn as D,Xn as E,Fn as F,zn as G,Qt as H,Xt as I,vt as J,Dn as K,Un as L,dn as M,Bt as N,ir as O,sn as P,qn as R,an as S,Pt as T,Rt as U,Mt as V,wt as W,St as X,bt as Y,Qn as _,Nn as a,ln as b,xn as c,Et as d,kn as f,nr as g,pn as h,or as i,Wt as j,er as k,Kt as l,Q as m,Vn as n,Jt as o,At as p,Tn as q,Gn as r,Cn as s,Ln as t,nn as u,_n as v,jn as w,It as x,Ht as y,cr as z}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/ec22.js b/spinifex/services/spinifexui/frontend/dist/assets/ec22.js index 7c5b9cfcb..f7893103e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/ec22.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/ec22.js @@ -1 +1 @@ -import{Q as e,X as t,Z as n}from"./ec2.js";import{Br as r,Ir as i,Lr as a,Rr as o,Ur as s,Vr as c,zr as l}from"./index.js";var u=/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/,d=s().min(1,`Key name is required`).max(255,`Key name must be 255 characters or less`).regex(/^[\w\s._\-:/()#,@[\]+=&;{}!$*]+$/,`Key name contains invalid characters`),f=[`gp3`],p=`launch-wizard-`,m=c({imageId:s(`Please select an Image`),instanceType:s(`Please select an instance type`),keyName:s(`Please select a key pair`).min(1,`Key pair is required`),subnetId:s().optional(),placementGroupName:s().optional(),count:l(`Instance count must be a whole number`).min(1,`Instance count must be at least 1`),rootDeviceName:s().optional(),rootVolumeSize:r().int(`Volume size must be a whole number`).min(1,`Volume size must be at least 1 GiB`).max(16384,`Volume size must be at most 16384 GiB`).optional(),rootVolumeType:i(f).optional(),rootDeleteOnTermination:o().optional(),securityGroupMode:i([`create`,`existing`]).optional(),securityGroupIds:a(s()).optional(),newSgName:s().optional(),newSgDescription:s().optional(),allowSsh:o().optional(),allowHttp:o().optional(),allowHttps:o().optional(),ruleSource:i([`anywhere`,`custom`]).optional(),customCidr:s().optional()}).superRefine((t,n)=>{if(t.securityGroupMode){if(t.securityGroupMode===`existing`){(t.securityGroupIds??[]).length===0&&n.addIssue({code:`custom`,message:`Select at least one security group`,path:[`securityGroupIds`]});return}(!t.newSgName||t.newSgName.trim().length===0)&&n.addIssue({code:`custom`,message:`Security group name is required`,path:[`newSgName`]}),t.ruleSource===`custom`&&(!t.customCidr||!e(t.customCidr))&&n.addIssue({code:`custom`,message:`Enter a valid CIDR (e.g. 203.0.113.0/24)`,path:[`customCidr`]})}});function h(e){let t=0,n=RegExp(`^${p}(\\d+)$`);for(let r of e){let e=n.exec(r);if(e){let n=Number(e[1]);n>t&&(t=n)}}return`${p}${t+1}`}var g=c({keyName:d}),_=c({keyName:d,publicKeyMaterial:s().min(1,`Public key is required`).refine(e=>e.trim().length>0,`Public key cannot be empty`)}),v=c({size:r().int(`Size must be a whole number`).min(1,`Size must be at least 1 GiB`).max(16384,`Size must be at most 16384 GiB`),availabilityZone:s().min(1,`Availability zone is required`)}),y=c({size:r().int(`Size must be a whole number`).min(1,`Size must be at least 1 GiB`)}),b=c({volumeId:s().min(1,`Volume is required`),description:s().optional()});c({sourceSnapshotId:s().min(1,`Source snapshot is required`),sourceRegion:s().min(1,`Source region is required`),description:s().optional()}),c({volumeId:s().min(1,`Volume is required`),instanceId:s().min(1,`Instance is required`),device:s().optional()}),c({volumeId:s().min(1,`Volume is required`),instanceId:s().optional(),force:o().optional()}),c({instanceType:s().min(1,`Instance type is required`)}),c({name:s().min(1,`Name is required`),description:s().optional()});var x=c({vpcId:s().min(1,`VPC is required`),cidrBlock:s().min(1,`CIDR block is required`).regex(u,`Must be a valid CIDR block (e.g. 10.0.1.0/24)`),availabilityZone:s().optional(),mapPublicIpOnLaunch:o().optional()}),S=c({name:s().optional()}),C=c({subnetId:s().min(1,`Subnet is required`),allocationId:s().min(1,`Elastic IP is required`),name:s().optional()}),w=c({name:s().optional()}),T=c({vpcId:s().min(1,`VPC is required`),name:s().optional()}),E=c({destinationCidrBlock:s().min(1,`Destination is required`).regex(u,`Must be a valid CIDR block (e.g. 0.0.0.0/0)`),targetType:i([`igw`,`nat`]),targetId:s().min(1,`Target is required`)}),D=c({subnetId:s().min(1,`Subnet is required`)});c({cidrBlock:s().min(1,`CIDR block is required`).regex(u,`Must be a valid CIDR block (e.g. 10.0.0.0/16)`),name:s().optional()});var O=c({key:s().min(1,`Key is required`),value:s()}),k=c({mode:i([`vpc-only`,`vpc-and-more`]),namePrefix:s().optional(),autoGenerateNames:o(),cidrBlock:s().min(1,`CIDR block is required`).regex(u,`Must be a valid CIDR block (e.g. 10.0.0.0/16)`).refine(t=>e(t),`CIDR has invalid octets or prefix length (must be /16 to /28)`),tenancy:i([`default`,`dedicated`]),natGateway:i([`none`,`single`]).optional(),publicSubnetCount:r().int().min(0).max(1),privateSubnetCount:r().int().min(0).max(2),publicSubnetCidrs:a(s()),privateSubnetCidrs:a(s()),tags:a(O)}).superRefine((r,i)=>{if(r.mode!==`vpc-and-more`)return;let a=[];for(let[n,o]of r.publicSubnetCidrs.entries())e(o,16,28)?(t(r.cidrBlock,o)||i.addIssue({code:`custom`,message:`Subnet CIDR must be within the VPC CIDR range`,path:[`publicSubnetCidrs`,n]}),a.push({cidr:o,field:`publicSubnetCidrs`,index:n})):i.addIssue({code:`custom`,message:`Invalid subnet CIDR format or prefix length`,path:[`publicSubnetCidrs`,n]});for(let[n,o]of r.privateSubnetCidrs.entries())e(o,16,28)?(t(r.cidrBlock,o)||i.addIssue({code:`custom`,message:`Subnet CIDR must be within the VPC CIDR range`,path:[`privateSubnetCidrs`,n]}),a.push({cidr:o,field:`privateSubnetCidrs`,index:n})):i.addIssue({code:`custom`,message:`Invalid subnet CIDR format or prefix length`,path:[`privateSubnetCidrs`,n]});for(let e=0;e{if(e.securityGroupMode){if(e.securityGroupMode===`existing`){(e.securityGroupIds??[]).length===0&&t.addIssue({code:`custom`,message:`Select at least one security group`,path:[`securityGroupIds`]});return}(!e.newSgName||e.newSgName.trim().length===0)&&t.addIssue({code:`custom`,message:`Security group name is required`,path:[`newSgName`]}),e.ruleSource===`custom`&&(!e.customCidr||!s(e.customCidr))&&t.addIssue({code:`custom`,message:`Enter a valid CIDR (e.g. 203.0.113.0/24)`,path:[`customCidr`]})}});function h(e){let t=0,n=RegExp(`^${p}(\\d+)$`);for(let r of e){let e=n.exec(r);if(e){let n=Number(e[1]);n>t&&(t=n)}}return`${p}${t+1}`}var g=e({keyName:d}),_=e({keyName:d,publicKeyMaterial:o().min(1,`Public key is required`).refine(e=>e.trim().length>0,`Public key cannot be empty`)}),v=e({size:n().int(`Size must be a whole number`).min(1,`Size must be at least 1 GiB`).max(16384,`Size must be at most 16384 GiB`),availabilityZone:o().min(1,`Availability zone is required`)}),y=e({size:n().int(`Size must be a whole number`).min(1,`Size must be at least 1 GiB`)}),b=e({volumeId:o().min(1,`Volume is required`),description:o().optional()});e({sourceSnapshotId:o().min(1,`Source snapshot is required`),sourceRegion:o().min(1,`Source region is required`),description:o().optional()}),e({volumeId:o().min(1,`Volume is required`),instanceId:o().min(1,`Instance is required`),device:o().optional()}),e({volumeId:o().min(1,`Volume is required`),instanceId:o().optional(),force:i().optional()}),e({instanceType:o().min(1,`Instance type is required`)}),e({name:o().min(1,`Name is required`),description:o().optional()});var x=e({vpcId:o().min(1,`VPC is required`),cidrBlock:o().min(1,`CIDR block is required`).regex(u,`Must be a valid CIDR block (e.g. 10.0.1.0/24)`),availabilityZone:o().optional(),mapPublicIpOnLaunch:i().optional()}),S=e({name:o().optional()}),C=e({subnetId:o().min(1,`Subnet is required`),allocationId:o().min(1,`Elastic IP is required`),name:o().optional()}),w=e({name:o().optional()}),T=e({vpcId:o().min(1,`VPC is required`),name:o().optional()}),E=e({destinationCidrBlock:o().min(1,`Destination is required`).regex(u,`Must be a valid CIDR block (e.g. 0.0.0.0/0)`),targetType:t([`igw`,`nat`]),targetId:o().min(1,`Target is required`)}),D=e({subnetId:o().min(1,`Subnet is required`)});e({cidrBlock:o().min(1,`CIDR block is required`).regex(u,`Must be a valid CIDR block (e.g. 10.0.0.0/16)`),name:o().optional()});var O=e({key:o().min(1,`Key is required`),value:o()}),k=e({mode:t([`vpc-only`,`vpc-and-more`]),namePrefix:o().optional(),autoGenerateNames:i(),cidrBlock:o().min(1,`CIDR block is required`).regex(u,`Must be a valid CIDR block (e.g. 10.0.0.0/16)`).refine(e=>s(e),`CIDR has invalid octets or prefix length (must be /16 to /28)`),tenancy:t([`default`,`dedicated`]),natGateway:t([`none`,`single`]).optional(),publicSubnetCount:n().int().min(0).max(1),privateSubnetCount:n().int().min(0).max(2),publicSubnetCidrs:a(o()),privateSubnetCidrs:a(o()),tags:a(O)}).superRefine((e,t)=>{if(e.mode!==`vpc-and-more`)return;let n=[];for(let[r,i]of e.publicSubnetCidrs.entries())s(i,16,28)?(c(e.cidrBlock,i)||t.addIssue({code:`custom`,message:`Subnet CIDR must be within the VPC CIDR range`,path:[`publicSubnetCidrs`,r]}),n.push({cidr:i,field:`publicSubnetCidrs`,index:r})):t.addIssue({code:`custom`,message:`Invalid subnet CIDR format or prefix length`,path:[`publicSubnetCidrs`,r]});for(let[r,i]of e.privateSubnetCidrs.entries())s(i,16,28)?(c(e.cidrBlock,i)||t.addIssue({code:`custom`,message:`Subnet CIDR must be within the VPC CIDR range`,path:[`privateSubnetCidrs`,r]}),n.push({cidr:i,field:`privateSubnetCidrs`,index:r})):t.addIssue({code:`custom`,message:`Invalid subnet CIDR format or prefix length`,path:[`privateSubnetCidrs`,r]});for(let e=0;e{n.invalidateQueries({queryKey:[`eks`,`clusters`]})}},e[0]=n,e[1]=r),t(r)}async function R(e){let t=new E({name:e.name,version:e.version,roleArn:e.roleArn,resourcesVpcConfig:{subnetIds:e.subnetIds,endpointPublicAccess:e.endpointPublicAccess,endpointPrivateAccess:e.endpointPrivateAccess,publicAccessCidrs:e.endpointPublicAccess?e.publicAccessCidrs:void 0},accessConfig:{authenticationMode:`API`,bootstrapClusterCreatorAdminPermissions:e.bootstrapClusterCreatorAdminPermissions}});return await m().send(t)}function z(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:B,onSuccess:()=>{n.invalidateQueries({queryKey:[`eks`,`clusters`]})}},e[0]=n,e[1]=r),t(r)}async function B(e){let t=new A({name:e});return await m().send(t)}function V(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:H,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`nodegroups`]})}},e[0]=n,e[1]=r),t(r)}async function H(e){let t=new D({clusterName:e.clusterName,nodegroupName:e.nodegroupName,nodeRole:e.nodeRole,subnets:e.subnetIds,instanceTypes:e.instanceTypes,amiType:e.amiType,capacityType:e.capacityType,diskSize:e.diskSize,scalingConfig:{minSize:e.minSize,maxSize:e.maxSize,desiredSize:e.desiredSize}});return await m().send(t)}function U(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:ie,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`nodegroups`]})}},e[0]=n,e[1]=r),t(r)}async function ie(e){let t=new P({clusterName:e.clusterName,nodegroupName:e.nodegroupName,scalingConfig:{minSize:e.minSize,maxSize:e.maxSize,desiredSize:e.desiredSize}});return await m().send(t)}function W(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:G,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`nodegroups`]})}},e[0]=n,e[1]=r),t(r)}async function G(e){let t=new F({clusterName:e.clusterName,nodegroupName:e.nodegroupName,version:e.version});return await m().send(t)}function K(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:q,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`nodegroups`]})}},e[0]=n,e[1]=r),t(r)}async function q(e){let t=new j({clusterName:e.clusterName,nodegroupName:e.nodegroupName});return await m().send(t)}function J(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:Y,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`addons`]})}},e[0]=n,e[1]=r),t(r)}async function Y(e){let t=new T({clusterName:e.clusterName,addonName:e.addonName,addonVersion:e.addonVersion,serviceAccountRoleArn:e.serviceAccountRoleArn,configurationValues:e.configurationValues});return await m().send(t)}function ae(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:oe,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`addons`]})}},e[0]=n,e[1]=r),t(r)}async function oe(e){let t=new N({clusterName:e.clusterName,addonName:e.addonName,addonVersion:e.addonVersion,serviceAccountRoleArn:e.serviceAccountRoleArn,configurationValues:e.configurationValues});return await m().send(t)}function se(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:ce,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`addons`]})}},e[0]=n,e[1]=r),t(r)}async function ce(e){let t=new k({clusterName:e.clusterName,addonName:e.addonName});return await m().send(t)}function le(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:ue,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`access-entries`]})}},e[0]=n,e[1]=r),t(r)}async function ue(e){let t=new w({clusterName:e.clusterName,principalArn:e.principalArn,kubernetesGroups:e.kubernetesGroups,type:e.type});return await m().send(t)}function de(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:fe,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`access-entries`]})}},e[0]=n,e[1]=r),t(r)}async function fe(e){let t=new O({clusterName:e.clusterName,principalArn:e.principalArn});return await m().send(t)}function pe(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:me,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`access-entries`,t.principalArn,`policies`]})}},e[0]=n,e[1]=r),t(r)}async function me(e){let t=new C({clusterName:e.clusterName,principalArn:e.principalArn,policyArn:e.policyArn,accessScope:{type:e.accessScopeType,namespaces:e.namespaces}});return await m().send(t)}function he(){let e=(0,I.c)(2),n=x(),r;return e[0]===n?r=e[1]:(r={mutationFn:ge,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`eks`,`clusters`,t.clusterName,`access-entries`,t.principalArn,`policies`]})}},e[0]=n,e[1]=r),t(r)}async function ge(e){let t=new M({clusterName:e.clusterName,principalArn:e.principalArn,policyArn:e.policyArn});return await m().send(t)}var X=[`1.32`,`1.31`,`1.30`],_e=[{arn:`arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy`,name:`AmazonEKSClusterAdminPolicy`},{arn:`arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminPolicy`,name:`AmazonEKSAdminPolicy`},{arn:`arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy`,name:`AmazonEKSEditPolicy`},{arn:`arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy`,name:`AmazonEKSViewPolicy`}],Z=[`AL2_x86_64`,`AL2_ARM_64`],Q=[`ON_DEMAND`,`SPOT`],$=b().min(1,`Name is required`).max(100,`Name must be 100 characters or fewer`).regex(/^[0-9A-Za-z][A-Za-z0-9\-_]*$/,`Must start with a letter or digit and contain only letters, digits, hyphens, or underscores`),ve=h({name:$,version:b().min(1,`Version is required`),roleArn:b().min(1,`Cluster IAM role is required`),vpcId:b().min(1,`VPC is required`),subnetIds:y(b()).min(1,`At least 1 subnet is required`),bootstrapClusterCreatorAdminPermissions:v(),endpointPublicAccess:v(),endpointPrivateAccess:v(),publicAccessCidrs:y(b())}).refine(e=>e.endpointPublicAccess||e.endpointPrivateAccess,{message:`Enable public access, private access, or both`,path:[`endpointPublicAccess`]}).refine(e=>!e.endpointPublicAccess||e.publicAccessCidrs.every(e=>S(e,0,32)),{message:`Each public access CIDR must be valid (e.g. 203.0.113.0/24)`,path:[`publicAccessCidrs`]}),ye=h({nodegroupName:$,nodeRole:b().min(1,`Node IAM role is required`),subnetIds:y(b()).min(1,`At least 1 subnet is required`),instanceTypes:b().min(1,`At least 1 instance type is required`),amiType:g(Z),capacityType:g(Q),diskSize:_().int().min(1),minSize:_().int().min(0),desiredSize:_().int().min(0),maxSize:_().int().min(1)}).refine(e=>e.minSize<=e.desiredSize&&e.desiredSize<=e.maxSize,{message:`Sizes must satisfy min ≤ desired ≤ max`,path:[`desiredSize`]}),be=h({addonName:b().min(1,`Add-on is required`),addonVersion:b().min(1,`Version is required`),serviceAccountRoleArn:b(),configurationValues:b().refine(e=>{if(e.trim()===``)return!0;try{return JSON.parse(e),!0}catch{return!1}},{message:`Configuration must be valid JSON`})});function xe(e){switch(e??``){case`ACTIVE`:return`default`;case`CREATE_FAILED`:case`DELETE_FAILED`:case`UPDATE_FAILED`:case`DEGRADED`:return`destructive`;default:return`secondary`}}var Se=h({principalArn:b().min(1,`Principal ARN is required`),kubernetesGroups:b()});export{W as S,z as _,xe as a,U as b,ve as c,le as d,J as f,se as g,de as h,X as i,ye as l,V as m,Z as n,Se as o,L as p,Q as r,be as s,_e as t,pe as u,K as v,ae as x,he as y}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/elbv2.js b/spinifex/services/spinifexui/frontend/dist/assets/elbv2.js index 23a95c6f8..c1156ee3b 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/elbv2.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/elbv2.js @@ -1 +1 @@ -import{n as e}from"./error-banner.js";import{$t as t,Fo as n,Jt as r,Qt as i,Vo as a,Xt as o,Yt as s,Zt as ee,an as c,cn as l,en as u,in as d,nn as f,on as te,or as p,rn as ne,sn as m,sr as h,tn as g,yt as _}from"./index.js";var v=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`CreateListener`,{}).n(`ElasticLoadBalancingV2Client`,`CreateListenerCommand`).sc(r).build(){},y=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`CreateLoadBalancer`,{}).n(`ElasticLoadBalancingV2Client`,`CreateLoadBalancerCommand`).sc(s).build(){},b=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`CreateRule`,{}).n(`ElasticLoadBalancingV2Client`,`CreateRuleCommand`).sc(o).build(){},x=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`CreateTargetGroup`,{}).n(`ElasticLoadBalancingV2Client`,`CreateTargetGroupCommand`).sc(ee).build(){},S=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeleteListener`,{}).n(`ElasticLoadBalancingV2Client`,`DeleteListenerCommand`).sc(i).build(){},C=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeleteLoadBalancer`,{}).n(`ElasticLoadBalancingV2Client`,`DeleteLoadBalancerCommand`).sc(t).build(){},w=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeleteRule`,{}).n(`ElasticLoadBalancingV2Client`,`DeleteRuleCommand`).sc(u).build(){},T=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeleteTargetGroup`,{}).n(`ElasticLoadBalancingV2Client`,`DeleteTargetGroupCommand`).sc(g).build(){},E=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeregisterTargets`,{}).n(`ElasticLoadBalancingV2Client`,`DeregisterTargetsCommand`).sc(f).build(){},D=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`ModifyListener`,{}).n(`ElasticLoadBalancingV2Client`,`ModifyListenerCommand`).sc(ne).build(){},O=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`ModifyLoadBalancerAttributes`,{}).n(`ElasticLoadBalancingV2Client`,`ModifyLoadBalancerAttributesCommand`).sc(d).build(){},k=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`ModifyRule`,{}).n(`ElasticLoadBalancingV2Client`,`ModifyRuleCommand`).sc(c).build(){},A=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`ModifyTargetGroupAttributes`,{}).n(`ElasticLoadBalancingV2Client`,`ModifyTargetGroupAttributesCommand`).sc(te).build(){},j=class extends p.classBuilder().ep(l).m(function(e,t,n,r){return[h(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`RegisterTargets`,{}).n(`ElasticLoadBalancingV2Client`,`RegisterTargetsCommand`).sc(m).build(){},M=n();function N(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:P,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`loadBalancers`]})}},t[0]=n,t[1]=r),e(r)}async function P(e){let t=new C({LoadBalancerArn:e});return await _().send(t)}function F(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:I,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`loadBalancers`,t.loadBalancerArn,`attributes`]})}},t[0]=n,t[1]=r),e(r)}async function I(e){let t=new O({LoadBalancerArn:e.loadBalancerArn,Attributes:e.attributes.map(L)});return await _().send(t)}function L(e){return{Key:e.key,Value:e.value}}function re(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:R,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`]})}},t[0]=n,t[1]=r),e(r)}async function R(e){let t=e.tags.filter(B).map(z),n=new x({Name:e.name,Protocol:e.protocol,Port:e.port,VpcId:e.vpcId,TargetType:`instance`,HealthCheckProtocol:e.healthCheck.protocol,HealthCheckPath:e.healthCheck.path,HealthCheckPort:e.healthCheck.port,HealthCheckIntervalSeconds:e.healthCheck.intervalSeconds,HealthCheckTimeoutSeconds:e.healthCheck.timeoutSeconds,HealthyThresholdCount:e.healthCheck.healthyThresholdCount,UnhealthyThresholdCount:e.healthCheck.unhealthyThresholdCount,Matcher:{HttpCode:e.healthCheck.matcher},Tags:t.length>0?t:void 0});return await _().send(n)}function z(e){return{Key:e.key,Value:e.value}}function B(e){return e.key.length>0}function V(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:H,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`]})}},t[0]=n,t[1]=r),e(r)}async function H(e){let t=new T({TargetGroupArn:e});return await _().send(t)}function U(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:W,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`,t.targetGroupArn,`attributes`]})}},t[0]=n,t[1]=r),e(r)}async function W(e){let t=new A({TargetGroupArn:e.targetGroupArn,Attributes:e.attributes.map(G)});return await _().send(t)}function G(e){return{Key:e.key,Value:e.value}}function K(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:q,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.loadBalancerArn]})}},t[0]=n,t[1]=r),e(r)}async function q(e){let t=[{Type:`forward`,TargetGroupArn:e.defaultTargetGroupArn}],n=new v({LoadBalancerArn:e.loadBalancerArn,Protocol:e.protocol,Port:e.port,DefaultActions:t});return await _().send(n)}function J(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:Y,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`]})}},t[0]=n,t[1]=r),e(r)}async function Y(e){let t=new S({ListenerArn:e});return await _().send(t)}function X(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:ie,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.loadBalancerArn]})}},t[0]=n,t[1]=r),e(r)}async function ie(e){let t=[{Type:`forward`,TargetGroupArn:e.defaultTargetGroupArn}],n=new D({ListenerArn:e.listenerArn,Protocol:e.protocol,Port:e.port,DefaultActions:t});return await _().send(n)}function ae(){let t=a();return e({mutationFn:async e=>{let t=_(),n=[],r=``;try{let i=e.listener.existingTargetGroupArn;if(e.listener.targetGroupMode===`new`){let a=e.listener.newTargetGroup;if(!a)throw Error(`internal error: new target group requested but no data supplied`);r=`creating target group`;let o=a.tags.filter(e=>e.key.length>0).map(e=>({Key:e.key,Value:e.value}));if(i=(await t.send(new x({Name:a.name,Protocol:a.protocol,Port:a.port,VpcId:a.vpcId,TargetType:`instance`,HealthCheckProtocol:a.healthCheck.protocol,HealthCheckPath:a.healthCheck.path,HealthCheckPort:a.healthCheck.port,HealthCheckIntervalSeconds:a.healthCheck.intervalSeconds,HealthCheckTimeoutSeconds:a.healthCheck.timeoutSeconds,HealthyThresholdCount:a.healthCheck.healthyThresholdCount,UnhealthyThresholdCount:a.healthCheck.unhealthyThresholdCount,Matcher:{HttpCode:a.healthCheck.matcher},Tags:o.length>0?o:void 0}))).TargetGroups?.[0]?.TargetGroupArn,!i)throw Error(`Target group was created but no ARN was returned by the API`);n.push({type:`Target Group`,id:i})}if(!i)throw Error(`Target group selection is required`);r=`creating load balancer`;let a=e.lb.tags.filter(e=>e.key.length>0).map(e=>({Key:e.key,Value:e.value})),o=(await t.send(new y({Name:e.lb.name,Scheme:e.lb.scheme,Type:`application`,IpAddressType:`ipv4`,Subnets:e.lb.subnetIds,SecurityGroups:e.lb.securityGroupIds.length>0?e.lb.securityGroupIds:void 0,Tags:a.length>0?a:void 0}))).LoadBalancers?.[0]?.LoadBalancerArn;if(!o)throw Error(`Load balancer was created but no ARN was returned by the API`);return n.push({type:`Load Balancer`,id:o}),r=`creating listener`,await t.send(new v({LoadBalancerArn:o,Protocol:e.listener.protocol,Port:e.listener.port,DefaultActions:[{Type:`forward`,TargetGroupArn:i}]})),n.push({type:`Listener`,id:void 0}),{loadBalancerArn:o,created:n}}catch(e){return{loadBalancerArn:void 0,created:n,error:e instanceof Error?e:Error(String(e)),failedStep:r}}},onSettled:()=>{t.invalidateQueries({queryKey:[`elbv2`]})}})}function Z(e){return e.map(e=>({Id:e.id,Port:e.port}))}function oe(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:se,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`,t.targetGroupArn,`health`]})}},t[0]=n,t[1]=r),e(r)}async function se(e){let t=new j({TargetGroupArn:e.targetGroupArn,Targets:Z(e.targets)});return await _().send(t)}function Q(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:ce,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`,t.targetGroupArn,`health`]})}},t[0]=n,t[1]=r),e(r)}async function ce(e){let t=new E({TargetGroupArn:e.targetGroupArn,Targets:Z(e.targets)});return await _().send(t)}function $(e){return e.map(e=>{switch(e.field){case`host-header`:return{Field:e.field,HostHeaderConfig:{Values:e.values}};case`path-pattern`:return{Field:e.field,PathPatternConfig:{Values:e.values}};case`http-header`:return{Field:e.field,HttpHeaderConfig:{HttpHeaderName:e.httpHeaderName??``,Values:e.values}};case`http-request-method`:return{Field:e.field,HttpRequestMethodConfig:{Values:e.values}};case`source-ip`:return{Field:e.field,SourceIpConfig:{Values:e.values}};case`query-string`:return{Field:e.field,QueryStringConfig:{Values:(e.queryStringPairs??[]).map(e=>({Key:e.key&&e.key.length>0?e.key:void 0,Value:e.value}))}};default:return{Field:e.field,Values:e.values}}})}function le(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:ue,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.listenerArn,`rules`]})}},t[0]=n,t[1]=r),e(r)}async function ue(e){let t=[{Type:`forward`,TargetGroupArn:e.forwardTargetGroupArn}],n=new b({ListenerArn:e.listenerArn,Priority:e.priority,Conditions:$(e.conditions),Actions:t});return await _().send(n)}function de(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:fe,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.listenerArn,`rules`]})}},t[0]=n,t[1]=r),e(r)}async function fe(e){let t=new k({RuleArn:e.ruleArn,Conditions:e.conditions?$(e.conditions):void 0,Actions:e.forwardTargetGroupArn?[{Type:`forward`,TargetGroupArn:e.forwardTargetGroupArn}]:void 0});return await _().send(t)}function pe(){let t=(0,M.c)(2),n=a(),r;return t[0]===n?r=t[1]:(r={mutationFn:me,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.listenerArn,`rules`]})}},t[0]=n,t[1]=r),e(r)}async function me(e){let t=new w({RuleArn:e.ruleArn});return await _().send(t)}export{J as a,V as c,F as d,de as f,re as i,Q as l,oe as m,ae as n,N as o,U as p,le as r,pe as s,K as t,X as u}; \ No newline at end of file +import{t as e}from"./compiler-runtime.js";import{n as t}from"./error-banner.js";import{Cr as n,Ct as r,Dt as i,Et as a,Ot as o,St as s,Tr as c,Tt as ee,a as l,at as u,ct as d,dt as f,it as p,kt as m,lt as te,nt as ne,ot as re,rt as ie,st as h,ut as g,wt as _,xt as v}from"./awsClient.js";import{Lr as y}from"./index.js";var b=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`AddTags`,{}).n(`ElasticLoadBalancingV2Client`,`AddTagsCommand`).sc(ne).build(){},x=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`CreateListener`,{}).n(`ElasticLoadBalancingV2Client`,`CreateListenerCommand`).sc(ie).build(){},S=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`CreateLoadBalancer`,{}).n(`ElasticLoadBalancingV2Client`,`CreateLoadBalancerCommand`).sc(p).build(){},C=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`CreateRule`,{}).n(`ElasticLoadBalancingV2Client`,`CreateRuleCommand`).sc(u).build(){},w=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`CreateTargetGroup`,{}).n(`ElasticLoadBalancingV2Client`,`CreateTargetGroupCommand`).sc(re).build(){},T=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeleteListener`,{}).n(`ElasticLoadBalancingV2Client`,`DeleteListenerCommand`).sc(h).build(){},E=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeleteLoadBalancer`,{}).n(`ElasticLoadBalancingV2Client`,`DeleteLoadBalancerCommand`).sc(d).build(){},D=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeleteRule`,{}).n(`ElasticLoadBalancingV2Client`,`DeleteRuleCommand`).sc(te).build(){},O=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeleteTargetGroup`,{}).n(`ElasticLoadBalancingV2Client`,`DeleteTargetGroupCommand`).sc(g).build(){},k=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DeregisterTargets`,{}).n(`ElasticLoadBalancingV2Client`,`DeregisterTargetsCommand`).sc(f).build(){},A=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`ModifyListener`,{}).n(`ElasticLoadBalancingV2Client`,`ModifyListenerCommand`).sc(v).build(){},j=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`ModifyLoadBalancerAttributes`,{}).n(`ElasticLoadBalancingV2Client`,`ModifyLoadBalancerAttributesCommand`).sc(s).build(){},M=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`ModifyRule`,{}).n(`ElasticLoadBalancingV2Client`,`ModifyRuleCommand`).sc(r).build(){},N=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`ModifyTargetGroupAttributes`,{}).n(`ElasticLoadBalancingV2Client`,`ModifyTargetGroupAttributesCommand`).sc(_).build(){},P=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`RegisterTargets`,{}).n(`ElasticLoadBalancingV2Client`,`RegisterTargetsCommand`).sc(ee).build(){},F=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`RemoveTags`,{}).n(`ElasticLoadBalancingV2Client`,`RemoveTagsCommand`).sc(a).build(){},I=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`SetSecurityGroups`,{}).n(`ElasticLoadBalancingV2Client`,`SetSecurityGroupsCommand`).sc(i).build(){},L=class extends c.classBuilder().ep(m).m(function(e,t,r,i){return[n(r,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`SetSubnets`,{}).n(`ElasticLoadBalancingV2Client`,`SetSubnetsCommand`).sc(o).build(){},R=e();function z(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:B,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`loadBalancers`]})}},e[0]=n,e[1]=r),t(r)}async function B(e){let t=new E({LoadBalancerArn:e});return await l().send(t)}function V(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:H,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`loadBalancers`,t.loadBalancerArn,`attributes`]})}},e[0]=n,e[1]=r),t(r)}async function H(e){let t=new j({LoadBalancerArn:e.loadBalancerArn,Attributes:e.attributes.map(U)});return await l().send(t)}function U(e){return{Key:e.key,Value:e.value}}function W(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:G,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`tags`]})}},e[0]=n,e[1]=r),t(r)}async function G(e){let t=new Set(e.tags.map(q)),n=e.initialKeys.filter(e=>!t.has(e)),r=l();e.tags.length>0&&await r.send(new b({ResourceArns:[e.resourceArn],Tags:e.tags.map(K)})),n.length>0&&await r.send(new F({ResourceArns:[e.resourceArn],TagKeys:n}))}function K(e){return{Key:e.key,Value:e.value}}function q(e){return e.key}function J(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:Y,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`loadBalancers`,t.loadBalancerArn]})}},e[0]=n,e[1]=r),t(r)}async function Y(e){let t=new I({LoadBalancerArn:e.loadBalancerArn,SecurityGroups:e.securityGroupIds});return await l().send(t)}function ae(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:oe,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`loadBalancers`,t.loadBalancerArn]})}},e[0]=n,e[1]=r),t(r)}async function oe(e){let t=new L({LoadBalancerArn:e.loadBalancerArn,Subnets:e.subnetIds});return await l().send(t)}function se(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:ce,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`]})}},e[0]=n,e[1]=r),t(r)}async function ce(e){let t=e.tags.filter(ue).map(le),n=e.healthCheck.protocol===`HTTP`,r=new w({Name:e.name,Protocol:e.protocol,Port:e.port,VpcId:e.vpcId,TargetType:`instance`,HealthCheckProtocol:e.healthCheck.protocol,HealthCheckPath:n?e.healthCheck.path:void 0,HealthCheckPort:e.healthCheck.port,HealthCheckIntervalSeconds:e.healthCheck.intervalSeconds,HealthCheckTimeoutSeconds:e.healthCheck.timeoutSeconds,HealthyThresholdCount:e.healthCheck.healthyThresholdCount,UnhealthyThresholdCount:e.healthCheck.unhealthyThresholdCount,Matcher:n?{HttpCode:e.healthCheck.matcher}:void 0,Tags:t.length>0?t:void 0});return await l().send(r)}function le(e){return{Key:e.key,Value:e.value}}function ue(e){return e.key.length>0}function de(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:fe,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`]})}},e[0]=n,e[1]=r),t(r)}async function fe(e){let t=new O({TargetGroupArn:e});return await l().send(t)}function pe(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:me,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`,t.targetGroupArn,`attributes`]})}},e[0]=n,e[1]=r),t(r)}async function me(e){let t=new N({TargetGroupArn:e.targetGroupArn,Attributes:e.attributes.map(he)});return await l().send(t)}function he(e){return{Key:e.key,Value:e.value}}function X(e,t,n){return!(e===`HTTPS`||e===`TLS`)||!t?{}:{Certificates:[{CertificateArn:t}],SslPolicy:e===`HTTPS`?n:void 0}}function ge(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:_e,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.loadBalancerArn]})}},e[0]=n,e[1]=r),t(r)}async function _e(e){let t=[{Type:`forward`,TargetGroupArn:e.defaultTargetGroupArn}],n=new x({LoadBalancerArn:e.loadBalancerArn,Protocol:e.protocol,Port:e.port,DefaultActions:t,...X(e.protocol,e.certificateArn,e.sslPolicy)});return await l().send(n)}function ve(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:ye,onSuccess:()=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`]})}},e[0]=n,e[1]=r),t(r)}async function ye(e){let t=new T({ListenerArn:e});return await l().send(t)}function be(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:xe,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.loadBalancerArn]})}},e[0]=n,e[1]=r),t(r)}async function xe(e){let t=[{Type:`forward`,TargetGroupArn:e.defaultTargetGroupArn}],n=new A({ListenerArn:e.listenerArn,Protocol:e.protocol,Port:e.port,DefaultActions:t,...X(e.protocol,e.certificateArn,e.sslPolicy)});return await l().send(n)}function Se(){let e=y();return t({mutationFn:async e=>{let t=l(),n=[],r=``;try{let i=e.listener.existingTargetGroupArn;if(e.listener.targetGroupMode===`new`){let a=e.listener.newTargetGroup;if(!a)throw Error(`internal error: new target group requested but no data supplied`);r=`creating target group`;let o=a.tags.filter(e=>e.key.length>0).map(e=>({Key:e.key,Value:e.value})),s=a.healthCheck.protocol===`HTTP`;if(i=(await t.send(new w({Name:a.name,Protocol:a.protocol,Port:a.port,VpcId:a.vpcId,TargetType:`instance`,HealthCheckProtocol:a.healthCheck.protocol,HealthCheckPath:s?a.healthCheck.path:void 0,HealthCheckPort:a.healthCheck.port,HealthCheckIntervalSeconds:a.healthCheck.intervalSeconds,HealthCheckTimeoutSeconds:a.healthCheck.timeoutSeconds,HealthyThresholdCount:a.healthCheck.healthyThresholdCount,UnhealthyThresholdCount:a.healthCheck.unhealthyThresholdCount,Matcher:s?{HttpCode:a.healthCheck.matcher}:void 0,Tags:o.length>0?o:void 0}))).TargetGroups?.[0]?.TargetGroupArn,!i)throw Error(`Target group was created but no ARN was returned by the API`);n.push({type:`Target Group`,id:i})}if(!i)throw Error(`Target group selection is required`);r=`creating load balancer`;let a=e.lb.tags.filter(e=>e.key.length>0).map(e=>({Key:e.key,Value:e.value})),o=(await t.send(new S({Name:e.lb.name,Scheme:e.lb.scheme,Type:e.lb.type,IpAddressType:`ipv4`,Subnets:e.lb.subnetIds,SecurityGroups:e.lb.securityGroupIds.length>0?e.lb.securityGroupIds:void 0,Tags:a.length>0?a:void 0}))).LoadBalancers?.[0]?.LoadBalancerArn;if(!o)throw Error(`Load balancer was created but no ARN was returned by the API`);return n.push({type:`Load Balancer`,id:o}),r=`creating listener`,await t.send(new x({LoadBalancerArn:o,Protocol:e.listener.protocol,Port:e.listener.port,DefaultActions:[{Type:`forward`,TargetGroupArn:i}],...X(e.listener.protocol,e.listener.certificateArn,e.listener.sslPolicy)})),n.push({type:`Listener`,id:void 0}),{loadBalancerArn:o,created:n}}catch(e){return{loadBalancerArn:void 0,created:n,error:e instanceof Error?e:Error(String(e)),failedStep:r}}},onSettled:()=>{e.invalidateQueries({queryKey:[`elbv2`]})}})}function Z(e){return e.map(e=>({Id:e.id,Port:e.port}))}function Ce(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:we,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`,t.targetGroupArn,`health`]})}},e[0]=n,e[1]=r),t(r)}async function we(e){let t=new P({TargetGroupArn:e.targetGroupArn,Targets:Z(e.targets)});return await l().send(t)}function Q(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:Te,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`targetGroups`,t.targetGroupArn,`health`]})}},e[0]=n,e[1]=r),t(r)}async function Te(e){let t=new k({TargetGroupArn:e.targetGroupArn,Targets:Z(e.targets)});return await l().send(t)}function $(e){return e.map(e=>{switch(e.field){case`host-header`:return{Field:e.field,HostHeaderConfig:{Values:e.values}};case`path-pattern`:return{Field:e.field,PathPatternConfig:{Values:e.values}};case`http-header`:return{Field:e.field,HttpHeaderConfig:{HttpHeaderName:e.httpHeaderName??``,Values:e.values}};case`http-request-method`:return{Field:e.field,HttpRequestMethodConfig:{Values:e.values}};case`source-ip`:return{Field:e.field,SourceIpConfig:{Values:e.values}};case`query-string`:return{Field:e.field,QueryStringConfig:{Values:(e.queryStringPairs??[]).map(e=>({Key:e.key&&e.key.length>0?e.key:void 0,Value:e.value}))}};default:return{Field:e.field,Values:e.values}}})}function Ee(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:De,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.listenerArn,`rules`]})}},e[0]=n,e[1]=r),t(r)}async function De(e){let t=[{Type:`forward`,TargetGroupArn:e.forwardTargetGroupArn}],n=new C({ListenerArn:e.listenerArn,Priority:e.priority,Conditions:$(e.conditions),Actions:t});return await l().send(n)}function Oe(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:ke,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.listenerArn,`rules`]})}},e[0]=n,e[1]=r),t(r)}async function ke(e){let t=new M({RuleArn:e.ruleArn,Conditions:e.conditions?$(e.conditions):void 0,Actions:e.forwardTargetGroupArn?[{Type:`forward`,TargetGroupArn:e.forwardTargetGroupArn}]:void 0});return await l().send(t)}function Ae(){let e=(0,R.c)(2),n=y(),r;return e[0]===n?r=e[1]:(r={mutationFn:je,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`elbv2`,`listeners`,t.listenerArn,`rules`]})}},e[0]=n,e[1]=r),t(r)}async function je(e){let t=new D({RuleArn:e.ruleArn});return await l().send(t)}export{W as _,ve as a,de as c,V as d,Oe as f,ae as g,J as h,se as i,Q as l,Ce as m,Se as n,z as o,pe as p,Ee as r,Ae as s,ge as t,be as u}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/elbv22.js b/spinifex/services/spinifexui/frontend/dist/assets/elbv22.js index a0bc8efe0..c6de29a47 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/elbv22.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/elbv22.js @@ -1 +1 @@ -import{Br as e,Hr as t,Ir as n,Lr as r,Ur as i,Vr as a}from"./index.js";var o=/^(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])$/,s=i().min(1,`Name is required`).max(32,`Name must be 32 characters or less`).regex(o,`Name may contain only letters, digits, and hyphens; must start and end with alphanumeric`).refine(e=>!e.startsWith(`internal-`),`Name cannot start with 'internal-'`),c=i().min(1,`Name is required`).max(32,`Name must be 32 characters or less`).regex(o,`Name may contain only letters, digits, and hyphens; must start and end with alphanumeric`),l=e().int(`Port must be a whole number`).min(1,`Port must be at least 1`).max(65535,`Port must be at most 65535`),u=a({key:i().min(1,`Tag key is required`).max(128),value:i().max(256)}),d=a({protocol:n([`HTTP`]),path:i().min(1,`Path is required`).max(1024),port:i().min(1),intervalSeconds:e().int().min(5).max(300),timeoutSeconds:e().int().min(2).max(120),healthyThresholdCount:e().int().min(2).max(10),unhealthyThresholdCount:e().int().min(2).max(10),matcher:i().regex(/^\d{3}(?:[-,]\d{3})*$/,`Matcher must be HTTP codes like 200 or 200-299 or 200,201`)}),f=a({name:c,protocol:n([`HTTP`]),port:l,vpcId:i().min(1,`VPC is required`),healthCheck:d,tags:r(u)}),p=a({protocol:n([`HTTP`]),port:l,defaultTargetGroupArn:i().min(1,`Target group is required`)}),m=/^[!-~]+$/,h=a({field:n([`host-header`,`path-pattern`,`http-header`,`http-request-method`,`source-ip`,`query-string`]),httpHeaderName:i().max(40).optional(),rawValues:i().min(1,`At least one value is required`).max(640,`Too many values`)}).superRefine((e,t)=>{e.field===`http-header`&&!e.httpHeaderName&&t.addIssue({code:`custom`,path:[`httpHeaderName`],message:`Header name is required`});let n=e.rawValues.split(/[\n,]/).map(e=>e.trim()).filter(e=>e.length>0);if(n.length===0||n.length>5){t.addIssue({code:`custom`,path:[`rawValues`],message:`Enter 1 to 5 values, comma or newline separated`});return}for(let e of n)if(!m.test(e)){t.addIssue({code:`custom`,path:[`rawValues`],message:`Value "${e}" contains whitespace or non-ASCII characters`});return}}),g=a({priority:e().int(`Priority must be a whole number`).min(1,`Priority must be at least 1`).max(5e4,`Priority must be at most 50000`),conditions:r(h).min(1,`At least one condition is required`).max(5,`Up to 5 conditions per rule`),forwardTargetGroupArn:i().min(1,`Target group is required`)}),_=a({name:s,scheme:n([`internet-facing`,`internal`]),vpcId:i().min(1,`VPC is required`),subnetIds:r(i()).min(2,`At least 2 subnets are required for an ALB`),securityGroupIds:r(i()),tags:r(u),listener:a({protocol:n([`HTTP`]),port:l,targetGroupMode:n([`new`,`existing`]),existingTargetGroupArn:i().optional()}).refine(e=>e.targetGroupMode===`existing`?!!e.existingTargetGroupArn:!0,{error:`Target group is required`,path:[`existingTargetGroupArn`]})});a({targets:r(a({instanceId:i().min(1,`Instance is required`),port:l.optional()})).min(1,`At least one target is required`)}),t(i(),i());export{f as i,_ as n,g as r,p as t}; \ No newline at end of file +import{_ as e,f as t,g as n,p as r,v as i,y as a}from"./SignatureV4.js";var o=/^(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])$/,s=a().min(1,`Name is required`).max(32,`Name must be 32 characters or less`).regex(o,`Name may contain only letters, digits, and hyphens; must start and end with alphanumeric`).refine(e=>!e.startsWith(`internal-`),`Name cannot start with 'internal-'`),c=a().min(1,`Name is required`).max(32,`Name must be 32 characters or less`).regex(o,`Name may contain only letters, digits, and hyphens; must start and end with alphanumeric`),l=n().int(`Port must be a whole number`).min(1,`Port must be at least 1`).max(65535,`Port must be at most 65535`),u=e({key:a().min(1,`Tag key is required`).max(128),value:a().max(256)}),d=e({protocol:t([`HTTP`,`TCP`]),path:a().min(1,`Path is required`).max(1024),port:a().min(1),intervalSeconds:n().int().min(5).max(300),timeoutSeconds:n().int().min(2).max(120),healthyThresholdCount:n().int().min(2).max(10),unhealthyThresholdCount:n().int().min(2).max(10),matcher:a().regex(/^\d{3}(?:[-,]\d{3})*$/,`Matcher must be HTTP codes like 200 or 200-299 or 200,201`)}),f=e({name:c,protocol:t([`HTTP`,`HTTPS`,`TCP`,`UDP`,`TLS`,`TCP_UDP`]),port:l,vpcId:a().min(1,`VPC is required`),healthCheck:d,tags:r(u)}),p=`ELBSecurityPolicy-2016-08`,m=[`application`,`network`],h=[`HTTP`,`HTTPS`],g=[`TCP`,`UDP`,`TLS`,`TCP_UDP`],_=[...h,...g];function v(e){return e===`network`?g:h}function y(e){return e===`network`?`TCP`:`HTTP`}function b(e){return e===`HTTPS`||e===`TLS`}var x=e({protocol:t(_),port:l,defaultTargetGroupArn:a().min(1,`Target group is required`),certificateArn:a().optional(),sslPolicy:a().optional()}).superRefine((e,t)=>{b(e.protocol)&&!e.certificateArn&&t.addIssue({code:`custom`,path:[`certificateArn`],message:`A certificate is required for HTTPS and TLS`})}),S=/^[!-~]+$/,C=e({field:t([`host-header`,`path-pattern`,`http-header`,`http-request-method`,`source-ip`,`query-string`]),httpHeaderName:a().max(40).optional(),rawValues:a().min(1,`At least one value is required`).max(640,`Too many values`)}).superRefine((e,t)=>{e.field===`http-header`&&!e.httpHeaderName&&t.addIssue({code:`custom`,path:[`httpHeaderName`],message:`Header name is required`});let n=e.rawValues.split(/[\n,]/).map(e=>e.trim()).filter(e=>e.length>0);if(n.length===0||n.length>5){t.addIssue({code:`custom`,path:[`rawValues`],message:`Enter 1 to 5 values, comma or newline separated`});return}for(let e of n)if(!S.test(e)){t.addIssue({code:`custom`,path:[`rawValues`],message:`Value "${e}" contains whitespace or non-ASCII characters`});return}}),w=e({priority:n().int(`Priority must be a whole number`).min(1,`Priority must be at least 1`).max(5e4,`Priority must be at most 50000`),conditions:r(C).min(1,`At least one condition is required`).max(5,`Up to 5 conditions per rule`),forwardTargetGroupArn:a().min(1,`Target group is required`)}),T=e({name:s,type:t(m),scheme:t([`internet-facing`,`internal`]),vpcId:a().min(1,`VPC is required`),subnetIds:r(a()).min(1,`At least 1 subnet is required`),securityGroupIds:r(a()),tags:r(u),listener:e({protocol:t(_),port:l,targetGroupMode:t([`new`,`existing`]),existingTargetGroupArn:a().optional(),certificateArn:a().optional(),sslPolicy:a().optional()}).superRefine((e,t)=>{e.targetGroupMode===`existing`&&!e.existingTargetGroupArn&&t.addIssue({code:`custom`,path:[`existingTargetGroupArn`],message:`Target group is required`}),b(e.protocol)&&!e.certificateArn&&t.addIssue({code:`custom`,path:[`certificateArn`],message:`A certificate is required for HTTPS/TLS`})})});e({targets:r(e({instanceId:a().min(1,`Instance is required`),port:l.optional()})).min(1,`At least one target is required`)}),i(a(),a());export{f as a,v as c,w as i,x as n,y as o,T as r,b as s,p as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/error-banner.js b/spinifex/services/spinifexui/frontend/dist/assets/error-banner.js index a1313bca6..2f25310cd 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/error-banner.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/error-banner.js @@ -1 +1 @@ -import{Fo as e,Ho as t,Ko as n,Uo as r,Vo as i,Wo as a,Yo as o,Zo as s,cs as c,ns as l,os as u,ts as d}from"./index.js";var f=class extends u{#e;#t=void 0;#n;#r;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#i()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),d(this.options,t)||this.#e.getMutationCache().notify({type:`observerOptionsUpdated`,mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&o(t.mutationKey)!==o(this.options.mutationKey)?this.reset():this.#n?.state.status===`pending`&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#i(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#i(),this.#a()}mutate(e,t){return this.#r=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#i(){let e=this.#n?.state??a();this.#t={...e,isPending:e.status===`pending`,isSuccess:e.status===`success`,isError:e.status===`error`,isIdle:e.status===`idle`,mutate:this.mutate,reset:this.reset}}#a(e){n.batch(()=>{if(this.#r&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,r={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type===`success`){try{this.#r.onSuccess?.(e.data,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(e.data,null,t,n,r)}catch(e){Promise.reject(e)}}else if(e?.type===`error`){try{this.#r.onError?.(e.error,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(void 0,e.error,t,n,r)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},p=c(r(),1);function m(e,t){let r=i(t),[a]=p.useState(()=>new f(r,e));p.useEffect(()=>{a.setOptions(e)},[a,e]);let o=p.useSyncExternalStore(p.useCallback(e=>a.subscribe(n.batchCalls(e)),[a]),()=>a.getCurrentResult(),()=>a.getCurrentResult()),c=p.useCallback((e,t)=>{a.mutate(e,t).catch(s)},[a]);if(o.error&&l(a.options.throwOnError,[o.error]))throw o.error;return{...o,mutate:c,mutateAsync:o.mutate}}var h=e(),g=t();function _(e){let t=(0,h.c)(7),{msg:n,error:r}=e,i;r&&(i=r.name===`Error`?r.message:r.name);let a;t[0]===n?a=t[1]:(a=n&&(0,g.jsx)(`h2`,{className:`text-sm text-destructive`,children:n}),t[0]=n,t[1]=a);let o;t[2]===i?o=t[3]:(o=i&&(0,g.jsx)(`p`,{className:`text-sm text-destructive`,children:i}),t[2]=i,t[3]=o);let s;return t[4]!==a||t[5]!==o?(s=(0,g.jsxs)(`div`,{className:`mb-6 max-w-4xl rounded-md border border-destructive bg-destructive/10 p-4`,children:[a,o]}),t[4]=a,t[5]=o,t[6]=s):s=t[6],s}export{m as n,_ as t}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{Br as i,Gr as a,Lr as o,Rr as s,Ur as c,Xr as l,Yr as u,ei as d}from"./index.js";var f=class extends d{#e;#t=void 0;#n;#r;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#i()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),u(this.options,t)||this.#e.getMutationCache().notify({type:`observerOptionsUpdated`,mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&c(t.mutationKey)!==c(this.options.mutationKey)?this.reset():this.#n?.state.status===`pending`&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#i(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#i(),this.#a()}mutate(e,t){return this.#r=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#i(){let e=this.#n?.state??s();this.#t={...e,isPending:e.status===`pending`,isSuccess:e.status===`success`,isError:e.status===`error`,isIdle:e.status===`idle`,mutate:this.mutate,reset:this.reset}}#a(e){i.batch(()=>{if(this.#r&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,r={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type===`success`){try{this.#r.onSuccess?.(e.data,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(e.data,null,t,n,r)}catch(e){Promise.reject(e)}}else if(e?.type===`error`){try{this.#r.onError?.(e.error,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(void 0,e.error,t,n,r)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},p=t(n(),1);function m(e,t){let n=o(t),[r]=p.useState(()=>new f(n,e));p.useEffect(()=>{r.setOptions(e)},[r,e]);let s=p.useSyncExternalStore(p.useCallback(e=>r.subscribe(i.batchCalls(e)),[r]),()=>r.getCurrentResult(),()=>r.getCurrentResult()),c=p.useCallback((e,t)=>{r.mutate(e,t).catch(a)},[r]);if(s.error&&l(r.options.throwOnError,[s.error]))throw s.error;return{...s,mutate:c,mutateAsync:s.mutate}}var h=r(),g=e();function _(e){let t=(0,h.c)(7),{msg:n,error:r}=e,i;r&&(i=r.name===`Error`?r.message:r.name);let a;t[0]===n?a=t[1]:(a=n&&(0,g.jsx)(`h2`,{className:`text-sm text-destructive`,children:n}),t[0]=n,t[1]=a);let o;t[2]===i?o=t[3]:(o=i&&(0,g.jsx)(`p`,{className:`text-sm text-destructive`,children:i}),t[2]=i,t[3]=o);let s;return t[4]!==a||t[5]!==o?(s=(0,g.jsxs)(`div`,{className:`mb-6 max-w-4xl rounded-md border border-destructive bg-destructive/10 p-4`,children:[a,o]}),t[4]=a,t[5]=o,t[6]=s):s=t[6],s}export{m as n,_ as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/event-streams.js b/spinifex/services/spinifexui/frontend/dist/assets/event-streams.js deleted file mode 100644 index c6213feed..000000000 --- a/spinifex/services/spinifexui/frontend/dist/assets/event-streams.js +++ /dev/null @@ -1 +0,0 @@ -import{Sr as e,br as t}from"./index.js";var n=class{marshaller;serializer;deserializer;serdeContext;defaultContentType;constructor({marshaller:e,serializer:t,deserializer:n,serdeContext:r,defaultContentType:i}){this.marshaller=e,this.serializer=t,this.deserializer=n,this.serdeContext=r,this.defaultContentType=i}async serializeEventStream({eventStream:e,requestSchema:t,initialRequest:n}){let r=this.marshaller,i=t.getEventStreamMember(),a=t.getMemberSchema(i),o=this.serializer,s=this.defaultContentType,c=Symbol(`initialRequestMarker`),l={async*[Symbol.asyncIterator](){if(n){let e={":event-type":{type:`string`,value:`initial-request`},":message-type":{type:`string`,value:`event`},":content-type":{type:`string`,value:s}};o.write(t,n);let r=o.flush();yield{[c]:!0,headers:e,body:r}}for await(let t of e)yield t}};return r.serialize(l,e=>{if(e[c])return{headers:e.headers,body:e.body};let t=``;for(let n in e)if(n!==`__type`){t=n;break}let{additionalHeaders:n,body:r,eventType:i,explicitPayloadContentType:o}=this.writeEventBody(t,a,e);return{headers:{":event-type":{type:`string`,value:i},":message-type":{type:`string`,value:`event`},":content-type":{type:`string`,value:o??s},...n},body:r}})}async deserializeEventStream({response:e,responseSchema:n,initialResponseContainer:r}){let i=this.marshaller,a=n.getEventStreamMember(),o=n.getMemberSchema(a).getMemberSchemas(),s=Symbol(`initialResponseMarker`),c=i.deserialize(e.body,async e=>{let r=``;for(let t in e)if(t!==`__type`){r=t;break}let i=e[r].body;if(r===`initial-response`){let e=await this.deserializer.read(n,i);return delete e[a],{[s]:!0,...e}}else if(r in o){let n=o[r];if(n.isStructSchema()){let a={},o=!1;for(let[s,c]of n.structIterator()){let{eventHeader:n,eventPayload:l}=c.getMergedTraits();if(o||=!!(n||l),l)c.isBlobSchema()?a[s]=i:c.isStringSchema()?a[s]=(this.serdeContext?.utf8Encoder??t)(i):c.isStructSchema()&&(a[s]=await this.deserializer.read(c,i));else if(n){let t=e[r].headers[s]?.value;t!=null&&(c.isNumericSchema()?t&&typeof t==`object`&&`bytes`in t?a[s]=BigInt(t.toString()):a[s]=Number(t):a[s]=t)}}if(o)return{[r]:a};if(i.byteLength===0)return{[r]:{}}}return{[r]:await this.deserializer.read(n,i)}}else return{$unknown:e}}),l=c[Symbol.asyncIterator](),u=await l.next();if(u.done)return c;if(u.value?.[s]){if(!n)throw Error(`@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.`);for(let e in u.value)r[e]=u.value[e]}return{async*[Symbol.asyncIterator](){for(u?.value?.[s]||(yield u.value);;){let{done:e,value:t}=await l.next();if(e)break;yield t}}}}writeEventBody(t,n,r){let i=this.serializer,a=t,o=null,s,c=n.getSchema()[4].includes(t),l={};if(c){let e=n.getMemberSchema(t);if(e.isStructSchema()){for(let[n,i]of e.structIterator()){let{eventHeader:e,eventPayload:a}=i.getMergedTraits();if(a)o=n;else if(e){let e=r[t][n],a=`binary`;i.isNumericSchema()?a=(-2)**31<=e&&e<=2**31-1?`integer`:`long`:i.isTimestampSchema()?a=`timestamp`:i.isStringSchema()?a=`string`:i.isBooleanSchema()&&(a=`boolean`),e!=null&&(l[n]={type:a,value:e},delete r[t][n])}}if(o!==null){let n=e.getMemberSchema(o);n.isBlobSchema()?s=`application/octet-stream`:n.isStringSchema()&&(s=`text/plain`),i.write(n,r[t][o])}else i.write(e,r[t])}else if(e.isUnitSchema())i.write(e,{});else throw Error(`@smithy/core/event-streams - non-struct member not supported in event stream union.`)}else{let[e,n]=r[t];a=e,i.write(15,n)}let u=i.flush()??new Uint8Array;return{body:typeof u==`string`?(this.serdeContext?.utf8Decoder??e)(u):u,eventType:a,explicitPayloadContentType:s,additionalHeaders:l}}};export{n as EventStreamSerde}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/field.js b/spinifex/services/spinifexui/frontend/dist/assets/field.js index a57c97b02..b21eecf74 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/field.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/field.js @@ -1 +1 @@ -import{$a as e,Fo as t,Ho as n,To as r,Uo as i}from"./index.js";i();var a=t(),o=n();function s(t){let n=(0,a.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let s;n[3]===r?s=n[4]:(s=e(`group/field-group @container/field-group flex w-full flex-col gap-4 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4`,r),n[3]=r,n[4]=s);let c;return n[5]!==i||n[6]!==s?(c=(0,o.jsx)(`div`,{className:s,"data-slot":`field-group`,...i}),n[5]=i,n[6]=s,n[7]=c):c=n[7],c}var c=r(`group/field flex w-full gap-2 data-[invalid=true]:text-destructive`,{variants:{orientation:{vertical:`flex-col [&>*]:w-full [&>.sr-only]:w-auto`,horizontal:`flex-row items-center has-[>[data-slot=field-content]]:items-start [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px`,responsive:`flex-col @md/field-group:flex-row @md/field-group:items-center @md/field-group:has-[>[data-slot=field-content]]:items-start [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px`}},defaultVariants:{orientation:`vertical`}});function l(t){let n=(0,a.c)(11),r,i,s;n[0]===t?(r=n[1],i=n[2],s=n[3]):({className:r,orientation:s,...i}=t,n[0]=t,n[1]=r,n[2]=i,n[3]=s);let l=s===void 0?`vertical`:s,u;n[4]!==r||n[5]!==l?(u=e(c({orientation:l}),r),n[4]=r,n[5]=l,n[6]=u):u=n[6];let d;return n[7]!==l||n[8]!==i||n[9]!==u?(d=(0,o.jsx)(`div`,{className:u,"data-orientation":l,"data-slot":`field`,role:`group`,...i}),n[7]=l,n[8]=i,n[9]=u,n[10]=d):d=n[10],d}function u(t){let n=(0,a.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let s;n[3]===r?s=n[4]:(s=e(`flex w-fit items-center gap-2 text-xs/relaxed leading-snug font-medium group-data-[disabled=true]/field:opacity-50`,r),n[3]=r,n[4]=s);let c;return n[5]!==i||n[6]!==s?(c=(0,o.jsx)(`div`,{className:s,"data-slot":`field-label`,...i}),n[5]=i,n[6]=s,n[7]=c):c=n[7],c}function d(t){let n=(0,a.c)(8),r,i;n[0]===t?(r=n[1],i=n[2]):({className:r,...i}=t,n[0]=t,n[1]=r,n[2]=i);let s;n[3]===r?s=n[4]:(s=e(`text-left text-xs/relaxed leading-normal font-normal text-muted-foreground group-has-data-[orientation=horizontal]/field:text-balance [[data-variant=legend]+&]:-mt-1.5`,`last:mt-0 nth-last-2:-mt-1`,`[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary`,r),n[3]=r,n[4]=s);let c;return n[5]!==i||n[6]!==s?(c=(0,o.jsx)(`p`,{className:s,"data-slot":`field-description`,...i}),n[5]=i,n[6]=s,n[7]=c):c=n[7],c}function f(t){let n=(0,a.c)(15),r,i,s,c;n[0]===t?(r=n[1],i=n[2],s=n[3],c=n[4]):({className:i,children:r,errors:s,...c}=t,n[0]=t,n[1]=r,n[2]=i,n[3]=s,n[4]=c);let l;bb0:{if(r){l=r;break bb0}if(!s?.length){l=null;break bb0}let e;n[5]===s?e=n[6]:(e=[...new Map(s.map(m)).values()],n[5]=s,n[6]=e);let t=e;if(t?.length===1){l=t[0]?.message;break bb0}let i;n[7]===t?i=n[8]:(i=(0,o.jsx)(`ul`,{className:`ml-4 flex list-disc flex-col gap-1`,children:t.map(p)}),n[7]=t,n[8]=i),l=i}let u=l;if(!u)return null;let d;n[9]===i?d=n[10]:(d=e(`text-xs/relaxed font-normal text-destructive`,i),n[9]=i,n[10]=d);let f;return n[11]!==u||n[12]!==c||n[13]!==d?(f=(0,o.jsx)(`div`,{className:d,"data-slot":`field-error`,role:`alert`,...c,children:u}),n[11]=u,n[12]=c,n[13]=d,n[14]=f):f=n[14],f}function p(e){return e?.message&&(0,o.jsx)(`li`,{children:e.message},e.message)}function m(e){return[e?.message,e]}export{u as a,s as i,d as n,f as r,l as t}; \ No newline at end of file +import{n as e,r as t,t as n}from"./compiler-runtime.js";import{t as r}from"./dist.js";import{w as i}from"./SignatureV4.js";import"./index.js";t();var a=n(),o=e();function s(e){let t=(0,a.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let s;t[3]===n?s=t[4]:(s=i(`group/field-group @container/field-group flex w-full flex-col gap-4 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4`,n),t[3]=n,t[4]=s);let c;return t[5]!==r||t[6]!==s?(c=(0,o.jsx)(`div`,{className:s,"data-slot":`field-group`,...r}),t[5]=r,t[6]=s,t[7]=c):c=t[7],c}var c=r(`group/field flex w-full gap-2 data-[invalid=true]:text-destructive`,{variants:{orientation:{vertical:`flex-col [&>*]:w-full [&>.sr-only]:w-auto`,horizontal:`flex-row items-center has-[>[data-slot=field-content]]:items-start [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px`,responsive:`flex-col @md/field-group:flex-row @md/field-group:items-center @md/field-group:has-[>[data-slot=field-content]]:items-start [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px`}},defaultVariants:{orientation:`vertical`}});function l(e){let t=(0,a.c)(11),n,r,s;t[0]===e?(n=t[1],r=t[2],s=t[3]):({className:n,orientation:s,...r}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=s);let l=s===void 0?`vertical`:s,u;t[4]!==n||t[5]!==l?(u=i(c({orientation:l}),n),t[4]=n,t[5]=l,t[6]=u):u=t[6];let d;return t[7]!==l||t[8]!==r||t[9]!==u?(d=(0,o.jsx)(`div`,{className:u,"data-orientation":l,"data-slot":`field`,role:`group`,...r}),t[7]=l,t[8]=r,t[9]=u,t[10]=d):d=t[10],d}function u(e){let t=(0,a.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let s;t[3]===n?s=t[4]:(s=i(`flex w-fit items-center gap-2 text-xs/relaxed leading-snug font-medium group-data-[disabled=true]/field:opacity-50`,n),t[3]=n,t[4]=s);let c;return t[5]!==r||t[6]!==s?(c=(0,o.jsx)(`div`,{className:s,"data-slot":`field-label`,...r}),t[5]=r,t[6]=s,t[7]=c):c=t[7],c}function d(e){let t=(0,a.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let s;t[3]===n?s=t[4]:(s=i(`text-left text-xs/relaxed leading-normal font-normal text-muted-foreground group-has-data-[orientation=horizontal]/field:text-balance [[data-variant=legend]+&]:-mt-1.5`,`last:mt-0 nth-last-2:-mt-1`,`[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary`,n),t[3]=n,t[4]=s);let c;return t[5]!==r||t[6]!==s?(c=(0,o.jsx)(`p`,{className:s,"data-slot":`field-description`,...r}),t[5]=r,t[6]=s,t[7]=c):c=t[7],c}function f(e){let t=(0,a.c)(15),n,r,s,c;t[0]===e?(n=t[1],r=t[2],s=t[3],c=t[4]):({className:r,children:n,errors:s,...c}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=s,t[4]=c);let l;bb0:{if(n){l=n;break bb0}if(!s?.length){l=null;break bb0}let e;t[5]===s?e=t[6]:(e=[...new Map(s.map(m)).values()],t[5]=s,t[6]=e);let r=e;if(r?.length===1){l=r[0]?.message;break bb0}let i;t[7]===r?i=t[8]:(i=(0,o.jsx)(`ul`,{className:`ml-4 flex list-disc flex-col gap-1`,children:r.map(p)}),t[7]=r,t[8]=i),l=i}let u=l;if(!u)return null;let d;t[9]===r?d=t[10]:(d=i(`text-xs/relaxed font-normal text-destructive`,r),t[9]=r,t[10]=d);let f;return t[11]!==u||t[12]!==c||t[13]!==d?(f=(0,o.jsx)(`div`,{className:d,"data-slot":`field-error`,role:`alert`,...c,children:u}),t[11]=u,t[12]=c,t[13]=d,t[14]=f):f=t[14],f}function p(e){return e?.message&&(0,o.jsx)(`li`,{children:e.message},e.message)}function m(e){return[e?.message,e]}export{u as a,s as i,d as n,f as r,l as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/form-actions.js b/spinifex/services/spinifexui/frontend/dist/assets/form-actions.js index 42245ee74..f2074c408 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/form-actions.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/form-actions.js @@ -1 +1 @@ -import{Fo as e,Ho as t,Xa as n}from"./index.js";var r=e(),i=t();function a(e){let t=(0,r.c)(9),{isSubmitting:a,isPending:o,onCancel:s,submitLabel:c,pendingLabel:l}=e,u=a||o,d;t[0]!==u||t[1]!==s?(d=(0,i.jsx)(n,{disabled:u,onClick:s,type:`button`,variant:`outline`,children:`Cancel`}),t[0]=u,t[1]=s,t[2]=d):d=t[2];let f=u?l:c,p;t[3]!==u||t[4]!==f?(p=(0,i.jsx)(n,{disabled:u,type:`submit`,children:f}),t[3]=u,t[4]=f,t[5]=p):p=t[5];let m;return t[6]!==d||t[7]!==p?(m=(0,i.jsxs)(`div`,{className:`flex gap-2`,children:[d,p]}),t[6]=d,t[7]=p,t[8]=m):m=t[8],m}export{a as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./button.js";var r=t(),i=e();function a(e){let t=(0,r.c)(9),{isSubmitting:a,isPending:o,onCancel:s,submitLabel:c,pendingLabel:l}=e,u=a||o,d;t[0]!==u||t[1]!==s?(d=(0,i.jsx)(n,{disabled:u,onClick:s,type:`button`,variant:`outline`,children:`Cancel`}),t[0]=u,t[1]=s,t[2]=d):d=t[2];let f=u?l:c,p;t[3]!==u||t[4]!==f?(p=(0,i.jsx)(n,{disabled:u,type:`submit`,children:f}),t[3]=u,t[4]=f,t[5]=p):p=t[5];let m;return t[6]!==d||t[7]!==p?(m=(0,i.jsxs)(`div`,{className:`flex gap-2`,children:[d,p]}),t[6]=d,t[7]=p,t[8]=m):m=t[8],m}export{a as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/geist-cyrillic-ext-wght-normal.woff2 b/spinifex/services/spinifexui/frontend/dist/assets/geist-cyrillic-ext-wght-normal.woff2 new file mode 100644 index 000000000..4c462958a Binary files /dev/null and b/spinifex/services/spinifexui/frontend/dist/assets/geist-cyrillic-ext-wght-normal.woff2 differ diff --git a/spinifex/services/spinifexui/frontend/dist/assets/geist-cyrillic-wght-normal.woff2 b/spinifex/services/spinifexui/frontend/dist/assets/geist-cyrillic-wght-normal.woff2 index 2000e32fd..4b7a2bec6 100644 Binary files a/spinifex/services/spinifexui/frontend/dist/assets/geist-cyrillic-wght-normal.woff2 and b/spinifex/services/spinifexui/frontend/dist/assets/geist-cyrillic-wght-normal.woff2 differ diff --git a/spinifex/services/spinifexui/frontend/dist/assets/geist-latin-ext-wght-normal.woff2 b/spinifex/services/spinifexui/frontend/dist/assets/geist-latin-ext-wght-normal.woff2 index 9608c47c4..ba90e2094 100644 Binary files a/spinifex/services/spinifexui/frontend/dist/assets/geist-latin-ext-wght-normal.woff2 and b/spinifex/services/spinifexui/frontend/dist/assets/geist-latin-ext-wght-normal.woff2 differ diff --git a/spinifex/services/spinifexui/frontend/dist/assets/geist-latin-wght-normal.woff2 b/spinifex/services/spinifexui/frontend/dist/assets/geist-latin-wght-normal.woff2 index 93552f55a..991445d78 100644 Binary files a/spinifex/services/spinifexui/frontend/dist/assets/geist-latin-wght-normal.woff2 and b/spinifex/services/spinifexui/frontend/dist/assets/geist-latin-wght-normal.woff2 differ diff --git a/spinifex/services/spinifexui/frontend/dist/assets/geist-vietnamese-wght-normal.woff2 b/spinifex/services/spinifexui/frontend/dist/assets/geist-vietnamese-wght-normal.woff2 new file mode 100644 index 000000000..ee965898b Binary files /dev/null and b/spinifex/services/spinifexui/frontend/dist/assets/geist-vietnamese-wght-normal.woff2 differ diff --git a/spinifex/services/spinifexui/frontend/dist/assets/iam.js b/spinifex/services/spinifexui/frontend/dist/assets/iam.js index a5218c60c..19fc48f2e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/iam.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/iam.js @@ -1 +1 @@ -import{n as e}from"./error-banner.js";import{Bt as t,Fo as n,Gt as r,Ht as i,Kt as a,Rt as o,Ut as s,Vo as c,Vt as l,Wt as u,bt as d,or as f,qt as p,sr as m,zt as h}from"./index.js";var g=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`AttachUserPolicy`,{}).n(`IAMClient`,`AttachUserPolicyCommand`).sc(o).build(){},_=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`CreateAccessKey`,{}).n(`IAMClient`,`CreateAccessKeyCommand`).sc(h).build(){},v=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`CreatePolicy`,{}).n(`IAMClient`,`CreatePolicyCommand`).sc(t).build(){},y=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`CreateUser`,{}).n(`IAMClient`,`CreateUserCommand`).sc(l).build(){},b=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DeleteAccessKey`,{}).n(`IAMClient`,`DeleteAccessKeyCommand`).sc(i).build(){},x=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DeletePolicy`,{}).n(`IAMClient`,`DeletePolicyCommand`).sc(s).build(){},S=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DeleteUser`,{}).n(`IAMClient`,`DeleteUserCommand`).sc(u).build(){},C=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DetachUserPolicy`,{}).n(`IAMClient`,`DetachUserPolicyCommand`).sc(r).build(){},w=class extends f.classBuilder().ep(p).m(function(e,t,n,r){return[m(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`UpdateAccessKey`,{}).n(`IAMClient`,`UpdateAccessKeyCommand`).sc(a).build(){},T=n();function E(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:D,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`users`]})}},t[0]=n,t[1]=r),e(r)}async function D(e){let t=new y({UserName:e.userName,Path:e.path||void 0});return await d().send(t)}function O(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:k,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`users`]})}},t[0]=n,t[1]=r),e(r)}async function k(e){let t=new S({UserName:e});return await d().send(t)}function A(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:j,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`access-keys`]})}},t[0]=n,t[1]=r),e(r)}async function j(e){let t=new _({UserName:e});return await d().send(t)}function M(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:N,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`access-keys`]})}},t[0]=n,t[1]=r),e(r)}async function N(e){let{userName:t,accessKeyId:n}=e,r=new b({UserName:t,AccessKeyId:n});return await d().send(r)}function P(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:F,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`access-keys`]})}},t[0]=n,t[1]=r),e(r)}async function F(e){let{userName:t,accessKeyId:n,status:r}=e,i=new w({UserName:t,AccessKeyId:n,Status:r});return await d().send(i)}function I(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:L,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`policies`]})}},t[0]=n,t[1]=r),e(r)}async function L(e){let t=new v({PolicyName:e.policyName,Description:e.description||void 0,PolicyDocument:e.policyDocument});return await d().send(t)}function R(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:z,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`policies`]})}},t[0]=n,t[1]=r),e(r)}async function z(e){let t=new x({PolicyArn:e});return await d().send(t)}function B(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:V,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`attached-user-policies`]})}},t[0]=n,t[1]=r),e(r)}async function V(e){let{userName:t,policyArn:n}=e,r=new g({UserName:t,PolicyArn:n});return await d().send(r)}function H(){let t=(0,T.c)(2),n=c(),r;return t[0]===n?r=t[1]:(r={mutationFn:U,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`attached-user-policies`]})}},t[0]=n,t[1]=r),e(r)}async function U(e){let{userName:t,policyArn:n}=e,r=new C({UserName:t,PolicyArn:n});return await d().send(r)}export{M as a,H as c,E as i,P as l,A as n,R as o,I as r,O as s,B as t}; \ No newline at end of file +import{t as e}from"./compiler-runtime.js";import{n as t}from"./error-banner.js";import{$ as n,A as r,Cr as i,D as a,E as o,F as s,I as c,L as l,M as ee,N as u,O as d,P as f,R as p,T as m,Tr as h,et as g,j as _,k as v,o as y,tt as b,z as x}from"./awsClient.js";import{Lr as S}from"./index.js";var C=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`AddRoleToInstanceProfile`,{}).n(`IAMClient`,`AddRoleToInstanceProfileCommand`).sc(m).build(){},w=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`AttachRolePolicy`,{}).n(`IAMClient`,`AttachRolePolicyCommand`).sc(o).build(){},T=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`AttachUserPolicy`,{}).n(`IAMClient`,`AttachUserPolicyCommand`).sc(a).build(){},E=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`CreateAccessKey`,{}).n(`IAMClient`,`CreateAccessKeyCommand`).sc(d).build(){},D=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`CreateInstanceProfile`,{}).n(`IAMClient`,`CreateInstanceProfileCommand`).sc(v).build(){},O=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`CreatePolicy`,{}).n(`IAMClient`,`CreatePolicyCommand`).sc(r).build(){},k=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`CreateRole`,{}).n(`IAMClient`,`CreateRoleCommand`).sc(_).build(){},A=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`CreateUser`,{}).n(`IAMClient`,`CreateUserCommand`).sc(ee).build(){},j=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DeleteAccessKey`,{}).n(`IAMClient`,`DeleteAccessKeyCommand`).sc(u).build(){},M=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DeleteInstanceProfile`,{}).n(`IAMClient`,`DeleteInstanceProfileCommand`).sc(f).build(){},N=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DeletePolicy`,{}).n(`IAMClient`,`DeletePolicyCommand`).sc(s).build(){},P=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DeleteRole`,{}).n(`IAMClient`,`DeleteRoleCommand`).sc(c).build(){},F=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DeleteUser`,{}).n(`IAMClient`,`DeleteUserCommand`).sc(l).build(){},I=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DetachRolePolicy`,{}).n(`IAMClient`,`DetachRolePolicyCommand`).sc(p).build(){},L=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`DetachUserPolicy`,{}).n(`IAMClient`,`DetachUserPolicyCommand`).sc(x).build(){},R=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`RemoveRoleFromInstanceProfile`,{}).n(`IAMClient`,`RemoveRoleFromInstanceProfileCommand`).sc(n).build(){},z=class extends h.classBuilder().ep(b).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`UpdateAccessKey`,{}).n(`IAMClient`,`UpdateAccessKeyCommand`).sc(g).build(){},B=e();function V(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:H,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`users`]})}},e[0]=n,e[1]=r),t(r)}async function H(e){let t=new A({UserName:e.userName,Path:e.path||void 0});return await y().send(t)}function U(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:W,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`users`]})}},e[0]=n,e[1]=r),t(r)}async function W(e){let t=new F({UserName:e});return await y().send(t)}function G(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:te,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`access-keys`]})}},e[0]=n,e[1]=r),t(r)}async function te(e){let t=new E({UserName:e});return await y().send(t)}function K(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:q,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`access-keys`]})}},e[0]=n,e[1]=r),t(r)}async function q(e){let{userName:t,accessKeyId:n}=e,r=new j({UserName:t,AccessKeyId:n});return await y().send(r)}function J(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:Y,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`access-keys`]})}},e[0]=n,e[1]=r),t(r)}async function Y(e){let{userName:t,accessKeyId:n,status:r}=e,i=new z({UserName:t,AccessKeyId:n,Status:r});return await y().send(i)}function X(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:Z,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`policies`]})}},e[0]=n,e[1]=r),t(r)}async function Z(e){let t=new O({PolicyName:e.policyName,Description:e.description||void 0,PolicyDocument:e.policyDocument});return await y().send(t)}function Q(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:ne,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`policies`]})}},e[0]=n,e[1]=r),t(r)}async function ne(e){let t=new N({PolicyArn:e});return await y().send(t)}function re(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:ie,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`attached-user-policies`]})}},e[0]=n,e[1]=r),t(r)}async function ie(e){let{userName:t,policyArn:n}=e,r=new T({UserName:t,PolicyArn:n});return await y().send(r)}function ae(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:oe,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`attached-user-policies`]})}},e[0]=n,e[1]=r),t(r)}async function oe(e){let{userName:t,policyArn:n}=e,r=new L({UserName:t,PolicyArn:n});return await y().send(r)}function se(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:ce,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`roles`]})}},e[0]=n,e[1]=r),t(r)}async function ce(e){let t=new k({RoleName:e.roleName,Path:e.path||void 0,Description:e.description||void 0,AssumeRolePolicyDocument:e.assumeRolePolicyDocument});return await y().send(t)}function le(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:ue,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`roles`]})}},e[0]=n,e[1]=r),t(r)}async function ue(e){let t=new P({RoleName:e});return await y().send(t)}function de(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:fe,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`attached-role-policies`]})}},e[0]=n,e[1]=r),t(r)}async function fe(e){let{roleName:t,policyArn:n}=e,r=new w({RoleName:t,PolicyArn:n});return await y().send(r)}function pe(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:$,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`attached-role-policies`]})}},e[0]=n,e[1]=r),t(r)}async function $(e){let{roleName:t,policyArn:n}=e,r=new I({RoleName:t,PolicyArn:n});return await y().send(r)}function me(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:he,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`instance-profiles`]})}},e[0]=n,e[1]=r),t(r)}async function he(e){let t=new D({InstanceProfileName:e.instanceProfileName,Path:e.path||void 0});return await y().send(t)}function ge(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:_e,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`instance-profiles`]})}},e[0]=n,e[1]=r),t(r)}async function _e(e){let t=new M({InstanceProfileName:e});return await y().send(t)}function ve(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:ye,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`instance-profiles`]})}},e[0]=n,e[1]=r),t(r)}async function ye(e){let{instanceProfileName:t,roleName:n}=e,r=new C({InstanceProfileName:t,RoleName:n});return await y().send(r)}function be(){let e=(0,B.c)(2),n=S(),r;return e[0]===n?r=e[1]:(r={mutationFn:xe,onSuccess:()=>{n.invalidateQueries({queryKey:[`iam`,`instance-profiles`]})}},e[0]=n,e[1]=r),t(r)}async function xe(e){let{instanceProfileName:t,roleName:n}=e,r=new R({InstanceProfileName:t,RoleName:n});return await y().send(r)}export{J as _,me as a,V as c,Q as d,le as f,be as g,ae as h,G as i,K as l,pe as m,de as n,X as o,U as p,re as r,se as s,ve as t,ge as u}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/iam2.js b/spinifex/services/spinifexui/frontend/dist/assets/iam2.js index f8a12a2fd..7622be6d8 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/iam2.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/iam2.js @@ -1 +1 @@ -import{Ur as e,Vr as t}from"./index.js";var n=t({userName:e().min(1,`User name is required`).max(64,`User name must be 64 characters or less`).regex(/^[\w+=,.@-]+$/,`User name contains invalid characters`),path:e().optional()}),r=t({policyName:e().min(1,`Policy name is required`).max(128,`Policy name must be 128 characters or less`).regex(/^[\w+=,.@-]+$/,`Policy name contains invalid characters`),description:e().optional(),policyDocument:e().min(1,`Policy document is required`).refine(e=>{try{return JSON.parse(e),!0}catch{return!1}},{message:`Policy document must be valid JSON`})});export{n,r as t}; \ No newline at end of file +import{_ as e,y as t}from"./SignatureV4.js";var n=e({userName:t().min(1,`User name is required`).max(64,`User name must be 64 characters or less`).regex(/^[\w+=,.@-]+$/,`User name contains invalid characters`),path:t().optional()}),r=e({policyName:t().min(1,`Policy name is required`).max(128,`Policy name must be 128 characters or less`).regex(/^[\w+=,.@-]+$/,`Policy name contains invalid characters`),description:t().optional(),policyDocument:t().min(1,`Policy document is required`).refine(e=>{try{return JSON.parse(e),!0}catch{return!1}},{message:`Policy document must be valid JSON`})}),i=JSON.stringify({Version:`2012-10-17`,Statement:[{Effect:`Allow`,Principal:{Service:`ec2.amazonaws.com`},Action:`sts:AssumeRole`}]},null,2),a=e({roleName:t().min(1,`Role name is required`).max(64,`Role name must be 64 characters or less`).regex(/^[\w+=,.@-]+$/,`Role name contains invalid characters`),path:t().optional(),description:t().optional(),assumeRolePolicyDocument:t().min(1,`Trust policy is required`).refine(e=>{try{return JSON.parse(e),!0}catch{return!1}},{message:`Trust policy must be valid JSON`})}),o=e({instanceProfileName:t().min(1,`Instance profile name is required`).max(128,`Instance profile name must be 128 characters or less`).regex(/^[\w+=,.@-]+$/,`Instance profile name contains invalid characters`),path:t().optional()});export{n as a,a as i,o as n,r,i as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/import-key-pair.js b/spinifex/services/spinifexui/frontend/dist/assets/import-key-pair.js index 62364d25e..062c871d6 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/import-key-pair.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/import-key-pair.js @@ -1,3 +1,3 @@ -import{t as e}from"./error-banner.js";import{t}from"./back-link.js";import{t as n}from"./cli-command-panel.js";import{z as r}from"./ec2.js";import{Fo as i,Ho as a,Ro as o,Xi as s}from"./index.js";import{i as c,t as l}from"./zod.js";import{t as u}from"./form-actions.js";import{t as d}from"./page-heading.js";import{a as f,r as p,t as m}from"./field.js";import{g as h}from"./ec22.js";import{t as g}from"./textarea.js";var _=i(),v=a();function y(){let i=(0,_.c)(53),a=o(),y=r(),x;i[0]===Symbol.for(`react.memo_cache_sentinel`)?(x={resolver:l(h)},i[0]=x):x=i[0];let{handleSubmit:S,register:C,watch:w,formState:T}=c(x),{errors:E,isSubmitting:D}=T,O;i[1]!==y||i[2]!==a?(O=async e=>{await y.mutateAsync(e),a({to:`/ec2/describe-key-pairs`})},i[1]=y,i[2]=a,i[3]=O):O=i[3];let k=O,A,j;i[4]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,v.jsx)(t,{to:`/ec2/describe-key-pairs`,children:`Back to key pairs`}),j=(0,v.jsx)(d,{title:`Import Key Pair`}),i[4]=A,i[5]=j):(A=i[4],j=i[5]);let M;i[6]===y.error?M=i[7]:(M=y.error&&(0,v.jsx)(e,{error:y.error,msg:`Failed to import key pair`}),i[6]=y.error,i[7]=M);let N;i[8]!==S||i[9]!==k?(N=S(k),i[8]=S,i[9]=k,i[10]=N):N=i[10];let P;i[11]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,v.jsx)(f,{children:(0,v.jsx)(`label`,{htmlFor:`keyName`,children:`Key Pair Name`})}),i[11]=P):P=i[11];let F=!!E.keyName,I;i[12]===C?I=i[13]:(I=C(`keyName`),i[12]=C,i[13]=I);let L;i[14]!==F||i[15]!==I?(L=(0,v.jsx)(s,{"aria-invalid":F,id:`keyName`,placeholder:`my-key-pair…`,...I}),i[14]=F,i[15]=I,i[16]=L):L=i[16];let R;i[17]===E.keyName?R=i[18]:(R=(0,v.jsx)(p,{errors:[E.keyName]}),i[17]=E.keyName,i[18]=R);let z;i[19]!==L||i[20]!==R?(z=(0,v.jsxs)(m,{children:[P,L,R]}),i[19]=L,i[20]=R,i[21]=z):z=i[21];let B,V;i[22]===Symbol.for(`react.memo_cache_sentinel`)?(B=(0,v.jsx)(f,{children:(0,v.jsx)(`label`,{htmlFor:`publicKeyMaterial`,children:`Public Key`})}),V=(0,v.jsx)(`p`,{className:`text-xs text-muted-foreground`,id:`publicKey-description`,children:`Paste your OpenSSH public key (e.g. the contents of id_rsa.pub)`}),i[22]=B,i[23]=V):(B=i[22],V=i[23]);let H=!!E.publicKeyMaterial,U;i[24]===C?U=i[25]:(U=C(`publicKeyMaterial`),i[24]=C,i[25]=U);let W;i[26]!==H||i[27]!==U?(W=(0,v.jsx)(g,{"aria-invalid":H,id:`publicKeyMaterial`,placeholder:`ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC…`,rows:8,...U}),i[26]=H,i[27]=U,i[28]=W):W=i[28];let G;i[29]===E.publicKeyMaterial?G=i[30]:(G=(0,v.jsx)(p,{errors:[E.publicKeyMaterial]}),i[29]=E.publicKeyMaterial,i[30]=G);let K;i[31]!==W||i[32]!==G?(K=(0,v.jsxs)(m,{children:[B,V,W,G]}),i[31]=W,i[32]=G,i[33]=K):K=i[33];let q;i[34]===w?q=i[35]:(q=b(w),i[34]=w,i[35]=q);let J;i[36]===q?J=i[37]:(J=(0,v.jsx)(n,{commands:q}),i[36]=q,i[37]=J);let Y;i[38]===a?Y=i[39]:(Y=async()=>await a({to:`/ec2/describe-key-pairs`}),i[38]=a,i[39]=Y);let X;i[40]!==y.isPending||i[41]!==D||i[42]!==Y?(X=(0,v.jsx)(u,{isPending:y.isPending,isSubmitting:D,onCancel:Y,pendingLabel:`Importing…`,submitLabel:`Import Key Pair`}),i[40]=y.isPending,i[41]=D,i[42]=Y,i[43]=X):X=i[43];let Z;i[44]!==z||i[45]!==K||i[46]!==J||i[47]!==X||i[48]!==N?(Z=(0,v.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:N,children:[z,K,J,X]}),i[44]=z,i[45]=K,i[46]=J,i[47]=X,i[48]=N,i[49]=Z):Z=i[49];let Q;return i[50]!==Z||i[51]!==M?(Q=(0,v.jsxs)(v.Fragment,{children:[A,j,M,Z]}),i[50]=Z,i[51]=M,i[52]=Q):Q=i[52],Q}function b(e){let t=e(`keyName`);return[{label:`Import Key Pair`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 import-key-pair`},{type:`flag`,value:` \\ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as ee}from"./cli-command-panel.js";import{V as i}from"./ec2.js";import{Ir as te,Rn as a}from"./index.js";import{a as o,i as s,t as ne}from"./zod.js";import{t as re}from"./form-actions.js";import{t as c}from"./page-heading.js";import{a as l,r as u,t as d}from"./field.js";import{g as f}from"./ec22.js";import{t as p}from"./textarea.js";var m=t(),h=e();function g(){let e=(0,m.c)(57),t=te(),g=i(),v;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(v={resolver:ne(f)},e[0]=v):v=e[0];let{handleSubmit:y,register:b,control:x,formState:S}=s(v),{errors:C,isSubmitting:w}=S,T;e[1]===x?T=e[2]:(T={control:x},e[1]=x,e[2]=T);let E=o(T),D;e[3]===E?D=e[4]:(D=e=>e?E[e]:void 0,e[3]=E,e[4]=D);let O=D,k;e[5]!==g||e[6]!==t?(k=async e=>{await g.mutateAsync(e),t({to:`/ec2/describe-key-pairs`})},e[5]=g,e[6]=t,e[7]=k):k=e[7];let A=k,j,M;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,h.jsx)(r,{to:`/ec2/describe-key-pairs`,children:`Back to key pairs`}),M=(0,h.jsx)(c,{title:`Import Key Pair`}),e[8]=j,e[9]=M):(j=e[8],M=e[9]);let N;e[10]===g.error?N=e[11]:(N=g.error&&(0,h.jsx)(n,{error:g.error,msg:`Failed to import key pair`}),e[10]=g.error,e[11]=N);let P;e[12]!==y||e[13]!==A?(P=y(A),e[12]=y,e[13]=A,e[14]=P):P=e[14];let F;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(F=(0,h.jsx)(l,{children:(0,h.jsx)(`label`,{htmlFor:`keyName`,children:`Key Pair Name`})}),e[15]=F):F=e[15];let I=!!C.keyName,L;e[16]===b?L=e[17]:(L=b(`keyName`),e[16]=b,e[17]=L);let R;e[18]!==I||e[19]!==L?(R=(0,h.jsx)(a,{"aria-invalid":I,id:`keyName`,placeholder:`my-key-pair…`,...L}),e[18]=I,e[19]=L,e[20]=R):R=e[20];let z;e[21]===C.keyName?z=e[22]:(z=(0,h.jsx)(u,{errors:[C.keyName]}),e[21]=C.keyName,e[22]=z);let B;e[23]!==R||e[24]!==z?(B=(0,h.jsxs)(d,{children:[F,R,z]}),e[23]=R,e[24]=z,e[25]=B):B=e[25];let V,H;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(V=(0,h.jsx)(l,{children:(0,h.jsx)(`label`,{htmlFor:`publicKeyMaterial`,children:`Public Key`})}),H=(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,id:`publicKey-description`,children:`Paste your OpenSSH public key (e.g. the contents of id_rsa.pub)`}),e[26]=V,e[27]=H):(V=e[26],H=e[27]);let U=!!C.publicKeyMaterial,W;e[28]===b?W=e[29]:(W=b(`publicKeyMaterial`),e[28]=b,e[29]=W);let G;e[30]!==U||e[31]!==W?(G=(0,h.jsx)(p,{"aria-invalid":U,id:`publicKeyMaterial`,placeholder:`ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC…`,rows:8,...W}),e[30]=U,e[31]=W,e[32]=G):G=e[32];let K;e[33]===C.publicKeyMaterial?K=e[34]:(K=(0,h.jsx)(u,{errors:[C.publicKeyMaterial]}),e[33]=C.publicKeyMaterial,e[34]=K);let q;e[35]!==G||e[36]!==K?(q=(0,h.jsxs)(d,{children:[V,H,G,K]}),e[35]=G,e[36]=K,e[37]=q):q=e[37];let J;e[38]===O?J=e[39]:(J=_(O),e[38]=O,e[39]=J);let Y;e[40]===J?Y=e[41]:(Y=(0,h.jsx)(ee,{commands:J}),e[40]=J,e[41]=Y);let X;e[42]===t?X=e[43]:(X=async()=>await t({to:`/ec2/describe-key-pairs`}),e[42]=t,e[43]=X);let Z;e[44]!==g.isPending||e[45]!==w||e[46]!==X?(Z=(0,h.jsx)(re,{isPending:g.isPending,isSubmitting:w,onCancel:X,pendingLabel:`Importing…`,submitLabel:`Import Key Pair`}),e[44]=g.isPending,e[45]=w,e[46]=X,e[47]=Z):Z=e[47];let Q;e[48]!==B||e[49]!==q||e[50]!==Y||e[51]!==Z||e[52]!==P?(Q=(0,h.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:P,children:[B,q,Y,Z]}),e[48]=B,e[49]=q,e[50]=Y,e[51]=Z,e[52]=P,e[53]=Q):Q=e[53];let $;return e[54]!==Q||e[55]!==N?($=(0,h.jsxs)(h.Fragment,{children:[j,M,N,Q]}),e[54]=Q,e[55]=N,e[56]=$):$=e[56],$}function _(e){let t=e(`keyName`);return[{label:`Import Key Pair`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 import-key-pair`},{type:`flag`,value:` \\ --key-name`},{type:`value`,value:` ${(typeof t==`string`?t:``)||``}`},{type:`flag`,value:` \\ - --public-key-material`},{type:`value`,value:` fileb://path/to/key.pub`}]}]}export{y as component}; \ No newline at end of file + --public-key-material`},{type:`value`,value:` fileb://path/to/key.pub`}]}]}export{g as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/index.browser.js b/spinifex/services/spinifexui/frontend/dist/assets/index.browser.js new file mode 100644 index 000000000..0d31b7af8 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/index.browser.js @@ -0,0 +1,6 @@ +var e=e=>new TextEncoder().encode(e),t=typeof Buffer<`u`&&Buffer.from?function(e){return Buffer.from(e,`utf8`)}:e;function n(e){return e instanceof Uint8Array?e:typeof e==`string`?t(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}function r(e){return typeof e==`string`?e.length===0:e.byteLength===0}function i(e){return new Uint8Array([(e&4278190080)>>24,(e&16711680)>>16,(e&65280)>>8,e&255])}function a(e){if(!Uint32Array.from){for(var t=new Uint32Array(e.length),n=0;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw TypeError(t?`Object is not iterable.`:`Symbol.iterator is not defined.`)}var l=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`,u=Object.entries(l).reduce((e,[t,n])=>(e[n]=Number(t),e),{}),d=l.split(``),f=e=>{let t=e.length/4*3;e.slice(-2)===`==`?t-=2:e.slice(-1)===`=`&&t--;let n=new ArrayBuffer(t),r=new DataView(n);for(let t=0;t>=6;let a=t/4*3;n>>=i%8;let o=Math.floor(i/8);for(let e=0;e>t)}}return new Uint8Array(n)},p=e=>new TextEncoder().encode(e);function m(e){let t;t=typeof e==`string`?p(e):e;let n=typeof t==`object`&&typeof t.length==`number`,r=typeof t==`object`&&typeof t.byteOffset==`number`&&typeof t.byteLength==`number`;if(!n&&!r)throw Error(`@smithy/util-base64: toBase64 encoder function only accepts string | Uint8Array.`);let i=``;for(let e=0;e>t]}i+=`==`.slice(0,4-a)}return i}function ee(e,t,n,r){return class i extends Uint8Array{static fromString(e,n=`utf-8`){if(typeof e==`string`)return n===`base64`?i.mutate(r(e)):i.mutate(t(e));throw Error(`Unsupported conversion from ${typeof e} to Uint8ArrayBlobAdapter.`)}static mutate(e){return Object.setPrototypeOf(e,i.prototype),e}transformToString(t=`utf-8`){return t===`base64`?n(this):e(this)}}}var h=e=>{if(typeof e==`string`)return e;if(typeof e!=`object`||typeof e.byteOffset!=`number`||typeof e.byteLength!=`number`)throw Error(`@smithy/util-utf8: toUtf8 encoder function only accepts string | Uint8Array.`);return new TextDecoder(`utf-8`).decode(e)},g=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`));function _(e){return typeof crypto<`u`&&typeof crypto.randomUUID==`function`?()=>crypto.randomUUID():()=>{let t=new Uint8Array(16);return e(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128,g[t[0]]+g[t[1]]+g[t[2]]+g[t[3]]+`-`+g[t[4]]+g[t[5]]+`-`+g[t[6]]+g[t[7]]+`-`+g[t[8]]+g[t[9]]+`-`+g[t[10]]+g[t[11]]+g[t[12]]+g[t[13]]+g[t[14]]+g[t[15]]}}var v=e=>{if(e!=null){if(typeof e==`string`){let t=parseFloat(e);if(!Number.isNaN(t))return String(t)!==String(e)&&de.warn(ue(`Expected number but observed string: ${e}`)),t}if(typeof e==`number`)return e;throw TypeError(`Expected number, got ${typeof e}: ${e}`)}},te=Math.ceil(2**127*(2-2**-23)),ne=e=>{let t=v(e);if(t!==void 0&&!Number.isNaN(t)&&t!==1/0&&t!==-1/0&&Math.abs(t)>te)throw TypeError(`Expected 32-bit float, got ${e}`);return t},re=e=>{if(e!=null){if(Number.isInteger(e)&&!Number.isNaN(e))return e;throw TypeError(`Expected integer, got ${typeof e}: ${e}`)}},ie=e=>y(e,16),ae=e=>y(e,8),y=(e,t)=>{let n=re(e);if(n!==void 0&&oe(n,t)!==n)throw TypeError(`Expected ${t}-bit integer, got ${e}`);return n},oe=(e,t)=>{switch(t){case 32:return Int32Array.of(e)[0];case 16:return Int16Array.of(e)[0];case 8:return Int8Array.of(e)[0]}},se=e=>v(typeof e==`string`?x(e):e),ce=e=>ne(typeof e==`string`?x(e):e),b=/(-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)|(-?Infinity)|(NaN)/g,x=e=>{let t=e.match(b);if(t===null||t[0].length!==e.length)throw TypeError(`Expected real number, got implicit NaN`);return parseFloat(e)},S=e=>ie(typeof e==`string`?x(e):e),le=e=>ae(typeof e==`string`?x(e):e),ue=e=>String(TypeError(e).stack||e).split(` +`).slice(0,5).filter(e=>!e.includes(`stackTraceWarning`)).join(` +`),de={warn:console.warn},fe=[`Sun`,`Mon`,`Tue`,`Wed`,`Thu`,`Fri`,`Sat`],C=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`];function pe(e){let t=e.getUTCFullYear(),n=e.getUTCMonth(),r=e.getUTCDay(),i=e.getUTCDate(),a=e.getUTCHours(),o=e.getUTCMinutes(),s=e.getUTCSeconds(),c=i<10?`0${i}`:`${i}`,l=a<10?`0${a}`:`${a}`,u=o<10?`0${o}`:`${o}`,d=s<10?`0${s}`:`${s}`;return`${fe[r]}, ${c} ${C[n]} ${t} ${l}:${u}:${d} GMT`}var me=new RegExp(/^(\d{4})-(\d{2})-(\d{2})[tT](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(([-+]\d{2}\:\d{2})|[zZ])$/),he=e=>{if(e==null)return;if(typeof e!=`string`)throw TypeError(`RFC-3339 date-times must be expressed as strings`);let t=me.exec(e);if(!t)throw TypeError(`Invalid RFC-3339 date-time value`);let[n,r,i,a,o,s,c,l,u]=t,d=w(S(P(r)),M(i,`month`,1,12),M(a,`day`,1,31),{hours:o,minutes:s,seconds:c,fractionalMilliseconds:l});return u.toUpperCase()!=`Z`&&d.setTime(d.getTime()-xe(u)),d},ge=new RegExp(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d{2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? GMT$/),_e=new RegExp(/^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d{2})-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d{2}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? GMT$/),ve=new RegExp(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( [1-9]|\d{2}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? (\d{4})$/),ye=e=>{if(e==null)return;if(typeof e!=`string`)throw TypeError(`RFC-7231 date-times must be expressed as strings`);let t=ge.exec(e);if(t){let[e,n,r,i,a,o,s,c]=t;return w(S(P(i)),O(r),M(n,`day`,1,31),{hours:a,minutes:o,seconds:s,fractionalMilliseconds:c})}if(t=_e.exec(e),t){let[e,n,r,i,a,o,s,c]=t;return D(w(T(i),O(r),M(n,`day`,1,31),{hours:a,minutes:o,seconds:s,fractionalMilliseconds:c}))}if(t=ve.exec(e),t){let[e,n,r,i,a,o,s,c]=t;return w(S(P(c)),O(n),M(r.trimLeft(),`day`,1,31),{hours:i,minutes:a,seconds:o,fractionalMilliseconds:s})}throw TypeError(`Invalid RFC-7231 date-time value`)},be=e=>{if(e==null)return;let t;if(typeof e==`number`)t=e;else if(typeof e==`string`)t=se(e);else if(typeof e==`object`&&e.tag===1)t=e.value;else throw TypeError(`Epoch timestamps must be expressed as floating point numbers or their string representation`);if(Number.isNaN(t)||t===1/0||t===-1/0)throw TypeError(`Epoch timestamps must be valid, non-Infinite, non-NaN numerics`);return new Date(Math.round(t*1e3))},w=(e,t,n,r)=>{let i=t-1;return A(e,i,n),new Date(Date.UTC(e,i,n,M(r.hours,`hour`,0,23),M(r.minutes,`minute`,0,59),M(r.seconds,`seconds`,0,60),N(r.fractionalMilliseconds)))},T=e=>{let t=new Date().getUTCFullYear(),n=Math.floor(t/100)*100+S(P(e));return ne.getTime()-new Date().getTime()>E?new Date(Date.UTC(e.getUTCFullYear()-100,e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds())):e,O=e=>{let t=C.indexOf(e);if(t<0)throw TypeError(`Invalid month: ${e}`);return t+1},k=[31,28,31,30,31,30,31,31,30,31,30,31],A=(e,t,n)=>{let r=k[t];if(t===1&&j(e)&&(r=29),n>r)throw TypeError(`Invalid day for ${C[t]} in ${e}: ${n}`)},j=e=>e%4==0&&(e%100!=0||e%400==0),M=(e,t,n,r)=>{let i=le(P(e));if(ir)throw TypeError(`${t} must be between ${n} and ${r}, inclusive`);return i},N=e=>e==null?0:ce(`0.`+e)*1e3,xe=e=>{let t=e[0],n=1;if(t==`+`)n=1;else if(t==`-`)n=-1;else throw TypeError(`Offset direction, ${t}, must be "+" or "-"`);let r=Number(e.substring(1,3)),i=Number(e.substring(4,6));return n*(r*60+i)*60*1e3},P=e=>{let t=0;for(;te&&typeof e==`object`&&(e instanceof F||`deserializeJSON`in e)?e:typeof e==`string`||Object.getPrototypeOf(e)===String.prototype?F(String(e)):F(JSON.stringify(e)),F.fromObject=F.from;function Se(e){return(e.includes(`,`)||e.includes(`"`))&&(e=`"${e.replace(/"/g,`\\"`)}"`),e}var I=`(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)(?:[ne|u?r]?s?day)?`,L=`(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)`,R=`(\\d?\\d):(\\d{2}):(\\d{2})(?:\\.(\\d+))?`,z=`(\\d?\\d)`,B=`(\\d{4})`,Ce=new RegExp(/^(\d{4})-(\d\d)-(\d\d)[tT](\d\d):(\d\d):(\d\d)(\.(\d+))?(([-+]\d\d:\d\d)|[zZ])$/),we=RegExp(`^${I}, ${z} ${L} ${B} ${R} GMT$`),Te=RegExp(`^${I}, ${z}-${L}-(\\d\\d) ${R} GMT$`),Ee=RegExp(`^${I} ${L} ( [1-9]|\\d\\d) ${R} ${B}$`),V=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],De=e=>{if(e==null)return;let t=NaN;if(typeof e==`number`)t=e;else if(typeof e==`string`){if(!/^-?\d*\.?\d+$/.test(e))throw TypeError(`parseEpochTimestamp - numeric string invalid.`);t=Number.parseFloat(e)}else typeof e==`object`&&e.tag===1&&(t=e.value);if(isNaN(t)||Math.abs(t)===1/0)throw TypeError(`Epoch timestamps must be valid finite numbers.`);return new Date(Math.round(t*1e3))},Oe=e=>{if(e==null)return;if(typeof e!=`string`)throw TypeError(`RFC3339 timestamps must be strings`);let t=Ce.exec(e);if(!t)throw TypeError(`Invalid RFC3339 timestamp format ${e}`);let[,n,r,i,a,o,s,,c,l]=t;H(r,1,12),H(i,1,31),H(a,0,23),H(o,0,59),H(s,0,60);let u=new Date(Date.UTC(Number(n),Number(r)-1,Number(i),Number(a),Number(o),Number(s),Number(c)?Math.round(parseFloat(`0.${c}`)*1e3):0));if(u.setUTCFullYear(Number(n)),l.toUpperCase()!=`Z`){let[,e,t,n]=/([+-])(\d\d):(\d\d)/.exec(l)||[void 0,`+`,0,0],r=e===`-`?1:-1;u.setTime(u.getTime()+r*(Number(t)*60*60*1e3+Number(n)*60*1e3))}return u},ke=e=>{if(e==null)return;if(typeof e!=`string`)throw TypeError(`RFC7231 timestamps must be strings.`);let t,n,r,i,a,o,s,c;if((c=we.exec(e))?[,t,n,r,i,a,o,s]=c:(c=Te.exec(e))?([,t,n,r,i,a,o,s]=c,r=(Number(r)+1900).toString()):(c=Ee.exec(e))&&([,n,t,i,a,o,s,r]=c),r&&o){let e=Date.UTC(Number(r),V.indexOf(n),Number(t),Number(i),Number(a),Number(o),s?Math.round(parseFloat(`0.${s}`)*1e3):0);H(t,1,31),H(i,0,23),H(a,0,59),H(o,0,60);let c=new Date(e);return c.setUTCFullYear(Number(r)),c}throw TypeError(`Invalid RFC7231 date-time value ${e}.`)};function H(e,t,n){let r=Number(e);if(rn)throw Error(`Value ${r} out of range [${t}, ${n}]`)}function Ae(e,t,n){if(n<=0||!Number.isInteger(n))throw Error(`Invalid number of delimiters (`+n+`) for splitEvery.`);let r=e.split(t);if(n===1)return r;let i=[],a=``;for(let e=0;e{let t=e.length,n=[],r=!1,i,a=0;for(let o=0;o{e=e.trim();let t=e.length;return t<2?e:(e[0]===`"`&&e[t-1]===`"`&&(e=e.slice(1,t-1)),e.replace(/\\"/g,`"`))})},U=/^-?\d*(\.\d+)?$/,Me=class e{string;type;constructor(e,t){if(this.string=e,this.type=t,!U.test(e))throw Error(`@smithy/core/serde - NumericValue must only contain [0-9], at most one decimal point ".", and an optional negation prefix "-".`)}toString(){return this.string}static[Symbol.hasInstance](t){if(!t||typeof t!=`object`)return!1;let n=t;return e.prototype.isPrototypeOf(t)||n.type===`bigDecimal`&&U.test(n.string)}},W={},G={};for(let e=0;e<256;e++){let t=e.toString(16).toLowerCase();t.length===1&&(t=`0${t}`),W[e]=t,G[t]=e}function Ne(e){if(e.length%2!=0)throw Error(`Hex encoded strings must have an even number length`);let t=new Uint8Array(e.length/2);for(let n=0;n{if(typeof e==`string`){if(q)return q.encode(e).byteLength;let t=e.length;for(let n=t-1;n>=0;n--){let r=e.charCodeAt(n);r>127&&r<=2047?t++:r>2047&&r<=65535&&(t+=2),r>=56320&&r<=57343&&n--}return t}else if(typeof e.byteLength==`number`)return e.byteLength;else if(typeof e.size==`number`)return e.size;throw Error(`Body Length computation failed for ${e}`)},Fe=e=>typeof e==`string`?p(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e),Ie=e=>typeof ArrayBuffer==`function`&&e instanceof ArrayBuffer||Object.prototype.toString.call(e)===`[object ArrayBuffer]`,Le=typeof ReadableStream==`function`?ReadableStream:function(){},Re=class extends Le{},J=e=>typeof ReadableStream==`function`&&(e?.constructor?.name===ReadableStream.name||e instanceof ReadableStream),ze=({expectedChecksum:e,checksum:t,source:n,checksumSourceLocation:r,base64Encoder:i})=>{if(!J(n))throw Error(`@smithy/util-stream: unsupported source type ${n?.constructor?.name??n} in ChecksumStream.`);let a=i??m;if(typeof TransformStream!=`function`)throw Error(`@smithy/util-stream: unable to instantiate ChecksumStream because API unavailable: ReadableStream/TransformStream.`);let o=new TransformStream({start(){},async transform(e,n){t.update(e),n.enqueue(e)},async flush(n){let i=a(await t.digest());if(e!==i){let t=Error(`Checksum mismatch: expected "${e}" but received "${i}" in response header "${r}".`);n.error(t)}else n.terminate()}});n.pipeThrough(o);let s=o.readable;return Object.setPrototypeOf(s,Re.prototype),s},Be=class{allocByteArray;byteLength=0;byteArrays=[];constructor(e){this.allocByteArray=e}push(e){this.byteArrays.push(e),this.byteLength+=e.byteLength}flush(){if(this.byteArrays.length===1){let e=this.byteArrays[0];return this.reset(),e}let e=this.allocByteArray(this.byteLength),t=0;for(let n=0;nnew Uint8Array(e))],s=-1,c=async e=>{let{value:l,done:u}=await r.read(),d=l;if(u){if(s!==-1){let t=Y(o,s);X(t)>0&&e.enqueue(t)}e.close()}else{let r=We(d,!1);if(s!==r&&(s>=0&&e.enqueue(Y(o,s)),s=r),s===-1){e.enqueue(d);return}let l=X(d);a+=l;let u=X(o[s]);if(l>=t&&u===0)e.enqueue(d);else{let r=Ue(o,s,d);!i&&a>t*2&&(i=!0,n?.warn(`@smithy/util-stream - stream chunk size ${l} is below threshold of ${t}, automatically buffering.`)),r>=t?e.enqueue(Y(o,s)):await c(e)}}};return new ReadableStream({pull:c})}var He=Ve;function Ue(e,t,n){switch(t){case 0:return e[0]+=n,X(e[0]);case 1:case 2:return e[t].push(n),X(e[t])}}function Y(e,t){switch(t){case 0:let n=e[0];return e[0]=``,n;case 1:case 2:return e[t].flush()}throw Error(`@smithy/util-stream - invalid index ${t} given to flush()`)}function X(e){return e?.byteLength??e?.length??0}function We(e,t=!0){return t&&typeof Buffer<`u`&&e instanceof Buffer?2:e instanceof Uint8Array?1:typeof e==`string`?0:-1}var Ge=(e,t)=>{let{base64Encoder:n,bodyLengthChecker:r,checksumAlgorithmFn:i,checksumLocationName:a,streamHasher:o}=t,s=n!==void 0&&r!==void 0&&i!==void 0&&a!==void 0&&o!==void 0,c=s?o(i,e):void 0,l=e.getReader();return new ReadableStream({async pull(e){let{value:t,done:i}=await l.read();if(i){if(e.enqueue(`0\r +`),s){let t=n(await c);e.enqueue(`${a}:${t}\r\n`),e.enqueue(`\r +`)}e.close()}else e.enqueue(`${(r(t)||0).toString(16)}\r\n${t}\r\n`)}})},Ke=async e=>typeof Blob==`function`&&e instanceof Blob||e.constructor?.name===`Blob`?Blob.prototype.arrayBuffer===void 0?qe(e):new Uint8Array(await e.arrayBuffer()):Je(e);async function qe(e){let t=f(await Ye(e));return new Uint8Array(t)}async function Je(e){let t=[],n=e.getReader(),r=!1,i=0;for(;!r;){let{done:e,value:a}=await n.read();a&&(t.push(a),i+=a.length),r=e}let a=new Uint8Array(i),o=0;for(let e of t)a.set(e,o),o+=e.length;return a}function Ye(e){return new Promise((t,n)=>{let r=new FileReader;r.onloadend=()=>{if(r.readyState!==2)return n(Error(`Reader aborted too early`));let e=r.result??``,i=e.indexOf(`,`),a=i>-1?i+1:e.length;t(e.substring(a))},r.onabort=()=>n(Error(`Read aborted`)),r.onerror=()=>n(r.error),r.readAsDataURL(e)})}var Z=`The stream has already been transformed.`,Xe=e=>{if(!Q(e)&&!J(e)){let t=e?.__proto__?.constructor?.name||e;throw Error(`Unexpected stream implementation, expect Blob or ReadableStream, got ${t}`)}let t=!1,n=async()=>{if(t)throw Error(Z);return t=!0,await Ke(e)},r=e=>{if(typeof e.stream!=`function`)throw Error(`Cannot transform payload Blob to web stream. Please make sure the Blob.stream() is polyfilled. +If you are using React Native, this API is not yet supported, see: https://react-native.canny.io/feature-requests/p/fetch-streaming-body`);return e.stream()};return Object.assign(e,{transformToByteArray:n,transformToString:async e=>{let t=await n();if(e===`base64`)return m(t);if(e===`hex`)return K(t);if(e===void 0||e===`utf8`||e===`utf-8`)return h(t);if(typeof TextDecoder==`function`)return new TextDecoder(e).decode(t);throw Error(`TextDecoder is not available, please make sure polyfill is provided.`)},transformToWebStream:()=>{if(t)throw Error(Z);if(t=!0,Q(e))return r(e);if(J(e))return e;throw Error(`Cannot transform payload to web stream, got ${e}`)}})},Q=e=>typeof Blob==`function`&&e instanceof Blob,Ze=class extends ee(h,p,m,f){},$=_(e=>crypto.getRandomValues(e)),Qe=$;export{s as A,he as C,p as D,m as E,n as F,e as I,a as M,i as N,f as O,r as P,be as S,h as T,Oe as _,Ge as a,F as b,Ie as c,Ne as d,K as f,De as g,Ae as h,Xe as i,c as j,o as k,Fe as l,je as m,Qe as n,He as o,Me as p,$ as r,ze as s,Ze as t,Pe as u,ke as v,ye as w,pe as x,Se as y}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/index.css b/spinifex/services/spinifexui/frontend/dist/assets/index.css index 9e3b15d43..687571047 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/index.css +++ b/spinifex/services/spinifexui/frontend/dist/assets/index.css @@ -1,2 +1,2 @@ /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-100:#ffe2e2;--color-red-800:#9f0712;--color-red-900:#82181a;--color-amber-500:#f99c00;--color-yellow-100:#fef9c2;--color-yellow-800:#874b00;--color-yellow-900:#733e0a;--color-green-100:#dcfce7;--color-green-600:#00a544;--color-green-800:#016630;--color-green-900:#0d542b;--color-emerald-400:#00d294;--color-emerald-500:#00bb7f;--color-emerald-700:#007956;--color-blue-500:#3080ff;--color-gray-100:#f3f4f6;--color-gray-800:#1e2939;--color-zinc-100:#f4f4f5;--color-zinc-400:#9f9fa9;--color-zinc-500:#71717b;--color-zinc-800:#27272a;--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-2xl:42rem;--container-4xl:56rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--tracking-widest:.1em;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:calc(var(--radius) - 2px);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-xs:4px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}@supports (color:lab(0% 0 0)){:root,:host{--color-red-100:lab(92.243% 10.2865 3.83865);--color-red-800:lab(33.7174% 55.8993 41.0293);--color-red-900:lab(28.5139% 44.5539 29.0463);--color-amber-500:lab(72.7183% 31.8672 97.9407);--color-yellow-100:lab(97.3564% -4.51407 27.344);--color-yellow-800:lab(38.7484% 23.5833 51.4916);--color-yellow-900:lab(32.3865% 21.1273 38.5959);--color-green-100:lab(96.1861% -13.8464 6.52365);--color-green-600:lab(59.0978% -58.6621 41.2579);--color-green-800:lab(37.4616% -36.7971 22.9692);--color-green-900:lab(30.797% -29.6927 17.382);--color-emerald-400:lab(75.0771% -60.7313 19.4147);--color-emerald-500:lab(66.9756% -58.27 19.5419);--color-emerald-700:lab(44.4871% -41.0396 11.0361);--color-blue-500:lab(54.1736% 13.3369 -74.6839);--color-gray-100:lab(96.1596% -.0823438 -1.13575);--color-gray-800:lab(16.1051% -1.18239 -11.7533);--color-zinc-100:lab(96.1634% .0993311 -.364041);--color-zinc-400:lab(65.6464% 1.53497 -5.42429);--color-zinc-500:lab(47.8878% 1.65477 -5.77283);--color-zinc-800:lab(15.7305% .613764 -2.16959)}}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{overscroll-behavior:none;border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){*{outline-color:color-mix(in oklab, var(--ring) 50%, transparent)}}*{scrollbar-width:thin}body{background-color:var(--background);width:100%;min-height:100svh;color:var(--foreground);font-family:var(--font-sans), sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.font-mono{font-family:var(--font-mono), monospace}button:not(:disabled),[role=button]:not(:disabled){cursor:pointer}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.\@container\/field-group{container:field-group/inline-size}.\@container{container-type:inline-size}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.not-sr-only{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.top-0{top:calc(var(--spacing) * 0)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-1\/2{top:50%}.top-3\.5{top:calc(var(--spacing) * 3.5)}.top-4{top:calc(var(--spacing) * 4)}.right-0{right:calc(var(--spacing) * 0)}.right-1{right:calc(var(--spacing) * 1)}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.isolate{isolation:isolate}.isolation-auto{isolation:auto}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.order-first{order:-9999}.order-last{order:9999}.col-span-2{grid-column:span 2/span 2}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-0{margin-inline:calc(var(--spacing) * 0)}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mx-3\.5{margin-inline:calc(var(--spacing) * 3.5)}.mx-auto{margin-inline:auto}.-my-2{margin-block:calc(var(--spacing) * -2)}.my-1{margin-block:calc(var(--spacing) * 1)}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-auto{margin-top:auto}.mr-2{margin-right:calc(var(--spacing) * 2)}.mr-auto{margin-right:auto}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.inline-table{display:inline-table}.list-item{display:list-item}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row{display:table-row}.table-row-group{display:table-row-group}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-3\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-12{width:calc(var(--spacing) * 12);height:calc(var(--spacing) * 12)}.size-\[1\.2rem\]{width:1.2rem;height:1.2rem}.size-full{width:100%;height:100%}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-\[calc\(--spacing\(4\.75\)\)\]{height:calc(calc(var(--spacing) * 4.75))}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-svh{height:100svh}.max-h-\(--available-height\){max-height:var(--available-height)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[min\(calc\(--spacing\(72\)---spacing\(9\)\)\,calc\(var\(--available-height\)---spacing\(9\)\)\)\]{max-height:min(calc(calc(var(--spacing) * 72) - calc(var(--spacing) * 9)), calc(var(--available-height) - calc(var(--spacing) * 9)))}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-7{min-height:calc(var(--spacing) * 7)}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-svh{min-height:100svh}.w-\(--anchor-width\){width:var(--anchor-width)}.w-\(--sidebar-width\){width:var(--sidebar-width)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-24{width:calc(var(--spacing) * 24)}.w-32{width:calc(var(--spacing) * 32)}.w-36{width:calc(var(--spacing) * 36)}.w-48{width:calc(var(--spacing) * 48)}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\(--available-width\){max-width:var(--available-width)}.max-w-\(--skeleton-width\){max-width:var(--skeleton-width)}.max-w-2xl{max-width:var(--container-2xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-5{min-width:calc(var(--spacing) * 5)}.min-w-16{min-width:calc(var(--spacing) * 16)}.min-w-32{min-width:calc(var(--spacing) * 32)}.min-w-\[calc\(var\(--anchor-width\)\+--spacing\(7\)\)\]{min-width:calc(var(--anchor-width) + calc(var(--spacing) * 7))}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.origin-\(--transform-origin\){transform-origin:var(--transform-origin)}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-px{--tw-translate-x:1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-\[calc\(-50\%-2px\)\]{--tw-translate-y:calc(-50% - 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-none{translate:none}.scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-3d{scale:var(--tw-scale-x) var(--tw-scale-y) var(--tw-scale-z)}.rotate-0{rotate:0deg}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.touch-pinch-zoom{--tw-pinch-zoom:pinch-zoom;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.resize{resize:both}.resize-none{resize:none}.scroll-my-1{scroll-margin-block:calc(var(--spacing) * 1)}.scroll-py-1{scroll-padding-block:calc(var(--spacing) * 1)}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1fr)}.grid-rows-\[auto_1fr\]{grid-template-rows:auto 1fr}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0{gap:calc(var(--spacing) * 0)}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-8{gap:calc(var(--spacing) * 8)}.gap-px{gap:1px}:where(.space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-reverse>:not(:last-child)){--tw-space-y-reverse:1}:where(.-space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * -2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * -2) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-reverse>:not(:last-child)){--tw-space-x-reverse:1}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-reverse>:not(:last-child)){--tw-divide-y-reverse:1}.self-end{align-self:flex-end}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded{border-radius:.25rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[calc\(var\(--radius-sm\)\+2px\)\]{border-radius:calc(var(--radius-sm) + 2px)}.rounded-\[calc\(var\(--radius-sm\)-2px\)\]{border-radius:calc(var(--radius-sm) - 2px)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:calc(var(--radius) + 2px)}.rounded-md{border-radius:var(--radius)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 2px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-s{border-start-start-radius:.25rem;border-end-start-radius:.25rem}.rounded-ss{border-start-start-radius:.25rem}.rounded-e{border-start-end-radius:.25rem;border-end-end-radius:.25rem}.rounded-se{border-start-end-radius:.25rem}.rounded-ee{border-end-end-radius:.25rem}.rounded-es{border-end-start-radius:.25rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-lg{border-top-left-radius:calc(var(--radius) + 2px);border-top-right-radius:calc(var(--radius) + 2px)}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-tl{border-top-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-tr{border-top-right-radius:.25rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-b-lg{border-bottom-right-radius:calc(var(--radius) + 2px);border-bottom-left-radius:calc(var(--radius) + 2px)}.rounded-br{border-bottom-right-radius:.25rem}.rounded-bl{border-bottom-left-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-s{border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}.border-e{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}.border-bs{border-block-start-style:var(--tw-border-style);border-block-start-width:1px}.border-be{border-block-end-style:var(--tw-border-style);border-block-end-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab, red, red)){.border-amber-500\/30{border-color:color-mix(in oklab, var(--color-amber-500) 30%, transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab, red, red)){.border-blue-500\/30{border-color:color-mix(in oklab, var(--color-blue-500) 30%, transparent)}}.border-border{border-color:var(--border)}.border-destructive{border-color:var(--destructive)}.border-emerald-500{border-color:var(--color-emerald-500)}.border-emerald-500\/30{border-color:#00bb7f4d}@supports (color:color-mix(in lab, red, red)){.border-emerald-500\/30{border-color:color-mix(in oklab, var(--color-emerald-500) 30%, transparent)}}.border-foreground\/10{border-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.border-foreground\/10{border-color:color-mix(in oklab, var(--foreground) 10%, transparent)}}.border-foreground\/20{border-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.border-foreground\/20{border-color:color-mix(in oklab, var(--foreground) 20%, transparent)}}.border-input{border-color:var(--input)}.border-muted{border-color:var(--muted)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-transparent{border-color:#0000}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/10{background-color:color-mix(in oklab, var(--color-amber-500) 10%, transparent)}}.bg-background{background-color:var(--background)}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab, red, red)){.bg-black\/80{background-color:color-mix(in oklab, var(--color-black) 80%, transparent)}}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab, red, red)){.bg-blue-500\/10{background-color:color-mix(in oklab, var(--color-blue-500) 10%, transparent)}}.bg-border,.bg-border\/50{background-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.bg-border\/50{background-color:color-mix(in oklab, var(--border) 50%, transparent)}}.bg-card{background-color:var(--card)}.bg-destructive\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.bg-destructive\/10{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab, red, red)){.bg-emerald-500\/10{background-color:color-mix(in oklab, var(--color-emerald-500) 10%, transparent)}}.bg-foreground{background-color:var(--foreground)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-green-100{background-color:var(--color-green-100)}.bg-input\/20{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.bg-input\/20{background-color:color-mix(in oklab, var(--input) 20%, transparent)}}.bg-muted{background-color:var(--muted)}.bg-muted-foreground\/10{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.bg-muted-foreground\/10{background-color:color-mix(in oklab, var(--muted-foreground) 10%, transparent)}}.bg-muted\/20{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.bg-muted\/20{background-color:color-mix(in oklab, var(--muted) 20%, transparent)}}.bg-muted\/30{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.bg-muted\/30{background-color:color-mix(in oklab, var(--muted) 30%, transparent)}}.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-red-100{background-color:var(--color-red-100)}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-transparent{background-color:#0000}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-clip-padding{background-clip:padding-box}.bg-repeat{background-repeat:repeat}.mask-no-clip{-webkit-mask-clip:no-clip;mask-clip:no-clip}.mask-repeat{-webkit-mask-repeat:repeat;mask-repeat:repeat}.fill-foreground{fill:var(--foreground)}.object-cover{object-fit:cover}.p-0{padding:calc(var(--spacing) * 0)}.p-1{padding:calc(var(--spacing) * 1)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-0{padding-inline:calc(var(--spacing) * 0)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.pt-0{padding-top:calc(var(--spacing) * 0)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-0{padding-bottom:calc(var(--spacing) * 0)}.pb-0\.5{padding-bottom:calc(var(--spacing) * .5)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-sm\/relaxed{font-size:var(--text-sm);line-height:var(--leading-relaxed)}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-xs\/relaxed{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.text-\[0\.625rem\]{font-size:.625rem}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-normal{--tw-leading:var(--leading-normal);line-height:var(--leading-normal)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-balance{text-wrap:balance}.text-wrap{text-wrap:wrap}.break-all{word-break:break-all}.text-clip{text-overflow:clip}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-500{color:var(--color-amber-500)}.text-background{color:var(--background)}.text-card-foreground{color:var(--card-foreground)}.text-destructive{color:var(--destructive)}.text-emerald-700{color:var(--color-emerald-700)}.text-foreground{color:var(--foreground)}.text-gray-800{color:var(--color-gray-800)}.text-green-600{color:var(--color-green-600)}.text-green-800{color:var(--color-green-800)}.text-muted-foreground{color:var(--muted-foreground)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-red-800{color:var(--color-red-800)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground,.text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab, red, red)){.text-sidebar-foreground\/70{color:color-mix(in oklab, var(--sidebar-foreground) 70%, transparent)}}.text-tactical-amber{color:var(--tactical-amber)}.text-tactical-cyan{color:var(--tactical-cyan)}.text-tactical-green{color:var(--tactical-green)}.text-yellow-800{color:var(--color-yellow-800)}.text-zinc-500{color:var(--color-zinc-500)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.not-italic{font-style:normal}.diagonal-fractions{--tw-numeric-fraction:diagonal-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.lining-nums{--tw-numeric-figure:lining-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.oldstyle-nums{--tw-numeric-figure:oldstyle-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.proportional-nums{--tw-numeric-spacing:proportional-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.slashed-zero{--tw-slashed-zero:slashed-zero;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.stacked-fractions{--tw-numeric-fraction:stacked-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.normal-nums{font-variant-numeric:normal}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subpixel-antialiased{-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.opacity-50{opacity:.5}.bg-blend-color{background-blend-mode:color}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-border)));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.inset-ring{--tw-inset-ring-shadow:inset 0 0 0 1px var(--tw-inset-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-background{--tw-ring-color:var(--background)}.ring-foreground\/10{--tw-ring-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ring-foreground\/10{--tw-ring-color:color-mix(in oklab, var(--foreground) 10%, transparent)}}.ring-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#0000001a)) drop-shadow(0 1px 1px var(--tw-drop-shadow-color,#0000000f));--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-grayscale{--tw-backdrop-grayscale:grayscale(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert{--tw-backdrop-invert:invert(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia{--tw-backdrop-sepia:sepia(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-linear{--tw-ease:linear;transition-timing-function:linear}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}:where(.divide-x-reverse>:not(:last-child)){--tw-divide-x-reverse:1}.ring-inset{--tw-ring-inset:inset}.group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *){opacity:1}@media (hover:hover){.group-hover\/menu-item\:opacity-100:is(:where(.group\/menu-item):hover *){opacity:1}}.group-focus\/dropdown-menu-item\:text-accent-foreground:is(:where(.group\/dropdown-menu-item):focus *){color:var(--accent-foreground)}.group-has-data-\[orientation\=horizontal\]\/field\:text-balance:is(:where(.group\/field):has([data-orientation=horizontal]) *){text-wrap:balance}.group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--spacing) * 8)}.group-has-data-\[size\=lg\]\/avatar-group\:size-10:is(:where(.group\/avatar-group):has([data-size=lg]) *){width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.group-has-data-\[size\=sm\]\/avatar-group\:size-6:is(:where(.group\/avatar-group):has([data-size=sm]) *){width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.group-has-data-\[slot\=combobox-clear\]\/input-group\:hidden:is(:where(.group\/input-group):has([data-slot=combobox-clear]) *){display:none}.group-has-\[\>input\]\/input-group\:pt-2:is(:where(.group\/input-group):has(>input) *){padding-top:calc(var(--spacing) * 2)}.group-has-\[\>input\]\/input-group\:pb-2:is(:where(.group\/input-group):has(>input) *){padding-bottom:calc(var(--spacing) * 2)}.group-data-empty\/combobox-content\:flex:is(:where(.group\/combobox-content)[data-empty] *){display:flex}.group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.group)[data-collapsible=icon] *){margin-top:calc(var(--spacing) * -8)}.group-data-\[collapsible\=icon\]\:hidden:is(:where(.group)[data-collapsible=icon] *){display:none}.group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--spacing) * 8)!important;height:calc(var(--spacing) * 8)!important}.group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4)))}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4)) + 2px)}.group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.group)[data-collapsible=icon] *){overflow:hidden}.group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing) * 0)!important}.group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing) * 2)!important}.group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.group)[data-collapsible=icon] *){opacity:0}.group-data-\[collapsible\=offExamples\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offExamples] *){right:calc(var(--sidebar-width) * -1)}.group-data-\[collapsible\=offExamples\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offExamples] *){left:calc(var(--sidebar-width) * -1)}.group-data-\[collapsible\=offExamples\]\:w-0:is(:where(.group)[data-collapsible=offExamples] *){width:calc(var(--spacing) * 0)}.group-data-\[collapsible\=offExamples\]\:translate-x-0:is(:where(.group)[data-collapsible=offExamples] *){--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *),.group-data-\[disabled\=true\]\/field\:opacity-50:is(:where(.group\/field)[data-disabled=true] *),.group-data-\[disabled\=true\]\/input-group\:opacity-50:is(:where(.group\/input-group)[data-disabled=true] *){opacity:.5}.group-data-\[panel-open\]\:rotate-180:is(:where(.group)[data-panel-open] *){rotate:180deg}.group-data-\[side\=left\]\:-right-4:is(:where(.group)[data-side=left] *){right:calc(var(--spacing) * -4)}.group-data-\[side\=left\]\:border-r:is(:where(.group)[data-side=left] *){border-right-style:var(--tw-border-style);border-right-width:1px}.group-data-\[side\=right\]\:left-0:is(:where(.group)[data-side=right] *){left:calc(var(--spacing) * 0)}.group-data-\[side\=right\]\:rotate-180:is(:where(.group)[data-side=right] *){rotate:180deg}.group-data-\[side\=right\]\:border-l:is(:where(.group)[data-side=right] *){border-left-style:var(--tw-border-style);border-left-width:1px}.group-data-\[size\=default\]\/avatar\:size-2\.5:is(:where(.group\/avatar)[data-size=default] *){width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.group-data-\[size\=lg\]\/avatar\:size-3:is(:where(.group\/avatar)[data-size=lg] *){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.group-data-\[size\=sm\]\/alert-dialog-content\:grid:is(:where(.group\/alert-dialog-content)[data-size=sm] *){display:grid}.group-data-\[size\=sm\]\/alert-dialog-content\:grid-cols-2:is(:where(.group\/alert-dialog-content)[data-size=sm] *){grid-template-columns:repeat(2,minmax(0,1fr))}.group-data-\[size\=sm\]\/avatar\:size-2:is(:where(.group\/avatar)[data-size=sm] *){width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.group-data-\[size\=sm\]\/avatar\:text-xs:is(:where(.group\/avatar)[data-size=sm] *){font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.group-data-\[size\=sm\]\/card\:px-3:is(:where(.group\/card)[data-size=sm] *){padding-inline:calc(var(--spacing) * 3)}.group-data-\[variant\=floating\]\:rounded-lg:is(:where(.group)[data-variant=floating] *){border-radius:calc(var(--radius) + 2px)}.group-data-\[variant\=floating\]\:shadow-sm:is(:where(.group)[data-variant=floating] *){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.group-data-\[variant\=floating\]\:ring-1:is(:where(.group)[data-variant=floating] *){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.group-data-\[variant\=floating\]\:ring-sidebar-border:is(:where(.group)[data-variant=floating] *){--tw-ring-color:var(--sidebar-border)}.group-data-\[variant\=outline\]\/field-group\:-mb-2:is(:where(.group\/field-group)[data-variant=outline] *){margin-bottom:calc(var(--spacing) * -2)}@media (hover:hover){.peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.peer-data-active\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button)[data-active]~*){color:var(--sidebar-accent-foreground)}.peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.peer\/menu-button)[data-size=default]~*){top:calc(var(--spacing) * 1.5)}.peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.peer\/menu-button)[data-size=lg]~*){top:calc(var(--spacing) * 2.5)}.peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.peer\/menu-button)[data-size=sm]~*){top:calc(var(--spacing) * 1)}.file\:inline-flex::file-selector-button{display:inline-flex}.file\:h-6::file-selector-button{height:calc(var(--spacing) * 6)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-xs\/relaxed::file-selector-button{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--spacing) * -2)}.after\:inset-0:after{content:var(--tw-content);inset:calc(var(--spacing) * 0)}.after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--spacing) * 0)}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.after\:rounded-full:after{content:var(--tw-content);border-radius:3.40282e38px}.after\:border:after{content:var(--tw-content);border-style:var(--tw-border-style);border-width:1px}.after\:border-border:after{content:var(--tw-content);border-color:var(--border)}.after\:mix-blend-darken:after{content:var(--tw-content);mix-blend-mode:darken}.group-data-\[collapsible\=offExamples\]\:after\:left-full:is(:where(.group)[data-collapsible=offExamples] *):after{content:var(--tw-content);left:100%}.last\:mt-0:last-child{margin-top:calc(var(--spacing) * 0)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.focus-within\:border-ring:focus-within{border-color:var(--ring)}.focus-within\:ring-2:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-within\:ring-ring\/30:focus-within{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.focus-within\:ring-ring\/30:focus-within{--tw-ring-color:color-mix(in oklab, var(--ring) 30%, transparent)}}@media (hover:hover){.hover\:bg-accent:hover{background-color:var(--accent)}.hover\:bg-destructive\/20:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/20:hover{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.hover\:bg-input\/40:hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-input\/40:hover{background-color:color-mix(in oklab, var(--input) 40%, transparent)}}.hover\:bg-input\/50:hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-input\/50:hover{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:bg-primary\/80:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/80:hover{background-color:color-mix(in oklab, var(--primary) 80%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}.hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-muted-foreground:hover{color:var(--muted-foreground)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-accent)));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:group-data-\[collapsible\=offExamples\]\:bg-sidebar:hover:is(:where(.group)[data-collapsible=offExamples] *){background-color:var(--sidebar)}.hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:text-accent-foreground:focus,:is(.focus\:\*\*\:text-accent-foreground:focus *),:is(.not-data-\[variant\=destructive\]\:focus\:\*\*\:text-accent-foreground:not([data-variant=destructive]):focus *){color:var(--accent-foreground)}.focus-visible\:border-destructive\/40:focus-visible{border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:border-destructive\/40:focus-visible{border-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-0:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-2:focus-visible,.focus-visible\:ring-\[2px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.focus-visible\:ring-ring\/30:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-ring\/30:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 30%, transparent)}}.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}:where([data-side=left]) .in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}:where([data-slot=combobox-content]) .in-data-\[slot\=combobox-content\]\:focus-within\:border-inherit:focus-within{border-color:inherit}:where([data-slot=combobox-content]) .in-data-\[slot\=combobox-content\]\:focus-within\:ring-0:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-disabled\:pointer-events-none:has(:disabled){pointer-events:none}.has-disabled\:cursor-not-allowed:has(:disabled){cursor:not-allowed}.has-disabled\:opacity-50:has(:disabled){opacity:.5}.has-aria-invalid\:border-destructive:has([aria-invalid=true]){border-color:var(--destructive)}.has-aria-invalid\:ring-2:has([aria-invalid=true]){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-aria-invalid\:ring-destructive\/20:has([aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.has-aria-invalid\:ring-destructive\/20:has([aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.has-data-checked\:bg-primary\/5:has([data-checked]){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.has-data-checked\:bg-primary\/5:has([data-checked]){background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.has-data-\[align\=block-end\]\:rounded-md:has([data-align=block-end]),.has-data-\[align\=block-start\]\:rounded-md:has([data-align=block-start]){border-radius:var(--radius)}.has-data-\[icon\=inline-end\]\:pr-1\.5:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 1.5)}.has-data-\[icon\=inline-end\]\:pr-2:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 2)}.has-data-\[icon\=inline-start\]\:pl-1\.5:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 1.5)}.has-data-\[icon\=inline-start\]\:pl-2:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 2)}.has-data-\[slot\=alert-dialog-media\]\:grid-rows-\[auto_auto_1fr\]:has([data-slot=alert-dialog-media]){grid-template-rows:auto auto 1fr}.has-data-\[slot\=alert-dialog-media\]\:gap-x-4:has([data-slot=alert-dialog-media]){column-gap:calc(var(--spacing) * 4)}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\[slot\=card-description\]\:grid-rows-\[auto_auto\]:has([data-slot=card-description]){grid-template-rows:auto auto}.has-data-\[slot\=combobox-chip\]\:px-1:has([data-slot=combobox-chip]){padding-inline:calc(var(--spacing) * 1)}.has-data-\[slot\=combobox-chip-remove\]\:pr-0:has([data-slot=combobox-chip-remove]){padding-right:calc(var(--spacing) * 0)}.has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:border-ring:has([data-slot=input-group-control]:focus-visible){border-color:var(--ring)}.has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-2:has([data-slot=input-group-control]:focus-visible){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/30:has([data-slot=input-group-control]:focus-visible){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/30:has([data-slot=input-group-control]:focus-visible){--tw-ring-color:color-mix(in oklab, var(--ring) 30%, transparent)}}.has-\[\[data-slot\]\[aria-invalid\=true\]\]\:border-destructive:has([data-slot][aria-invalid=true]){border-color:var(--destructive)}.has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-2:has([data-slot][aria-invalid=true]){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has([data-slot][aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has([data-slot][aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.has-\[textarea\]\:rounded-md:has(:is(textarea)){border-radius:var(--radius)}.has-\[\>\[data-align\=block-end\]\]\:h-auto:has(>[data-align=block-end]){height:auto}.has-\[\>\[data-align\=block-end\]\]\:flex-col:has(>[data-align=block-end]){flex-direction:column}.has-\[\>\[data-align\=block-start\]\]\:h-auto:has(>[data-align=block-start]){height:auto}.has-\[\>\[data-align\=block-start\]\]\:flex-col:has(>[data-align=block-start]){flex-direction:column}.has-\[\>\[data-slot\=checkbox-group\]\]\:gap-3:has(>[data-slot=checkbox-group]){gap:calc(var(--spacing) * 3)}.has-\[\>\[data-slot\=field-content\]\]\:items-start:has(>[data-slot=field-content]){align-items:flex-start}.has-\[\>\[data-slot\=field\]\]\:w-full:has(>[data-slot=field]){width:100%}.has-\[\>\[data-slot\=field\]\]\:flex-col:has(>[data-slot=field]){flex-direction:column}.has-\[\>\[data-slot\=field\]\]\:rounded-md:has(>[data-slot=field]){border-radius:var(--radius)}.has-\[\>\[data-slot\=field\]\]\:border:has(>[data-slot=field]){border-style:var(--tw-border-style);border-width:1px}.has-\[\>\[data-slot\=radio-group\]\]\:gap-3:has(>[data-slot=radio-group]){gap:calc(var(--spacing) * 3)}.has-\[\>button\]\:mr-\[-0\.275rem\]:has(>button){margin-right:-.275rem}.has-\[\>button\]\:ml-\[-0\.275rem\]:has(>button){margin-left:-.275rem}.has-\[\>img\:first-child\]\:pt-0:has(>img:first-child){padding-top:calc(var(--spacing) * 0)}.has-\[\>kbd\]\:mr-\[-0\.275rem\]:has(>kbd){margin-right:-.275rem}.has-\[\>kbd\]\:ml-\[-0\.275rem\]:has(>kbd){margin-left:-.275rem}.has-\[\>svg\]\:p-0:has(>svg){padding:calc(var(--spacing) * 0)}.has-\[\>textarea\]\:h-auto:has(>textarea){height:auto}.aria-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.aria-expanded\:bg-muted[aria-expanded=true]{background-color:var(--muted)}.aria-expanded\:bg-secondary[aria-expanded=true]{background-color:var(--secondary)}.aria-expanded\:text-foreground[aria-expanded=true]{color:var(--foreground)}.aria-expanded\:text-secondary-foreground[aria-expanded=true]{color:var(--secondary-foreground)}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-0[aria-invalid=true]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.aria-invalid\:ring-2[aria-invalid=true],.aria-invalid\:ring-\[2px\][aria-invalid=true]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.data-active\:bg-sidebar-accent[data-active]{background-color:var(--sidebar-accent)}.data-active\:font-medium[data-active]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.data-active\:text-sidebar-accent-foreground[data-active]{color:var(--sidebar-accent-foreground)}.data-closed\:overflow-hidden[data-closed]{overflow:hidden}.data-disabled\:pointer-events-none[data-disabled]{pointer-events:none}.data-disabled\:opacity-50[data-disabled]{opacity:.5}.data-empty\:p-0[data-empty]{padding:calc(var(--spacing) * 0)}.data-ending-style\:opacity-0[data-ending-style]{opacity:0}.data-highlighted\:bg-accent[data-highlighted]{background-color:var(--accent)}.data-highlighted\:text-accent-foreground[data-highlighted],:is(.not-data-\[variant\=destructive\]\:data-highlighted\:\*\*\:text-accent-foreground:not([data-variant=destructive])[data-highlighted] *){color:var(--accent-foreground)}.data-inset\:pl-8[data-inset]{padding-left:calc(var(--spacing) * 8)}.data-open\:bg-accent[data-open]{background-color:var(--accent)}.data-open\:text-accent-foreground[data-open]{color:var(--accent-foreground)}.data-open\:opacity-100[data-open]{opacity:1}@media (hover:hover){.data-open\:hover\:bg-sidebar-accent[data-open]:hover{background-color:var(--sidebar-accent)}.data-open\:hover\:text-sidebar-accent-foreground[data-open]:hover{color:var(--sidebar-accent-foreground)}}.data-placeholder\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-pressed\:bg-transparent[data-pressed]{background-color:#0000}.data-selected\:border-primary[data-selected]{border-color:var(--primary)}.data-selected\:text-foreground[data-selected]{color:var(--foreground)}.data-starting-style\:opacity-0[data-starting-style]{opacity:0}.data-\[chips\=true\]\:min-w-\(--anchor-width\)[data-chips=true]{min-width:var(--anchor-width)}.data-\[invalid\=true\]\:text-destructive[data-invalid=true]{color:var(--destructive)}.data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.data-\[orientation\=vertical\]\:self-stretch[data-orientation=vertical]{align-self:stretch}.data-\[side\=bottom\]\:inset-x-0[data-side=bottom]{inset-inline:calc(var(--spacing) * 0)}.data-\[side\=bottom\]\:top-1[data-side=bottom]{top:calc(var(--spacing) * 1)}.data-\[side\=bottom\]\:bottom-0[data-side=bottom]{bottom:calc(var(--spacing) * 0)}.data-\[side\=bottom\]\:h-auto[data-side=bottom]{height:auto}.data-\[side\=bottom\]\:border-t[data-side=bottom]{border-top-style:var(--tw-border-style);border-top-width:1px}.data-\[side\=left\]\:inset-y-0[data-side=left]{inset-block:calc(var(--spacing) * 0)}.data-\[side\=left\]\:top-1\/2\![data-side=left]{top:50%!important}.data-\[side\=left\]\:-right-1[data-side=left]{right:calc(var(--spacing) * -1)}.data-\[side\=left\]\:left-0[data-side=left]{left:calc(var(--spacing) * 0)}.data-\[side\=left\]\:h-full[data-side=left]{height:100%}.data-\[side\=left\]\:w-3\/4[data-side=left]{width:75%}.data-\[side\=left\]\:-translate-y-1\/2[data-side=left]{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=left\]\:border-r[data-side=left]{border-right-style:var(--tw-border-style);border-right-width:1px}.data-\[side\=right\]\:inset-y-0[data-side=right]{inset-block:calc(var(--spacing) * 0)}.data-\[side\=right\]\:top-1\/2\![data-side=right]{top:50%!important}.data-\[side\=right\]\:right-0[data-side=right]{right:calc(var(--spacing) * 0)}.data-\[side\=right\]\:-left-1[data-side=right]{left:calc(var(--spacing) * -1)}.data-\[side\=right\]\:h-full[data-side=right]{height:100%}.data-\[side\=right\]\:w-3\/4[data-side=right]{width:75%}.data-\[side\=right\]\:-translate-y-1\/2[data-side=right]{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=right\]\:border-l[data-side=right]{border-left-style:var(--tw-border-style);border-left-width:1px}.data-\[side\=top\]\:inset-x-0[data-side=top]{inset-inline:calc(var(--spacing) * 0)}.data-\[side\=top\]\:top-0[data-side=top]{top:calc(var(--spacing) * 0)}.data-\[side\=top\]\:-bottom-2\.5[data-side=top]{bottom:calc(var(--spacing) * -2.5)}.data-\[side\=top\]\:h-auto[data-side=top]{height:auto}.data-\[side\=top\]\:border-b[data-side=top]{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.data-\[size\=default\]\:h-7[data-size=default]{height:calc(var(--spacing) * 7)}.data-\[size\=default\]\:max-w-xs[data-size=default]{max-width:var(--container-xs)}.data-\[size\=lg\]\:size-10[data-size=lg]{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.data-\[size\=md\]\:text-xs[data-size=md]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.data-\[size\=sm\]\:size-6[data-size=sm]{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.data-\[size\=sm\]\:h-6[data-size=sm]{height:calc(var(--spacing) * 6)}.data-\[size\=sm\]\:max-w-64[data-size=sm]{max-width:calc(var(--spacing) * 64)}.data-\[size\=sm\]\:gap-3[data-size=sm]{gap:calc(var(--spacing) * 3)}.data-\[size\=sm\]\:py-3[data-size=sm]{padding-block:calc(var(--spacing) * 3)}.data-\[size\=sm\]\:text-xs[data-size=sm]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}:is(.\*\:data-\[slot\=avatar\]\:ring-2>*)[data-slot=avatar]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:is(.\*\:data-\[slot\=avatar\]\:ring-background>*)[data-slot=avatar]{--tw-ring-color:var(--background)}.data-\[slot\=checkbox-group\]\:gap-3[data-slot=checkbox-group]{gap:calc(var(--spacing) * 3)}:is(.\*\:data-\[slot\=field\]\:p-2>*)[data-slot=field]{padding:calc(var(--spacing) * 2)}:is(.\*\:data-\[slot\=field-group\]\:gap-4>*)[data-slot=field-group]{gap:calc(var(--spacing) * 4)}:is(.\*\:data-\[slot\=input-group\]\:m-1>*)[data-slot=input-group]{margin:calc(var(--spacing) * 1)}:is(.\*\:data-\[slot\=input-group\]\:mb-0>*)[data-slot=input-group]{margin-bottom:calc(var(--spacing) * 0)}:is(.\*\:data-\[slot\=input-group\]\:h-7>*)[data-slot=input-group]{height:calc(var(--spacing) * 7)}:is(.\*\:data-\[slot\=input-group\]\:border-none>*)[data-slot=input-group]{--tw-border-style:none;border-style:none}:is(.\*\:data-\[slot\=input-group\]\:bg-input\/20>*)[data-slot=input-group]{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){:is(.\*\:data-\[slot\=input-group\]\:bg-input\/20>*)[data-slot=input-group]{background-color:color-mix(in oklab, var(--input) 20%, transparent)}}:is(.\*\:data-\[slot\=input-group\]\:shadow-none>*)[data-slot=input-group]{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:is(.\*\*\:data-\[slot\=kbd\]\:rounded-\[calc\(var\(--radius-sm\)-2px\)\] *)[data-slot=kbd]{border-radius:calc(var(--radius-sm) - 2px)}:is(.\*\*\:data-\[slot\=kbd\]\:rounded-md *)[data-slot=kbd]{border-radius:var(--radius)}:is(.\*\*\:data-\[slot\=kbd\]\:bg-muted-foreground\/10 *)[data-slot=kbd]{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){:is(.\*\*\:data-\[slot\=kbd\]\:bg-muted-foreground\/10 *)[data-slot=kbd]{background-color:color-mix(in oklab, var(--muted-foreground) 10%, transparent)}}:is(.\*\*\:data-\[slot\=kbd\]\:px-1 *)[data-slot=kbd]{padding-inline:calc(var(--spacing) * 1)}:is(.\*\*\:data-\[slot\=kbd\]\:text-\[0\.625rem\] *)[data-slot=kbd]{font-size:.625rem}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-1\.5>*)[data-slot=select-value]{gap:calc(var(--spacing) * 1.5)}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}.data-\[variant\=label\]\:text-xs\/relaxed[data-variant=label]{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.data-\[variant\=legend\]\:text-sm[data-variant=legend]{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.nth-last-2\:-mt-1:nth-last-child(2){margin-top:calc(var(--spacing) * -1)}@supports ((-webkit-backdrop-filter:var(--tw)) or (backdrop-filter:var(--tw))){.supports-backdrop-filter\:backdrop-blur-xs{--tw-backdrop-blur:blur(var(--blur-xs));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}}@media (width>=40rem){.sm\:flex{display:flex}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-\[1fr_1fr_1fr_auto\]{grid-template-columns:1fr 1fr 1fr auto}.sm\:grid-cols-\[1fr_auto\]{grid-template-columns:1fr auto}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:row-span-2:is(:where(.group\/alert-dialog-content)[data-size=default] *){grid-row:span 2/span 2}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:place-items-start:is(:where(.group\/alert-dialog-content)[data-size=default] *){place-items:start}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:text-left:is(:where(.group\/alert-dialog-content)[data-size=default] *){text-align:left}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:group-has-data-\[slot\=alert-dialog-media\]\/alert-dialog-content\:col-start-2:is(:where(.group\/alert-dialog-content)[data-size=default] *):is(:where(.group\/alert-dialog-content):has([data-slot=alert-dialog-media]) *){grid-column-start:2}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:has-data-\[slot\=alert-dialog-media\]\:grid-rows-\[auto_1fr\]:is(:where(.group\/alert-dialog-content)[data-size=default] *):has([data-slot=alert-dialog-media]){grid-template-rows:auto 1fr}.data-\[side\=left\]\:sm\:max-w-sm[data-side=left],.data-\[side\=right\]\:sm\:max-w-sm[data-side=right],.data-\[size\=default\]\:sm\:max-w-sm[data-size=default]{max-width:var(--container-sm)}}@media (width>=48rem){.md\:block{display:block}.md\:flex{display:flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:text-xs\/relaxed{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.md\:text-pretty{text-wrap:pretty}.md\:opacity-0{opacity:0}.md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.peer)[data-variant=inset]~*){margin:calc(var(--spacing) * 2)}.md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.peer)[data-variant=inset]~*){margin-left:calc(var(--spacing) * 0)}.md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.peer)[data-variant=inset]~*){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.peer)[data-variant=inset]~*):is(:where(.peer)[data-state=collapsed]~*){margin-left:calc(var(--spacing) * 2)}.md\:after\:hidden:after{content:var(--tw-content);display:none}}@media (width>=64rem){.lg\:block{display:block}}@media (width>=80rem){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@container field-group (width>=28rem){.\@md\/field-group\:flex-row{flex-direction:row}.\@md\/field-group\:items-center{align-items:center}.\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:items-start:has(>[data-slot=field-content]){align-items:flex-start}}.dark\:scale-0:where(.dark,.dark *){--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.dark\:scale-100:where(.dark,.dark *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.dark\:-rotate-90:where(.dark,.dark *){rotate:-90deg}.dark\:rotate-0:where(.dark,.dark *){rotate:0deg}.dark\:bg-destructive\/20:where(.dark,.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:bg-destructive\/20:where(.dark,.dark *){background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.dark\:bg-gray-800:where(.dark,.dark *){background-color:var(--color-gray-800)}.dark\:bg-green-900:where(.dark,.dark *){background-color:var(--color-green-900)}.dark\:bg-input\/30:where(.dark,.dark *){background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\:bg-input\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.dark\:bg-muted\/30:where(.dark,.dark *){background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.dark\:bg-muted\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--muted) 30%, transparent)}}.dark\:bg-popover:where(.dark,.dark *){background-color:var(--popover)}.dark\:bg-red-900:where(.dark,.dark *){background-color:var(--color-red-900)}.dark\:bg-transparent:where(.dark,.dark *){background-color:#0000}.dark\:bg-yellow-900:where(.dark,.dark *){background-color:var(--color-yellow-900)}.dark\:bg-zinc-800:where(.dark,.dark *){background-color:var(--color-zinc-800)}.dark\:text-emerald-400:where(.dark,.dark *){color:var(--color-emerald-400)}.dark\:text-gray-100:where(.dark,.dark *){color:var(--color-gray-100)}.dark\:text-green-100:where(.dark,.dark *){color:var(--color-green-100)}.dark\:text-red-100:where(.dark,.dark *){color:var(--color-red-100)}.dark\:text-yellow-100:where(.dark,.dark *){color:var(--color-yellow-100)}.dark\:text-zinc-400:where(.dark,.dark *){color:var(--color-zinc-400)}.dark\:invert:where(.dark,.dark *){--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.dark\:after\:mix-blend-lighten:where(.dark,.dark *):after{content:var(--tw-content);mix-blend-mode:lighten}@media (hover:hover){.dark\:hover\:bg-destructive\/30:where(.dark,.dark *):hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-destructive\/30:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--destructive) 30%, transparent)}}.dark\:hover\:bg-input\/50:where(.dark,.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-input\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}.dark\:hover\:bg-muted\/50:where(.dark,.dark *):hover{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-muted\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}}.dark\:focus-visible\:ring-destructive\/40:where(.dark,.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:focus-visible\:ring-destructive\/40:where(.dark,.dark *):focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:has-aria-invalid\:border-destructive\/50:where(.dark,.dark *):has([aria-invalid=true]){border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:has-aria-invalid\:border-destructive\/50:where(.dark,.dark *):has([aria-invalid=true]){border-color:color-mix(in oklab, var(--destructive) 50%, transparent)}}.dark\:has-aria-invalid\:ring-destructive\/40:where(.dark,.dark *):has([aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:has-aria-invalid\:ring-destructive\/40:where(.dark,.dark *):has([aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:has-data-checked\:bg-primary\/10:where(.dark,.dark *):has([data-checked]){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.dark\:has-data-checked\:bg-primary\/10:where(.dark,.dark *):has([data-checked]){background-color:color-mix(in oklab, var(--primary) 10%, transparent)}}.dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:where(.dark,.dark *):has([data-slot][aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:where(.dark,.dark *):has([data-slot][aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:aria-invalid\:border-destructive\/50:where(.dark,.dark *)[aria-invalid=true]{border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:aria-invalid\:border-destructive\/50:where(.dark,.dark *)[aria-invalid=true]{border-color:color-mix(in oklab, var(--destructive) 50%, transparent)}}.dark\:aria-invalid\:ring-destructive\/40:where(.dark,.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:aria-invalid\:ring-destructive\/40:where(.dark,.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:where(.dark,.dark *)[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:where(.dark,.dark *)[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-2\.5 svg:not([class*=size-]){width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 svg:not([class*=size-]){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3\.5 svg:not([class*=size-]){width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\.border-b\]\:pb-2.border-b{padding-bottom:calc(var(--spacing) * 2)}.\[\.border-b\]\:pb-4.border-b{padding-bottom:calc(var(--spacing) * 4)}.group-data-\[size\=sm\]\/card\:\[\.border-b\]\:pb-3:is(:where(.group\/card)[data-size=sm] *).border-b{padding-bottom:calc(var(--spacing) * 3)}.\[\.border-t\]\:pt-2.border-t{padding-top:calc(var(--spacing) * 2)}.\[\.border-t\]\:pt-4.border-t{padding-top:calc(var(--spacing) * 4)}.group-data-\[size\=sm\]\/card\:\[\.border-t\]\:pt-3:is(:where(.group\/card)[data-size=sm] *).border-t{padding-top:calc(var(--spacing) * 3)}:is(.\*\:\[a\]\:underline>*):is(a){text-decoration-line:underline}:is(.\*\:\[a\]\:underline-offset-3>*):is(a){text-underline-offset:3px}@media (hover:hover){.\[a\]\:hover\:bg-destructive\/20:is(a):hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-destructive\/20:is(a):hover{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.\[a\]\:hover\:bg-muted:is(a):hover{background-color:var(--muted)}.\[a\]\:hover\:bg-primary\/80:is(a):hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-primary\/80:is(a):hover{background-color:color-mix(in oklab, var(--primary) 80%, transparent)}}.\[a\]\:hover\:bg-secondary\/80:is(a):hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-secondary\/80:is(a):hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}.\[a\]\:hover\:text-muted-foreground:is(a):hover{color:var(--muted-foreground)}:is(.\*\:\[a\]\:hover\:text-foreground>*):is(a):hover{color:var(--foreground)}}:is(.\*\:\[img\:first-child\]\:rounded-t-lg>*):is(img:first-child){border-top-left-radius:calc(var(--radius) + 2px);border-top-right-radius:calc(var(--radius) + 2px)}:is(.\*\:\[img\:last-child\]\:rounded-b-lg>*):is(img:last-child){border-bottom-right-radius:calc(var(--radius) + 2px);border-bottom-left-radius:calc(var(--radius) + 2px)}:is(.\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--spacing) * 2)}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)}:is(.\*\:\[svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4>*):is(svg:not([class*=size-])){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&\>\*\]\:w-full>*{width:100%}@container field-group (width>=28rem){.\@md\/field-group\:\[\&\>\*\]\:w-auto>*{width:auto}}.\[\&\>\.sr-only\]\:w-auto>.sr-only{width:auto}.\[\&\>\[data-slot\=field-label\]\]\:flex-auto>[data-slot=field-label]{flex:auto}@container field-group (width>=28rem){.\@md\/field-group\:\[\&\>\[data-slot\=field-label\]\]\:flex-auto>[data-slot=field-label]{flex:auto}}.has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content])>[role=checkbox],.has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content]) [role=radio]{margin-top:1px}@container field-group (width>=28rem){.\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content])>[role=checkbox],.\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content]) [role=radio]{margin-top:1px}}.\[\&\>a\]\:underline>a{text-decoration-line:underline}.\[\&\>a\]\:underline-offset-4>a{text-underline-offset:4px}.\[\&\>a\:hover\]\:text-primary>a:hover{color:var(--primary)}.\[\&\>button\]\:hidden>button{display:none}.has-\[\>\[data-align\=block-end\]\]\:\[\&\>input\]\:pt-3:has(>[data-align=block-end])>input{padding-top:calc(var(--spacing) * 3)}.has-\[\>\[data-align\=block-start\]\]\:\[\&\>input\]\:pb-3:has(>[data-align=block-start])>input{padding-bottom:calc(var(--spacing) * 3)}.has-\[\>\[data-align\=inline-end\]\]\:\[\&\>input\]\:pr-1\.5:has(>[data-align=inline-end])>input{padding-right:calc(var(--spacing) * 1.5)}.has-\[\>\[data-align\=inline-start\]\]\:\[\&\>input\]\:pl-1\.5:has(>[data-align=inline-start])>input{padding-left:calc(var(--spacing) * 1.5)}.\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-2\.5\!>svg{width:calc(var(--spacing) * 2.5)!important;height:calc(var(--spacing) * 2.5)!important}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.group-has-data-\[size\=lg\]\/avatar-group\:\[\&\>svg\]\:size-5:is(:where(.group\/avatar-group):has([data-size=lg]) *)>svg{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.group-has-data-\[size\=sm\]\/avatar-group\:\[\&\>svg\]\:size-3:is(:where(.group\/avatar-group):has([data-size=sm]) *)>svg{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.group-data-\[size\=default\]\/avatar\:\[\&\>svg\]\:size-2:is(:where(.group\/avatar)[data-size=default] *)>svg,.group-data-\[size\=lg\]\/avatar\:\[\&\>svg\]\:size-2:is(:where(.group\/avatar)[data-size=lg] *)>svg{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.group-data-\[size\=sm\]\/avatar\:\[\&\>svg\]\:hidden:is(:where(.group\/avatar)[data-size=sm] *)>svg{display:none}.\[\&\>svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3>svg:not([class*=size-]){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.\[\&\>svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3\.5>svg:not([class*=size-]){width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}[data-side=left][data-collapsible=offExamples] .\[\[data-side\=left\]\[data-collapsible\=offExamples\]_\&\]\:-right-2{right:calc(var(--spacing) * -2)}[data-side=left][data-state=collapsed] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offExamples] .\[\[data-side\=right\]\[data-collapsible\=offExamples\]_\&\]\:-left-2{left:calc(var(--spacing) * -2)}[data-side=right][data-state=collapsed] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}[data-variant=legend]+.\[\[data-variant\=legend\]\+\&\]\:-mt-1\.5{margin-top:calc(var(--spacing) * -1.5)}}@font-face{font-family:Geist Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/geist-cyrillic-wght-normal.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Geist Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/geist-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Geist Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/geist-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(data:font/woff2;base64,d09GMgABAAAAAAfsABQAAAAAEAwAAAeCAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhwbHhwoP0hWQVJbBmA/U1RBVIFiJyYAdC9qEQgKhGSEAAsgADCGCAE2AiQDOgQgBYlMB4EUDAcbLQ4onoexrSC/2ZyLAa8p8VHB8/x3Vue+V0hVJalMJg2nx/TCrQXxBeqLjQG7FyM1WEa/X1tEXN7cFz9EJEMmMUz3RihWSSKeQCbcIou0izz/C8v+fq3VfajEa9gDD11CImXS7qL/RJFVzC1qiB6KmKeD6TZdQ6IRGv78dL6uSVVCfgni5mzu7kcgQBgAEAQTQRCoL++STTYybkJxNfQxAAIAGu8OdEB9teW2jh4BpgDqFjAeSEByW3zFP0CBBgNMsMCGEDjgggdhiEAUAeIIED7ABTDUEnkIE9Q9ahFgKttcVhApo4ACB4qobHaccgDfEjFO6aaWUhjMLt2SyIvHKoDqoA4CSUwEIYQCEjhAO9R1G6keDeDZGjNo+AhxOjCEGTr1WeIF3kYBiLAOKvkJSMiKX0VdAyQt3SDJClCkxJCHkCzfqyVTriJZLcolS32JZHUekq2TYNkYtCtjYHMQXSxGjXDz2t/yLWXzDzxz+o3zFwDEaN23F+13pyMdQAEaSKAR9vcGq4A4MTSKCElGW+M7UcY7xqkggITb28ZJhlqc9q2twYKTt0NjixBgYvO9BIihEBLYuOFXQzfIQ7dXGUEEEgFDooBfAzqiQbpJrhiWSuKJCRFKYbHCyJKI2G5GiZbNAvgAu5pc3vwx4G+g3aDkhklABiSz0BICXrYghtYhx/cdJ+44rY2oZ0aMNRFz3VZjb6W33F3gzltqtOCV8tTHSpOeXuItfvr5lCdfzFpqtEitvqdcdGGFd28ZqqC0tPbeChGXgrIlnhSWu/eUso4uKWFLugyDzQJhflY4659+WjQ++6x72WUMv9G8mw6QJl7BVxX5fe/kpUsOvnZwee9uQ0cGXYd0o89XB2748sDSnt8d2VphdOTTgceDVvOds0v9P/s7HPq15aGun/6Vllb56f1dl0t1LejqrNkpdRZsG8TOnM5vkBG5oiVyVGnS8LHps5cfNWJs6qKPfaNSxiQNBUm3cKNWROr0GSur7Za31k1vieq7LH11VF+jXdRIasRKflc7jkobm1Z9te1IyZA0pDkhLR98+H37Zf1c/8at+dB7x+7GfVyTfJMPiYztsnl59Y5l4j+0n1RXlpHnF3Tq7HecmNF/CJodEMAikruxiyJaGLvHOdAfoA+oDvpjBm2b91cHGRZMU9n25xEU0A8fgEEAdKI3Q1iDtc034sug5YVMkE2jsE+BIkwSoQ3gxXMqz9tELp48bd0cFKOKS7xYjEuXBnZP5ia7DyiO/X/YI+PQSbt2uSdqAkWL9nQbV1XB94/+uPfdZz8dnXYFBYrcTl2SIR/ybxJNJPz/Gupb0JaZeens2ekC7EKr8t+Ls/P5VJPYJdHKyqfg2nqU6bhlidzcddQV/7MmecTzJ5VPcKXkNKSEogHjYFx6QZ7rQ+FSe8njaiNuOnXS8H2ScQ619c2mC3VTtauL0rRbXd/CkSOP37FY9Zkjz8+GibYUMOEWF+RdrFS8Ecv1SHOpPUPZGEIpjPvFyU5cXKjd6OXqorTqy9GwRd++HVufPGnVsW+aO3vggKZ18jR9sXaTC1PWTEsVUaK0FkNySbTQDqlm2PfDjZcu4aalnSLKjnOoYQ0nUlqqXcGpPu/4VgV/xU2pAqW4BW3qzhQ8/hFKhV2qE3+BKAtDqBXjfgnVdH4y0wg5tbVNRenNdTWOrenWLcupQdmsbq5b+18piTe/xRdp1xbILxNPJGInm2z6hoB21Lal0i+ePTtd7B45+3XhFJ329evskXm7qurUVREotqSluSo/L29d3qDhI4YOQqWhI4YNvBNfsMHeXKemXrxQfKeuPOGRVayA3JtkJKEgbPp+dXUDluddutRYLFoXGXWX6N3WFaGLbQtRSitVYNacTNSdy7AaG/HSaUEANcBoGXNdcZvZsOqQ1icBDv21/gzAoYPHH/WDW0qNR3QTYKEAEHig6o13NXbND06CQPlRtYjGNnSktRc09k1mAMDvAlDKfQjgy6fssInlfzmNAjKkDxoxHOBLdVRAIVt9j4qo+hA1w9T1aNBNTUOTTNUHLbqokE+UAfJXCIGw/IxCSL5GRUJeR40rL/UxTm4Q08H6MbCs70ObuNyIIXrINHQYInF06UUlevTjbQzTh5upiDMzMMogUtEnjPs/Y7jAHCJeB0GBHh04tC6FiB6ZFB1oArUSIoFoqhzCeAN6lHwm0T4C3VVPWvjpSMXReuWesMEcoqrmgtNBGd2noWeV0hNAz9rFeShNJxHGsPa3HXeKTk8b55hahySYHaYKKFFLpCfN8rsoaJn01CR04Gkc+5k7KVTCmClX8Q10HCrUEkVlSX+XO33oQR9609tJ516H497WSobWs5Up6TLaS10/dessIskgJSLiDlWvHVUywpkQ7hdPZqGyiEF0uVQerVcPamT1A3eKXdyI1vG9OoflrSXihZ1qqGE3nhmAgiIbRCQgPLEPtOM3UQwTLYaYYomNlpA44opnjV6jkD6id80OOrzf6BzmMD6eEa1zKyeYG1fzfEf16V6jw9XYOaar1/b2kP/IYX8oR2mcFvv2GtBV3JXgd437AQAA)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-cyrillic-wght-normal.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-greek-wght-normal.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-vietnamese-wght-normal.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--font-sans:"Geist Variable", sans-serif;--font-mono:"JetBrains Mono Variable", monospace;--background:#f4f1ec;--foreground:#1d2930;--card:#f9f8f6;--card-foreground:#1d2930;--popover:#fbfaf9;--popover-foreground:#1d2930;--primary:#305030;--primary-foreground:#f5f3ef;--secondary:#eae5d7;--secondary-foreground:#263740;--muted:#e9e7e2;--muted-foreground:#576b75;--accent:#e7e2da;--accent-foreground:#1d2930;--destructive:#c32222;--destructive-foreground:#fff;--border:#b6c9b6;--input:#dedbd3;--ring:#3e743e;--chart-1:#2e6b2e;--chart-2:#cfa117;--chart-3:#297aa3;--chart-4:#cc8c33;--chart-5:#b82e2e;--radius:.25rem;--sidebar:#f5f3f0;--sidebar-background:#f5f3f0;--sidebar-foreground:#263740;--sidebar-primary:#305030;--sidebar-primary-foreground:#f5f3ef;--sidebar-accent:#e7e2da;--sidebar-accent-foreground:#1d2930;--sidebar-border:#c4d4c4;--sidebar-ring:#3e743e;--tactical-amber:#c2940a;--tactical-amber-dim:#d6b85c;--tactical-green:#267326;--tactical-green-dim:#7ab87a;--tactical-cyan:#24868f;--tactical-cyan-dim:#5eb2ba;--tactical-red:#bd2828;--tactical-olive:#64783a;--scanline-opacity:.015}.dark{--background:#0d1012;--foreground:#e0ddd1;--card:#14181a;--card-foreground:#e0ddd1;--popover:#111517;--popover-foreground:#e0ddd1;--primary:#fa0;--primary-foreground:#0d1012;--secondary:#323922;--secondary-foreground:#c6d2ac;--muted:#202527;--muted-foreground:#819098;--accent:#292f32;--accent-foreground:#ebe8e0;--destructive:#c32222;--destructive-foreground:#fff;--border:#203c32;--input:#292f32;--ring:#ffbf00;--chart-1:#ffbf00;--chart-2:#22c38e;--chart-3:#1aa1e6;--chart-4:#86ac39;--chart-5:#d92626;--sidebar:#111517;--sidebar-background:#111517;--sidebar-foreground:#e0ddd1;--sidebar-primary:#ffbf00;--sidebar-primary-foreground:#0d1012;--sidebar-accent:#292f32;--sidebar-accent-foreground:#ebe8e0;--sidebar-border:#203c32;--sidebar-ring:#ffbf00;--tactical-amber:#ffbf00;--tactical-amber-dim:#a17d12;--tactical-green:#22c38e;--tactical-green-dim:#20604a;--tactical-cyan:#17bfcf;--tactical-cyan-dim:#196066;--tactical-red:#d92626;--tactical-olive:#576b2e;--scanline-opacity:.03}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-pan-x{syntax:"*";inherits:false}@property --tw-pan-y{syntax:"*";inherits:false}@property --tw-pinch-zoom{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes pulse{50%{opacity:.5}} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-100:#ffe2e2;--color-red-800:#9f0712;--color-red-900:#82181a;--color-amber-400:#fcbb00;--color-amber-500:#f99c00;--color-amber-700:#b75000;--color-yellow-100:#fef9c2;--color-yellow-800:#874b00;--color-yellow-900:#733e0a;--color-green-100:#dcfce7;--color-green-600:#00a544;--color-green-800:#016630;--color-green-900:#0d542b;--color-emerald-400:#00d294;--color-emerald-500:#00bb7f;--color-emerald-700:#007956;--color-blue-500:#3080ff;--color-gray-100:#f3f4f6;--color-gray-800:#1e2939;--color-zinc-100:#f4f4f5;--color-zinc-400:#9f9fa9;--color-zinc-500:#71717b;--color-zinc-800:#27272a;--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-2xl:42rem;--container-4xl:56rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--tracking-widest:.1em;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:calc(var(--radius) - 2px);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-xs:4px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-foreground:var(--foreground)}@supports (color:lab(0% 0 0)){:root,:host{--color-red-100:lab(92.243% 10.2865 3.83865);--color-red-800:lab(33.7174% 55.8993 41.0293);--color-red-900:lab(28.5139% 44.5539 29.0463);--color-amber-400:lab(80.1641% 16.6016 99.2089);--color-amber-500:lab(72.7183% 31.8672 97.9407);--color-amber-700:lab(47.2709% 42.9082 69.2966);--color-yellow-100:lab(97.3564% -4.51407 27.344);--color-yellow-800:lab(38.7484% 23.5833 51.4916);--color-yellow-900:lab(32.3865% 21.1273 38.5959);--color-green-100:lab(96.1861% -13.8464 6.52365);--color-green-600:lab(59.0978% -58.6621 41.2579);--color-green-800:lab(37.4616% -36.7971 22.9692);--color-green-900:lab(30.797% -29.6927 17.382);--color-emerald-400:lab(75.0771% -60.7313 19.4147);--color-emerald-500:lab(66.9756% -58.27 19.5419);--color-emerald-700:lab(44.4871% -41.0396 11.0361);--color-blue-500:lab(54.1736% 13.3369 -74.6839);--color-gray-100:lab(96.1596% -.0823438 -1.13575);--color-gray-800:lab(16.1051% -1.18239 -11.7533);--color-zinc-100:lab(96.1634% .0993311 -.364041);--color-zinc-400:lab(65.6464% 1.53497 -5.42429);--color-zinc-500:lab(47.8878% 1.65477 -5.77283);--color-zinc-800:lab(15.7305% .613764 -2.16959)}}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){*{outline-color:color-mix(in oklab, var(--ring) 50%, transparent)}}*{scrollbar-width:thin}html,body{overscroll-behavior:none}body{background-color:var(--background);width:100%;min-height:100svh;color:var(--foreground);font-family:var(--font-sans), sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.font-mono{font-family:var(--font-mono), monospace}button:not(:disabled),[role=button]:not(:disabled){cursor:pointer}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.\@container\/field-group{container:field-group/inline-size}.\@container{container-type:inline-size}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.not-sr-only{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.top-0{top:calc(var(--spacing) * 0)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-1\/2{top:50%}.top-3\.5{top:calc(var(--spacing) * 3.5)}.top-4{top:calc(var(--spacing) * 4)}.right-0{right:calc(var(--spacing) * 0)}.right-1{right:calc(var(--spacing) * 1)}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.isolate{isolation:isolate}.isolation-auto{isolation:auto}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.order-first{order:-9999}.order-last{order:9999}.col-span-2{grid-column:span 2/span 2}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-0{margin-inline:calc(var(--spacing) * 0)}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mx-3\.5{margin-inline:calc(var(--spacing) * 3.5)}.mx-auto{margin-inline:auto}.-my-2{margin-block:calc(var(--spacing) * -2)}.my-1{margin-block:calc(var(--spacing) * 1)}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-auto{margin-top:auto}.mr-2{margin-right:calc(var(--spacing) * 2)}.mr-auto{margin-right:auto}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.inline-table{display:inline-table}.list-item{display:list-item}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row{display:table-row}.table-row-group{display:table-row-group}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-3\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-12{width:calc(var(--spacing) * 12);height:calc(var(--spacing) * 12)}.size-\[1\.2rem\]{width:1.2rem;height:1.2rem}.size-full{width:100%;height:100%}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-\[calc\(--spacing\(4\.75\)\)\]{height:calc(calc(var(--spacing) * 4.75))}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-svh{height:100svh}.max-h-\(--available-height\){max-height:var(--available-height)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[85vh\]{max-height:85vh}.max-h-\[min\(calc\(--spacing\(72\)---spacing\(9\)\)\,calc\(var\(--available-height\)---spacing\(9\)\)\)\]{max-height:min(calc(calc(var(--spacing) * 72) - calc(var(--spacing) * 9)), calc(var(--available-height) - calc(var(--spacing) * 9)))}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-7{min-height:calc(var(--spacing) * 7)}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-svh{min-height:100svh}.w-\(--anchor-width\){width:var(--anchor-width)}.w-\(--sidebar-width\){width:var(--sidebar-width)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-24{width:calc(var(--spacing) * 24)}.w-32{width:calc(var(--spacing) * 32)}.w-36{width:calc(var(--spacing) * 36)}.w-48{width:calc(var(--spacing) * 48)}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\(--available-width\){max-width:var(--available-width)}.max-w-\(--skeleton-width\){max-width:var(--skeleton-width)}.max-w-2xl{max-width:var(--container-2xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-5{min-width:calc(var(--spacing) * 5)}.min-w-16{min-width:calc(var(--spacing) * 16)}.min-w-32{min-width:calc(var(--spacing) * 32)}.min-w-\[calc\(var\(--anchor-width\)\+--spacing\(7\)\)\]{min-width:calc(var(--anchor-width) + calc(var(--spacing) * 7))}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.origin-\(--transform-origin\){transform-origin:var(--transform-origin)}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-px{--tw-translate-x:1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-\[calc\(-50\%-2px\)\]{--tw-translate-y:calc(-50% - 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-none{translate:none}.scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-3d{scale:var(--tw-scale-x) var(--tw-scale-y) var(--tw-scale-z)}.rotate-0{rotate:0deg}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.touch-pinch-zoom{--tw-pinch-zoom:pinch-zoom;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.resize{resize:both}.resize-none{resize:none}.scroll-my-1{scroll-margin-block:calc(var(--spacing) * 1)}.scroll-py-1{scroll-padding-block:calc(var(--spacing) * 1)}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-\[minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1fr)}.grid-rows-\[auto_1fr\]{grid-template-rows:auto 1fr}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0{gap:calc(var(--spacing) * 0)}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-8{gap:calc(var(--spacing) * 8)}.gap-px{gap:1px}:where(.space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-reverse>:not(:last-child)){--tw-space-y-reverse:1}:where(.-space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * -2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * -2) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-reverse>:not(:last-child)){--tw-space-x-reverse:1}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-reverse>:not(:last-child)){--tw-divide-y-reverse:1}.self-end{align-self:flex-end}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded{border-radius:.25rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[calc\(var\(--radius-sm\)\+2px\)\]{border-radius:calc(var(--radius-sm) + 2px)}.rounded-\[calc\(var\(--radius-sm\)-2px\)\]{border-radius:calc(var(--radius-sm) - 2px)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:calc(var(--radius) + 2px)}.rounded-md{border-radius:var(--radius)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 2px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-s{border-start-start-radius:.25rem;border-end-start-radius:.25rem}.rounded-ss{border-start-start-radius:.25rem}.rounded-e{border-start-end-radius:.25rem;border-end-end-radius:.25rem}.rounded-se{border-start-end-radius:.25rem}.rounded-ee{border-end-end-radius:.25rem}.rounded-es{border-end-start-radius:.25rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-lg{border-top-left-radius:calc(var(--radius) + 2px);border-top-right-radius:calc(var(--radius) + 2px)}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-tl{border-top-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-tr{border-top-right-radius:.25rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-b-lg{border-bottom-right-radius:calc(var(--radius) + 2px);border-bottom-left-radius:calc(var(--radius) + 2px)}.rounded-br{border-bottom-right-radius:.25rem}.rounded-bl{border-bottom-left-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-s{border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}.border-e{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}.border-bs{border-block-start-style:var(--tw-border-style);border-block-start-width:1px}.border-be{border-block-end-style:var(--tw-border-style);border-block-end-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-500{border-color:var(--color-amber-500)}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab, red, red)){.border-amber-500\/30{border-color:color-mix(in oklab, var(--color-amber-500) 30%, transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab, red, red)){.border-blue-500\/30{border-color:color-mix(in oklab, var(--color-blue-500) 30%, transparent)}}.border-border{border-color:var(--border)}.border-destructive{border-color:var(--destructive)}.border-emerald-500{border-color:var(--color-emerald-500)}.border-emerald-500\/30{border-color:#00bb7f4d}@supports (color:color-mix(in lab, red, red)){.border-emerald-500\/30{border-color:color-mix(in oklab, var(--color-emerald-500) 30%, transparent)}}.border-foreground\/10{border-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.border-foreground\/10{border-color:color-mix(in oklab, var(--foreground) 10%, transparent)}}.border-foreground\/20{border-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.border-foreground\/20{border-color:color-mix(in oklab, var(--foreground) 20%, transparent)}}.border-input{border-color:var(--input)}.border-muted{border-color:var(--muted)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-tactical-amber\/40{border-color:var(--tactical-amber)}@supports (color:color-mix(in lab, red, red)){.border-tactical-amber\/40{border-color:color-mix(in oklab, var(--tactical-amber) 40%, transparent)}}.border-transparent{border-color:#0000}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/10{background-color:color-mix(in oklab, var(--color-amber-500) 10%, transparent)}}.bg-background{background-color:var(--background)}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab, red, red)){.bg-black\/80{background-color:color-mix(in oklab, var(--color-black) 80%, transparent)}}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab, red, red)){.bg-blue-500\/10{background-color:color-mix(in oklab, var(--color-blue-500) 10%, transparent)}}.bg-border,.bg-border\/50{background-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.bg-border\/50{background-color:color-mix(in oklab, var(--border) 50%, transparent)}}.bg-card{background-color:var(--card)}.bg-destructive,.bg-destructive\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.bg-destructive\/10{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab, red, red)){.bg-emerald-500\/10{background-color:color-mix(in oklab, var(--color-emerald-500) 10%, transparent)}}.bg-foreground{background-color:var(--foreground)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-green-100{background-color:var(--color-green-100)}.bg-input\/20{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.bg-input\/20{background-color:color-mix(in oklab, var(--input) 20%, transparent)}}.bg-muted{background-color:var(--muted)}.bg-muted-foreground\/10{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.bg-muted-foreground\/10{background-color:color-mix(in oklab, var(--muted-foreground) 10%, transparent)}}.bg-muted\/20{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.bg-muted\/20{background-color:color-mix(in oklab, var(--muted) 20%, transparent)}}.bg-muted\/30{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.bg-muted\/30{background-color:color-mix(in oklab, var(--muted) 30%, transparent)}}.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-red-100{background-color:var(--color-red-100)}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-tactical-amber\/5{background-color:var(--tactical-amber)}@supports (color:color-mix(in lab, red, red)){.bg-tactical-amber\/5{background-color:color-mix(in oklab, var(--tactical-amber) 5%, transparent)}}.bg-transparent{background-color:#0000}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-clip-padding{background-clip:padding-box}.bg-repeat{background-repeat:repeat}.mask-no-clip{-webkit-mask-clip:no-clip;mask-clip:no-clip}.mask-repeat{-webkit-mask-repeat:repeat;mask-repeat:repeat}.fill-foreground{fill:var(--foreground)}.object-cover{object-fit:cover}.p-0{padding:calc(var(--spacing) * 0)}.p-1{padding:calc(var(--spacing) * 1)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-0{padding-inline:calc(var(--spacing) * 0)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.pt-0{padding-top:calc(var(--spacing) * 0)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-0{padding-bottom:calc(var(--spacing) * 0)}.pb-0\.5{padding-bottom:calc(var(--spacing) * .5)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-sm\/relaxed{font-size:var(--text-sm);line-height:var(--leading-relaxed)}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-xs\/relaxed{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.text-\[0\.625rem\]{font-size:.625rem}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-normal{--tw-leading:var(--leading-normal);line-height:var(--leading-normal)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-balance{text-wrap:balance}.text-wrap{text-wrap:wrap}.break-all{word-break:break-all}.text-clip{text-overflow:clip}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-500{color:var(--color-amber-500)}.text-amber-700{color:var(--color-amber-700)}.text-background{color:var(--background)}.text-card-foreground{color:var(--card-foreground)}.text-destructive{color:var(--destructive)}.text-emerald-700{color:var(--color-emerald-700)}.text-foreground{color:var(--foreground)}.text-gray-800{color:var(--color-gray-800)}.text-green-600{color:var(--color-green-600)}.text-green-800{color:var(--color-green-800)}.text-muted-foreground{color:var(--muted-foreground)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-red-800{color:var(--color-red-800)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground,.text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab, red, red)){.text-sidebar-foreground\/70{color:color-mix(in oklab, var(--sidebar-foreground) 70%, transparent)}}.text-tactical-amber{color:var(--tactical-amber)}.text-tactical-cyan{color:var(--tactical-cyan)}.text-tactical-green{color:var(--tactical-green)}.text-yellow-800{color:var(--color-yellow-800)}.text-zinc-500{color:var(--color-zinc-500)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.not-italic{font-style:normal}.diagonal-fractions{--tw-numeric-fraction:diagonal-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.lining-nums{--tw-numeric-figure:lining-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.oldstyle-nums{--tw-numeric-figure:oldstyle-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.proportional-nums{--tw-numeric-spacing:proportional-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.slashed-zero{--tw-slashed-zero:slashed-zero;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.stacked-fractions{--tw-numeric-fraction:stacked-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.normal-nums{font-variant-numeric:normal}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.underline-offset-4{text-underline-offset:4px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subpixel-antialiased{-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.opacity-50{opacity:.5}.bg-blend-color{background-blend-mode:color}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-border)));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.inset-ring{--tw-inset-ring-shadow:inset 0 0 0 1px var(--tw-inset-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-background{--tw-ring-color:var(--background)}.ring-foreground\/10{--tw-ring-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ring-foreground\/10{--tw-ring-color:color-mix(in oklab, var(--foreground) 10%, transparent)}}.ring-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#0000001a)) drop-shadow(0 1px 1px var(--tw-drop-shadow-color,#0000000f));--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-grayscale{--tw-backdrop-grayscale:grayscale(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert{--tw-backdrop-invert:invert(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia{--tw-backdrop-sepia:sepia(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-linear{--tw-ease:linear;transition-timing-function:linear}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}:where(.divide-x-reverse>:not(:last-child)){--tw-divide-x-reverse:1}.ring-inset{--tw-ring-inset:inset}.group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *){opacity:1}@media (hover:hover){.group-hover\/menu-item\:opacity-100:is(:where(.group\/menu-item):hover *){opacity:1}}.group-focus\/dropdown-menu-item\:text-accent-foreground:is(:where(.group\/dropdown-menu-item):focus *){color:var(--accent-foreground)}.group-has-data-\[orientation\=horizontal\]\/field\:text-balance:is(:where(.group\/field):has([data-orientation=horizontal]) *){text-wrap:balance}.group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--spacing) * 8)}.group-has-data-\[size\=lg\]\/avatar-group\:size-10:is(:where(.group\/avatar-group):has([data-size=lg]) *){width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.group-has-data-\[size\=sm\]\/avatar-group\:size-6:is(:where(.group\/avatar-group):has([data-size=sm]) *){width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.group-has-data-\[slot\=combobox-clear\]\/input-group\:hidden:is(:where(.group\/input-group):has([data-slot=combobox-clear]) *){display:none}.group-has-\[\>input\]\/input-group\:pt-2:is(:where(.group\/input-group):has(>input) *){padding-top:calc(var(--spacing) * 2)}.group-has-\[\>input\]\/input-group\:pb-2:is(:where(.group\/input-group):has(>input) *){padding-bottom:calc(var(--spacing) * 2)}.group-data-empty\/combobox-content\:flex:is(:where(.group\/combobox-content)[data-empty] *){display:flex}.group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.group)[data-collapsible=icon] *){margin-top:calc(var(--spacing) * -8)}.group-data-\[collapsible\=icon\]\:hidden:is(:where(.group)[data-collapsible=icon] *){display:none}.group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--spacing) * 8)!important;height:calc(var(--spacing) * 8)!important}.group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4)))}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4)) + 2px)}.group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.group)[data-collapsible=icon] *){overflow:hidden}.group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing) * 0)!important}.group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing) * 2)!important}.group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.group)[data-collapsible=icon] *){opacity:0}.group-data-\[collapsible\=offExamples\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offExamples] *){right:calc(var(--sidebar-width) * -1)}.group-data-\[collapsible\=offExamples\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offExamples] *){left:calc(var(--sidebar-width) * -1)}.group-data-\[collapsible\=offExamples\]\:w-0:is(:where(.group)[data-collapsible=offExamples] *){width:calc(var(--spacing) * 0)}.group-data-\[collapsible\=offExamples\]\:translate-x-0:is(:where(.group)[data-collapsible=offExamples] *){--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *),.group-data-\[disabled\=true\]\/field\:opacity-50:is(:where(.group\/field)[data-disabled=true] *),.group-data-\[disabled\=true\]\/input-group\:opacity-50:is(:where(.group\/input-group)[data-disabled=true] *){opacity:.5}.group-data-\[panel-open\]\:rotate-180:is(:where(.group)[data-panel-open] *){rotate:180deg}.group-data-\[side\=left\]\:-right-4:is(:where(.group)[data-side=left] *){right:calc(var(--spacing) * -4)}.group-data-\[side\=left\]\:border-r:is(:where(.group)[data-side=left] *){border-right-style:var(--tw-border-style);border-right-width:1px}.group-data-\[side\=right\]\:left-0:is(:where(.group)[data-side=right] *){left:calc(var(--spacing) * 0)}.group-data-\[side\=right\]\:rotate-180:is(:where(.group)[data-side=right] *){rotate:180deg}.group-data-\[side\=right\]\:border-l:is(:where(.group)[data-side=right] *){border-left-style:var(--tw-border-style);border-left-width:1px}.group-data-\[size\=default\]\/avatar\:size-2\.5:is(:where(.group\/avatar)[data-size=default] *){width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.group-data-\[size\=lg\]\/avatar\:size-3:is(:where(.group\/avatar)[data-size=lg] *){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.group-data-\[size\=sm\]\/alert-dialog-content\:grid:is(:where(.group\/alert-dialog-content)[data-size=sm] *){display:grid}.group-data-\[size\=sm\]\/alert-dialog-content\:grid-cols-2:is(:where(.group\/alert-dialog-content)[data-size=sm] *){grid-template-columns:repeat(2,minmax(0,1fr))}.group-data-\[size\=sm\]\/avatar\:size-2:is(:where(.group\/avatar)[data-size=sm] *){width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.group-data-\[size\=sm\]\/avatar\:text-xs:is(:where(.group\/avatar)[data-size=sm] *){font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.group-data-\[size\=sm\]\/card\:px-3:is(:where(.group\/card)[data-size=sm] *){padding-inline:calc(var(--spacing) * 3)}.group-data-\[variant\=floating\]\:rounded-lg:is(:where(.group)[data-variant=floating] *){border-radius:calc(var(--radius) + 2px)}.group-data-\[variant\=floating\]\:shadow-sm:is(:where(.group)[data-variant=floating] *){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.group-data-\[variant\=floating\]\:ring-1:is(:where(.group)[data-variant=floating] *){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.group-data-\[variant\=floating\]\:ring-sidebar-border:is(:where(.group)[data-variant=floating] *){--tw-ring-color:var(--sidebar-border)}.group-data-\[variant\=outline\]\/field-group\:-mb-2:is(:where(.group\/field-group)[data-variant=outline] *){margin-bottom:calc(var(--spacing) * -2)}@media (hover:hover){.peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.peer-data-active\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button)[data-active]~*){color:var(--sidebar-accent-foreground)}.peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.peer\/menu-button)[data-size=default]~*){top:calc(var(--spacing) * 1.5)}.peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.peer\/menu-button)[data-size=lg]~*){top:calc(var(--spacing) * 2.5)}.peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.peer\/menu-button)[data-size=sm]~*){top:calc(var(--spacing) * 1)}.file\:inline-flex::file-selector-button{display:inline-flex}.file\:h-6::file-selector-button{height:calc(var(--spacing) * 6)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-xs\/relaxed::file-selector-button{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--spacing) * -2)}.after\:inset-0:after{content:var(--tw-content);inset:calc(var(--spacing) * 0)}.after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--spacing) * 0)}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.after\:rounded-full:after{content:var(--tw-content);border-radius:3.40282e38px}.after\:border:after{content:var(--tw-content);border-style:var(--tw-border-style);border-width:1px}.after\:border-border:after{content:var(--tw-content);border-color:var(--border)}.after\:mix-blend-darken:after{content:var(--tw-content);mix-blend-mode:darken}.group-data-\[collapsible\=offExamples\]\:after\:left-full:is(:where(.group)[data-collapsible=offExamples] *):after{content:var(--tw-content);left:100%}.last\:mt-0:last-child{margin-top:calc(var(--spacing) * 0)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.autofill\:\[-webkit-text-fill-color\:var\(--color-foreground\)\]:autofill{-webkit-text-fill-color:var(--color-foreground)}.autofill\:\[transition\:background-color_9999s_ease-in-out_0s\]:autofill{transition:background-color 9999s ease-in-out}.focus-within\:border-ring:focus-within{border-color:var(--ring)}.focus-within\:ring-2:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-within\:ring-ring\/30:focus-within{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.focus-within\:ring-ring\/30:focus-within{--tw-ring-color:color-mix(in oklab, var(--ring) 30%, transparent)}}@media (hover:hover){.hover\:bg-accent:hover{background-color:var(--accent)}.hover\:bg-destructive\/20:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/20:hover{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.hover\:bg-input\/40:hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-input\/40:hover{background-color:color-mix(in oklab, var(--input) 40%, transparent)}}.hover\:bg-input\/50:hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-input\/50:hover{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:bg-primary\/80:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/80:hover{background-color:color-mix(in oklab, var(--primary) 80%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}.hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-muted-foreground:hover{color:var(--muted-foreground)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-accent)));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:group-data-\[collapsible\=offExamples\]\:bg-sidebar:hover:is(:where(.group)[data-collapsible=offExamples] *){background-color:var(--sidebar)}.hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:text-accent-foreground:focus,:is(.focus\:\*\*\:text-accent-foreground:focus *),:is(.not-data-\[variant\=destructive\]\:focus\:\*\*\:text-accent-foreground:not([data-variant=destructive]):focus *){color:var(--accent-foreground)}.focus-visible\:border-destructive\/40:focus-visible{border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:border-destructive\/40:focus-visible{border-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-0:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-2:focus-visible,.focus-visible\:ring-\[2px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.focus-visible\:ring-ring\/30:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-ring\/30:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 30%, transparent)}}.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}:where([data-side=left]) .in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}:where([data-slot=combobox-content]) .in-data-\[slot\=combobox-content\]\:focus-within\:border-inherit:focus-within{border-color:inherit}:where([data-slot=combobox-content]) .in-data-\[slot\=combobox-content\]\:focus-within\:ring-0:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-disabled\:pointer-events-none:has(:disabled){pointer-events:none}.has-disabled\:cursor-not-allowed:has(:disabled){cursor:not-allowed}.has-disabled\:opacity-50:has(:disabled){opacity:.5}.has-aria-invalid\:border-destructive:has([aria-invalid=true]){border-color:var(--destructive)}.has-aria-invalid\:ring-2:has([aria-invalid=true]){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-aria-invalid\:ring-destructive\/20:has([aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.has-aria-invalid\:ring-destructive\/20:has([aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.has-data-checked\:bg-primary\/5:has([data-checked]){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.has-data-checked\:bg-primary\/5:has([data-checked]){background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.has-data-\[align\=block-end\]\:rounded-md:has([data-align=block-end]),.has-data-\[align\=block-start\]\:rounded-md:has([data-align=block-start]){border-radius:var(--radius)}.has-data-\[icon\=inline-end\]\:pr-1\.5:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 1.5)}.has-data-\[icon\=inline-end\]\:pr-2:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 2)}.has-data-\[icon\=inline-start\]\:pl-1\.5:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 1.5)}.has-data-\[icon\=inline-start\]\:pl-2:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 2)}.has-data-\[slot\=alert-dialog-media\]\:grid-rows-\[auto_auto_1fr\]:has([data-slot=alert-dialog-media]){grid-template-rows:auto auto 1fr}.has-data-\[slot\=alert-dialog-media\]\:gap-x-4:has([data-slot=alert-dialog-media]){column-gap:calc(var(--spacing) * 4)}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\[slot\=card-description\]\:grid-rows-\[auto_auto\]:has([data-slot=card-description]){grid-template-rows:auto auto}.has-data-\[slot\=combobox-chip\]\:px-1:has([data-slot=combobox-chip]){padding-inline:calc(var(--spacing) * 1)}.has-data-\[slot\=combobox-chip-remove\]\:pr-0:has([data-slot=combobox-chip-remove]){padding-right:calc(var(--spacing) * 0)}.has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:border-ring:has([data-slot=input-group-control]:focus-visible){border-color:var(--ring)}.has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-2:has([data-slot=input-group-control]:focus-visible){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/30:has([data-slot=input-group-control]:focus-visible){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/30:has([data-slot=input-group-control]:focus-visible){--tw-ring-color:color-mix(in oklab, var(--ring) 30%, transparent)}}.has-\[\[data-slot\]\[aria-invalid\=true\]\]\:border-destructive:has([data-slot][aria-invalid=true]){border-color:var(--destructive)}.has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-2:has([data-slot][aria-invalid=true]){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has([data-slot][aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has([data-slot][aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.has-\[textarea\]\:rounded-md:has(:is(textarea)){border-radius:var(--radius)}.has-\[\>\[data-align\=block-end\]\]\:h-auto:has(>[data-align=block-end]){height:auto}.has-\[\>\[data-align\=block-end\]\]\:flex-col:has(>[data-align=block-end]){flex-direction:column}.has-\[\>\[data-align\=block-start\]\]\:h-auto:has(>[data-align=block-start]){height:auto}.has-\[\>\[data-align\=block-start\]\]\:flex-col:has(>[data-align=block-start]){flex-direction:column}.has-\[\>\[data-slot\=checkbox-group\]\]\:gap-3:has(>[data-slot=checkbox-group]){gap:calc(var(--spacing) * 3)}.has-\[\>\[data-slot\=field-content\]\]\:items-start:has(>[data-slot=field-content]){align-items:flex-start}.has-\[\>\[data-slot\=field\]\]\:w-full:has(>[data-slot=field]){width:100%}.has-\[\>\[data-slot\=field\]\]\:flex-col:has(>[data-slot=field]){flex-direction:column}.has-\[\>\[data-slot\=field\]\]\:rounded-md:has(>[data-slot=field]){border-radius:var(--radius)}.has-\[\>\[data-slot\=field\]\]\:border:has(>[data-slot=field]){border-style:var(--tw-border-style);border-width:1px}.has-\[\>\[data-slot\=radio-group\]\]\:gap-3:has(>[data-slot=radio-group]){gap:calc(var(--spacing) * 3)}.has-\[\>button\]\:mr-\[-0\.275rem\]:has(>button){margin-right:-.275rem}.has-\[\>button\]\:ml-\[-0\.275rem\]:has(>button){margin-left:-.275rem}.has-\[\>img\:first-child\]\:pt-0:has(>img:first-child){padding-top:calc(var(--spacing) * 0)}.has-\[\>kbd\]\:mr-\[-0\.275rem\]:has(>kbd){margin-right:-.275rem}.has-\[\>kbd\]\:ml-\[-0\.275rem\]:has(>kbd){margin-left:-.275rem}.has-\[\>svg\]\:p-0:has(>svg){padding:calc(var(--spacing) * 0)}.has-\[\>textarea\]\:h-auto:has(>textarea){height:auto}.aria-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.aria-expanded\:bg-muted[aria-expanded=true]{background-color:var(--muted)}.aria-expanded\:bg-secondary[aria-expanded=true]{background-color:var(--secondary)}.aria-expanded\:text-foreground[aria-expanded=true]{color:var(--foreground)}.aria-expanded\:text-secondary-foreground[aria-expanded=true]{color:var(--secondary-foreground)}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-0[aria-invalid=true]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.aria-invalid\:ring-2[aria-invalid=true],.aria-invalid\:ring-\[2px\][aria-invalid=true]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.data-active\:bg-sidebar-accent[data-active]{background-color:var(--sidebar-accent)}.data-active\:font-medium[data-active]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.data-active\:text-sidebar-accent-foreground[data-active]{color:var(--sidebar-accent-foreground)}.data-closed\:overflow-hidden[data-closed]{overflow:hidden}.data-disabled\:pointer-events-none[data-disabled]{pointer-events:none}.data-disabled\:opacity-50[data-disabled]{opacity:.5}.data-empty\:p-0[data-empty]{padding:calc(var(--spacing) * 0)}.data-ending-style\:opacity-0[data-ending-style]{opacity:0}.data-highlighted\:bg-accent[data-highlighted]{background-color:var(--accent)}.data-highlighted\:text-accent-foreground[data-highlighted],:is(.not-data-\[variant\=destructive\]\:data-highlighted\:\*\*\:text-accent-foreground:not([data-variant=destructive])[data-highlighted] *){color:var(--accent-foreground)}.data-inset\:pl-8[data-inset]{padding-left:calc(var(--spacing) * 8)}.data-open\:bg-accent[data-open]{background-color:var(--accent)}.data-open\:text-accent-foreground[data-open]{color:var(--accent-foreground)}.data-open\:opacity-100[data-open]{opacity:1}@media (hover:hover){.data-open\:hover\:bg-sidebar-accent[data-open]:hover{background-color:var(--sidebar-accent)}.data-open\:hover\:text-sidebar-accent-foreground[data-open]:hover{color:var(--sidebar-accent-foreground)}}.data-placeholder\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-pressed\:bg-transparent[data-pressed]{background-color:#0000}.data-selected\:border-primary[data-selected]{border-color:var(--primary)}.data-selected\:text-foreground[data-selected]{color:var(--foreground)}.data-starting-style\:opacity-0[data-starting-style]{opacity:0}.data-\[chips\=true\]\:min-w-\(--anchor-width\)[data-chips=true]{min-width:var(--anchor-width)}.data-\[invalid\=true\]\:text-destructive[data-invalid=true]{color:var(--destructive)}.data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.data-\[orientation\=vertical\]\:self-stretch[data-orientation=vertical]{align-self:stretch}.data-\[side\=bottom\]\:inset-x-0[data-side=bottom]{inset-inline:calc(var(--spacing) * 0)}.data-\[side\=bottom\]\:top-1[data-side=bottom]{top:calc(var(--spacing) * 1)}.data-\[side\=bottom\]\:bottom-0[data-side=bottom]{bottom:calc(var(--spacing) * 0)}.data-\[side\=bottom\]\:h-auto[data-side=bottom]{height:auto}.data-\[side\=bottom\]\:border-t[data-side=bottom]{border-top-style:var(--tw-border-style);border-top-width:1px}.data-\[side\=left\]\:inset-y-0[data-side=left]{inset-block:calc(var(--spacing) * 0)}.data-\[side\=left\]\:top-1\/2\![data-side=left]{top:50%!important}.data-\[side\=left\]\:-right-1[data-side=left]{right:calc(var(--spacing) * -1)}.data-\[side\=left\]\:left-0[data-side=left]{left:calc(var(--spacing) * 0)}.data-\[side\=left\]\:h-full[data-side=left]{height:100%}.data-\[side\=left\]\:w-3\/4[data-side=left]{width:75%}.data-\[side\=left\]\:-translate-y-1\/2[data-side=left]{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=left\]\:border-r[data-side=left]{border-right-style:var(--tw-border-style);border-right-width:1px}.data-\[side\=right\]\:inset-y-0[data-side=right]{inset-block:calc(var(--spacing) * 0)}.data-\[side\=right\]\:top-1\/2\![data-side=right]{top:50%!important}.data-\[side\=right\]\:right-0[data-side=right]{right:calc(var(--spacing) * 0)}.data-\[side\=right\]\:-left-1[data-side=right]{left:calc(var(--spacing) * -1)}.data-\[side\=right\]\:h-full[data-side=right]{height:100%}.data-\[side\=right\]\:w-3\/4[data-side=right]{width:75%}.data-\[side\=right\]\:-translate-y-1\/2[data-side=right]{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=right\]\:border-l[data-side=right]{border-left-style:var(--tw-border-style);border-left-width:1px}.data-\[side\=top\]\:inset-x-0[data-side=top]{inset-inline:calc(var(--spacing) * 0)}.data-\[side\=top\]\:top-0[data-side=top]{top:calc(var(--spacing) * 0)}.data-\[side\=top\]\:-bottom-2\.5[data-side=top]{bottom:calc(var(--spacing) * -2.5)}.data-\[side\=top\]\:h-auto[data-side=top]{height:auto}.data-\[side\=top\]\:border-b[data-side=top]{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.data-\[size\=default\]\:h-7[data-size=default]{height:calc(var(--spacing) * 7)}.data-\[size\=default\]\:max-w-xs[data-size=default]{max-width:var(--container-xs)}.data-\[size\=lg\]\:size-10[data-size=lg]{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.data-\[size\=md\]\:text-xs[data-size=md]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.data-\[size\=sm\]\:size-6[data-size=sm]{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.data-\[size\=sm\]\:h-6[data-size=sm]{height:calc(var(--spacing) * 6)}.data-\[size\=sm\]\:max-w-64[data-size=sm]{max-width:calc(var(--spacing) * 64)}.data-\[size\=sm\]\:gap-3[data-size=sm]{gap:calc(var(--spacing) * 3)}.data-\[size\=sm\]\:py-3[data-size=sm]{padding-block:calc(var(--spacing) * 3)}.data-\[size\=sm\]\:text-xs[data-size=sm]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}:is(.\*\:data-\[slot\=avatar\]\:ring-2>*)[data-slot=avatar]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:is(.\*\:data-\[slot\=avatar\]\:ring-background>*)[data-slot=avatar]{--tw-ring-color:var(--background)}.data-\[slot\=checkbox-group\]\:gap-3[data-slot=checkbox-group]{gap:calc(var(--spacing) * 3)}:is(.\*\:data-\[slot\=field\]\:p-2>*)[data-slot=field]{padding:calc(var(--spacing) * 2)}:is(.\*\:data-\[slot\=field-group\]\:gap-4>*)[data-slot=field-group]{gap:calc(var(--spacing) * 4)}:is(.\*\:data-\[slot\=input-group\]\:m-1>*)[data-slot=input-group]{margin:calc(var(--spacing) * 1)}:is(.\*\:data-\[slot\=input-group\]\:mb-0>*)[data-slot=input-group]{margin-bottom:calc(var(--spacing) * 0)}:is(.\*\:data-\[slot\=input-group\]\:h-7>*)[data-slot=input-group]{height:calc(var(--spacing) * 7)}:is(.\*\:data-\[slot\=input-group\]\:border-none>*)[data-slot=input-group]{--tw-border-style:none;border-style:none}:is(.\*\:data-\[slot\=input-group\]\:bg-input\/20>*)[data-slot=input-group]{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){:is(.\*\:data-\[slot\=input-group\]\:bg-input\/20>*)[data-slot=input-group]{background-color:color-mix(in oklab, var(--input) 20%, transparent)}}:is(.\*\:data-\[slot\=input-group\]\:shadow-none>*)[data-slot=input-group]{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:is(.\*\*\:data-\[slot\=kbd\]\:rounded-\[calc\(var\(--radius-sm\)-2px\)\] *)[data-slot=kbd]{border-radius:calc(var(--radius-sm) - 2px)}:is(.\*\*\:data-\[slot\=kbd\]\:rounded-md *)[data-slot=kbd]{border-radius:var(--radius)}:is(.\*\*\:data-\[slot\=kbd\]\:bg-muted-foreground\/10 *)[data-slot=kbd]{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){:is(.\*\*\:data-\[slot\=kbd\]\:bg-muted-foreground\/10 *)[data-slot=kbd]{background-color:color-mix(in oklab, var(--muted-foreground) 10%, transparent)}}:is(.\*\*\:data-\[slot\=kbd\]\:px-1 *)[data-slot=kbd]{padding-inline:calc(var(--spacing) * 1)}:is(.\*\*\:data-\[slot\=kbd\]\:text-\[0\.625rem\] *)[data-slot=kbd]{font-size:.625rem}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-1\.5>*)[data-slot=select-value]{gap:calc(var(--spacing) * 1.5)}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}.data-\[variant\=label\]\:text-xs\/relaxed[data-variant=label]{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.data-\[variant\=legend\]\:text-sm[data-variant=legend]{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.nth-last-2\:-mt-1:nth-last-child(2){margin-top:calc(var(--spacing) * -1)}@supports ((-webkit-backdrop-filter:var(--tw)) or (backdrop-filter:var(--tw))){.supports-backdrop-filter\:backdrop-blur-xs{--tw-backdrop-blur:blur(var(--blur-xs));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}}@media (width>=40rem){.sm\:flex{display:flex}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-\[1fr_1fr_1fr_auto\]{grid-template-columns:1fr 1fr 1fr auto}.sm\:grid-cols-\[1fr_auto\]{grid-template-columns:1fr auto}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:row-span-2:is(:where(.group\/alert-dialog-content)[data-size=default] *){grid-row:span 2/span 2}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:place-items-start:is(:where(.group\/alert-dialog-content)[data-size=default] *){place-items:start}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:text-left:is(:where(.group\/alert-dialog-content)[data-size=default] *){text-align:left}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:group-has-data-\[slot\=alert-dialog-media\]\/alert-dialog-content\:col-start-2:is(:where(.group\/alert-dialog-content)[data-size=default] *):is(:where(.group\/alert-dialog-content):has([data-slot=alert-dialog-media]) *){grid-column-start:2}.sm\:group-data-\[size\=default\]\/alert-dialog-content\:has-data-\[slot\=alert-dialog-media\]\:grid-rows-\[auto_1fr\]:is(:where(.group\/alert-dialog-content)[data-size=default] *):has([data-slot=alert-dialog-media]){grid-template-rows:auto 1fr}.data-\[side\=left\]\:sm\:max-w-sm[data-side=left],.data-\[side\=right\]\:sm\:max-w-sm[data-side=right],.data-\[size\=default\]\:sm\:max-w-sm[data-size=default]{max-width:var(--container-sm)}}@media (width>=48rem){.md\:block{display:block}.md\:flex{display:flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:text-xs\/relaxed{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.md\:text-pretty{text-wrap:pretty}.md\:opacity-0{opacity:0}.md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.peer)[data-variant=inset]~*){margin:calc(var(--spacing) * 2)}.md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.peer)[data-variant=inset]~*){margin-left:calc(var(--spacing) * 0)}.md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.peer)[data-variant=inset]~*){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.peer)[data-variant=inset]~*):is(:where(.peer)[data-state=collapsed]~*){margin-left:calc(var(--spacing) * 2)}.md\:after\:hidden:after{content:var(--tw-content);display:none}}@media (width>=64rem){.lg\:block{display:block}}@media (width>=80rem){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@container field-group (width>=28rem){.\@md\/field-group\:flex-row{flex-direction:row}.\@md\/field-group\:items-center{align-items:center}.\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:items-start:has(>[data-slot=field-content]){align-items:flex-start}}.dark\:scale-0:where(.dark,.dark *){--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.dark\:scale-100:where(.dark,.dark *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.dark\:-rotate-90:where(.dark,.dark *){rotate:-90deg}.dark\:rotate-0:where(.dark,.dark *){rotate:0deg}.dark\:bg-destructive\/20:where(.dark,.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:bg-destructive\/20:where(.dark,.dark *){background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.dark\:bg-gray-800:where(.dark,.dark *){background-color:var(--color-gray-800)}.dark\:bg-green-900:where(.dark,.dark *){background-color:var(--color-green-900)}.dark\:bg-input\/30:where(.dark,.dark *){background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\:bg-input\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.dark\:bg-muted\/30:where(.dark,.dark *){background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.dark\:bg-muted\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--muted) 30%, transparent)}}.dark\:bg-popover:where(.dark,.dark *){background-color:var(--popover)}.dark\:bg-red-900:where(.dark,.dark *){background-color:var(--color-red-900)}.dark\:bg-transparent:where(.dark,.dark *){background-color:#0000}.dark\:bg-yellow-900:where(.dark,.dark *){background-color:var(--color-yellow-900)}.dark\:bg-zinc-800:where(.dark,.dark *){background-color:var(--color-zinc-800)}.dark\:text-amber-400:where(.dark,.dark *){color:var(--color-amber-400)}.dark\:text-emerald-400:where(.dark,.dark *){color:var(--color-emerald-400)}.dark\:text-gray-100:where(.dark,.dark *){color:var(--color-gray-100)}.dark\:text-green-100:where(.dark,.dark *){color:var(--color-green-100)}.dark\:text-red-100:where(.dark,.dark *){color:var(--color-red-100)}.dark\:text-yellow-100:where(.dark,.dark *){color:var(--color-yellow-100)}.dark\:text-zinc-400:where(.dark,.dark *){color:var(--color-zinc-400)}.dark\:invert:where(.dark,.dark *){--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.dark\:after\:mix-blend-lighten:where(.dark,.dark *):after{content:var(--tw-content);mix-blend-mode:lighten}@media (hover:hover){.dark\:hover\:bg-destructive\/30:where(.dark,.dark *):hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-destructive\/30:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--destructive) 30%, transparent)}}.dark\:hover\:bg-input\/50:where(.dark,.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-input\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}.dark\:hover\:bg-muted\/50:where(.dark,.dark *):hover{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-muted\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}}.dark\:focus-visible\:ring-destructive\/40:where(.dark,.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:focus-visible\:ring-destructive\/40:where(.dark,.dark *):focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:has-aria-invalid\:border-destructive\/50:where(.dark,.dark *):has([aria-invalid=true]){border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:has-aria-invalid\:border-destructive\/50:where(.dark,.dark *):has([aria-invalid=true]){border-color:color-mix(in oklab, var(--destructive) 50%, transparent)}}.dark\:has-aria-invalid\:ring-destructive\/40:where(.dark,.dark *):has([aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:has-aria-invalid\:ring-destructive\/40:where(.dark,.dark *):has([aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:has-data-checked\:bg-primary\/10:where(.dark,.dark *):has([data-checked]){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.dark\:has-data-checked\:bg-primary\/10:where(.dark,.dark *):has([data-checked]){background-color:color-mix(in oklab, var(--primary) 10%, transparent)}}.dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:where(.dark,.dark *):has([data-slot][aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:where(.dark,.dark *):has([data-slot][aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:aria-invalid\:border-destructive\/50:where(.dark,.dark *)[aria-invalid=true]{border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:aria-invalid\:border-destructive\/50:where(.dark,.dark *)[aria-invalid=true]{border-color:color-mix(in oklab, var(--destructive) 50%, transparent)}}.dark\:aria-invalid\:ring-destructive\/40:where(.dark,.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:aria-invalid\:ring-destructive\/40:where(.dark,.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:where(.dark,.dark *)[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:where(.dark,.dark *)[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-2\.5 svg:not([class*=size-]){width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 svg:not([class*=size-]){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3\.5 svg:not([class*=size-]){width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\.border-b\]\:pb-2.border-b{padding-bottom:calc(var(--spacing) * 2)}.\[\.border-b\]\:pb-4.border-b{padding-bottom:calc(var(--spacing) * 4)}.group-data-\[size\=sm\]\/card\:\[\.border-b\]\:pb-3:is(:where(.group\/card)[data-size=sm] *).border-b{padding-bottom:calc(var(--spacing) * 3)}.\[\.border-t\]\:pt-2.border-t{padding-top:calc(var(--spacing) * 2)}.\[\.border-t\]\:pt-4.border-t{padding-top:calc(var(--spacing) * 4)}.group-data-\[size\=sm\]\/card\:\[\.border-t\]\:pt-3:is(:where(.group\/card)[data-size=sm] *).border-t{padding-top:calc(var(--spacing) * 3)}:is(.\*\:\[a\]\:underline>*):is(a){text-decoration-line:underline}:is(.\*\:\[a\]\:underline-offset-3>*):is(a){text-underline-offset:3px}@media (hover:hover){.\[a\]\:hover\:bg-destructive\/20:is(a):hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-destructive\/20:is(a):hover{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.\[a\]\:hover\:bg-muted:is(a):hover{background-color:var(--muted)}.\[a\]\:hover\:bg-primary\/80:is(a):hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-primary\/80:is(a):hover{background-color:color-mix(in oklab, var(--primary) 80%, transparent)}}.\[a\]\:hover\:bg-secondary\/80:is(a):hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-secondary\/80:is(a):hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}.\[a\]\:hover\:text-muted-foreground:is(a):hover{color:var(--muted-foreground)}:is(.\*\:\[a\]\:hover\:text-foreground>*):is(a):hover{color:var(--foreground)}}:is(.\*\:\[img\:first-child\]\:rounded-t-lg>*):is(img:first-child){border-top-left-radius:calc(var(--radius) + 2px);border-top-right-radius:calc(var(--radius) + 2px)}:is(.\*\:\[img\:last-child\]\:rounded-b-lg>*):is(img:last-child){border-bottom-right-radius:calc(var(--radius) + 2px);border-bottom-left-radius:calc(var(--radius) + 2px)}:is(.\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--spacing) * 2)}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)}:is(.\*\:\[svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4>*):is(svg:not([class*=size-])){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&\>\*\]\:w-full>*{width:100%}@container field-group (width>=28rem){.\@md\/field-group\:\[\&\>\*\]\:w-auto>*{width:auto}}.\[\&\>\.sr-only\]\:w-auto>.sr-only{width:auto}.\[\&\>\[data-slot\=field-label\]\]\:flex-auto>[data-slot=field-label]{flex:auto}@container field-group (width>=28rem){.\@md\/field-group\:\[\&\>\[data-slot\=field-label\]\]\:flex-auto>[data-slot=field-label]{flex:auto}}.has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content])>[role=checkbox],.has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content]) [role=radio]{margin-top:1px}@container field-group (width>=28rem){.\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content])>[role=checkbox],.\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content]) [role=radio]{margin-top:1px}}.\[\&\>a\]\:underline>a{text-decoration-line:underline}.\[\&\>a\]\:underline-offset-4>a{text-underline-offset:4px}.\[\&\>a\:hover\]\:text-primary>a:hover{color:var(--primary)}.\[\&\>button\]\:hidden>button{display:none}.has-\[\>\[data-align\=block-end\]\]\:\[\&\>input\]\:pt-3:has(>[data-align=block-end])>input{padding-top:calc(var(--spacing) * 3)}.has-\[\>\[data-align\=block-start\]\]\:\[\&\>input\]\:pb-3:has(>[data-align=block-start])>input{padding-bottom:calc(var(--spacing) * 3)}.has-\[\>\[data-align\=inline-end\]\]\:\[\&\>input\]\:pr-1\.5:has(>[data-align=inline-end])>input{padding-right:calc(var(--spacing) * 1.5)}.has-\[\>\[data-align\=inline-start\]\]\:\[\&\>input\]\:pl-1\.5:has(>[data-align=inline-start])>input{padding-left:calc(var(--spacing) * 1.5)}.\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-2\.5\!>svg{width:calc(var(--spacing) * 2.5)!important;height:calc(var(--spacing) * 2.5)!important}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.group-has-data-\[size\=lg\]\/avatar-group\:\[\&\>svg\]\:size-5:is(:where(.group\/avatar-group):has([data-size=lg]) *)>svg{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.group-has-data-\[size\=sm\]\/avatar-group\:\[\&\>svg\]\:size-3:is(:where(.group\/avatar-group):has([data-size=sm]) *)>svg{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.group-data-\[size\=default\]\/avatar\:\[\&\>svg\]\:size-2:is(:where(.group\/avatar)[data-size=default] *)>svg,.group-data-\[size\=lg\]\/avatar\:\[\&\>svg\]\:size-2:is(:where(.group\/avatar)[data-size=lg] *)>svg{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.group-data-\[size\=sm\]\/avatar\:\[\&\>svg\]\:hidden:is(:where(.group\/avatar)[data-size=sm] *)>svg{display:none}.\[\&\>svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3>svg:not([class*=size-]){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.\[\&\>svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3\.5>svg:not([class*=size-]){width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}[data-side=left][data-collapsible=offExamples] .\[\[data-side\=left\]\[data-collapsible\=offExamples\]_\&\]\:-right-2{right:calc(var(--spacing) * -2)}[data-side=left][data-state=collapsed] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offExamples] .\[\[data-side\=right\]\[data-collapsible\=offExamples\]_\&\]\:-left-2{left:calc(var(--spacing) * -2)}[data-side=right][data-state=collapsed] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}[data-variant=legend]+.\[\[data-variant\=legend\]\+\&\]\:-mt-1\.5{margin-top:calc(var(--spacing) * -1.5)}}@font-face{font-family:Geist Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/geist-cyrillic-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Geist Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/geist-cyrillic-wght-normal.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Geist Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/geist-vietnamese-wght-normal.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Geist Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/geist-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Geist Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/geist-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(data:font/woff2;base64,d09GMgABAAAAAAfsABQAAAAAEAwAAAeCAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhwbHhwoP0hWQVJbBmA/U1RBVIFiJyYAdC9qEQgKhGSEAAsgADCGCAE2AiQDOgQgBYlMB4EUDAcbLQ4onoexrSC/2ZyLAa8p8VHB8/x3Vue+V0hVJalMJg2nx/TCrQXxBeqLjQG7FyM1WEa/X1tEXN7cFz9EJEMmMUz3RihWSSKeQCbcIou0izz/C8v+fq3VfajEa9gDD11CImXS7qL/RJFVzC1qiB6KmKeD6TZdQ6IRGv78dL6uSVVCfgni5mzu7kcgQBgAEAQTQRCoL++STTYybkJxNfQxAAIAGu8OdEB9teW2jh4BpgDqFjAeSEByW3zFP0CBBgNMsMCGEDjgggdhiEAUAeIIED7ABTDUEnkIE9Q9ahFgKttcVhApo4ACB4qobHaccgDfEjFO6aaWUhjMLt2SyIvHKoDqoA4CSUwEIYQCEjhAO9R1G6keDeDZGjNo+AhxOjCEGTr1WeIF3kYBiLAOKvkJSMiKX0VdAyQt3SDJClCkxJCHkCzfqyVTriJZLcolS32JZHUekq2TYNkYtCtjYHMQXSxGjXDz2t/yLWXzDzxz+o3zFwDEaN23F+13pyMdQAEaSKAR9vcGq4A4MTSKCElGW+M7UcY7xqkggITb28ZJhlqc9q2twYKTt0NjixBgYvO9BIihEBLYuOFXQzfIQ7dXGUEEEgFDooBfAzqiQbpJrhiWSuKJCRFKYbHCyJKI2G5GiZbNAvgAu5pc3vwx4G+g3aDkhklABiSz0BICXrYghtYhx/cdJ+44rY2oZ0aMNRFz3VZjb6W33F3gzltqtOCV8tTHSpOeXuItfvr5lCdfzFpqtEitvqdcdGGFd28ZqqC0tPbeChGXgrIlnhSWu/eUso4uKWFLugyDzQJhflY4659+WjQ++6x72WUMv9G8mw6QJl7BVxX5fe/kpUsOvnZwee9uQ0cGXYd0o89XB2748sDSnt8d2VphdOTTgceDVvOds0v9P/s7HPq15aGun/6Vllb56f1dl0t1LejqrNkpdRZsG8TOnM5vkBG5oiVyVGnS8LHps5cfNWJs6qKPfaNSxiQNBUm3cKNWROr0GSur7Za31k1vieq7LH11VF+jXdRIasRKflc7jkobm1Z9te1IyZA0pDkhLR98+H37Zf1c/8at+dB7x+7GfVyTfJMPiYztsnl59Y5l4j+0n1RXlpHnF3Tq7HecmNF/CJodEMAikruxiyJaGLvHOdAfoA+oDvpjBm2b91cHGRZMU9n25xEU0A8fgEEAdKI3Q1iDtc034sug5YVMkE2jsE+BIkwSoQ3gxXMqz9tELp48bd0cFKOKS7xYjEuXBnZP5ia7DyiO/X/YI+PQSbt2uSdqAkWL9nQbV1XB94/+uPfdZz8dnXYFBYrcTl2SIR/ybxJNJPz/Gupb0JaZeens2ekC7EKr8t+Ls/P5VJPYJdHKyqfg2nqU6bhlidzcddQV/7MmecTzJ5VPcKXkNKSEogHjYFx6QZ7rQ+FSe8njaiNuOnXS8H2ScQ619c2mC3VTtauL0rRbXd/CkSOP37FY9Zkjz8+GibYUMOEWF+RdrFS8Ecv1SHOpPUPZGEIpjPvFyU5cXKjd6OXqorTqy9GwRd++HVufPGnVsW+aO3vggKZ18jR9sXaTC1PWTEsVUaK0FkNySbTQDqlm2PfDjZcu4aalnSLKjnOoYQ0nUlqqXcGpPu/4VgV/xU2pAqW4BW3qzhQ8/hFKhV2qE3+BKAtDqBXjfgnVdH4y0wg5tbVNRenNdTWOrenWLcupQdmsbq5b+18piTe/xRdp1xbILxNPJGInm2z6hoB21Lal0i+ePTtd7B45+3XhFJ329evskXm7qurUVREotqSluSo/L29d3qDhI4YOQqWhI4YNvBNfsMHeXKemXrxQfKeuPOGRVayA3JtkJKEgbPp+dXUDluddutRYLFoXGXWX6N3WFaGLbQtRSitVYNacTNSdy7AaG/HSaUEANcBoGXNdcZvZsOqQ1icBDv21/gzAoYPHH/WDW0qNR3QTYKEAEHig6o13NXbND06CQPlRtYjGNnSktRc09k1mAMDvAlDKfQjgy6fssInlfzmNAjKkDxoxHOBLdVRAIVt9j4qo+hA1w9T1aNBNTUOTTNUHLbqokE+UAfJXCIGw/IxCSL5GRUJeR40rL/UxTm4Q08H6MbCs70ObuNyIIXrINHQYInF06UUlevTjbQzTh5upiDMzMMogUtEnjPs/Y7jAHCJeB0GBHh04tC6FiB6ZFB1oArUSIoFoqhzCeAN6lHwm0T4C3VVPWvjpSMXReuWesMEcoqrmgtNBGd2noWeV0hNAz9rFeShNJxHGsPa3HXeKTk8b55hahySYHaYKKFFLpCfN8rsoaJn01CR04Gkc+5k7KVTCmClX8Q10HCrUEkVlSX+XO33oQR9609tJ516H497WSobWs5Up6TLaS10/dessIskgJSLiDlWvHVUywpkQ7hdPZqGyiEF0uVQerVcPamT1A3eKXdyI1vG9OoflrSXihZ1qqGE3nhmAgiIbRCQgPLEPtOM3UQwTLYaYYomNlpA44opnjV6jkD6id80OOrzf6BzmMD6eEa1zKyeYG1fzfEf16V6jw9XYOaar1/b2kP/IYX8oR2mcFvv2GtBV3JXgd437AQAA)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-cyrillic-wght-normal.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-greek-wght-normal.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-vietnamese-wght-normal.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--font-sans:"Geist Variable", sans-serif;--font-mono:"JetBrains Mono Variable", monospace;--background:#f4f1ec;--foreground:#1d2930;--card:#f9f8f6;--card-foreground:#1d2930;--popover:#fbfaf9;--popover-foreground:#1d2930;--primary:#305030;--primary-foreground:#f5f3ef;--secondary:#eae5d7;--secondary-foreground:#263740;--muted:#e9e7e2;--muted-foreground:#576b75;--accent:#e7e2da;--accent-foreground:#1d2930;--destructive:#c32222;--destructive-foreground:#fff;--border:#b6c9b6;--input:#dedbd3;--ring:#3e743e;--chart-1:#2e6b2e;--chart-2:#cfa117;--chart-3:#297aa3;--chart-4:#cc8c33;--chart-5:#b82e2e;--radius:.25rem;--sidebar:#f5f3f0;--sidebar-background:#f5f3f0;--sidebar-foreground:#263740;--sidebar-primary:#305030;--sidebar-primary-foreground:#f5f3ef;--sidebar-accent:#e7e2da;--sidebar-accent-foreground:#1d2930;--sidebar-border:#c4d4c4;--sidebar-ring:#3e743e;--tactical-amber:#c2940a;--tactical-amber-dim:#d6b85c;--tactical-green:#267326;--tactical-green-dim:#7ab87a;--tactical-cyan:#24868f;--tactical-cyan-dim:#5eb2ba;--tactical-red:#bd2828;--tactical-olive:#64783a;--scanline-opacity:.015}.dark{--background:#0d1012;--foreground:#e0ddd1;--card:#14181a;--card-foreground:#e0ddd1;--popover:#111517;--popover-foreground:#e0ddd1;--primary:#fa0;--primary-foreground:#0d1012;--secondary:#323922;--secondary-foreground:#c6d2ac;--muted:#202527;--muted-foreground:#819098;--accent:#292f32;--accent-foreground:#ebe8e0;--destructive:#c32222;--destructive-foreground:#fff;--border:#203c32;--input:#292f32;--ring:#ffbf00;--chart-1:#ffbf00;--chart-2:#22c38e;--chart-3:#1aa1e6;--chart-4:#86ac39;--chart-5:#d92626;--sidebar:#111517;--sidebar-background:#111517;--sidebar-foreground:#e0ddd1;--sidebar-primary:#ffbf00;--sidebar-primary-foreground:#0d1012;--sidebar-accent:#292f32;--sidebar-accent-foreground:#ebe8e0;--sidebar-border:#203c32;--sidebar-ring:#ffbf00;--tactical-amber:#ffbf00;--tactical-amber-dim:#a17d12;--tactical-green:#22c38e;--tactical-green-dim:#20604a;--tactical-cyan:#17bfcf;--tactical-cyan-dim:#196066;--tactical-red:#d92626;--tactical-olive:#576b2e;--scanline-opacity:.03}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-pan-x{syntax:"*";inherits:false}@property --tw-pan-y{syntax:"*";inherits:false}@property --tw-pinch-zoom{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}} diff --git a/spinifex/services/spinifexui/frontend/dist/assets/index.js b/spinifex/services/spinifexui/frontend/dist/assets/index.js index 780dbbb86..80a390990 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/index.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/index.js @@ -1,92 +1,10 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/login.js","assets/collapsible.js","assets/zod.js","assets/card.js","assets/field.js","assets/_auth.js","assets/useQuery.js","assets/useBaseQuery.js","assets/chevron-down.js","assets/chevron-right.js","assets/page-heading.js","assets/state-badge.js","assets/badge.js","assets/admin.js","assets/nodes.js","assets/service-metrics.js","assets/ls.js","assets/useSuspenseQuery.js","assets/list-card.js","assets/create-bucket.js","assets/s3.js","assets/error-banner.js","assets/back-link.js","assets/cli-command-panel.js","assets/check.js","assets/copy.js","assets/use-copy-to-clipboard.js","assets/form-actions.js","assets/create-user.js","assets/iam.js","assets/iam2.js","assets/create-policy.js","assets/textarea.js","assets/create-vpc.js","assets/ec2.js","assets/select.js","assets/ec22.js","assets/create-volume.js","assets/create-target-group.js","assets/elbv2.js","assets/target-group-form.js","assets/plus.js","assets/trash-2.js","assets/elbv22.js","assets/create-subnet.js","assets/create-snapshot.js","assets/create-security-group.js","assets/create-route-table.js","assets/create-placement-group.js","assets/create-nat-gateway.js","assets/create-load-balancer.js","assets/import-key-pair.js","assets/create-key-pair.js","assets/alert-dialog.js","assets/download.js","assets/triangle-alert.js","assets/create-internet-gateway.js","assets/run-instances.js","assets/allocate-address.js","assets/_bucket.js","assets/upload-button.js","assets/delete-confirmation-dialog.js","assets/list-users.js","assets/list-policies.js","assets/describe-vpcs.js","assets/describe-volumes.js","assets/describe-target-groups.js","assets/describe-subnets.js","assets/describe-snapshots.js","assets/describe-security-groups.js","assets/describe-route-tables.js","assets/describe-placement-groups.js","assets/describe-nat-gateways.js","assets/describe-load-balancers.js","assets/describe-key-pairs.js","assets/describe-internet-gateways.js","assets/describe-instances.js","assets/describe-images.js","assets/system-managed.js","assets/describe-addresses.js","assets/_.js","assets/_userName.js","assets/detail-row.js","assets/_policyArn.js","assets/_id16.js","assets/_id15.js","assets/_id14.js","assets/_id13.js","assets/tabs.js","assets/_id12.js","assets/_id11.js","assets/_id10.js","assets/_id9.js","assets/_id8.js","assets/_id7.js","assets/_id6.js","assets/_id5.js","assets/_id4.js","assets/_id3.js","assets/ami-details.js","assets/_id2.js","assets/_id.js"])))=>i.map(i=>d[i]); -var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var l=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},u=new class extends l{#e;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e();return window.addEventListener(`visibilitychange`,t,!1),()=>{window.removeEventListener(`visibilitychange`,t)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(e=>{typeof e==`boolean`?this.setFocused(e):this.onFocus()})}setFocused(e){this.#e!==e&&(this.#e=e,this.onFocus())}onFocus(){let e=this.isFocused();this.listeners.forEach(t=>{t(e)})}isFocused(){return typeof this.#e==`boolean`?this.#e:globalThis.document?.visibilityState!==`hidden`}},d={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},f=new class{#e=d;setTimeoutProvider(e){this.#e=e}setTimeout(e,t){return this.#e.setTimeout(e,t)}clearTimeout(e){this.#e.clearTimeout(e)}setInterval(e,t){return this.#e.setInterval(e,t)}clearInterval(e){this.#e.clearInterval(e)}};function p(e){setTimeout(e,0)}var m=typeof window>`u`||`Deno`in globalThis;function h(){}function g(e,t){return typeof e==`function`?e(t):e}function _(e){return typeof e==`number`&&e>=0&&e!==1/0}function v(e,t){return Math.max(e+(t||0)-Date.now(),0)}function y(e,t){return typeof e==`function`?e(t):e}function b(e,t){return typeof e==`function`?e(t):e}function x(e,t){let{type:n=`all`,exact:r,fetchStatus:i,predicate:a,queryKey:o,stale:s}=e;if(o){if(r){if(t.queryHash!==C(o,t.options))return!1}else if(!T(t.queryKey,o))return!1}if(n!==`all`){let e=t.isActive();if(n===`active`&&!e||n===`inactive`&&e)return!1}return!(typeof s==`boolean`&&t.isStale()!==s||i&&i!==t.state.fetchStatus||a&&!a(t))}function S(e,t){let{exact:n,status:r,predicate:i,mutationKey:a}=e;if(a){if(!t.options.mutationKey)return!1;if(n){if(w(t.options.mutationKey)!==w(a))return!1}else if(!T(t.options.mutationKey,a))return!1}return!(r&&t.state.status!==r||i&&!i(t))}function C(e,t){return(t?.queryKeyHashFn||w)(e)}function w(e){return JSON.stringify(e,(e,t)=>te(t)?Object.keys(t).sort().reduce((e,n)=>(e[n]=t[n],e),{}):t)}function T(e,t){return e===t?!0:typeof e==typeof t&&e&&t&&typeof e==`object`&&typeof t==`object`?Object.keys(t).every(n=>T(e[n],t[n])):!1}var E=Object.prototype.hasOwnProperty;function ee(e,t,n=0){if(e===t)return e;if(n>500)return t;let r=O(e)&&O(t);if(!r&&!(te(e)&&te(t)))return t;let i=(r?e:Object.keys(e)).length,a=r?t:Object.keys(t),o=a.length,s=r?Array(o):{},c=0;for(let l=0;l{f.setTimeout(t,e)})}function k(e,t,n){return typeof n.structuralSharing==`function`?n.structuralSharing(e,t):n.structuralSharing===!1?t:ee(e,t)}function A(e,t,n=0){let r=[...e,t];return n&&r.length>n?r.slice(1):r}function ie(e,t,n=0){let r=[t,...e];return n&&r.length>n?r.slice(0,-1):r}var j=Symbol();function ae(e,t){return!e.queryFn&&t?.initialPromise?()=>t.initialPromise:!e.queryFn||e.queryFn===j?()=>Promise.reject(Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function oe(e,t){return typeof e==`function`?e(...t):!!e}function se(e,t,n){let r=!1,i;return Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(i??=t(),r?i:(r=!0,i.aborted?n():i.addEventListener(`abort`,n,{once:!0}),i))}),e}var ce=(()=>{let e=()=>m;return{isServer(){return e()},setIsServer(t){e=t}}})();function M(){let e,t,n=new Promise((n,r)=>{e=n,t=r});n.status=`pending`,n.catch(()=>{});function r(e){Object.assign(n,e),delete n.resolve,delete n.reject}return n.resolve=t=>{r({status:`fulfilled`,value:t}),e(t)},n.reject=e=>{r({status:`rejected`,reason:e}),t(e)},n}var le=p;function ue(){let e=[],t=0,n=e=>{e()},r=e=>{e()},i=le,a=r=>{t?e.push(r):i(()=>{n(r)})},o=()=>{let t=e;e=[],t.length&&i(()=>{r(()=>{t.forEach(e=>{n(e)})})})};return{batch:e=>{let n;t++;try{n=e()}finally{t--,t||o()}return n},batchCalls:e=>(...t)=>{a(()=>{e(...t)})},schedule:a,setNotifyFunction:e=>{n=e},setBatchNotifyFunction:e=>{r=e},setScheduler:e=>{i=e}}}var N=ue(),de=new class extends l{#e=!0;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e(!0),n=()=>e(!1);return window.addEventListener(`online`,t,!1),window.addEventListener(`offline`,n,!1),()=>{window.removeEventListener(`online`,t),window.removeEventListener(`offline`,n)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(this.setOnline.bind(this))}setOnline(e){this.#e!==e&&(this.#e=e,this.listeners.forEach(t=>{t(e)}))}isOnline(){return this.#e}};function fe(e){return Math.min(1e3*2**e,3e4)}function pe(e){return(e??`online`)===`online`?de.isOnline():!0}var me=class extends Error{constructor(e){super(`CancelledError`),this.revert=e?.revert,this.silent=e?.silent}};function he(e){let t=!1,n=0,r,i=M(),a=()=>i.status!==`pending`,o=t=>{if(!a()){let n=new me(t);p(n),e.onCancel?.(n)}},s=()=>{t=!0},c=()=>{t=!1},l=()=>u.isFocused()&&(e.networkMode===`always`||de.isOnline())&&e.canRun(),d=()=>pe(e.networkMode)&&e.canRun(),f=e=>{a()||(r?.(),i.resolve(e))},p=e=>{a()||(r?.(),i.reject(e))},m=()=>new Promise(t=>{r=e=>{(a()||l())&&t(e)},e.onPause?.()}).then(()=>{r=void 0,a()||e.onContinue?.()}),h=()=>{if(a())return;let r,i=n===0?e.initialPromise:void 0;try{r=i??e.fn()}catch(e){r=Promise.reject(e)}Promise.resolve(r).then(f).catch(r=>{if(a())return;let i=e.retry??(ce.isServer()?0:3),o=e.retryDelay??fe,s=typeof o==`function`?o(n,r):o,c=i===!0||typeof i==`number`&&nl()?void 0:m()).then(()=>{t?p(r):h()})})};return{promise:i,status:()=>i.status,cancel:o,continue:()=>(r?.(),i),cancelRetry:s,continueRetry:c,canStart:d,start:()=>(d()?h():m().then(h),i)}}var ge=class{#e;destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),_(this.gcTime)&&(this.#e=f.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(ce.isServer()?1/0:300*1e3))}clearGcTimeout(){this.#e!==void 0&&(f.clearTimeout(this.#e),this.#e=void 0)}};function _e(e){return{onFetch:(t,n)=>{let r=t.options,i=t.fetchOptions?.meta?.fetchMore?.direction,a=t.state.data?.pages||[],o=t.state.data?.pageParams||[],s={pages:[],pageParams:[]},c=0,l=async()=>{let n=!1,l=e=>{se(e,()=>t.signal,()=>n=!0)},u=ae(t.options,t.fetchOptions),d=async(e,r,i)=>{if(n)return Promise.reject(t.signal.reason);if(r==null&&e.pages.length)return Promise.resolve(e);let a=await u((()=>{let e={client:t.client,queryKey:t.queryKey,pageParam:r,direction:i?`backward`:`forward`,meta:t.options.meta};return l(e),e})()),{maxPages:o}=t.options,s=i?ie:A;return{pages:s(e.pages,a,o),pageParams:s(e.pageParams,r,o)}};if(i&&a.length){let e=i===`backward`,t=e?ye:ve,n={pages:a,pageParams:o};s=await d(n,t(r,n),e)}else{let t=e??a.length;do{let e=c===0?o[0]??r.initialPageParam:ve(r,s);if(c>0&&e==null)break;s=await d(s,e),c++}while(ct.options.persister?.(l,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n):t.fetchFn=l}}}function ve(e,{pages:t,pageParams:n}){let r=t.length-1;return t.length>0?e.getNextPageParam(t[r],t,n[r],n):void 0}function ye(e,{pages:t,pageParams:n}){return t.length>0?e.getPreviousPageParam?.(t[0],t,n[0],n):void 0}var be=class extends ge{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e){super(),this.#s=!1,this.#o=e.defaultOptions,this.setOptions(e.options),this.observers=[],this.#i=e.client,this.#r=this.#i.getQueryCache(),this.queryKey=e.queryKey,this.queryHash=e.queryHash,this.#t=Ce(this.options),this.state=e.state??this.#t,this.scheduleGc()}get meta(){return this.options.meta}get queryType(){return this.#e}get promise(){return this.#a?.promise}setOptions(e){if(this.options={...this.#o,...e},e?._type&&(this.#e=e._type),this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){let e=Ce(this.options);e.data!==void 0&&(this.setState(Se(e.data,e.dataUpdatedAt)),this.#t=e)}}optionalRemove(){!this.observers.length&&this.state.fetchStatus===`idle`&&this.#r.remove(this)}setData(e,t){let n=k(this.state.data,e,this.options);return this.#l({data:n,type:`success`,dataUpdatedAt:t?.updatedAt,manual:t?.manual}),n}setState(e){this.#l({type:`setState`,state:e})}cancel(e){let t=this.#a?.promise;return this.#a?.cancel(e),t?t.then(h).catch(h):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}get resetState(){return this.#t}reset(){this.destroy(),this.setState(this.resetState)}isActive(){return this.observers.some(e=>b(e.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===j||!this.isFetched()}isFetched(){return this.state.dataUpdateCount+this.state.errorUpdateCount>0}isStatic(){return this.getObserversCount()>0?this.observers.some(e=>y(e.options.staleTime,this)===`static`):!1}isStale(){return this.getObserversCount()>0?this.observers.some(e=>e.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(e=0){return this.state.data===void 0?!0:e===`static`?!1:this.state.isInvalidated?!0:!v(this.state.dataUpdatedAt,e)}onFocus(){this.observers.find(e=>e.shouldFetchOnWindowFocus())?.refetch({cancelRefetch:!1}),this.#a?.continue()}onOnline(){this.observers.find(e=>e.shouldFetchOnReconnect())?.refetch({cancelRefetch:!1}),this.#a?.continue()}addObserver(e){this.observers.includes(e)||(this.observers.push(e),this.clearGcTimeout(),this.#r.notify({type:`observerAdded`,query:this,observer:e}))}removeObserver(e){this.observers.includes(e)&&(this.observers=this.observers.filter(t=>t!==e),this.observers.length||(this.#a&&(this.#s||this.#c()?this.#a.cancel({revert:!0}):this.#a.cancelRetry()),this.scheduleGc()),this.#r.notify({type:`observerRemoved`,query:this,observer:e}))}getObserversCount(){return this.observers.length}#c(){return this.state.fetchStatus===`paused`&&this.state.status===`pending`}invalidate(){this.state.isInvalidated||this.#l({type:`invalidate`})}async fetch(e,t){if(this.state.fetchStatus!==`idle`&&this.#a?.status()!==`rejected`){if(this.state.data!==void 0&&t?.cancelRefetch)this.cancel({silent:!0});else if(this.#a)return this.#a.continueRetry(),this.#a.promise}if(e&&this.setOptions(e),!this.options.queryFn){let e=this.observers.find(e=>e.options.queryFn);e&&this.setOptions(e.options)}let n=new AbortController,r=e=>{Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(this.#s=!0,n.signal)})},i=()=>{let e=ae(this.options,t),n=(()=>{let e={client:this.#i,queryKey:this.queryKey,meta:this.meta};return r(e),e})();return this.#s=!1,this.options.persister?this.options.persister(e,n,this):e(n)},a=(()=>{let e={fetchOptions:t,options:this.options,queryKey:this.queryKey,client:this.#i,state:this.state,fetchFn:i};return r(e),e})();(this.#e===`infinite`?_e(this.options.pages):this.options.behavior)?.onFetch(a,this),this.#n=this.state,(this.state.fetchStatus===`idle`||this.state.fetchMeta!==a.fetchOptions?.meta)&&this.#l({type:`fetch`,meta:a.fetchOptions?.meta}),this.#a=he({initialPromise:t?.initialPromise,fn:a.fetchFn,onCancel:e=>{e instanceof me&&e.revert&&this.setState({...this.#n,fetchStatus:`idle`}),n.abort()},onFail:(e,t)=>{this.#l({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#l({type:`pause`})},onContinue:()=>{this.#l({type:`continue`})},retry:a.options.retry,retryDelay:a.options.retryDelay,networkMode:a.options.networkMode,canRun:()=>!0});try{let e=await this.#a.start();if(e===void 0)throw Error(`${this.queryHash} data is undefined`);return this.setData(e),this.#r.config.onSuccess?.(e,this),this.#r.config.onSettled?.(e,this.state.error,this),e}catch(e){if(e instanceof me){if(e.silent)return this.#a.promise;if(e.revert){if(this.state.data===void 0)throw e;return this.state.data}}throw this.#l({type:`error`,error:e}),this.#r.config.onError?.(e,this),this.#r.config.onSettled?.(this.state.data,e,this),e}finally{this.scheduleGc()}}#l(e){let t=t=>{switch(e.type){case`failed`:return{...t,fetchFailureCount:e.failureCount,fetchFailureReason:e.error};case`pause`:return{...t,fetchStatus:`paused`};case`continue`:return{...t,fetchStatus:`fetching`};case`fetch`:return{...t,...xe(t.data,this.options),fetchMeta:e.meta??null};case`success`:let n={...t,...Se(e.data,e.dataUpdatedAt),dataUpdateCount:t.dataUpdateCount+1,...!e.manual&&{fetchStatus:`idle`,fetchFailureCount:0,fetchFailureReason:null}};return this.#n=e.manual?n:void 0,n;case`error`:let r=e.error;return{...t,error:r,errorUpdateCount:t.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:t.fetchFailureCount+1,fetchFailureReason:r,fetchStatus:`idle`,status:`error`,isInvalidated:!0};case`invalidate`:return{...t,isInvalidated:!0};case`setState`:return{...t,...e.state}}};this.state=t(this.state),N.batch(()=>{this.observers.forEach(e=>{e.onQueryUpdate()}),this.#r.notify({query:this,type:`updated`,action:e})})}};function xe(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:pe(t.networkMode)?`fetching`:`paused`,...e===void 0&&{error:null,status:`pending`}}}function Se(e,t){return{data:e,dataUpdatedAt:t??Date.now(),error:null,isInvalidated:!1,status:`success`}}function Ce(e){let t=typeof e.initialData==`function`?e.initialData():e.initialData,n=t!==void 0,r=n?typeof e.initialDataUpdatedAt==`function`?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?r??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?`success`:`pending`,fetchStatus:`idle`}}var we=class extends ge{#e;#t;#n;#r;constructor(e){super(),this.#e=e.client,this.mutationId=e.mutationId,this.#n=e.mutationCache,this.#t=[],this.state=e.state||Te(),this.setOptions(e.options),this.scheduleGc()}setOptions(e){this.options=e,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(e){this.#t.includes(e)||(this.#t.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,mutation:this,observer:e}))}removeObserver(e){this.#t=this.#t.filter(t=>t!==e),this.scheduleGc(),this.#n.notify({type:`observerRemoved`,mutation:this,observer:e})}optionalRemove(){this.#t.length||(this.state.status===`pending`?this.scheduleGc():this.#n.remove(this))}continue(){return this.#r?.continue()??this.execute(this.state.variables)}async execute(e){let t=()=>{this.#i({type:`continue`})},n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};this.#r=he({fn:()=>this.options.mutationFn?this.options.mutationFn(e,n):Promise.reject(Error(`No mutationFn found`)),onFail:(e,t)=>{this.#i({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#i({type:`pause`})},onContinue:t,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>this.#n.canRun(this)});let r=this.state.status===`pending`,i=!this.#r.canStart();try{if(r)t();else{this.#i({type:`pending`,variables:e,isPaused:i}),this.#n.config.onMutate&&await this.#n.config.onMutate(e,this,n);let t=await this.options.onMutate?.(e,n);t!==this.state.context&&this.#i({type:`pending`,context:t,variables:e,isPaused:i})}let a=await this.#r.start();return await this.#n.config.onSuccess?.(a,e,this.state.context,this,n),await this.options.onSuccess?.(a,e,this.state.context,n),await this.#n.config.onSettled?.(a,null,this.state.variables,this.state.context,this,n),await this.options.onSettled?.(a,null,e,this.state.context,n),this.#i({type:`success`,data:a}),a}catch(t){try{await this.#n.config.onError?.(t,e,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onError?.(t,e,this.state.context,n)}catch(e){Promise.reject(e)}try{await this.#n.config.onSettled?.(void 0,t,this.state.variables,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onSettled?.(void 0,t,e,this.state.context,n)}catch(e){Promise.reject(e)}throw this.#i({type:`error`,error:t}),t}finally{this.#n.runNext(this)}}#i(e){let t=t=>{switch(e.type){case`failed`:return{...t,failureCount:e.failureCount,failureReason:e.error};case`pause`:return{...t,isPaused:!0};case`continue`:return{...t,isPaused:!1};case`pending`:return{...t,context:e.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:e.isPaused,status:`pending`,variables:e.variables,submittedAt:Date.now()};case`success`:return{...t,data:e.data,failureCount:0,failureReason:null,error:null,status:`success`,isPaused:!1};case`error`:return{...t,data:void 0,error:e.error,failureCount:t.failureCount+1,failureReason:e.error,isPaused:!1,status:`error`}}};this.state=t(this.state),N.batch(()=>{this.#t.forEach(t=>{t.onMutationUpdate(e)}),this.#n.notify({mutation:this,type:`updated`,action:e})})}};function Te(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:`idle`,variables:void 0,submittedAt:0}}var Ee=class extends l{constructor(e={}){super(),this.config=e,this.#e=new Set,this.#t=new Map,this.#n=0}#e;#t;#n;build(e,t,n){let r=new we({client:e,mutationCache:this,mutationId:++this.#n,options:e.defaultMutationOptions(t),state:n});return this.add(r),r}add(e){this.#e.add(e);let t=De(e);if(typeof t==`string`){let n=this.#t.get(t);n?n.push(e):this.#t.set(t,[e])}this.notify({type:`added`,mutation:e})}remove(e){if(this.#e.delete(e)){let t=De(e);if(typeof t==`string`){let n=this.#t.get(t);if(n)if(n.length>1){let t=n.indexOf(e);t!==-1&&n.splice(t,1)}else n[0]===e&&this.#t.delete(t)}}this.notify({type:`removed`,mutation:e})}canRun(e){let t=De(e);if(typeof t==`string`){let n=this.#t.get(t)?.find(e=>e.state.status===`pending`);return!n||n===e}else return!0}runNext(e){let t=De(e);return typeof t==`string`?(this.#t.get(t)?.find(t=>t!==e&&t.state.isPaused))?.continue()??Promise.resolve():Promise.resolve()}clear(){N.batch(()=>{this.#e.forEach(e=>{this.notify({type:`removed`,mutation:e})}),this.#e.clear(),this.#t.clear()})}getAll(){return Array.from(this.#e)}find(e){let t={exact:!0,...e};return this.getAll().find(e=>S(t,e))}findAll(e={}){return this.getAll().filter(t=>S(e,t))}notify(e){N.batch(()=>{this.listeners.forEach(t=>{t(e)})})}resumePausedMutations(){let e=this.getAll().filter(e=>e.state.isPaused);return N.batch(()=>Promise.all(e.map(e=>e.continue().catch(h))))}};function De(e){return e.options.scope?.id}var Oe=class extends l{constructor(e={}){super(),this.config=e,this.#e=new Map}#e;build(e,t,n){let r=t.queryKey,i=t.queryHash??C(r,t),a=this.get(i);return a||(a=new be({client:e,queryKey:r,queryHash:i,options:e.defaultQueryOptions(t),state:n,defaultOptions:e.getQueryDefaults(r)}),this.add(a)),a}add(e){this.#e.has(e.queryHash)||(this.#e.set(e.queryHash,e),this.notify({type:`added`,query:e}))}remove(e){let t=this.#e.get(e.queryHash);t&&(e.destroy(),t===e&&this.#e.delete(e.queryHash),this.notify({type:`removed`,query:e}))}clear(){N.batch(()=>{this.getAll().forEach(e=>{this.remove(e)})})}get(e){return this.#e.get(e)}getAll(){return[...this.#e.values()]}find(e){let t={exact:!0,...e};return this.getAll().find(e=>x(t,e))}findAll(e={}){let t=this.getAll();return Object.keys(e).length>0?t.filter(t=>x(e,t)):t}notify(e){N.batch(()=>{this.listeners.forEach(t=>{t(e)})})}onFocus(){N.batch(()=>{this.getAll().forEach(e=>{e.onFocus()})})}onOnline(){N.batch(()=>{this.getAll().forEach(e=>{e.onOnline()})})}},ke=class{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e={}){this.#e=e.queryCache||new Oe,this.#t=e.mutationCache||new Ee,this.#n=e.defaultOptions||{},this.#r=new Map,this.#i=new Map,this.#a=0}mount(){this.#a++,this.#a===1&&(this.#o=u.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onFocus())}),this.#s=de.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onOnline())}))}unmount(){this.#a--,this.#a===0&&(this.#o?.(),this.#o=void 0,this.#s?.(),this.#s=void 0)}isFetching(e){return this.#e.findAll({...e,fetchStatus:`fetching`}).length}isMutating(e){return this.#t.findAll({...e,status:`pending`}).length}getQueryData(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state.data}ensureQueryData(e){let t=this.defaultQueryOptions(e),n=this.#e.build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(y(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return this.#e.findAll(e).map(({queryKey:e,state:t})=>[e,t.data])}setQueryData(e,t,n){let r=this.defaultQueryOptions({queryKey:e}),i=this.#e.get(r.queryHash)?.state.data,a=g(t,i);if(a!==void 0)return this.#e.build(this,r).setData(a,{...n,manual:!0})}setQueriesData(e,t,n){return N.batch(()=>this.#e.findAll(e).map(({queryKey:e})=>[e,this.setQueryData(e,t,n)]))}getQueryState(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state}removeQueries(e){let t=this.#e;N.batch(()=>{t.findAll(e).forEach(e=>{t.remove(e)})})}resetQueries(e,t){let n=this.#e;return N.batch(()=>(n.findAll(e).forEach(e=>{e.reset()}),this.refetchQueries({type:`active`,...e},t)))}cancelQueries(e,t={}){let n={revert:!0,...t},r=N.batch(()=>this.#e.findAll(e).map(e=>e.cancel(n)));return Promise.all(r).then(h).catch(h)}invalidateQueries(e,t={}){return N.batch(()=>(this.#e.findAll(e).forEach(e=>{e.invalidate()}),e?.refetchType===`none`?Promise.resolve():this.refetchQueries({...e,type:e?.refetchType??e?.type??`active`},t)))}refetchQueries(e,t={}){let n={...t,cancelRefetch:t.cancelRefetch??!0},r=N.batch(()=>this.#e.findAll(e).filter(e=>!e.isDisabled()&&!e.isStatic()).map(e=>{let t=e.fetch(void 0,n);return n.throwOnError||(t=t.catch(h)),e.state.fetchStatus===`paused`?Promise.resolve():t}));return Promise.all(r).then(h)}fetchQuery(e){let t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);let n=this.#e.build(this,t);return n.isStaleByTime(y(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(h).catch(h)}fetchInfiniteQuery(e){return e._type=`infinite`,this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(h).catch(h)}ensureInfiniteQueryData(e){return e._type=`infinite`,this.ensureQueryData(e)}resumePausedMutations(){return de.isOnline()?this.#t.resumePausedMutations():Promise.resolve()}getQueryCache(){return this.#e}getMutationCache(){return this.#t}getDefaultOptions(){return this.#n}setDefaultOptions(e){this.#n=e}setQueryDefaults(e,t){this.#r.set(w(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){let t=[...this.#r.values()],n={};return t.forEach(t=>{T(e,t.queryKey)&&Object.assign(n,t.defaultOptions)}),n}setMutationDefaults(e,t){this.#i.set(w(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){let t=[...this.#i.values()],n={};return t.forEach(t=>{T(e,t.mutationKey)&&Object.assign(n,t.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;let t={...this.#n.queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||=C(t.queryKey,t),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!==`always`),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode=`offlineFirst`),t.queryFn===j&&(t.enabled=!1),t}defaultMutationOptions(e){return e?._defaulted?e:{...this.#n.mutations,...e?.mutationKey&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){this.#e.clear(),this.#t.clear()}},Ae=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},T=Object.prototype.hasOwnProperty;function E(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function ee(e,t){return E(e.type,t,e.props)}function D(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function O(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var te=/\/+/g;function ne(e,t){return typeof e==`object`&&e&&e.key!=null?O(``+e.key):t.toString(36)}function re(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function k(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,k(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+ne(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(te,`$&/`)+`/`),k(o,r,i,``,function(e){return e})):o!=null&&(D(o)&&(o=ee(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(te,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=Ae()})),Me=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),Ne=o(((e,t)=>{t.exports=Me()})),P=c(je(),1),F=Ne(),Pe=P.createContext(void 0),Fe=e=>{let t=P.useContext(Pe);if(e)return e;if(!t)throw Error(`No QueryClient set, use QueryClientProvider to set one`);return t},Ie=({client:e,children:t})=>(P.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),(0,F.jsx)(Pe.Provider,{value:e,children:t}));function I(e){return e}var Le=P.use,Re=typeof window<`u`?P.useLayoutEffect:P.useEffect;function ze(e){let t=P.useRef({value:e,prev:null}),n=t.current.value;return e!==n&&(t.current={value:e,prev:n}),t.current.prev}function Be(e,t,n={},r={}){P.useEffect(()=>{if(!e.current||r.disabled||typeof IntersectionObserver!=`function`)return;let i=new IntersectionObserver(([e])=>{t(e)},n);return i.observe(e.current),()=>{i.disconnect()}},[t,n,r.disabled,e])}function Ve(e){let t=P.useRef(null);return P.useImperativeHandle(e,()=>t.current,[]),t}function He(e){return e[e.length-1]}function Ue(e){return typeof e==`function`}function We(e,t){return Ue(e)?e(t):e}var Ge=Object.prototype.hasOwnProperty,Ke=Object.prototype.propertyIsEnumerable;function qe(e){for(let t in e)if(Ge.call(e,t))return!0;return!1}var Je=()=>Object.create(null),Ye=(e,t)=>Xe(e,t,Je);function Xe(e,t,n=()=>({}),r=0){if(e===t)return e;if(r>500)return t;let i=t,a=et(e)&&et(i);if(!a&&!(Qe(e)&&Qe(i)))return i;let o=a?e:Ze(e);if(!o)return i;let s=a?i:Ze(i);if(!s)return i;let c=o.length,l=s.length,u=a?Array(l):n(),d=0;for(let t=0;ti||!tt(e[o],t[o],n)))return!1;return i===a}return!1}function nt(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.status=`pending`,r.resolve=n=>{r.status=`resolved`,r.value=n,t(n),e?.(n)},r.reject=e=>{r.status=`rejected`,n(e)},r}function rt(e){return typeof e?.message==`string`?e.message.startsWith(`Failed to fetch dynamically imported module`)||e.message.startsWith(`error loading dynamically imported module`)||e.message.startsWith(`Importing a module script failed`):!1}function it(e){return!!(e&&typeof e==`object`&&typeof e.then==`function`)}function at(e){return e.replace(/[\x00-\x1f\x7f]/g,``)}function ot(e){let t;try{t=decodeURI(e)}catch{t=e.replaceAll(/%[0-9A-F]{2}/gi,e=>{try{return decodeURI(e)}catch{return e}})}return at(t)}var st=[`http:`,`https:`,`mailto:`,`tel:`];function ct(e,t){if(!e)return!1;try{let n=new URL(e);return!t.has(n.protocol)}catch{return!1}}var lt={"&":`\\u0026`,">":`\\u003e`,"<":`\\u003c`,"\u2028":`\\u2028`,"\u2029":`\\u2029`},ut=/[&><\u2028\u2029]/g;function dt(e){return e.replace(ut,e=>lt[e])}function ft(e){if(!e||!/[%\\\x00-\x1f\x7f]/.test(e)&&!e.startsWith(`//`))return{path:e,handledProtocolRelativeURL:!1};let t=/%25|%5C/gi,n=0,r=``,i;for(;(i=t.exec(e))!==null;)r+=ot(e.slice(n,i.index))+i[0],n=t.lastIndex;r+=ot(n?e.slice(n):e);let a=!1;return r.startsWith(`//`)&&(a=!0,r=`/`+r.replace(/^\/+/,``)),{path:r,handledProtocolRelativeURL:a}}function pt(e){return/\s|[^\u0000-\u007F]/.test(e)?e.replace(/\s|[^\u0000-\u007F]/gu,encodeURIComponent):e}function mt(e,t){if(e===t)return!0;if(e.length!==t.length)return!1;for(let n=0;n{e.next&&(e.prev?(e.prev.next=e.next,e.next.prev=e.prev,e.next=void 0,r&&(r.next=e,e.prev=r)):(e.next.prev=void 0,n=e.next,e.next=void 0,r&&(e.prev=r,r.next=e)),r=e)};return{get(e){let n=t.get(e);if(n)return i(n),n.value},set(a,o){if(t.size>=e&&n){let e=n;t.delete(e.key),e.next&&(n=e.next,e.next.prev=void 0),e===r&&(r=void 0)}let s=t.get(a);if(s)s.value=o,i(s);else{let e={key:a,value:o,prev:r};r&&(r.next=e),r=e,n||=e,t.set(a,e)}},clear(){t.clear(),n=void 0,r=void 0}}}var _t=4,vt=5;function yt(e){let t=e.indexOf(`{`);if(t===-1)return null;let n=e.indexOf(`}`,t);return n===-1||t+1>=e.length?null:[t,n]}function bt(e,t,n=new Uint16Array(6)){let r=e.indexOf(`/`,t),i=r===-1?e.length:r,a=e.substring(t,i);if(!a||!a.includes(`$`))return n[0]=0,n[1]=t,n[2]=t,n[3]=i,n[4]=i,n[5]=i,n;if(a===`$`){let r=e.length;return n[0]=2,n[1]=t,n[2]=t,n[3]=r,n[4]=r,n[5]=r,n}if(a.charCodeAt(0)===36)return n[0]=1,n[1]=t,n[2]=t+1,n[3]=i,n[4]=i,n[5]=i,n;let o=yt(a);if(o){let[r,s]=o,c=a.charCodeAt(r+1);if(c===45){if(r+2!e.parse&&e.caseSensitive===f&&e.prefix===p&&e.suffix===m);if(h)o=h;else{let e=Tt(1,n.fullPath??n.from,f,p,m);o=e,e.depth=a,e.parent=i,i.dynamic??=[],i.dynamic.push(e)}break}case 3:{let t=r.substring(u,e[1]),s=r.substring(e[4],d),f=c&&!!(t||s),p=t?f?t:t.toLowerCase():void 0,m=s?f?s:s.toLowerCase():void 0,h=!l&&i.optional?.find(e=>!e.parse&&e.caseSensitive===f&&e.prefix===p&&e.suffix===m);if(h)o=h;else{let e=Tt(3,n.fullPath??n.from,f,p,m);o=e,e.parent=i,e.depth=a,i.optional??=[],i.optional.push(e)}break}case 2:{let t=r.substring(u,e[1]),s=r.substring(e[4],d),l=c&&!!(t||s),f=t?l?t:t.toLowerCase():void 0,p=s?l?s:s.toLowerCase():void 0,m=Tt(2,n.fullPath??n.from,l,f,p);o=m,m.parent=i,m.depth=a,i.wildcard??=[],i.wildcard.push(m)}}i=o}if(l&&n.children&&!n.isRoot&&n.id&&n.id.charCodeAt(n.id.lastIndexOf(`/`)+1)===95){let e=wt(n.fullPath??n.from);e.kind=vt,e.parent=i,a++,e.depth=a,i.pathless??=[],i.pathless.push(e),i=e}let u=(n.path||!n.children)&&!n.isRoot;if(u&&r.endsWith(`/`)){let e=wt(n.fullPath??n.from);e.kind=_t,e.parent=i,a++,e.depth=a,i.index=e,i=e}i.parse=l??null,u&&!i.route&&(i.route=n,i.fullPath=n.fullPath??n.from)}if(n.children)for(let r of n.children)xt(e,t,r,s,i,a,o)}function St(e,t){if(e.parse&&!t.parse)return-1;if(!e.parse&&t.parse)return 1;if(e.prefix&&t.prefix&&e.prefix!==t.prefix){if(e.prefix.startsWith(t.prefix))return-1;if(t.prefix.startsWith(e.prefix))return 1}if(e.suffix&&t.suffix&&e.suffix!==t.suffix){if(e.suffix.endsWith(t.suffix))return-1;if(t.suffix.endsWith(e.suffix))return 1}return e.prefix&&!t.prefix?-1:!e.prefix&&t.prefix?1:e.suffix&&!t.suffix?-1:!e.suffix&&t.suffix?1:e.caseSensitive&&!t.caseSensitive?-1:!e.caseSensitive&&t.caseSensitive?1:0}function Ct(e){if(e.pathless)for(let t of e.pathless)Ct(t);if(e.static)for(let t of e.static.values())Ct(t);if(e.staticInsensitive)for(let t of e.staticInsensitive.values())Ct(t);if(e.dynamic?.length){e.dynamic.sort(St);for(let t of e.dynamic)Ct(t)}if(e.optional?.length){e.optional.sort(St);for(let t of e.optional)Ct(t)}if(e.wildcard?.length){e.wildcard.sort(St);for(let t of e.wildcard)Ct(t)}}function wt(e){return{kind:0,depth:0,pathless:null,index:null,static:null,staticInsensitive:null,dynamic:null,optional:null,wildcard:null,route:null,fullPath:e,parent:null,parse:null}}function Tt(e,t,n,r,i){return{kind:e,depth:0,pathless:null,index:null,static:null,staticInsensitive:null,dynamic:null,optional:null,wildcard:null,route:null,fullPath:t,parent:null,parse:null,caseSensitive:n,prefix:r,suffix:i}}function Et(e,t){let n=wt(`/`),r=new Uint16Array(6);for(let t of e)xt(!1,r,t,1,n,0);Ct(n),t.masksTree=n,t.flatCache=gt(1e3)}function Dt(e,t){e||=`/`;let n=t.flatCache.get(e);if(n)return n;let r=Mt(e,t.masksTree);return t.flatCache.set(e,r),r}function Ot(e,t,n,r,i){e||=`/`,r||=`/`;let a=t?`case\0${e}`:e,o=i.singleCache.get(a);return o||(o=wt(`/`),xt(t,new Uint16Array(6),{from:e},1,o,0),i.singleCache.set(a,o)),Mt(r,o,n)}function kt(e,t,n=!1){let r=n?e:`nofuzz\0${e}`,i=t.matchCache.get(r);if(i!==void 0)return i;e||=`/`;let a;try{a=Mt(e,t.segmentTree,n)}catch(e){if(e instanceof URIError)a=null;else throw e}return a&&(a.branch=Pt(a.route)),t.matchCache.set(r,a),a}function At(e){return e===`/`?e:e.replace(/\/{1,}$/,``)}function jt(e,t=!1,n){let r=wt(e.fullPath),i=new Uint16Array(6),a={},o={},s=0;return xt(t,i,e,1,r,0,e=>{if(n?.(e,s),e.id in a&&ht(),a[e.id]=e,s!==0&&e.path){let t=At(e.fullPath);(!o[t]||e.fullPath.endsWith(`/`))&&(o[t]=e)}s++}),Ct(r),{processedTree:{segmentTree:r,singleCache:gt(1e3),matchCache:gt(1e3),flatCache:null,masksTree:null},routesById:a,routesByPath:o}}function Mt(e,t,n=!1){let r=e.split(`/`),i=It(e,r,t,n);if(!i)return null;let[a]=Nt(e,r,i);return{route:i.node.route,rawParams:a}}function Nt(e,t,n){let r=Ft(n.node),i=null,a=Object.create(null),o=n.extract?.part??0,s=n.extract?.node??0,c=n.extract?.path??0,l=n.extract?.segment??0;for(;s=0;e--){let n=i.wildcard[e],{prefix:r,suffix:a}=n;if(!(r&&(v||!(n.caseSensitive?y:b??=y.toLowerCase()).startsWith(r)))){if(a){if(v)continue;let e=t.slice(u).join(`/`).slice(-a.length);if((n.caseSensitive?e:e.toLowerCase())!==a)continue}s.push({node:n,index:o,skipped:d,depth:f+1,statics:p,dynamics:m,optionals:h,extract:g,rawParams:_})}}if(i.optional){let e=d|1<=0;n--){let r=i.optional[n];s.push({node:r,index:u,skipped:e,depth:t,statics:p,dynamics:m,optionals:h,extract:g,rawParams:_})}if(!v)for(let e=i.optional.length-1;e>=0;e--){let n=i.optional[e],{prefix:r,suffix:a}=n;if(r||a){let e=n.caseSensitive?y:b??=y.toLowerCase();if(r&&!e.startsWith(r)||a&&!e.endsWith(a))continue}s.push({node:n,index:u+1,skipped:d,depth:t,statics:p,dynamics:m,optionals:h+Lt(o,u),extract:g,rawParams:_})}}if(!v&&i.dynamic&&y)for(let e=i.dynamic.length-1;e>=0;e--){let t=i.dynamic[e],{prefix:n,suffix:r}=t;if(n||r){let e=t.caseSensitive?y:b??=y.toLowerCase();if(n&&!e.startsWith(n)||r&&!e.endsWith(r))continue}s.push({node:t,index:u+1,skipped:d,depth:f+1,statics:p,dynamics:m+Lt(o,u),optionals:h,extract:g,rawParams:_})}if(!v&&i.staticInsensitive){let e=i.staticInsensitive.get(b??=y.toLowerCase());e&&s.push({node:e,index:u+1,skipped:d,depth:f+1,statics:p+Lt(o,u),dynamics:m,optionals:h,extract:g,rawParams:_})}if(!v&&i.static){let e=i.static.get(y);e&&s.push({node:e,index:u+1,skipped:d,depth:f+1,statics:p+Lt(o,u),dynamics:m,optionals:h,extract:g,rawParams:_})}if(i.pathless){let e=f+1;for(let t=i.pathless.length-1;t>=0;t--){let n=i.pathless[t];s.push({node:n,index:u,skipped:d,depth:e,statics:p,dynamics:m,optionals:h,extract:g,rawParams:_})}}}if(l)return l;if(r&&c){let n=c.index;for(let e=0;ee.statics||t.statics===e.statics&&(t.dynamics>e.dynamics||t.dynamics===e.dynamics&&(t.optionals>e.optionals||t.optionals===e.optionals&&((t.node.kind===_t)>(e.node.kind===_t)||t.node.kind===_t==(e.node.kind===_t)&&t.depth>e.depth))):!0}function Vt(e){return Ht(e.filter(e=>e!==void 0).join(`/`))}function Ht(e){return e.replace(/\/{2,}/g,`/`)}function Ut(e){return e===`/`?e:e.replace(/^\/{1,}/,``)}function Wt(e){let t=e.length;return t>1&&e[t-1]===`/`?e.replace(/\/{1,}$/,``):e}function Gt(e){return Wt(Ut(e))}function Kt(e,t){return e?.endsWith(`/`)&&e!==`/`&&e!==`${t}/`?e.slice(0,-1):e}function qt(e,t,n){return Kt(e,n)===Kt(t,n)}function Jt({base:e,to:t,trailingSlash:n=`never`,cache:r}){let i=t.startsWith(`/`),a=!i&&t===`.`,o;if(r){o=i?t:a?e:e+`\0`+t;let n=r.get(o);if(n)return n}let s;if(a)s=e.split(`/`);else if(i)s=t.split(`/`);else{for(s=e.split(`/`);s.length>1&&He(s)===``;)s.pop();let n=t.split(`/`);for(let e=0,t=n.length;e1&&(He(s)===``?n===`never`&&s.pop():n===`always`&&s.push(``));let c=Ht(s.join(`/`))||`/`;return o&&r&&r.set(o,c),c}function Yt(e){let t=new Map(e.map(e=>[encodeURIComponent(e),e])),n=Array.from(t.keys()).map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)).join(`|`),r=new RegExp(n,`g`);return e=>e.replace(r,e=>t.get(e)??e)}function Xt(e,t,n){let r=t[e];return typeof r==`string`?e===`_splat`?/^[a-zA-Z0-9\-._~!/]*$/.test(r)?r:r.split(`/`).map(e=>Qt(e,n)).join(`/`):Qt(r,n):r}function Zt({path:e,params:t,decoder:n,...r}){let i=!1,a=Object.create(null);if(!e||e===`/`)return{interpolatedPath:`/`,usedParams:a,isMissingParams:i};if(!e.includes(`$`))return{interpolatedPath:e,usedParams:a,isMissingParams:i};let o=e.length,s=0,c,l=``;for(;s{t=We(e,t)||t},persist:()=>{try{e.setItem(tn,JSON.stringify(t))}catch{}}}}var rn=nn(),an=e=>e.state.__TSR_key||e.href;function on(e){let t=[],n;for(;n=e.parentNode;)t.push(`${e.tagName}:nth-child(${Array.prototype.indexOf.call(n.children,e)+1})`),e=n;return`${t.reverse().join(` > `)}`.toLowerCase()}var sn=!1,cn=`window`,ln=`data-scroll-restoration-id`;function un(e,t){if(!rn)return;let n=rn;if((t??e.options.scrollRestoration??!1)&&(e.isScrollRestoring=!0),e.isScrollRestorationSetup||!n)return;e.isScrollRestorationSetup=!0,sn=!1;let r=e.options.getScrollRestorationKey||an,i=new Map;window.history.scrollRestoration=`manual`;let a=t=>{if(!(sn||!e.isScrollRestoring))if(t.target===document||t.target===window)i.set(cn,{scrollX:window.scrollX||0,scrollY:window.scrollY||0});else{let e=t.target;i.set(e,{scrollX:e.scrollLeft||0,scrollY:e.scrollTop||0})}},o=t=>{if(!e.isScrollRestoring||!t||i.size===0||!n)return;let r=n.state[t]||={};for(let[e,t]of i){let n;if(e===cn)n=cn;else if(e.isConnected){let t=e.getAttribute(ln);n=t?`[${ln}="${t}"]`:on(e)}n&&(r[n]=t)}};document.addEventListener(`scroll`,a,!0),e.subscribe(`onBeforeLoad`,e=>{o(e.fromLocation?r(e.fromLocation):void 0),i.clear()}),window.addEventListener(`pagehide`,()=>{o(r(e.stores.resolvedLocation.get()??e.stores.location.get())),n.persist()}),e.subscribe(`onRendered`,t=>{let a=r(t.toLocation),o=e.options.scrollRestorationBehavior,s=e.options.scrollToTopSelectors;if(i.clear(),!e.resetNextScroll){e.resetNextScroll=!0;return}if(!(typeof e.options.scrollRestoration==`function`&&!e.options.scrollRestoration({location:e.latestLocation}))){sn=!0;try{let t=e.isScrollRestoring?n.state[a]:void 0,r=!1;if(t)for(let e in t){let n=t[e];if(!Qe(n))continue;let{scrollX:i,scrollY:a}=n;if(!(!Number.isFinite(i)||!Number.isFinite(a))){if(e===cn)window.scrollTo({top:a,left:i,behavior:o}),r=!0;else if(e){let t;try{t=document.querySelector(e)}catch{continue}t&&(t.scrollLeft=i,t.scrollTop=a,r=!0)}}}if(!r){let t=e.history.location.hash.slice(1);if(t){let e=window.history.state?.__hashScrollIntoViewOptions??!0;if(e){let n=document.getElementById(t);n&&n.scrollIntoView(e)}}else{let e={top:0,left:0,behavior:o};if(window.scrollTo(e),s)for(let t of s){if(t===cn)continue;let n=typeof t==`function`?t():document.querySelector(t);n&&n.scrollTo(e)}}}}finally{sn=!1}e.isScrollRestoring&&n.set(e=>(e[a]||={},e))}})}function dn(e,t=String){let n=new URLSearchParams;for(let r in e){let i=e[r];i!==void 0&&n.set(r,t(i))}return n.toString()}function fn(e){return e?e===`false`?!1:e===`true`?!0:e*0==0&&+e+``===e?+e:e:``}function pn(e){let t=new URLSearchParams(e),n=Object.create(null);for(let[e,r]of t.entries()){let t=n[e];t==null?n[e]=fn(r):Array.isArray(t)?t.push(fn(r)):n[e]=[t,fn(r)]}return n}var mn=gn(JSON.parse),hn=_n(JSON.stringify,JSON.parse);function gn(e){return t=>{t[0]===`?`&&(t=t.substring(1));let n=pn(t);for(let t in n){let r=n[t];if(typeof r==`string`)try{n[t]=e(r)}catch{}}return n}}function _n(e,t){let n=typeof t==`function`;function r(r){if(typeof r==`object`&&r)try{return e(r)}catch{}else if(n&&typeof r==`string`)try{return t(r),e(r)}catch{}return r}return e=>{let t=dn(e,r);return t?`?${t}`:``}}var vn=`__root__`;function yn(e){if(e.statusCode=e.statusCode||e.code||307,!e._builtLocation&&!e.reloadDocument&&typeof e.href==`string`)try{new URL(e.href),e.reloadDocument=!0}catch{}let t=new Headers(e.headers);e.href&&t.get(`Location`)===null&&t.set(`Location`,e.href);let n=new Response(null,{status:e.statusCode,headers:t});if(n.options=e,e.throw)throw n;return n}function bn(e){return e instanceof Response&&!!e.options}function xn(e){return{input:({url:t})=>{for(let n of e)t=Cn(n,t);return t},output:({url:t})=>{for(let n=e.length-1;n>=0;n--)t=wn(e[n],t);return t}}}function Sn(e){let t=Gt(e.basepath),n=`/${t}`,r=`${n}/`,i=e.caseSensitive?n:n.toLowerCase(),a=e.caseSensitive?r:r.toLowerCase();return{input:({url:t})=>{let r=e.caseSensitive?t.pathname:t.pathname.toLowerCase();return r===i?t.pathname=`/`:r.startsWith(a)&&(t.pathname=t.pathname.slice(n.length)),t},output:({url:e})=>(e.pathname=Vt([`/`,t,e.pathname]),e)}}function Cn(e,t){let n=e?.input?.({url:t});if(n){if(typeof n==`string`)return new URL(n);if(n instanceof URL)return n}return t}function wn(e,t){let n=e?.output?.({url:t});if(n){if(typeof n==`string`)return new URL(n);if(n instanceof URL)return n}return t}function Tn(e,t){let{createMutableStore:n,createReadonlyStore:r,batch:i,init:a}=t,o=new Map,s=new Map,c=new Map,l=n(e.status),u=n(e.loadedAt),d=n(e.isLoading),f=n(e.isTransitioning),p=n(e.location),m=n(e.resolvedLocation),h=n(e.statusCode),g=n(e.redirect),_=n([]),v=n([]),y=n([]),b=r(()=>En(o,_.get())),x=r(()=>En(s,v.get())),S=r(()=>En(c,y.get())),C=r(()=>_.get()[0]),w=r(()=>_.get().some(e=>o.get(e)?.get().status===`pending`)),T=r(()=>({locationHref:p.get().href,resolvedLocationHref:m.get()?.href,status:l.get()})),E=r(()=>({status:l.get(),loadedAt:u.get(),isLoading:d.get(),isTransitioning:f.get(),matches:b.get(),location:p.get(),resolvedLocation:m.get(),statusCode:h.get(),redirect:g.get()})),ee=gt(64);function D(e){let t=ee.get(e);return t||(t=r(()=>{let t=_.get();for(let n of t){let t=o.get(n);if(t&&t.routeId===e)return t.get()}}),ee.set(e,t)),t}let O={status:l,loadedAt:u,isLoading:d,isTransitioning:f,location:p,resolvedLocation:m,statusCode:h,redirect:g,matchesId:_,pendingIds:v,cachedIds:y,matches:b,pendingMatches:x,cachedMatches:S,firstId:C,hasPending:w,matchRouteDeps:T,matchStores:o,pendingMatchStores:s,cachedMatchStores:c,__store:E,getRouteMatchStore:D,setMatches:te,setPending:ne,setCached:re};te(e.matches),a?.(O);function te(e){Dn(e,o,_,n,i)}function ne(e){Dn(e,s,v,n,i)}function re(e){Dn(e,c,y,n,i)}return O}function En(e,t){let n=[];for(let r of t){let t=e.get(r);t&&n.push(t.get())}return n}function Dn(e,t,n,r,i){let a=e.map(e=>e.id),o=new Set(a);i(()=>{for(let e of t.keys())o.has(e)||t.delete(e);for(let n of e){let e=t.get(n.id);if(!e){let e=r(n);e.routeId=n.routeId,t.set(n.id,e);continue}e.routeId=n.routeId,e.get()!==n&&e.set(n)}mt(n.get(),a)||n.set(a)})}var On=e=>{if(!e.rendered)return e.rendered=!0,e.onReady?.()},kn=e=>e.stores.matchesId.get().some(t=>e.stores.matchStores.get(t)?.get()._forcePending),An=(e,t)=>!!(e.preload&&!e.router.stores.matchStores.has(t)),jn=(e,t,n=!0)=>{let r={...e.router.options.context??{}},i=n?t:t-1;for(let t=0;t<=i;t++){let n=e.matches[t];if(!n)continue;let i=e.router.getMatch(n.id);i&&Object.assign(r,i.__routeContext,i.__beforeLoadContext)}return r},Mn=(e,t)=>{if(!e.matches.length)return;let n=t.routeId,r=e.matches.findIndex(t=>t.routeId===e.router.routeTree.id),i=r>=0?r:0,a=n?e.matches.findIndex(e=>e.routeId===n):e.firstBadMatchIndex??e.matches.length-1;a<0&&(a=i);for(let t=a;t>=0;t--){let n=e.matches[t];if(e.router.looseRoutesById[n.routeId].options.notFoundComponent)return t}return n?a:i},Nn=(e,t,n)=>{if(!(!bn(n)&&!$t(n)))throw bn(n)&&n.redirectHandled&&!n.options.reloadDocument?n:(t&&(t._nonReactive.beforeLoadPromise?.resolve(),t._nonReactive.loaderPromise?.resolve(),t._nonReactive.beforeLoadPromise=void 0,t._nonReactive.loaderPromise=void 0,t._nonReactive.error=n,e.updateMatch(t.id,r=>({...r,status:bn(n)?`redirected`:$t(n)?`notFound`:r.status===`pending`?`success`:r.status,context:jn(e,t.index),isFetching:!1,error:n})),$t(n)&&!n.routeId&&(n.routeId=t.routeId),t._nonReactive.loadPromise?.resolve()),bn(n)&&(e.rendered=!0,n.options._fromLocation=e.location,n.redirectHandled=!0,n=e.router.resolveRedirect(n)),n)},Pn=(e,t)=>{let n=e.router.getMatch(t);return!!(!n||n._nonReactive.dehydrated)},Fn=(e,t,n)=>{let r=jn(e,n);e.updateMatch(t,e=>({...e,context:r}))},In=(e,t,n,r)=>{let{id:i,routeId:a}=e.matches[t],o=e.router.looseRoutesById[a];if(n instanceof Promise)throw n;n.routerCode=r,e.firstBadMatchIndex??=t,Nn(e,e.router.getMatch(i),n);try{o.options.onError?.(n)}catch(t){n=t,Nn(e,e.router.getMatch(i),n)}e.updateMatch(i,e=>(e._nonReactive.beforeLoadPromise?.resolve(),e._nonReactive.beforeLoadPromise=void 0,e._nonReactive.loadPromise?.resolve(),{...e,error:n,status:`error`,isFetching:!1,updatedAt:Date.now(),abortController:new AbortController})),!e.preload&&!bn(n)&&!$t(n)&&(e.serialError??=n)},Ln=(e,t,n,r)=>{if(r._nonReactive.pendingTimeout!==void 0)return;let i=n.options.pendingMs??e.router.options.defaultPendingMs;if(e.onReady&&!An(e,t)&&(n.options.loader||n.options.beforeLoad||Jn(n))&&typeof i==`number`&&i!==1/0&&(n.options.pendingComponent??e.router.options?.defaultPendingComponent)){let t=setTimeout(()=>{On(e)},i);r._nonReactive.pendingTimeout=t}},Rn=(e,t,n)=>{let r=e.router.getMatch(t);if(!r._nonReactive.beforeLoadPromise&&!r._nonReactive.loaderPromise)return;Ln(e,t,n,r);let i=()=>{let n=e.router.getMatch(t);n.preload&&(n.status===`redirected`||n.status===`notFound`)&&Nn(e,n,n.error)};return r._nonReactive.beforeLoadPromise?r._nonReactive.beforeLoadPromise.then(i):i()},zn=(e,t,n,r)=>{let i=e.router.getMatch(t),a=i._nonReactive.loadPromise;i._nonReactive.loadPromise=nt(()=>{a?.resolve(),a=void 0});let{paramsError:o,searchError:s}=i;o&&In(e,n,o,`PARSE_PARAMS`),s&&In(e,n,s,`VALIDATE_SEARCH`),Ln(e,t,r,i);let c=new AbortController,l=!1,u=()=>{l||(l=!0,e.updateMatch(t,e=>({...e,isFetching:`beforeLoad`,fetchCount:e.fetchCount+1,abortController:c})))},d=()=>{i._nonReactive.beforeLoadPromise?.resolve(),i._nonReactive.beforeLoadPromise=void 0,e.updateMatch(t,e=>({...e,isFetching:!1}))};if(!r.options.beforeLoad){e.router.batch(()=>{u(),d()});return}i._nonReactive.beforeLoadPromise=nt();let f={...jn(e,n,!1),...i.__routeContext},{search:p,params:m,cause:h}=i,g=An(e,t),_={search:p,abortController:c,params:m,preload:g,context:f,location:e.location,navigate:t=>e.router.navigate({...t,_fromLocation:e.location}),buildLocation:e.router.buildLocation,cause:g?`preload`:h,matches:e.matches,routeId:r.id,...e.router.options.additionalContext},v=r=>{if(r===void 0){e.router.batch(()=>{u(),d()});return}(bn(r)||$t(r))&&(u(),In(e,n,r,`BEFORE_LOAD`)),e.router.batch(()=>{u(),e.updateMatch(t,e=>({...e,__beforeLoadContext:r})),d()})},y;try{if(y=r.options.beforeLoad(_),it(y))return u(),y.catch(t=>{In(e,n,t,`BEFORE_LOAD`)}).then(v)}catch(t){u(),In(e,n,t,`BEFORE_LOAD`)}v(y)},Bn=(e,t)=>{let{id:n,routeId:r}=e.matches[t],i=e.router.looseRoutesById[r],a=()=>s(),o=()=>zn(e,n,t,i),s=()=>{if(Pn(e,n))return;let t=Rn(e,n,i);return it(t)?t.then(o):o()};return a()},Vn=(e,t,n)=>{let r=e.router.getMatch(t);if(!r||!n.options.head&&!n.options.scripts&&!n.options.headers)return;let i={ssr:e.router.options.ssr,matches:e.matches,match:r,params:r.params,loaderData:r.loaderData};return Promise.all([n.options.head?.(i),n.options.scripts?.(i),n.options.headers?.(i)]).then(([e,t,n])=>({meta:e?.meta,links:e?.links,headScripts:e?.scripts,headers:n,scripts:t,styles:e?.styles}))},Hn=(e,t,n,r,i)=>{let a=t[r-1],{params:o,loaderDeps:s,abortController:c,cause:l}=e.router.getMatch(n),u=jn(e,r),d=An(e,n);return{params:o,deps:s,preload:!!d,parentMatchPromise:a,abortController:c,context:u,location:e.location,navigate:t=>e.router.navigate({...t,_fromLocation:e.location}),cause:d?`preload`:l,route:i,...e.router.options.additionalContext}},Un=async(e,t,n,r,i)=>{try{let a=e.router.getMatch(n);try{qn(i);let o=i.options.loader,s=typeof o==`function`?o:o?.handler,c=s?.(Hn(e,t,n,r,i)),l=!!s&&it(c);if((l||i._lazyPromise||i._componentsPromise||i.options.head||i.options.scripts||i.options.headers||a._nonReactive.minPendingPromise)&&e.updateMatch(n,e=>({...e,isFetching:`loader`})),s){let t=l?await c:c;Nn(e,e.router.getMatch(n),t),t!==void 0&&e.updateMatch(n,e=>({...e,loaderData:t}))}i._lazyPromise&&await i._lazyPromise;let u=a._nonReactive.minPendingPromise;u&&await u,i._componentsPromise&&await i._componentsPromise,e.updateMatch(n,t=>({...t,error:void 0,context:jn(e,r),status:`success`,isFetching:!1,updatedAt:Date.now()}))}catch(t){let o=t;if(o?.name===`AbortError`){if(a.abortController.signal.aborted){a._nonReactive.loaderPromise?.resolve(),a._nonReactive.loaderPromise=void 0;return}e.updateMatch(n,t=>({...t,status:t.status===`pending`?`success`:t.status,isFetching:!1,context:jn(e,r)}));return}let s=a._nonReactive.minPendingPromise;s&&await s,$t(t)&&await i.options.notFoundComponent?.preload?.(),Nn(e,e.router.getMatch(n),t);try{i.options.onError?.(t)}catch(t){o=t,Nn(e,e.router.getMatch(n),t)}!bn(o)&&!$t(o)&&await qn(i,[`errorComponent`]),e.updateMatch(n,t=>({...t,error:o,context:jn(e,r),status:`error`,isFetching:!1}))}}catch(t){let r=e.router.getMatch(n);r&&(r._nonReactive.loaderPromise=void 0),Nn(e,r,t)}},Wn=async(e,t,n)=>{async function r(r,a,c,l,d){let f=Date.now()-a.updatedAt,p=r?d.options.preloadStaleTime??e.router.options.defaultPreloadStaleTime??3e4:d.options.staleTime??e.router.options.defaultStaleTime??0,m=d.options.shouldReload,h=typeof m==`function`?m(Hn(e,t,i,n,d)):m,{status:g,invalid:_}=l,v=f>=p&&(!!e.forceStaleReload||l.cause===`enter`||c!==void 0&&c!==l.id);o=g===`success`&&(_||(h??v)),r&&d.options.preload===!1||(o&&!e.sync&&u?(s=!0,(async()=>{try{await Un(e,t,i,n,d);let r=e.router.getMatch(i);r._nonReactive.loaderPromise?.resolve(),r._nonReactive.loadPromise?.resolve(),r._nonReactive.loaderPromise=void 0,r._nonReactive.loadPromise=void 0}catch(t){bn(t)&&await e.router.navigate(t.options)}})()):g!==`success`||o?await Un(e,t,i,n,d):Fn(e,i,n))}let{id:i,routeId:a}=e.matches[n],o=!1,s=!1,c=e.router.looseRoutesById[a],l=c.options.loader,u=((typeof l==`function`?void 0:l?.staleReloadMode)??e.router.options.defaultStaleReloadMode)!==`blocking`;if(Pn(e,i)){if(!e.router.getMatch(i))return e.matches[n];Fn(e,i,n)}else{let t=e.router.getMatch(i),o=e.router.stores.matchesId.get()[n],s=(o&&e.router.stores.matchStores.get(o)||null)?.routeId===a?o:e.router.stores.matches.get().find(e=>e.routeId===a)?.id,l=An(e,i);if(t._nonReactive.loaderPromise){if(t.status===`success`&&!e.sync&&!t.preload&&u)return t;await t._nonReactive.loaderPromise;let n=e.router.getMatch(i),a=n._nonReactive.error||n.error;a&&Nn(e,n,a),n.status===`pending`&&await r(l,t,s,n,c)}else{let n=l&&!e.router.stores.matchStores.has(i),a=e.router.getMatch(i);a._nonReactive.loaderPromise=nt(),n!==a.preload&&e.updateMatch(i,e=>({...e,preload:n})),await r(l,t,s,a,c)}}let d=e.router.getMatch(i);s||(d._nonReactive.loaderPromise?.resolve(),d._nonReactive.loadPromise?.resolve(),d._nonReactive.loadPromise=void 0),clearTimeout(d._nonReactive.pendingTimeout),d._nonReactive.pendingTimeout=void 0,s||(d._nonReactive.loaderPromise=void 0),d._nonReactive.dehydrated=void 0;let f=s?d.isFetching:!1;return f!==d.isFetching||d.invalid!==!1?(e.updateMatch(i,e=>({...e,isFetching:f,invalid:!1})),e.router.getMatch(i)):d};async function Gn(e){let t=e,n=[];kn(t.router)&&On(t);let r;for(let e=0;e({...e,...a?{status:`success`,globalNotFound:!0,error:void 0}:{status:`notFound`,error:l},isFetching:!1})),u=e,await qn(r,[`notFoundComponent`])}else if(!t.preload){let e=t.matches[0];e.globalNotFound||t.router.getMatch(e.id)?.globalNotFound&&t.updateMatch(e.id,e=>({...e,globalNotFound:!1,error:void 0}))}if(t.serialError&&t.firstBadMatchIndex!==void 0){let e=t.router.looseRoutesById[t.matches[t.firstBadMatchIndex].routeId];await qn(e,[`errorComponent`])}for(let e=0;e<=u;e++){let{id:n,routeId:r}=t.matches[e],i=t.router.looseRoutesById[r];try{let e=Vn(t,n,i);if(e){let r=await e;t.updateMatch(n,e=>({...e,...r}))}}catch(e){console.error(`Error executing head for route ${r}:`,e)}}let d=On(t);if(it(d)&&await d,l)throw l;if(t.serialError&&!t.preload&&!t.onReady)throw t.serialError;return t.matches}function Kn(e,t){let n=t.map(t=>e.options[t]?.preload?.()).filter(Boolean);if(n.length!==0)return Promise.all(n)}function qn(e,t=Yn){!e._lazyLoaded&&e._lazyPromise===void 0&&(e.lazyFn?e._lazyPromise=e.lazyFn().then(t=>{let{id:n,...r}=t.options;Object.assign(e.options,r),e._lazyLoaded=!0,e._lazyPromise=void 0}):e._lazyLoaded=!0);let n=()=>e._componentsLoaded?void 0:t===Yn?(()=>{if(e._componentsPromise===void 0){let t=Kn(e,Yn);t?e._componentsPromise=t.then(()=>{e._componentsLoaded=!0,e._componentsPromise=void 0}):e._componentsLoaded=!0}return e._componentsPromise})():Kn(e,t);return e._lazyPromise?e._lazyPromise.then(n):n()}function Jn(e){for(let t of Yn)if(e.options[t]?.preload)return!0;return!1}var Yn=[`component`,`errorComponent`,`pendingComponent`,`notFoundComponent`],Xn=`__TSR_index`,Zn=`popstate`,Qn=`beforeunload`;function $n(e){let t=e.getLocation(),n=new Set,r=r=>{t=e.getLocation(),n.forEach(e=>e({location:t,action:r}))},i=n=>{e.notifyOnIndexChange??!0?r(n):t=e.getLocation()},a=async({task:n,navigateOpts:r,...i})=>{if(r?.ignoreBlocker??!1){n();return}let a=e.getBlockers?.()??[],o=i.type===`PUSH`||i.type===`REPLACE`;if(typeof document<`u`&&a.length&&o)for(let n of a){let r=rr(i.path,i.state);if(await n.blockerFn({currentLocation:t,nextLocation:r,action:i.type})){e.onBlocked?.();return}}n()};return{get location(){return t},get length(){return e.getLength()},subscribers:n,subscribe:e=>(n.add(e),()=>{n.delete(e)}),push:(n,i,o)=>{let s=t.state[Xn];i=er(s+1,i),a({task:()=>{e.pushState(n,i),r({type:`PUSH`})},navigateOpts:o,type:`PUSH`,path:n,state:i})},replace:(n,i,o)=>{let s=t.state[Xn];i=er(s,i),a({task:()=>{e.replaceState(n,i),r({type:`REPLACE`})},navigateOpts:o,type:`REPLACE`,path:n,state:i})},go:(t,n)=>{a({task:()=>{e.go(t),i({type:`GO`,index:t})},navigateOpts:n,type:`GO`})},back:t=>{a({task:()=>{e.back(t?.ignoreBlocker??!1),i({type:`BACK`})},navigateOpts:t,type:`BACK`})},forward:t=>{a({task:()=>{e.forward(t?.ignoreBlocker??!1),i({type:`FORWARD`})},navigateOpts:t,type:`FORWARD`})},canGoBack:()=>t.state[Xn]!==0,createHref:t=>e.createHref(t),block:t=>{if(!e.setBlockers)return()=>{};let n=e.getBlockers?.()??[];return e.setBlockers([...n,t]),()=>{let n=e.getBlockers?.()??[];e.setBlockers?.(n.filter(e=>e!==t))}},flush:()=>e.flush?.(),destroy:()=>e.destroy?.(),notify:r}}function er(e,t){t||={};let n=ir();return{...t,key:n,__TSR_key:n,[Xn]:e}}function tr(e){let t=e?.window??(typeof document<`u`?window:void 0),n=t.history.pushState,r=t.history.replaceState,i=[],a=()=>i,o=e=>i=e,s=e?.createHref??(e=>e),c=e?.parseLocation??(()=>rr(`${t.location.pathname}${t.location.search}${t.location.hash}`,t.history.state));if(!t.history.state?.__TSR_key&&!t.history.state?.key){let e=ir();t.history.replaceState({[Xn]:0,key:e,__TSR_key:e},``)}let l=c(),u,d=!1,f=!1,p=!1,m=!1,h=()=>l,g,_,v=()=>{g&&(C._ignoreSubscribers=!0,(g.isPush?t.history.pushState:t.history.replaceState)(g.state,``,g.href),C._ignoreSubscribers=!1,g=void 0,_=void 0,u=void 0)},y=(e,t,n)=>{let r=s(t);_||(u=l),l=rr(t,n),g={href:r,state:n,isPush:g?.isPush||e===`push`},_||=Promise.resolve().then(()=>v())},b=e=>{l=c(),C.notify({type:e})},x=async()=>{if(f){f=!1;return}let e=c(),n=e.state[Xn]-l.state[Xn],r=n===1,i=n===-1,o=!r&&!i||d;d=!1;let s=o?`GO`:i?`BACK`:`FORWARD`,u=o?{type:`GO`,index:n}:{type:i?`BACK`:`FORWARD`};if(p)p=!1;else{let n=a();if(typeof document<`u`&&n.length){for(let r of n)if(await r.blockerFn({currentLocation:l,nextLocation:e,action:s})){f=!0,t.history.go(1),C.notify(u);return}}}l=c(),C.notify(u)},S=e=>{if(m){m=!1;return}let t=!1,n=a();if(typeof document<`u`&&n.length)for(let e of n){let n=e.enableBeforeUnload??!0;if(n===!0){t=!0;break}if(typeof n==`function`&&n()===!0){t=!0;break}}if(t)return e.preventDefault(),e.returnValue=``},C=$n({getLocation:h,getLength:()=>t.history.length,pushState:(e,t)=>y(`push`,e,t),replaceState:(e,t)=>y(`replace`,e,t),back:e=>(e&&(p=!0),m=!0,t.history.back()),forward:e=>{e&&(p=!0),m=!0,t.history.forward()},go:e=>{d=!0,t.history.go(e)},createHref:e=>s(e),flush:v,destroy:()=>{t.history.pushState=n,t.history.replaceState=r,t.removeEventListener(Qn,S,{capture:!0}),t.removeEventListener(Zn,x)},onBlocked:()=>{u&&l!==u&&(l=u)},getBlockers:a,setBlockers:o,notifyOnIndexChange:!1});return t.addEventListener(Qn,S,{capture:!0}),t.addEventListener(Zn,x),t.history.pushState=function(...e){let r=n.apply(t.history,e);return C._ignoreSubscribers||b(`PUSH`),r},t.history.replaceState=function(...e){let n=r.apply(t.history,e);return C._ignoreSubscribers||b(`REPLACE`),n},C}function nr(e){let t=e.replace(/[\x00-\x1f\x7f]/g,``);return t.startsWith(`//`)&&(t=`/`+t.replace(/^\/+/,``)),t}function rr(e,t){let n=nr(e),r=n.indexOf(`#`),i=n.indexOf(`?`),a=ir();return{href:n,pathname:n.substring(0,r>0?i>0?Math.min(r,i):r:i>0?i:n.length),hash:r>-1?n.substring(r):``,search:i>-1?n.slice(i,r===-1?void 0:r):``,state:t||{[Xn]:0,key:a,__TSR_key:a}}}function ir(){return(Math.random()+1).toString(36).substring(7)}function ar(e,t){let n=t,r=e;return{fromLocation:n,toLocation:r,pathChanged:n?.pathname!==r.pathname,hrefChanged:n?.href!==r.href,hashChanged:n?.hash!==r.hash}}var or=class{constructor(e,t){this.tempLocationKey=`${Math.round(Math.random()*1e7)}`,this.resetNextScroll=!0,this.shouldViewTransition=void 0,this.isViewTransitionTypesSupported=void 0,this.subscribers=new Set,this.isScrollRestoring=!1,this.isScrollRestorationSetup=!1,this.routeBranchCache=new WeakMap,this.startTransition=e=>e(),this.update=e=>{let t=this.options,n=this.basepath??t?.basepath??`/`,r=this.basepath===void 0,i=t?.rewrite;if(this.options={...t,...e},this.isServer=this.options.isServer??typeof document>`u`,this.protocolAllowlist=new Set(this.options.protocolAllowlist),this.options.pathParamsAllowedCharacters&&(this.pathParamsDecoder=Yt(this.options.pathParamsAllowedCharacters)),(!this.history||this.options.history&&this.options.history!==this.history)&&(this.options.history?this.history=this.options.history:this.history=tr()),this.origin=this.options.origin,this.origin||(window?.origin&&window.origin!==`null`?this.origin=window.origin:this.origin=`http://localhost`),this.history&&this.updateLatestLocation(),this.options.routeTree!==this.routeTree){this.routeTree=this.options.routeTree;let e;this.resolvePathCache=gt(1e3),e=this.buildRouteTree(),this.setRoutes(e)}if(!this.stores&&this.latestLocation){let e=this.getStoreConfig(this);this.batch=e.batch,this.stores=Tn(lr(this.latestLocation),e),un(this)}let a=!1,o=this.options.basepath??`/`,s=this.options.rewrite;if(r||n!==o||i!==s){this.basepath=o;let e=[],t=Gt(o);t&&t!==`/`&&e.push(Sn({basepath:o})),s&&e.push(s),this.rewrite=e.length===0?void 0:e.length===1?e[0]:xn(e),this.history&&this.updateLatestLocation(),a=!0}a&&this.stores&&this.stores.location.set(this.latestLocation),typeof window<`u`&&`CSS`in window&&typeof window.CSS?.supports==`function`&&(this.isViewTransitionTypesSupported=window.CSS.supports(`selector(:active-view-transition-type(a))`))},this.updateLatestLocation=()=>{this.latestLocation=this.parseLocation(this.history.location,this.latestLocation)},this.buildRouteTree=()=>{let e=jt(this.routeTree,this.options.caseSensitive,(e,t)=>{e.init({originalIndex:t})});return this.options.routeMasks&&Et(this.options.routeMasks,e.processedTree),e},this.subscribe=(e,t)=>{let n={eventType:e,fn:t};return this.subscribers.add(n),()=>{this.subscribers.delete(n)}},this.emit=e=>{this.subscribers.forEach(t=>{t.eventType===e.type&&t.fn(e)})},this.parseLocation=(e,t)=>{let n=({pathname:e,search:n,hash:r,href:i,state:a})=>{if(!this.rewrite&&!/[ \x00-\x1f\x7f\u0080-\uffff]/.test(e)){let i=this.options.parseSearch(n),o=this.options.stringifySearch(i);return{href:e+o+r,publicHref:e+o+r,pathname:ft(e).path,external:!1,searchStr:o,search:Ye(t?.search,i),hash:ft(r.slice(1)).path,state:Xe(t?.state,a)}}let o=new URL(i,this.origin),s=Cn(this.rewrite,o),c=this.options.parseSearch(s.search),l=this.options.stringifySearch(c);return s.search=l,{href:s.href.replace(s.origin,``),publicHref:i,pathname:ft(s.pathname).path,external:!!this.rewrite&&s.origin!==this.origin,searchStr:l,search:Ye(t?.search,c),hash:ft(s.hash.slice(1)).path,state:Xe(t?.state,a)}},r=n(e),{__tempLocation:i,__tempKey:a}=r.state;if(i&&(!a||a===this.tempLocationKey)){let e=n(i);return e.state.key=r.state.key,e.state.__TSR_key=r.state.__TSR_key,delete e.state.__tempLocation,{...e,maskedLocation:r}}return r},this.resolvePathWithBase=(e,t)=>Jt({base:e,to:t.includes(`//`)?Ht(t):t,trailingSlash:this.options.trailingSlash,cache:this.resolvePathCache}),this.matchRoutes=(e,t,n)=>typeof e==`string`?this.matchRoutesInternal({pathname:e,search:t},n):this.matchRoutesInternal(e,t),this.getMatchedRoutes=e=>dr({pathname:e,routesById:this.routesById,processedTree:this.processedTree}),this.cancelMatch=e=>{let t=this.getMatch(e);t&&(t.abortController.abort(),clearTimeout(t._nonReactive.pendingTimeout),t._nonReactive.pendingTimeout=void 0)},this.cancelMatches=()=>{this.stores.pendingIds.get().forEach(e=>{this.cancelMatch(e)}),this.stores.matchesId.get().forEach(e=>{if(this.stores.pendingMatchStores.has(e))return;let t=this.stores.matchStores.get(e)?.get();t&&(t.status===`pending`||t.isFetching===`loader`)&&this.cancelMatch(e)})},this.buildLocation=e=>{let t=(t={})=>{let n=t._fromLocation||this.pendingBuiltLocation||this.latestLocation,r=this.matchRoutesLightweight(n);t.from;let i=t.unsafeRelative===`path`?n.pathname:t.from??r.fullPath,a=t.to?`${t.to}`:void 0,o=r.search,s=Object.assign(Object.create(null),r.params),c=a?.charCodeAt(0)===47?`/`:this.resolvePathWithBase(i,`.`),l=a?this.resolvePathWithBase(c,a):c,u=t.params===!1||t.params===null?Object.create(null):(t.params??!0)===!0?s:Object.assign(s,We(t.params,s)),d=this.routesByPath[Wt(l)],f;if(d)f=this.getRouteBranch(d);else if(l.includes(`$`))f=[];else{let e=this.getMatchedRoutes(l);f=e.matchedRoutes,this.options.notFoundRoute&&(!e.foundRoute||e.foundRoute.path!==`/`&&e.routeParams[`**`])&&(f=[...f,this.options.notFoundRoute])}if(f.length&&qe(u))for(let e of f){let t=e.options.params?.stringify??e.options.stringifyParams;if(t)try{Object.assign(u,t(u))}catch{}}let p=e.leaveParams?l:ft(Zt({path:l,params:u,decoder:this.pathParamsDecoder,server:this.isServer}).interpolatedPath).path,m=o;if(e._includeValidateSearch&&this.options.search?.strict){let e={};f.forEach(t=>{if(t.options.validateSearch)try{Object.assign(e,ur(t.options.validateSearch,{...e,...m}))}catch{}}),m=e}m=fr({search:m,dest:t,destRoutes:f,_includeValidateSearch:e._includeValidateSearch}),m=Ye(o,m);let h=this.options.stringifySearch(m),g=t.hash===!0?n.hash:t.hash?We(t.hash,n.hash):void 0,_=g?`#${g}`:``,v=t.state===!0?n.state:t.state?We(t.state,n.state):{};v=Xe(n.state,v);let y=`${p}${h}${_}`,b,x,S=!1;if(this.rewrite){let e=new URL(y,this.origin),t=wn(this.rewrite,e);b=e.href.replace(e.origin,``),t.origin===this.origin?x=t.pathname+t.search+t.hash:(x=t.href,S=!0)}else b=pt(y),x=b;return{publicHref:x,href:b,pathname:p,search:m,searchStr:h,state:v,hash:g??``,external:S,unmaskOnReload:t.unmaskOnReload}},n=(n={},r)=>{let i=t(n),a=r?t(r):void 0;if(!a){let n=Object.create(null);if(this.options.routeMasks){let o=Dt(i.pathname,this.processedTree);if(o){Object.assign(n,o.rawParams);let{from:i,params:s,...c}=o.route,l=s===!1||s===null?Object.create(null):(s??!0)===!0?n:Object.assign(n,We(s,n));r={from:e.from,...c,params:l},a=t(r)}}}return a&&(i.maskedLocation=a),i};return e.mask?n(e,{from:e.from,...e.mask}):n(e)},this.commitLocation=async({viewTransition:e,ignoreBlocker:t,...n})=>{let r=()=>{let e=[`key`,`__TSR_key`,`__TSR_index`,`__hashScrollIntoViewOptions`];e.forEach(e=>{n.state[e]=this.latestLocation.state[e]});let t=tt(n.state,this.latestLocation.state);return e.forEach(e=>{delete n.state[e]}),t},i=Wt(this.latestLocation.href)===Wt(n.href),a=this.commitLocationPromise;if(this.commitLocationPromise=nt(()=>{a?.resolve(),a=void 0}),i&&r())this.load();else{let{maskedLocation:r,hashScrollIntoView:i,...a}=n;r&&(a={...r,state:{...r.state,__tempKey:void 0,__tempLocation:{...a,search:a.searchStr,state:{...a.state,__tempKey:void 0,__tempLocation:void 0,__TSR_key:void 0,key:void 0}}}},(a.unmaskOnReload??this.options.unmaskOnReload??!1)&&(a.state.__tempKey=this.tempLocationKey)),a.state.__hashScrollIntoViewOptions=i??this.options.defaultHashScrollIntoView??!0,this.shouldViewTransition=e,this.history[n.replace?`replace`:`push`](a.publicHref,a.state,{ignoreBlocker:t})}return this.resetNextScroll=n.resetScroll??!0,this.history.subscribers.size||this.load(),this.commitLocationPromise},this.buildAndCommitLocation=({replace:e,resetScroll:t,hashScrollIntoView:n,viewTransition:r,ignoreBlocker:i,href:a,...o}={})=>{if(a){let t=this.history.location.state.__TSR_index,n=rr(a,{__TSR_index:e?t:t+1}),r=new URL(n.pathname,this.origin);o.to=Cn(this.rewrite,r).pathname,o.search=this.options.parseSearch(n.search),o.hash=n.hash.slice(1)}let s=this.buildLocation({...o,_includeValidateSearch:!0});this.pendingBuiltLocation=s;let c=this.commitLocation({...s,viewTransition:r,replace:e,resetScroll:t,hashScrollIntoView:n,ignoreBlocker:i});return Promise.resolve().then(()=>{this.pendingBuiltLocation===s&&(this.pendingBuiltLocation=void 0)}),c},this.navigate=async({to:e,reloadDocument:t,href:n,publicHref:r,...i})=>{let a=!1;if(n)try{new URL(`${n}`),a=!0}catch{}if(a&&!t&&(t=!0),t){if(e!==void 0||!n){let t=this.buildLocation({to:e,...i});n??=t.publicHref,r??=t.publicHref}let t=!a&&r?r:n;if(ct(t,this.protocolAllowlist))return Promise.resolve();if(!i.ignoreBlocker){let e=this.history.getBlockers?.()??[];for(let t of e)if(t?.blockerFn&&await t.blockerFn({currentLocation:this.latestLocation,nextLocation:this.latestLocation,action:`PUSH`}))return Promise.resolve()}return i.replace?window.location.replace(t):window.location.href=t,Promise.resolve()}return this.buildAndCommitLocation({...i,href:n,to:e,_isNavigate:!0})},this.beforeLoad=()=>{this.cancelMatches(),this.updateLatestLocation();let e=this.matchRoutes(this.latestLocation),t=this.stores.cachedMatches.get().filter(t=>!e.some(e=>e.id===t.id));this.batch(()=>{this.stores.status.set(`pending`),this.stores.statusCode.set(200),this.stores.isLoading.set(!0),this.stores.location.set(this.latestLocation),this.stores.setPending(e),this.stores.setCached(t)})},this.load=async e=>{let t,n,r,i=this.stores.resolvedLocation.get()??this.stores.location.get();for(r=new Promise(a=>{this.startTransition(async()=>{try{this.beforeLoad();let t=this.latestLocation,n=ar(t,this.stores.resolvedLocation.get());this.stores.redirect.get()||this.emit({type:`onBeforeNavigate`,...n}),this.emit({type:`onBeforeLoad`,...n}),await Gn({router:this,sync:e?.sync,forceStaleReload:i.href===t.href,matches:this.stores.pendingMatches.get(),location:t,updateMatch:this.updateMatch,onReady:async()=>{this.startTransition(()=>{this.startViewTransition(async()=>{let e=null,t=null,n=null,r=null;this.batch(()=>{let i=this.stores.pendingMatches.get(),a=i.length,o=this.stores.matches.get();e=a?o.filter(e=>!this.stores.pendingMatchStores.has(e.id)):null;let s=new Set;for(let e of this.stores.pendingMatchStores.values())e.routeId&&s.add(e.routeId);let c=new Set;for(let e of this.stores.matchStores.values())e.routeId&&c.add(e.routeId);t=a?o.filter(e=>!s.has(e.routeId)):null,n=a?i.filter(e=>!c.has(e.routeId)):null,r=a?i.filter(e=>c.has(e.routeId)):o,this.stores.isLoading.set(!1),this.stores.loadedAt.set(Date.now()),a&&(this.stores.setMatches(i),this.stores.setPending([]),this.stores.setCached([...this.stores.cachedMatches.get(),...e.filter(e=>e.status!==`error`&&e.status!==`notFound`&&e.status!==`redirected`)]),this.clearExpiredCache())});for(let[e,i]of[[t,`onLeave`],[n,`onEnter`],[r,`onStay`]])if(e)for(let t of e)this.looseRoutesById[t.routeId].options[i]?.(t)})})}})}catch(e){bn(e)?(t=e,this.navigate({...t.options,replace:!0,ignoreBlocker:!0})):$t(e)&&(n=e);let r=t?t.status:n?404:this.stores.matches.get().some(e=>e.status===`error`)?500:200;this.batch(()=>{this.stores.statusCode.set(r),this.stores.redirect.set(t)})}this.latestLoadPromise===r&&(this.commitLocationPromise?.resolve(),this.latestLoadPromise=void 0,this.commitLocationPromise=void 0),a()})}),this.latestLoadPromise=r,await r;this.latestLoadPromise&&r!==this.latestLoadPromise;)await this.latestLoadPromise;let a;this.hasNotFoundMatch()?a=404:this.stores.matches.get().some(e=>e.status===`error`)&&(a=500),a!==void 0&&this.stores.statusCode.set(a)},this.startViewTransition=e=>{let t=this.shouldViewTransition??this.options.defaultViewTransition;if(this.shouldViewTransition=void 0,t&&typeof document<`u`&&`startViewTransition`in document&&typeof document.startViewTransition==`function`){let n;if(typeof t==`object`&&this.isViewTransitionTypesSupported){let r=this.latestLocation,i=this.stores.resolvedLocation.get(),a=typeof t.types==`function`?t.types(ar(r,i)):t.types;if(a===!1){e();return}n={update:e,types:a}}else n=e;document.startViewTransition(n)}else e()},this.updateMatch=(e,t)=>{this.startTransition(()=>{let n=this.stores.pendingMatchStores.get(e);if(n){n.set(t);return}let r=this.stores.matchStores.get(e);if(r){r.set(t);return}let i=this.stores.cachedMatchStores.get(e);if(i){let n=t(i.get());n.status===`redirected`?this.stores.cachedMatchStores.delete(e)&&this.stores.cachedIds.set(t=>t.filter(t=>t!==e)):i.set(n)}})},this.getMatch=e=>this.stores.cachedMatchStores.get(e)?.get()??this.stores.pendingMatchStores.get(e)?.get()??this.stores.matchStores.get(e)?.get(),this.invalidate=e=>{let t=t=>e?.filter?.(t)??!0?{...t,invalid:!0,...e?.forcePending||t.status===`error`||t.status===`notFound`?{status:`pending`,error:void 0}:void 0}:t;return this.batch(()=>{this.stores.setMatches(this.stores.matches.get().map(t)),this.stores.setCached(this.stores.cachedMatches.get().map(t)),this.stores.setPending(this.stores.pendingMatches.get().map(t))}),this.shouldViewTransition=!1,this.load({sync:e?.sync})},this.getParsedLocationHref=e=>e.publicHref||`/`,this.resolveRedirect=e=>{let t=e.headers.get(`Location`);if(!e.options.href||e.options._builtLocation){let t=e.options._builtLocation??this.buildLocation(e.options),n=this.getParsedLocationHref(t);e.options.href=n,e.headers.set(`Location`,n)}else if(t)try{let n=new URL(t);if(this.origin&&n.origin===this.origin){let t=n.pathname+n.search+n.hash;e.options.href=t,e.headers.set(`Location`,t)}}catch{}if(e.options.href&&!e.options._builtLocation&&ct(e.options.href,this.protocolAllowlist))throw Error(`Redirect blocked: unsafe protocol`);return e.headers.get(`Location`)||e.headers.set(`Location`,e.options.href),e},this.clearCache=e=>{let t=e?.filter;t===void 0?this.stores.setCached([]):this.stores.setCached(this.stores.cachedMatches.get().filter(e=>!t(e)))},this.clearExpiredCache=()=>{let e=Date.now();this.clearCache({filter:t=>{let n=this.looseRoutesById[t.routeId];if(!n.options.loader)return!0;let r=(t.preload?n.options.preloadGcTime??this.options.defaultPreloadGcTime:n.options.gcTime??this.options.defaultGcTime)??300*1e3;return t.status===`error`?!0:e-t.updatedAt>=r}})},this.loadRouteChunk=qn,this.preloadRoute=async e=>{let t=e._builtLocation??this.buildLocation(e),n=this.matchRoutes(t,{throwOnError:!0,preload:!0,dest:e}),r=new Set([...this.stores.matchesId.get(),...this.stores.pendingIds.get()]),i=new Set([...r,...this.stores.cachedIds.get()]),a=n.filter(e=>!i.has(e.id));if(a.length){let e=this.stores.cachedMatches.get();this.stores.setCached([...e,...a])}try{return n=await Gn({router:this,matches:n,location:t,preload:!0,updateMatch:(e,t)=>{r.has(e)?n=n.map(n=>n.id===e?t(n):n):this.updateMatch(e,t)}}),n}catch(e){if(bn(e))return e.options.reloadDocument?void 0:await this.preloadRoute({...e.options,_fromLocation:t});$t(e)||console.error(e);return}},this.matchRoute=(e,t)=>{let n={...e,to:e.to?this.resolvePathWithBase(e.from||``,e.to):void 0,params:e.params||{},leaveParams:!0},r=this.buildLocation(n);if(t?.pending&&this.stores.status.get()!==`pending`)return!1;let i=(t?.pending===void 0?!this.stores.isLoading.get():t.pending)?this.latestLocation:this.stores.resolvedLocation.get()||this.stores.location.get(),a=Ot(r.pathname,t?.caseSensitive??!1,t?.fuzzy??!1,i.pathname,this.processedTree);return!a||e.params&&!tt(a.rawParams,e.params,{partial:!0})?!1:t?.includeSearch??!0?tt(i.search,r.search,{partial:!0})?a.rawParams:!1:a.rawParams},this.hasNotFoundMatch=()=>this.stores.matches.get().some(e=>e.status===`notFound`||e.globalNotFound),this.getStoreConfig=t,this.update({defaultPreloadDelay:50,defaultPendingMs:1e3,defaultPendingMinMs:500,context:void 0,...e,caseSensitive:e.caseSensitive??!1,notFoundMode:e.notFoundMode??`fuzzy`,stringifySearch:e.stringifySearch??hn,parseSearch:e.parseSearch??mn,protocolAllowlist:e.protocolAllowlist??st}),typeof document<`u`&&(self.__TSR_ROUTER__=this)}isShell(){return!!this.options.isShell}isPrerendering(){return!!this.options.isPrerendering}get state(){return this.stores.__store.get()}setRoutes({routesById:e,routesByPath:t,processedTree:n}){this.routesById=e,this.routesByPath=t,this.processedTree=n;let r=this.options.notFoundRoute;r&&(r.init({originalIndex:99999999999}),this.routesById[r.id]=r)}getRouteBranch(e){let t=this.routeBranchCache.get(e);return t||(t=Pt(e),this.routeBranchCache.set(e,t)),t}get looseRoutesById(){return this.routesById}getParentContext(e){return e?.id?e.context??this.options.context??void 0:this.options.context??void 0}matchRoutesInternal(e,t){let n=this.getMatchedRoutes(e.pathname),{foundRoute:r,routeParams:i}=n,{matchedRoutes:a}=n,o=!1;(r?r.path!==`/`&&i[`**`]:Wt(e.pathname))&&(this.options.notFoundRoute?a=[...a,this.options.notFoundRoute]:o=!0);let s=o?mr(this.options.notFoundMode,a):void 0,c=Array(a.length),l=new Map;for(let e of this.stores.matchStores.values())e.routeId&&l.set(e.routeId,e.get());for(let n=0;nthis.navigate({...t,_fromLocation:e}),buildLocation:this.buildLocation,cause:n.cause,abortController:n.abortController,preload:!!n.preload,matches:c,routeId:r.id};n.__routeContext=r.options.context(t)??void 0}n.context={...a,...n.__routeContext,...n.__beforeLoadContext}}}return c}matchRoutesLightweight(e){let{matchedRoutes:t,routeParams:n}=this.getMatchedRoutes(e.pathname),r=He(t),i={...e.search};for(let e of t)try{Object.assign(i,ur(e.options.validateSearch,i))}catch{}let a=He(this.stores.matchesId.get()),o=a&&this.stores.matchStores.get(a)?.get(),s=o&&o.routeId===r.id&&o.pathname===e.pathname,c;if(s)c=o.params;else{let e=Object.assign(Object.create(null),n);for(let n of t)try{hr(n,e)}catch{}c=e}return{matchedRoutes:t,fullPath:r.fullPath,search:i,params:c}}},sr=class extends Error{},cr=class extends Error{};function lr(e){return{loadedAt:0,isLoading:!1,isTransitioning:!1,status:`idle`,resolvedLocation:void 0,location:e,matches:[],statusCode:200}}function ur(e,t){if(e==null)return{};if(`~standard`in e){let n=e[`~standard`].validate(t);if(n instanceof Promise)throw new sr(`Async validation not supported`);if(n.issues)throw new sr(JSON.stringify(n.issues,void 0,2),{cause:n});return n.value}return`parse`in e?e.parse(t):typeof e==`function`?e(t):{}}function dr({pathname:e,routesById:t,processedTree:n}){let r=Object.create(null),i=Wt(e),a,o=kt(i,n,!0);return o&&(a=o.route,Object.assign(r,o.rawParams)),{matchedRoutes:o?.branch||[t.__root__],routeParams:r,foundRoute:a}}function fr({search:e,dest:t,destRoutes:n,_includeValidateSearch:r}){return pr(n)(e,t,r??!1)}function pr(e){let t={dest:null,_includeValidateSearch:!1,middlewares:[]};for(let n of e)`search`in n.options?n.options.search?.middlewares&&t.middlewares.push(...n.options.search.middlewares):(n.options.preSearchFilters||n.options.postSearchFilters)&&t.middlewares.push(({search:e,next:t})=>{let r=e;`preSearchFilters`in n.options&&n.options.preSearchFilters&&(r=n.options.preSearchFilters.reduce((e,t)=>t(e),e));let i=t(r);return`postSearchFilters`in n.options&&n.options.postSearchFilters?n.options.postSearchFilters.reduce((e,t)=>t(e),i):i}),n.options.validateSearch&&t.middlewares.push(({search:e,next:r})=>{let i=r(e);if(!t._includeValidateSearch)return i;try{return{...i,...ur(n.options.validateSearch,i)??void 0}}catch{return i}});t.middlewares.push(({search:e})=>{let n=t.dest;return n.search?n.search===!0?e:We(n.search,e):{}});let n=(e,t,r)=>{if(e>=r.length)return t;let i=r[e];return i({search:t,next:t=>n(e+1,t,r)})};return function(e,r,i){return t.dest=r,t._includeValidateSearch=i,n(0,e,t.middlewares)}}function mr(e,t){if(e!==`root`)for(let e=t.length-1;e>=0;e--){let n=t[e];if(n.children)return n.id}return vn}function hr(e,t){let n=e.options.params?.parse??e.options.parseParams;if(n){let e=n(t);if(e===!1)throw Error(`Route params.parse returned false for a matched route`);Object.assign(t,e)}}var gr=`Error preloading route! ☝️`;function _r(e,t){if(e)return typeof e==`string`?e:e[t]}function vr(e){return typeof e==`string`?{href:e,crossOrigin:void 0}:e}function yr(e){if(e.tag!==`link`)return;let t=e.attrs?.rel,n=e.attrs?.href;if(typeof n==`string`&&(typeof t==`string`?t.split(/\s+/):[]).includes(`stylesheet`))return n}function br(e,t){let n=yr(t);return!!n&&e?.inlineCss?.styles[n]!==void 0}var xr=class{get to(){return this._to}get id(){return this._id}get path(){return this._path}get fullPath(){return this._fullPath}constructor(e){if(this.init=e=>{this.originalIndex=e.originalIndex;let t=this.options,n=!t?.path&&!t?.id;this.parentRoute=this.options.getParentRoute?.(),n?this._path=vn:this.parentRoute||ht();let r=n?vn:t?.path;r&&r!==`/`&&(r=Ut(r));let i=t?.id||r,a=n?vn:Vt([this.parentRoute.id===`__root__`?``:this.parentRoute.id,i]);r===`__root__`&&(r=`/`),a!==`__root__`&&(a=Vt([`/`,a]));let o=a===`__root__`?`/`:Vt([this.parentRoute.fullPath,r]);this._path=r,this._id=a,this._fullPath=o,this._to=Wt(o)},this.addChildren=e=>this._addFileChildren(e),this._addFileChildren=e=>(Array.isArray(e)&&(this.children=e),typeof e==`object`&&e&&(this.children=Object.values(e)),this),this._addFileTypes=()=>this,this.updateLoader=e=>(Object.assign(this.options,e),this),this.update=e=>(Object.assign(this.options,e),this),this.lazy=e=>(this.lazyFn=e,this),this.redirect=e=>yn({from:this.fullPath,...e}),this.options=e||{},this.isRoot=!e?.getParentRoute,e?.id&&e?.path)throw Error(`Route cannot have both an 'id' and a 'path' option.`)}},Sr=class extends xr{constructor(e){super(e)}};function Cr(e){if(typeof document<`u`&&document.querySelector){let t=e.stores.location.get(),n=t.state.__hashScrollIntoViewOptions??!0;if(n&&t.hash!==``){let e=document.getElementById(t.hash);e&&e.scrollIntoView(n)}}}function wr(e){let t=e.errorComponent??Er;return(0,F.jsx)(Tr,{getResetKey:e.getResetKey,onCatch:e.onCatch,children:({error:n,reset:r})=>n?P.createElement(t,{error:n,reset:r}):e.children})}var Tr=class extends P.Component{constructor(...e){super(...e),this.state={error:null}}static getDerivedStateFromProps(e,t){let n=e.getResetKey();return t.error&&t.resetKey!==n?{resetKey:n,error:null}:{resetKey:n}}static getDerivedStateFromError(e){return{error:e}}reset(){this.setState({error:null})}componentDidCatch(e,t){this.props.onCatch&&this.props.onCatch(e,t)}render(){return this.props.children({error:this.state.error,reset:()=>{this.reset()}})}};function Er({error:e}){let[t,n]=P.useState(!1);return(0,F.jsxs)(`div`,{style:{padding:`.5rem`,maxWidth:`100%`},children:[(0,F.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:`.5rem`},children:[(0,F.jsx)(`strong`,{style:{fontSize:`1rem`},children:`Something went wrong!`}),(0,F.jsx)(`button`,{style:{appearance:`none`,fontSize:`.6em`,border:`1px solid currentColor`,padding:`.1rem .2rem`,fontWeight:`bold`,borderRadius:`.25rem`},onClick:()=>n(e=>!e),children:t?`Hide Error`:`Show Error`})]}),(0,F.jsx)(`div`,{style:{height:`.25rem`}}),t?(0,F.jsx)(`div`,{children:(0,F.jsx)(`pre`,{style:{fontSize:`.7em`,border:`1px solid red`,borderRadius:`.25rem`,padding:`.3rem`,color:`red`,overflow:`auto`},children:e.message?(0,F.jsx)(`code`,{children:e.message}):null})}):null]})}function Dr({children:e,fallback:t=null}){return Or()?(0,F.jsx)(P.Fragment,{children:e}):(0,F.jsx)(P.Fragment,{children:t})}function Or(){return P.useSyncExternalStore(kr,()=>!0,()=>!1)}function kr(){return()=>{}}var Ar=P.createContext(null);function jr(e){return P.useContext(Ar)}var Mr=P.createContext(void 0),Nr=P.createContext(void 0),Pr=(e=>(e[e.None=0]=`None`,e[e.Mutable=1]=`Mutable`,e[e.Watching=2]=`Watching`,e[e.RecursedCheck=4]=`RecursedCheck`,e[e.Recursed=8]=`Recursed`,e[e.Dirty=16]=`Dirty`,e[e.Pending=32]=`Pending`,e))(Pr||{});function Fr({update:e,notify:t,unwatched:n}){return{link:r,unlink:i,propagate:a,checkDirty:o,shallowPropagate:s};function r(e,t,n){let r=t.depsTail;if(r!==void 0&&r.dep===e)return;let i=r===void 0?t.deps:r.nextDep;if(i!==void 0&&i.dep===e){i.version=n,t.depsTail=i;return}let a=e.subsTail;if(a!==void 0&&a.version===n&&a.sub===t)return;let o=t.depsTail=e.subsTail={version:n,dep:e,sub:t,prevDep:r,nextDep:i,prevSub:a,nextSub:void 0};i!==void 0&&(i.prevDep=o),r===void 0?t.deps=o:r.nextDep=o,a===void 0?e.subs=o:a.nextSub=o}function i(e,t=e.sub){let r=e.dep,i=e.prevDep,a=e.nextDep,o=e.nextSub,s=e.prevSub;return a===void 0?t.depsTail=i:a.prevDep=i,i===void 0?t.deps=a:i.nextDep=a,o===void 0?r.subsTail=s:o.prevSub=s,s===void 0?(r.subs=o)===void 0&&n(r):s.nextSub=o,a}function a(e){let n=e.nextSub,r;top:do{let i=e.sub,a=i.flags;if(a&60?a&12?a&4?!(a&48)&&c(e,i)?(i.flags=a|40,a&=1):a=0:i.flags=a&-9|32:a=0:i.flags=a|32,a&2&&t(i),a&1){let t=i.subs;if(t!==void 0){let i=(e=t).nextSub;i!==void 0&&(r={value:n,prev:r},n=i);continue}}if((e=n)!==void 0){n=e.nextSub;continue}for(;r!==void 0;)if(e=r.value,r=r.prev,e!==void 0){n=e.nextSub;continue top}break}while(!0)}function o(t,n){let r,i=0,a=!1;top:do{let o=t.dep,c=o.flags;if(n.flags&16)a=!0;else if((c&17)==17){if(e(o)){let e=o.subs;e.nextSub!==void 0&&s(e),a=!0}}else if((c&33)==33){(t.nextSub!==void 0||t.prevSub!==void 0)&&(r={value:t,prev:r}),t=o.deps,n=o,++i;continue}if(!a){let e=t.nextDep;if(e!==void 0){t=e;continue}}for(;i--;){let i=n.subs,o=i.nextSub!==void 0;if(o?(t=r.value,r=r.prev):t=i,a){if(e(n)){o&&s(i),n=t.sub;continue}a=!1}else n.flags&=-33;n=t.sub;let c=t.nextDep;if(c!==void 0){t=c;continue top}}return a}while(!0)}function s(e){do{let n=e.sub,r=n.flags;(r&48)==32&&(n.flags=r|16,(r&6)==2&&t(n))}while((e=e.nextSub)!==void 0)}function c(e,t){let n=t.depsTail;for(;n!==void 0;){if(n===e)return!0;n=n.prevDep}return!1}}function Ir(e,t,n){let r=typeof e==`object`,i=r?e:void 0;return{next:(r?e.next:e)?.bind(i),error:(r?e.error:t)?.bind(i),complete:(r?e.complete:n)?.bind(i)}}var Lr=[],Rr=0,{link:zr,unlink:Br,propagate:Vr,checkDirty:Hr,shallowPropagate:Ur}=Fr({update(e){return e._update()},notify(e){Lr[Gr++]=e,e.flags&=~Pr.Watching},unwatched(e){e.depsTail!==void 0&&(e.depsTail=void 0,e.flags=Pr.Mutable|Pr.Dirty,Yr(e))}}),Wr=0,Gr=0,Kr,qr=0;function Jr(e){try{++qr,e()}finally{--qr||Xr()}}function Yr(e){let t=e.depsTail,n=t===void 0?e.deps:t.nextDep;for(;n!==void 0;)n=Br(n,e)}function Xr(){if(!(qr>0)){for(;Wr{i.get(),n.current?t.next?.(i._snapshot):n.current=!0});return{unsubscribe:()=>{r.stop()}}},_update(e){let a=Kr,o=t?.compare??Object.is;if(n)Kr=i,++Rr,i.depsTail=void 0;else if(e===void 0)return!1;n&&(i.flags=Pr.Mutable|Pr.RecursedCheck);try{let t=i._snapshot,a=typeof e==`function`?e(t):e===void 0&&n?r(t):e;return t===void 0||!o(t,a)?(i._snapshot=a,!0):!1}finally{Kr=a,n&&(i.flags&=~Pr.RecursedCheck),Yr(i)}}};return n?(i.flags=Pr.Mutable|Pr.Dirty,i.get=function(){let e=i.flags;if(e&Pr.Dirty||e&Pr.Pending&&Hr(i.deps,i)){if(i._update()){let e=i.subs;e!==void 0&&Ur(e)}}else e&Pr.Pending&&(i.flags=e&~Pr.Pending);return Kr!==void 0&&zr(i,Kr,Rr),i._snapshot}):i.set=function(e){if(i._update(e)){let e=i.subs;e!==void 0&&(Vr(e),Ur(e),Xr())}},i}function Qr(e){let t=()=>{let t=Kr;Kr=n,++Rr,n.depsTail=void 0,n.flags=Pr.Watching|Pr.RecursedCheck;try{return e()}finally{Kr=t,n.flags&=~Pr.RecursedCheck,Yr(n)}},n={deps:void 0,depsTail:void 0,subs:void 0,subsTail:void 0,flags:Pr.Watching|Pr.RecursedCheck,notify(){let e=this.flags;e&Pr.Dirty||e&Pr.Pending&&Hr(this.deps,this)?t():this.flags=Pr.Watching},stop(){this.flags=Pr.None,this.depsTail=void 0,Yr(this)}};return t(),n}var $r=o((e=>{var t=je();function n(e,t){return e===t&&(e!==0||1/e==1/t)||e!==e&&t!==t}var r=typeof Object.is==`function`?Object.is:n,i=t.useState,a=t.useEffect,o=t.useLayoutEffect,s=t.useDebugValue;function c(e,t){var n=t(),r=i({inst:{value:n,getSnapshot:t}}),c=r[0].inst,u=r[1];return o(function(){c.value=n,c.getSnapshot=t,l(c)&&u({inst:c})},[e,n,t]),a(function(){return l(c)&&u({inst:c}),e(function(){l(c)&&u({inst:c})})},[e]),s(n),n}function l(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch{return!0}}function u(e,t){return t()}var d=typeof window>`u`||window.document===void 0||window.document.createElement===void 0?u:c;e.useSyncExternalStore=t.useSyncExternalStore===void 0?d:t.useSyncExternalStore})),ei=o(((e,t)=>{t.exports=$r()})),ti=o((e=>{var t=je(),n=ei();function r(e,t){return e===t&&(e!==0||1/e==1/t)||e!==e&&t!==t}var i=typeof Object.is==`function`?Object.is:r,a=n.useSyncExternalStore,o=t.useRef,s=t.useEffect,c=t.useMemo,l=t.useDebugValue;e.useSyncExternalStoreWithSelector=function(e,t,n,r,u){var d=o(null);if(d.current===null){var f={hasValue:!1,value:null};d.current=f}else f=d.current;d=c(function(){function e(e){if(!a){if(a=!0,o=e,e=r(e),u!==void 0&&f.hasValue){var t=f.value;if(u(t,e))return s=t}return s=e}if(t=s,i(o,e))return t;var n=r(e);return u!==void 0&&u(t,n)?(o=e,t):(o=e,s=n)}var a=!1,o,s,c=n===void 0?null:n;return[function(){return e(t())},c===null?void 0:function(){return e(c())}]},[t,n,r,u]);var p=a(e,d[0],d[1]);return s(function(){f.hasValue=!0,f.value=p},[p]),l(p),p}})),ni=o(((e,t)=>{t.exports=ti()}))();function ri(e,t){return e===t}function ii(e,t,n=ri){let r=(0,P.useCallback)(t=>{if(!e)return()=>{};let{unsubscribe:n}=e.subscribe(t);return n},[e]),i=(0,P.useCallback)(()=>e?.get(),[e]);return(0,ni.useSyncExternalStoreWithSelector)(r,i,i,t,n)}var ai={get:()=>void 0,subscribe:()=>({unsubscribe:()=>{}})};function oi(e){let t=jr(),n=P.useContext(e.from?Nr:Mr),r=e.from??n,i=r?e.from?t.stores.getRouteMatchStore(r):t.stores.matchStores.get(r):void 0,a=P.useRef(void 0);return ii(i??ai,n=>{if((e.shouldThrow??!0)&&!n&&ht(),n===void 0)return;let r=e.select?e.select(n):n;if(e.structuralSharing??t.options.defaultStructuralSharing){let e=Xe(a.current,r);return a.current=e,e}return r})}function si(e){return oi({from:e.from,strict:e.strict,structuralSharing:e.structuralSharing,select:t=>e.select?e.select(t.loaderData):t.loaderData})}function ci(e){let{select:t,...n}=e;return oi({...n,select:e=>t?t(e.loaderDeps):e.loaderDeps})}function li(e){return oi({from:e.from,shouldThrow:e.shouldThrow,structuralSharing:e.structuralSharing,strict:e.strict,select:t=>{let n=e.strict===!1?t.params:t._strictParams;return e.select?e.select(n):n}})}function ui(e){return oi({from:e.from,strict:e.strict,shouldThrow:e.shouldThrow,structuralSharing:e.structuralSharing,select:t=>e.select?e.select(t.search):t.search})}function di(e){let t=jr();return P.useCallback(n=>t.navigate({...n,from:n.from??e?.from}),[e?.from,t])}function fi(e){return oi({...e,select:t=>e.select?e.select(t.context):t.context})}var pi=o((e=>{var t=je();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=pi()})),hi=c(mi(),1);function gi(e,t){let n=jr(),r=Ve(t),{activeProps:i,inactiveProps:a,activeOptions:o,to:s,preload:c,preloadDelay:l,preloadIntentProximity:u,hashScrollIntoView:d,replace:f,startTransition:p,resetScroll:m,viewTransition:h,children:g,target:_,disabled:v,style:y,className:b,onClick:x,onBlur:S,onFocus:C,onMouseEnter:w,onMouseLeave:T,onTouchStart:E,ignoreBlocker:ee,params:D,search:O,hash:te,state:ne,mask:re,reloadDocument:k,unsafeRelative:A,from:ie,_fromLocation:j,...ae}=e,oe=Or(),se=P.useMemo(()=>e,[n,e.from,e._fromLocation,e.hash,e.to,e.search,e.params,e.state,e.mask,e.unsafeRelative]),ce=ii(n.stores.location,e=>e,(e,t)=>e.href===t.href),M=P.useMemo(()=>{let e={_fromLocation:ce,...se};return n.buildLocation(e)},[n,ce,se]),le=M.maskedLocation?M.maskedLocation.publicHref:M.publicHref,ue=M.maskedLocation?M.maskedLocation.external:M.external,N=P.useMemo(()=>Ti(le,ue,n.history,v),[v,ue,le,n.history]),de=P.useMemo(()=>{if(N?.external)return ct(N.href,n.protocolAllowlist)?void 0:N.href;if(!Ei(s)&&!(typeof s!=`string`||s.indexOf(`:`)===-1))try{return new URL(s),ct(s,n.protocolAllowlist)?void 0:s}catch{}},[s,N,n.protocolAllowlist]),fe=P.useMemo(()=>{if(de)return!1;if(o?.exact){if(!qt(ce.pathname,M.pathname,n.basepath))return!1}else{let e=Kt(ce.pathname,n.basepath),t=Kt(M.pathname,n.basepath);if(!(e.startsWith(t)&&(e.length===t.length||e[t.length]===`/`)))return!1}return(o?.includeSearch??!0)&&!tt(ce.search,M.search,{partial:!o?.exact,ignoreUndefined:!o?.explicitUndefined})?!1:o?.includeHash?oe&&ce.hash===M.hash:!0},[o?.exact,o?.explicitUndefined,o?.includeHash,o?.includeSearch,ce,de,oe,M.hash,M.pathname,M.search,n.basepath]),pe=fe?We(i,{})??vi:_i,me=fe?_i:We(a,{})??_i,he=[b,pe.className,me.className].filter(Boolean).join(` `),ge=(y||pe.style||me.style)&&{...y,...pe.style,...me.style},[_e,ve]=P.useState(!1),ye=P.useRef(!1),be=e.reloadDocument||de?!1:c??n.options.defaultPreload,xe=l??n.options.defaultPreloadDelay??0,Se=P.useCallback(()=>{n.preloadRoute({...se,_builtLocation:M}).catch(e=>{console.warn(e),console.warn(gr)})},[n,se,M]);Be(r,P.useCallback(e=>{e?.isIntersecting&&Se()},[Se]),Ci,{disabled:!!v||be!==`viewport`}),P.useEffect(()=>{ye.current||!v&&be===`render`&&(Se(),ye.current=!0)},[v,Se,be]);let Ce=e=>{let t=e.currentTarget.getAttribute(`target`),r=_===void 0?t:_;if(!v&&!Oi(e)&&!e.defaultPrevented&&(!r||r===`_self`)&&e.button===0){e.preventDefault(),(0,hi.flushSync)(()=>{ve(!0)});let t=n.subscribe(`onResolved`,()=>{t(),ve(!1)});n.navigate({...se,replace:f,resetScroll:m,hashScrollIntoView:d,startTransition:p,viewTransition:h,ignoreBlocker:ee})}};if(de)return{...ae,ref:r,href:de,...g&&{children:g},..._&&{target:_},...v&&{disabled:v},...y&&{style:y},...b&&{className:b},...x&&{onClick:x},...S&&{onBlur:S},...C&&{onFocus:C},...w&&{onMouseEnter:w},...T&&{onMouseLeave:T},...E&&{onTouchStart:E}};let we=e=>{if(v||be!==`intent`)return;if(!xe){Se();return}let t=e.currentTarget;if(Si.has(t))return;let n=setTimeout(()=>{Si.delete(t),Se()},xe);Si.set(t,n)},Te=e=>{v||be!==`intent`||Se()},Ee=e=>{if(v||!be||!xe)return;let t=e.currentTarget,n=Si.get(t);n&&(clearTimeout(n),Si.delete(t))};return{...ae,...pe,...me,href:N?.href,ref:r,onClick:wi([x,Ce]),onBlur:wi([S,Ee]),onFocus:wi([C,we]),onMouseEnter:wi([w,we]),onMouseLeave:wi([T,Ee]),onTouchStart:wi([E,Te]),disabled:!!v,target:_,...ge&&{style:ge},...he&&{className:he},...v&&yi,...fe&&bi,...oe&&_e&&xi}}var _i={},vi={className:`active`},yi={role:`link`,"aria-disabled":!0},bi={"data-status":`active`,"aria-current":`page`},xi={"data-transitioning":`transitioning`},Si=new WeakMap,Ci={rootMargin:`100px`},wi=e=>t=>{for(let n of e)if(n){if(t.defaultPrevented)return;n(t)}};function Ti(e,t,n,r){if(!r)return t?{href:e,external:!0}:{href:n.createHref(e)||`/`,external:!1}}function Ei(e){if(typeof e!=`string`)return!1;let t=e.charCodeAt(0);return t===47?e.charCodeAt(1)!==47:t===46}var Di=P.forwardRef((e,t)=>{let{_asChild:n,...r}=e,{type:i,...a}=gi(r,t),o=typeof r.children==`function`?r.children({isActive:a[`data-status`]===`active`}):r.children;if(!n){let{disabled:e,...t}=a;return P.createElement(`a`,t,o)}return P.createElement(n,a,o)});function Oi(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}var ki=class extends xr{constructor(e){super(e),this.useMatch=e=>oi({select:e?.select,from:this.id,structuralSharing:e?.structuralSharing}),this.useRouteContext=e=>fi({...e,from:this.id}),this.useSearch=e=>ui({select:e?.select,structuralSharing:e?.structuralSharing,from:this.id}),this.useParams=e=>li({select:e?.select,structuralSharing:e?.structuralSharing,from:this.id}),this.useLoaderDeps=e=>ci({...e,from:this.id}),this.useLoaderData=e=>si({...e,from:this.id}),this.useNavigate=()=>di({from:this.fullPath}),this.Link=P.forwardRef((e,t)=>(0,F.jsx)(Di,{ref:t,from:this.fullPath,...e}))}};function Ai(e){return new ki(e)}function ji(){return e=>Ni(e)}var Mi=class extends Sr{constructor(e){super(e),this.useMatch=e=>oi({select:e?.select,from:this.id,structuralSharing:e?.structuralSharing}),this.useRouteContext=e=>fi({...e,from:this.id}),this.useSearch=e=>ui({select:e?.select,structuralSharing:e?.structuralSharing,from:this.id}),this.useParams=e=>li({select:e?.select,structuralSharing:e?.structuralSharing,from:this.id}),this.useLoaderDeps=e=>ci({...e,from:this.id}),this.useLoaderData=e=>si({...e,from:this.id}),this.useNavigate=()=>di({from:this.fullPath}),this.Link=P.forwardRef((e,t)=>(0,F.jsx)(Di,{ref:t,from:this.fullPath,...e}))}};function Ni(e){return new Mi(e)}function L(e){return new Pi(e,{silent:!0}).createRoute}var Pi=class{constructor(e,t){this.path=e,this.createRoute=e=>{let t=Ai(e);return t.isRoot=!1,t},this.silent=t?.silent}};function R(e,t){let n,r,i,a,o=()=>(n||=e().then(e=>{n=void 0,r=e[t??`default`]}).catch(e=>{if(i=e,rt(i)&&i instanceof Error&&typeof window<`u`&&typeof sessionStorage<`u`){let e=`tanstack_router_reload:${i.message}`;sessionStorage.getItem(e)||(sessionStorage.setItem(e,`1`),a=!0)}}),n),s=function(e){if(a)throw window.location.reload(),new Promise(()=>{});if(i)throw i;if(!r)if(Le)Le(o());else throw o();return P.createElement(r,e)};return s.preload=o,s}function Fi(e){let t=jr(),n=`not-found-${ii(t.stores.location,e=>e.pathname)}-${ii(t.stores.status,e=>e)}`;return(0,F.jsx)(wr,{getResetKey:()=>n,onCatch:(t,n)=>{if($t(t))e.onCatch?.(t,n);else throw t},errorComponent:({error:t})=>{if($t(t))return e.fallback?.(t);throw t},children:e.children})}function Ii(){return(0,F.jsx)(`p`,{children:`Not Found`})}function Li(e){return(0,F.jsx)(F.Fragment,{children:e.children})}function Ri(e,t,n){return t.options.notFoundComponent?(0,F.jsx)(t.options.notFoundComponent,{...n}):e.options.defaultNotFoundComponent?(0,F.jsx)(e.options.defaultNotFoundComponent,{...n}):(0,F.jsx)(Ii,{})}var zi=P.memo(function({matchId:e}){let t=jr(),n=t.stores.matchStores.get(e);n||ht();let r=ii(t.stores.loadedAt,e=>e),i=ii(n,e=>e);return(0,F.jsx)(Bi,{router:t,matchId:e,resetKey:r,matchState:P.useMemo(()=>{let e=i.routeId,n=t.routesById[e].parentRoute?.id;return{routeId:e,ssr:i.ssr,_displayPending:i._displayPending,parentRouteId:n}},[i._displayPending,i.routeId,i.ssr,t.routesById])})});function Bi({router:e,matchId:t,resetKey:n,matchState:r}){let i=e.routesById[r.routeId],a=i.options.pendingComponent??e.options.defaultPendingComponent,o=a?(0,F.jsx)(a,{}):null,s=i.options.errorComponent??e.options.defaultErrorComponent,c=i.options.onCatch??e.options.defaultOnCatch,l=i.isRoot?i.options.notFoundComponent??e.options.notFoundRoute?.options.component:i.options.notFoundComponent,u=r.ssr===!1||r.ssr===`data-only`,d=(!i.isRoot||i.options.wrapInSuspense||u)&&(i.options.wrapInSuspense??a??(i.options.errorComponent?.preload||u))?P.Suspense:Li,f=s?wr:Li,p=l?Fi:Li;return(0,F.jsxs)(i.isRoot?i.options.shellComponent??Li:Li,{children:[(0,F.jsx)(Mr.Provider,{value:t,children:(0,F.jsx)(d,{fallback:o,children:(0,F.jsx)(f,{getResetKey:()=>n,errorComponent:s||Er,onCatch:(e,t)=>{if($t(e))throw e.routeId??=r.routeId,e;c?.(e,t)},children:(0,F.jsx)(p,{fallback:e=>{if(e.routeId??=r.routeId,!l||e.routeId&&e.routeId!==r.routeId||!e.routeId&&!i.isRoot)throw e;return P.createElement(l,e)},children:u||r._displayPending?(0,F.jsx)(Dr,{fallback:o,children:(0,F.jsx)(Hi,{matchId:t})}):(0,F.jsx)(Hi,{matchId:t})})})})}),r.parentRouteId===`__root__`?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(Vi,{resetKey:n}),(e.options.scrollRestoration,null)]}):null]})}function Vi({resetKey:e}){let t=jr(),n=P.useRef(void 0);return Re(()=>{let e=t.latestLocation.href;(n.current===void 0||n.current!==e)&&(t.emit({type:`onRendered`,...ar(t.stores.location.get(),t.stores.resolvedLocation.get())}),n.current=e)},[t.latestLocation.state.__TSR_key,e,t]),null}var Hi=P.memo(function({matchId:e}){let t=jr(),n=(e,n)=>t.getMatch(e.id)?._nonReactive[n]??e._nonReactive[n],r=t.stores.matchStores.get(e);r||ht();let i=ii(r,e=>e),a=i.routeId,o=t.routesById[a],s=P.useMemo(()=>{let e=(t.routesById[a].options.remountDeps??t.options.defaultRemountDeps)?.({routeId:a,loaderDeps:i.loaderDeps,params:i._strictParams,search:i._strictSearch});return e?JSON.stringify(e):void 0},[a,i.loaderDeps,i._strictParams,i._strictSearch,t.options.defaultRemountDeps,t.routesById]),c=P.useMemo(()=>{let e=o.options.component??t.options.defaultComponent;return e?(0,F.jsx)(e,{},s):(0,F.jsx)(Ui,{})},[s,o.options.component,t.options.defaultComponent]);if(i._displayPending)throw n(i,`displayPendingPromise`);if(i._forcePending)throw n(i,`minPendingPromise`);if(i.status===`pending`){let e=o.options.pendingMinMs??t.options.defaultPendingMinMs;if(e){let n=t.getMatch(i.id);if(n&&!n._nonReactive.minPendingPromise){let t=nt();n._nonReactive.minPendingPromise=t,setTimeout(()=>{t.resolve(),n._nonReactive.minPendingPromise=void 0},e)}}throw n(i,`loadPromise`)}if(i.status===`notFound`)return $t(i.error)||ht(),Ri(t,o,i.error);if(i.status===`redirected`)throw bn(i.error)||ht(),n(i,`loadPromise`);if(i.status===`error`)throw i.error;return c}),Ui=P.memo(function(){let e=jr(),t=P.useContext(Mr),n,r=!1,i;{let a=t?e.stores.matchStores.get(t):void 0;[n,r]=ii(a,e=>[e?.routeId,e?.globalNotFound??!1]),i=ii(e.stores.matchesId,e=>e[e.findIndex(e=>e===t)+1])}let a=n?e.routesById[n]:void 0,o=e.options.defaultPendingComponent?(0,F.jsx)(e.options.defaultPendingComponent,{}):null;if(r)return a||ht(),Ri(e,a,void 0);if(!i)return null;let s=(0,F.jsx)(zi,{matchId:i});return n===`__root__`?(0,F.jsx)(P.Suspense,{fallback:o,children:s}):s});function Wi(){let e=jr(),t=P.useRef({router:e,mounted:!1}),[n,r]=P.useState(!1),i=ii(e.stores.isLoading,e=>e),a=ii(e.stores.hasPending,e=>e),o=ze(i),s=i||n||a,c=ze(s),l=i||a,u=ze(l);return e.startTransition=e=>{r(!0),P.startTransition(()=>{e(),r(!1)})},P.useEffect(()=>{let t=e.history.subscribe(e.load),n=e.buildLocation({to:e.latestLocation.pathname,search:!0,params:!0,hash:!0,state:!0,_includeValidateSearch:!0});return Wt(e.latestLocation.publicHref)!==Wt(n.publicHref)&&e.commitLocation({...n,replace:!0}),()=>{t()}},[e,e.history]),Re(()=>{typeof window<`u`&&e.ssr||t.current.router===e&&t.current.mounted||(t.current={router:e,mounted:!0},(async()=>{try{await e.load()}catch(e){console.error(e)}})())},[e]),Re(()=>{o&&!i&&e.emit({type:`onLoad`,...ar(e.stores.location.get(),e.stores.resolvedLocation.get())})},[o,e,i]),Re(()=>{u&&!l&&e.emit({type:`onBeforeRouteMount`,...ar(e.stores.location.get(),e.stores.resolvedLocation.get())})},[l,u,e]),Re(()=>{if(c&&!s){let t=ar(e.stores.location.get(),e.stores.resolvedLocation.get());e.emit({type:`onResolved`,...t}),Jr(()=>{e.stores.status.set(`idle`),e.stores.resolvedLocation.set(e.stores.location.get())}),t.hrefChanged&&Cr(e)}},[s,c,e]),null}function Gi(){let e=jr(),t=e.routesById.__root__.options.pendingComponent??e.options.defaultPendingComponent,n=t?(0,F.jsx)(t,{}):null,r=(0,F.jsxs)(typeof document<`u`&&e.ssr?Li:P.Suspense,{fallback:n,children:[(0,F.jsx)(Wi,{}),(0,F.jsx)(Ki,{})]});return e.options.InnerWrap?(0,F.jsx)(e.options.InnerWrap,{children:r}):r}function Ki(){let e=jr(),t=ii(e.stores.firstId,e=>e),n=ii(e.stores.loadedAt,e=>e),r=t?(0,F.jsx)(zi,{matchId:t}):null;return(0,F.jsx)(Mr.Provider,{value:t,children:e.options.disableGlobalCatchBoundary?r:(0,F.jsx)(wr,{getResetKey:()=>n,errorComponent:Er,onCatch:void 0,children:r})})}var qi=e=>({createMutableStore:Zr,createReadonlyStore:Zr,batch:Jr}),Ji=e=>new Yi(e),Yi=class extends or{constructor(e){super(e,qi)}};function Xi({router:e,children:t,...n}){qe(n)&&e.update({...e.options,...n,context:{...e.options.context,...n.context}});let r=(0,F.jsx)(Ar.Provider,{value:e,children:t});return e.options.Wrap?(0,F.jsx)(e.options.Wrap,{children:r}):r}function Zi({router:e,...t}){return(0,F.jsx)(Xi,{router:e,...t,children:(0,F.jsx)(Gi,{})})}function Qi(e){let t=jr(),n=(0,P.useRef)(void 0);return ii(t.stores.location,r=>{let i=e?.select?e.select(r):r;if(e?.structuralSharing??t.options.defaultStructuralSharing){let e=Xe(n.current,i);return n.current=e,e}return i})}function $i(e){let{attrs:t,children:n,nonce:r}=e;switch(e.tag){case`title`:return(0,F.jsx)(`title`,{...t,suppressHydrationWarning:!0,children:n});case`meta`:return(0,F.jsx)(`meta`,{...t,suppressHydrationWarning:!0});case`link`:return(0,F.jsx)(`link`,{...t,precedence:t?.precedence??(t?.rel===`stylesheet`?`default`:void 0),nonce:r,suppressHydrationWarning:!0});case`style`:return e.inlineCss,(0,F.jsx)(`style`,{...t,dangerouslySetInnerHTML:{__html:n},nonce:r});case`script`:return(0,F.jsx)(ea,{attrs:t,children:n});default:return null}}function ea({attrs:e,children:t}){jr();let n=Or(),r=typeof e?.type==`string`&&e.type!==``&&e.type!==`text/javascript`&&e.type!==`module`;if(P.useEffect(()=>{if(!r){if(e?.src){let t=(()=>{try{let t=document.baseURI||window.location.href;return new URL(e.src,t).href}catch{return e.src}})();if(Array.from(document.querySelectorAll(`script[src]`)).find(e=>e.src===t))return;let n=document.createElement(`script`);for(let[t,r]of Object.entries(e))t!==`suppressHydrationWarning`&&r!==void 0&&r!==!1&&n.setAttribute(t,typeof r==`boolean`?``:String(r));return document.head.appendChild(n),()=>{n.parentNode&&n.parentNode.removeChild(n)}}if(typeof t==`string`){let n=typeof e?.type==`string`?e.type:`text/javascript`,r=typeof e?.nonce==`string`?e.nonce:void 0;if(Array.from(document.querySelectorAll(`script:not([src])`)).find(e=>{if(!(e instanceof HTMLScriptElement))return!1;let i=e.getAttribute(`type`)??`text/javascript`,a=e.getAttribute(`nonce`)??void 0;return e.textContent===t&&i===n&&a===r}))return;let i=document.createElement(`script`);if(i.textContent=t,e)for(let[t,n]of Object.entries(e))t!==`suppressHydrationWarning`&&n!==void 0&&n!==!1&&i.setAttribute(t,typeof n==`boolean`?``:String(n));return document.head.appendChild(i),()=>{i.parentNode&&i.parentNode.removeChild(i)}}}},[e,t,r]),r&&typeof t==`string`)return(0,F.jsx)(`script`,{...e,suppressHydrationWarning:!0,dangerouslySetInnerHTML:{__html:t}});if(!n){if(e?.src)return(0,F.jsx)(`script`,{...e,suppressHydrationWarning:!0});if(typeof t==`string`)return(0,F.jsx)(`script`,{...e,dangerouslySetInnerHTML:{__html:t},suppressHydrationWarning:!0})}return null}var ta=e=>{let t=jr(),n=t.options.ssr?.nonce,r=ii(t.stores.matches,e=>e.map(e=>e.meta).filter(Boolean),tt),i=P.useMemo(()=>{let e=[],t={},i;for(let a=r.length-1;a>=0;a--){let o=r[a];for(let r=o.length-1;r>=0;r--){let a=o[r];if(a)if(a.title)i||={tag:`title`,children:a.title};else if(`script:ld+json`in a)try{let t=JSON.stringify(a[`script:ld+json`]);e.push({tag:`script`,attrs:{type:`application/ld+json`},children:dt(t)})}catch{}else{let r=a.name??a.property;if(r){if(t[r])continue;t[r]=!0}e.push({tag:`meta`,attrs:{...a,nonce:n}})}}}return i&&e.push(i),n&&e.push({tag:`meta`,attrs:{property:`csp-nonce`,content:n}}),e.reverse(),e},[r,n]),a=ii(t.stores.matches,r=>{let i=r.map(e=>e.links).filter(Boolean).flat(1).map(e=>({tag:`link`,attrs:{...e,nonce:n}})),a=t.ssr?.manifest,o=r.map(e=>a?.routes[e.routeId]?.assets??[]).filter(Boolean).flat(1).flatMap(t=>t.tag===`link`?br(a,t)?[]:[{tag:`link`,attrs:{...t.attrs,crossOrigin:_r(e,`stylesheet`)??t.attrs?.crossOrigin,suppressHydrationWarning:!0,nonce:n}}]:t.tag===`style`?[{tag:`style`,attrs:{...t.attrs,nonce:n},children:t.children,...t.inlineCss?{inlineCss:!0}:{}}]:[]);return[...i,...o]},tt),o=ii(t.stores.matches,r=>{let i=[];return r.map(e=>t.looseRoutesById[e.routeId]).forEach(r=>t.ssr?.manifest?.routes[r.id]?.preloads?.filter(Boolean).forEach(t=>{let r=vr(t);i.push({tag:`link`,attrs:{rel:`modulepreload`,href:r.href,crossOrigin:_r(e,`modulepreload`)??r.crossOrigin,nonce:n}})})),i},tt),s=ii(t.stores.matches,e=>e.map(e=>e.styles).flat(1).filter(Boolean).map(({children:e,...t})=>({tag:`style`,attrs:{...t,nonce:n},children:e})),tt),c=ii(t.stores.matches,e=>e.map(e=>e.headScripts).flat(1).filter(Boolean).map(({children:e,...t})=>({tag:`script`,attrs:{...t,nonce:n},children:e})),tt);return na([...i,...o,...a,...s,...c],e=>JSON.stringify(e))};function na(e,t){let n=new Set;return e.filter(e=>{let r=t(e);return n.has(r)?!1:(n.add(r),!0)})}function ra(e){let t=ta(e.assetCrossOrigin),n=jr().options.ssr?.nonce;return(0,F.jsx)(F.Fragment,{children:t.map(e=>(0,P.createElement)($i,{...e,key:`tsr-meta-${JSON.stringify(e)}`,nonce:n}))})}var ia=o((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,D());else{var t=n(l);t!==null&&ne(x,t.startTime-e)}}var S=!1,C=-1,w=5,T=-1;function E(){return g?!0:!(e.unstable_now()-Tt&&E());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&ne(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?D():S=!1}}}var D;if(typeof y==`function`)D=function(){y(ee)};else if(typeof MessageChannel<`u`){var O=new MessageChannel,te=O.port2;O.port1.onmessage=ee,D=function(){te.postMessage(null)}}else D=function(){_(ee,0)};function ne(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,ne(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,D()))),r},e.unstable_shouldYield=E,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),aa=o(((e,t)=>{t.exports=ia()})),oa=o((e=>{var t=aa(),n=je(),r=mi();function i(e){var t=`https://react.dev/errors/`+e;if(1ae||(e.current=j[ae],j[ae]=null,ae--)}function ce(e,t){ae++,j[ae]=e.current,e.current=t}var M=oe(null),le=oe(null),ue=oe(null),N=oe(null);function de(e,t){switch(ce(ue,t),ce(le,e),ce(M,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?ef(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=ef(t),e=tf(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}se(M),ce(M,e)}function fe(){se(M),se(le),se(ue)}function pe(e){e.memoizedState!==null&&ce(N,e);var t=M.current,n=tf(t,e.type);t!==n&&(ce(le,e),ce(M,n))}function me(e){le.current===e&&(se(M),se(le)),N.current===e&&(se(N),dp._currentValue=ie)}var he,ge;function _e(e){if(he===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);he=t&&t[1]||``,ge=-1i.map(i=>d[i]); +import{i as e,n as t,o as n,r,t as i}from"./compiler-runtime.js";import{t as a}from"./queryOptions.js";import{A as o,B as s,C as c,D as l,E as u,F as d,G as f,I as p,L as m,M as h,N as g,O as _,P as v,R as y,S as b,T as x,V as S,W as C,_ as w,a as T,b as E,c as D,d as O,f as k,g as A,h as j,i as M,j as ee,k as N,l as P,m as te,n as ne,o as re,r as ie,s as ae,t as F,v as oe,w as se,x as I,y as ce,z as le}from"./useStore.js";import{i as ue,n as de,r as fe,t as pe}from"./matchContext.js";import{n as me,t as he}from"./redirect.js";import{B as ge,Bn as _e,Bt as ve,Cr as L,Fn as ye,G as be,Gn as xe,Gt as Se,H as Ce,Hn as we,Ht as Te,In as Ee,J as De,Jn as Oe,Jt as ke,K as Ae,Kn as je,Kt as Me,Ln as Ne,Nn as Pe,Or as Fe,Pn as Ie,Q as Le,Rn as Re,Tr as R,U as ze,Un as Be,Ut as Ve,V as He,Vn as Ue,Vt as We,W as Ge,Wn as Ke,X as qe,Xt as Je,Y as Ye,Yn as Xe,Yt as Ze,Z as Qe,_r as $e,_t as et,a as tt,b as nt,br as z,bt as rt,en as it,ft as at,gr as ot,gt as st,h as ct,ht as lt,i as ut,kr as dt,kt as ft,m as pt,mr as mt,mt as ht,n as gt,o as _t,pt as vt,q as yt,qn as bt,qt as xt,r as St,s as Ct,t as wt,tt as Tt,vt as Et,y as Dt,yt as Ot,zn as kt,zt as At}from"./awsClient.js";import{a as jt,c as Mt,f as Nt,l as Pt,n as Ft,o as It,s as Lt,t as Rt,u as zt}from"./dist.js";import{C as Bt,T as Vt,a as Ht,i as Ut,l as Wt,t as Gt,u as Kt,w as qt}from"./SignatureV4.js";import{C as Jt,D as Yt,E as Xt,S as Zt,T as Qt,_ as $t,b as en,d as tn,f as nn,g as rn,h as an,l as on,m as sn,n as cn,o as B,p as ln,r as un,s as dn,t as fn,u as pn,v as mn,w as hn,x as gn,y as V}from"./button.js";import{C as _n,S as vn,a as yn,b as bn,f as xn,g as Sn,h as Cn,i as wn,m as Tn,o as En,p as Dn,r as On,s as kn,t as An,v as jn,x as Mn,y as H}from"./createBaseUIEventDetails.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var Nn=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},Pn=new class extends Nn{#e;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e();return window.addEventListener(`visibilitychange`,t,!1),()=>{window.removeEventListener(`visibilitychange`,t)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(e=>{typeof e==`boolean`?this.setFocused(e):this.onFocus()})}setFocused(e){this.#e!==e&&(this.#e=e,this.onFocus())}onFocus(){let e=this.isFocused();this.listeners.forEach(t=>{t(e)})}isFocused(){return typeof this.#e==`boolean`?this.#e:globalThis.document?.visibilityState!==`hidden`}},Fn={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},In=new class{#e=Fn;setTimeoutProvider(e){this.#e=e}setTimeout(e,t){return this.#e.setTimeout(e,t)}clearTimeout(e){this.#e.clearTimeout(e)}setInterval(e,t){return this.#e.setInterval(e,t)}clearInterval(e){this.#e.clearInterval(e)}};function Ln(e){setTimeout(e,0)}var Rn=typeof window>`u`||`Deno`in globalThis;function zn(){}function Bn(e,t){return typeof e==`function`?e(t):e}function Vn(e){return typeof e==`number`&&e>=0&&e!==1/0}function Hn(e,t){return Math.max(e+(t||0)-Date.now(),0)}function Un(e,t){return typeof e==`function`?e(t):e}function Wn(e,t){return typeof e==`function`?e(t):e}function Gn(e,t){let{type:n=`all`,exact:r,fetchStatus:i,predicate:a,queryKey:o,stale:s}=e;if(o){if(r){if(t.queryHash!==qn(o,t.options))return!1}else if(!Yn(t.queryKey,o))return!1}if(n!==`all`){let e=t.isActive();if(n===`active`&&!e||n===`inactive`&&e)return!1}return!(typeof s==`boolean`&&t.isStale()!==s||i&&i!==t.state.fetchStatus||a&&!a(t))}function Kn(e,t){let{exact:n,status:r,predicate:i,mutationKey:a}=e;if(a){if(!t.options.mutationKey)return!1;if(n){if(Jn(t.options.mutationKey)!==Jn(a))return!1}else if(!Yn(t.options.mutationKey,a))return!1}return!(r&&t.state.status!==r||i&&!i(t))}function qn(e,t){return(t?.queryKeyHashFn||Jn)(e)}function Jn(e){return JSON.stringify(e,(e,t)=>er(t)?Object.keys(t).sort().reduce((e,n)=>(e[n]=t[n],e),{}):t)}function Yn(e,t){return e===t?!0:typeof e==typeof t&&e&&t&&typeof e==`object`&&typeof t==`object`?Object.keys(t).every(n=>Yn(e[n],t[n])):!1}var Xn=Object.prototype.hasOwnProperty;function Zn(e,t,n=0){if(e===t)return e;if(n>500)return t;let r=$n(e)&&$n(t);if(!r&&!(er(e)&&er(t)))return t;let i=(r?e:Object.keys(e)).length,a=r?t:Object.keys(t),o=a.length,s=r?Array(o):{},c=0;for(let l=0;l{In.setTimeout(t,e)})}function rr(e,t,n){return typeof n.structuralSharing==`function`?n.structuralSharing(e,t):n.structuralSharing===!1?t:Zn(e,t)}function ir(e,t,n=0){let r=[...e,t];return n&&r.length>n?r.slice(1):r}function ar(e,t,n=0){let r=[t,...e];return n&&r.length>n?r.slice(0,-1):r}var or=Symbol();function sr(e,t){return!e.queryFn&&t?.initialPromise?()=>t.initialPromise:!e.queryFn||e.queryFn===or?()=>Promise.reject(Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function cr(e,t){return typeof e==`function`?e(...t):!!e}function lr(e,t,n){let r=!1,i;return Object.defineProperty(e,"signal",{enumerable:!0,get:()=>(i??=t(),r?i:(r=!0,i.aborted?n():i.addEventListener(`abort`,n,{once:!0}),i))}),e}var ur=(()=>{let e=()=>Rn;return{isServer(){return e()},setIsServer(t){e=t}}})();function dr(){let e,t,n=new Promise((n,r)=>{e=n,t=r});n.status=`pending`,n.catch(()=>{});function r(e){Object.assign(n,e),delete n.resolve,delete n.reject}return n.resolve=t=>{r({status:`fulfilled`,value:t}),e(t)},n.reject=e=>{r({status:`rejected`,reason:e}),t(e)},n}var fr=Ln;function pr(){let e=[],t=0,n=e=>{e()},r=e=>{e()},i=fr,a=r=>{t?e.push(r):i(()=>{n(r)})},o=()=>{let t=e;e=[],t.length&&i(()=>{r(()=>{t.forEach(e=>{n(e)})})})};return{batch:e=>{let n;t++;try{n=e()}finally{t--,t||o()}return n},batchCalls:e=>(...t)=>{a(()=>{e(...t)})},schedule:a,setNotifyFunction:e=>{n=e},setBatchNotifyFunction:e=>{r=e},setScheduler:e=>{i=e}}}var mr=pr(),hr=new class extends Nn{#e=!0;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e(!0),n=()=>e(!1);return window.addEventListener(`online`,t,!1),window.addEventListener(`offline`,n,!1),()=>{window.removeEventListener(`online`,t),window.removeEventListener(`offline`,n)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(this.setOnline.bind(this))}setOnline(e){this.#e!==e&&(this.#e=e,this.listeners.forEach(t=>{t(e)}))}isOnline(){return this.#e}};function gr(e){return Math.min(1e3*2**e,3e4)}function _r(e){return(e??`online`)===`online`?hr.isOnline():!0}var vr=class extends Error{constructor(e){super(`CancelledError`),this.revert=e?.revert,this.silent=e?.silent}};function yr(e){let t=!1,n=0,r,i=dr(),a=()=>i.status!==`pending`,o=t=>{if(!a()){let n=new vr(t);f(n),e.onCancel?.(n)}},s=()=>{t=!0},c=()=>{t=!1},l=()=>Pn.isFocused()&&(e.networkMode===`always`||hr.isOnline())&&e.canRun(),u=()=>_r(e.networkMode)&&e.canRun(),d=e=>{a()||(r?.(),i.resolve(e))},f=e=>{a()||(r?.(),i.reject(e))},p=()=>new Promise(t=>{r=e=>{(a()||l())&&t(e)},e.onPause?.()}).then(()=>{r=void 0,a()||e.onContinue?.()}),m=()=>{if(a())return;let r,i=n===0?e.initialPromise:void 0;try{r=i??e.fn()}catch(e){r=Promise.reject(e)}Promise.resolve(r).then(d).catch(r=>{if(a())return;let i=e.retry??(ur.isServer()?0:3),o=e.retryDelay??gr,s=typeof o==`function`?o(n,r):o,c=i===!0||typeof i==`number`&&nl()?void 0:p()).then(()=>{t?f(r):m()})})};return{promise:i,status:()=>i.status,cancel:o,continue:()=>(r?.(),i),cancelRetry:s,continueRetry:c,canStart:u,start:()=>(u()?m():p().then(m),i)}}var br=class{#e;destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),Vn(this.gcTime)&&(this.#e=In.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(ur.isServer()?1/0:300*1e3))}clearGcTimeout(){this.#e!==void 0&&(In.clearTimeout(this.#e),this.#e=void 0)}};function xr(e){return{onFetch:(t,n)=>{let r=t.options,i=t.fetchOptions?.meta?.fetchMore?.direction,a=t.state.data?.pages||[],o=t.state.data?.pageParams||[],s={pages:[],pageParams:[]},c=0,l=async()=>{let n=!1,l=e=>{lr(e,()=>t.signal,()=>n=!0)},u=sr(t.options,t.fetchOptions),d=async(e,r,i)=>{if(n)return Promise.reject(t.signal.reason);if(r==null&&e.pages.length)return Promise.resolve(e);let a=await u((()=>{let e={client:t.client,queryKey:t.queryKey,pageParam:r,direction:i?`backward`:`forward`,meta:t.options.meta};return l(e),e})()),{maxPages:o}=t.options,s=i?ar:ir;return{pages:s(e.pages,a,o),pageParams:s(e.pageParams,r,o)}};if(i&&a.length){let e=i===`backward`,t=e?Cr:Sr,n={pages:a,pageParams:o};s=await d(n,t(r,n),e)}else{let t=e??a.length;do{let e=c===0?o[0]??r.initialPageParam:Sr(r,s);if(c>0&&e==null)break;s=await d(s,e),c++}while(ct.options.persister?.(l,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n):t.fetchFn=l}}}function Sr(e,{pages:t,pageParams:n}){let r=t.length-1;return t.length>0?e.getNextPageParam(t[r],t,n[r],n):void 0}function Cr(e,{pages:t,pageParams:n}){return t.length>0?e.getPreviousPageParam?.(t[0],t,n[0],n):void 0}var wr=class extends br{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e){super(),this.#s=!1,this.#o=e.defaultOptions,this.setOptions(e.options),this.observers=[],this.#i=e.client,this.#r=this.#i.getQueryCache(),this.queryKey=e.queryKey,this.queryHash=e.queryHash,this.#t=Dr(this.options),this.state=e.state??this.#t,this.scheduleGc()}get meta(){return this.options.meta}get queryType(){return this.#e}get promise(){return this.#a?.promise}setOptions(e){if(this.options={...this.#o,...e},e?._type&&(this.#e=e._type),this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){let e=Dr(this.options);e.data!==void 0&&(this.setState(Er(e.data,e.dataUpdatedAt)),this.#t=e)}}optionalRemove(){!this.observers.length&&this.state.fetchStatus===`idle`&&this.#r.remove(this)}setData(e,t){let n=rr(this.state.data,e,this.options);return this.#l({data:n,type:`success`,dataUpdatedAt:t?.updatedAt,manual:t?.manual}),n}setState(e){this.#l({type:`setState`,state:e})}cancel(e){let t=this.#a?.promise;return this.#a?.cancel(e),t?t.then(zn).catch(zn):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}get resetState(){return this.#t}reset(){this.destroy(),this.setState(this.resetState)}isActive(){return this.observers.some(e=>Wn(e.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===or||!this.isFetched()}isFetched(){return this.state.dataUpdateCount+this.state.errorUpdateCount>0}isStatic(){return this.getObserversCount()>0?this.observers.some(e=>Un(e.options.staleTime,this)===`static`):!1}isStale(){return this.getObserversCount()>0?this.observers.some(e=>e.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(e=0){return this.state.data===void 0?!0:e===`static`?!1:this.state.isInvalidated?!0:!Hn(this.state.dataUpdatedAt,e)}onFocus(){this.observers.find(e=>e.shouldFetchOnWindowFocus())?.refetch({cancelRefetch:!1}),this.#a?.continue()}onOnline(){this.observers.find(e=>e.shouldFetchOnReconnect())?.refetch({cancelRefetch:!1}),this.#a?.continue()}addObserver(e){this.observers.includes(e)||(this.observers.push(e),this.clearGcTimeout(),this.#r.notify({type:`observerAdded`,query:this,observer:e}))}removeObserver(e){this.observers.includes(e)&&(this.observers=this.observers.filter(t=>t!==e),this.observers.length||(this.#a&&(this.#s||this.#c()?this.#a.cancel({revert:!0}):this.#a.cancelRetry()),this.scheduleGc()),this.#r.notify({type:`observerRemoved`,query:this,observer:e}))}getObserversCount(){return this.observers.length}#c(){return this.state.fetchStatus===`paused`&&this.state.status===`pending`}invalidate(){this.state.isInvalidated||this.#l({type:`invalidate`})}async fetch(e,t){if(this.state.fetchStatus!==`idle`&&this.#a?.status()!==`rejected`){if(this.state.data!==void 0&&t?.cancelRefetch)this.cancel({silent:!0});else if(this.#a)return this.#a.continueRetry(),this.#a.promise}if(e&&this.setOptions(e),!this.options.queryFn){let e=this.observers.find(e=>e.options.queryFn);e&&this.setOptions(e.options)}let n=new AbortController,r=e=>{Object.defineProperty(e,"signal",{enumerable:!0,get:()=>(this.#s=!0,n.signal)})},i=()=>{let e=sr(this.options,t),n=(()=>{let e={client:this.#i,queryKey:this.queryKey,meta:this.meta};return r(e),e})();return this.#s=!1,this.options.persister?this.options.persister(e,n,this):e(n)},a=(()=>{let e={fetchOptions:t,options:this.options,queryKey:this.queryKey,client:this.#i,state:this.state,fetchFn:i};return r(e),e})();(this.#e===`infinite`?xr(this.options.pages):this.options.behavior)?.onFetch(a,this),this.#n=this.state,(this.state.fetchStatus===`idle`||this.state.fetchMeta!==a.fetchOptions?.meta)&&this.#l({type:`fetch`,meta:a.fetchOptions?.meta}),this.#a=yr({initialPromise:t?.initialPromise,fn:a.fetchFn,onCancel:e=>{e instanceof vr&&e.revert&&this.setState({...this.#n,fetchStatus:`idle`}),n.abort()},onFail:(e,t)=>{this.#l({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#l({type:`pause`})},onContinue:()=>{this.#l({type:`continue`})},retry:a.options.retry,retryDelay:a.options.retryDelay,networkMode:a.options.networkMode,canRun:()=>!0});try{let e=await this.#a.start();if(e===void 0)throw Error(`${this.queryHash} data is undefined`);return this.setData(e),this.#r.config.onSuccess?.(e,this),this.#r.config.onSettled?.(e,this.state.error,this),e}catch(e){if(e instanceof vr){if(e.silent)return this.#a.promise;if(e.revert){if(this.state.data===void 0)throw e;return this.state.data}}throw this.#l({type:`error`,error:e}),this.#r.config.onError?.(e,this),this.#r.config.onSettled?.(this.state.data,e,this),e}finally{this.scheduleGc()}}#l(e){let t=t=>{switch(e.type){case`failed`:return{...t,fetchFailureCount:e.failureCount,fetchFailureReason:e.error};case`pause`:return{...t,fetchStatus:`paused`};case`continue`:return{...t,fetchStatus:`fetching`};case`fetch`:return{...t,...Tr(t.data,this.options),fetchMeta:e.meta??null};case`success`:let n={...t,...Er(e.data,e.dataUpdatedAt),dataUpdateCount:t.dataUpdateCount+1,...!e.manual&&{fetchStatus:`idle`,fetchFailureCount:0,fetchFailureReason:null}};return this.#n=e.manual?n:void 0,n;case`error`:let r=e.error;return{...t,error:r,errorUpdateCount:t.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:t.fetchFailureCount+1,fetchFailureReason:r,fetchStatus:`idle`,status:`error`,isInvalidated:!0};case`invalidate`:return{...t,isInvalidated:!0};case`setState`:return{...t,...e.state}}};this.state=t(this.state),mr.batch(()=>{this.observers.forEach(e=>{e.onQueryUpdate()}),this.#r.notify({query:this,type:`updated`,action:e})})}};function Tr(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:_r(t.networkMode)?`fetching`:`paused`,...e===void 0&&{error:null,status:`pending`}}}function Er(e,t){return{data:e,dataUpdatedAt:t??Date.now(),error:null,isInvalidated:!1,status:`success`}}function Dr(e){let t=typeof e.initialData==`function`?e.initialData():e.initialData,n=t!==void 0,r=n?typeof e.initialDataUpdatedAt==`function`?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?r??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?`success`:`pending`,fetchStatus:`idle`}}var Or=class extends br{#e;#t;#n;#r;constructor(e){super(),this.#e=e.client,this.mutationId=e.mutationId,this.#n=e.mutationCache,this.#t=[],this.state=e.state||kr(),this.setOptions(e.options),this.scheduleGc()}setOptions(e){this.options=e,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(e){this.#t.includes(e)||(this.#t.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,mutation:this,observer:e}))}removeObserver(e){this.#t=this.#t.filter(t=>t!==e),this.scheduleGc(),this.#n.notify({type:`observerRemoved`,mutation:this,observer:e})}optionalRemove(){this.#t.length||(this.state.status===`pending`?this.scheduleGc():this.#n.remove(this))}continue(){return this.#r?.continue()??this.execute(this.state.variables)}async execute(e){let t=()=>{this.#i({type:`continue`})},n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};this.#r=yr({fn:()=>this.options.mutationFn?this.options.mutationFn(e,n):Promise.reject(Error(`No mutationFn found`)),onFail:(e,t)=>{this.#i({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#i({type:`pause`})},onContinue:t,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>this.#n.canRun(this)});let r=this.state.status===`pending`,i=!this.#r.canStart();try{if(r)t();else{this.#i({type:`pending`,variables:e,isPaused:i}),this.#n.config.onMutate&&await this.#n.config.onMutate(e,this,n);let t=await this.options.onMutate?.(e,n);t!==this.state.context&&this.#i({type:`pending`,context:t,variables:e,isPaused:i})}let a=await this.#r.start();return await this.#n.config.onSuccess?.(a,e,this.state.context,this,n),await this.options.onSuccess?.(a,e,this.state.context,n),await this.#n.config.onSettled?.(a,null,this.state.variables,this.state.context,this,n),await this.options.onSettled?.(a,null,e,this.state.context,n),this.#i({type:`success`,data:a}),a}catch(t){try{await this.#n.config.onError?.(t,e,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onError?.(t,e,this.state.context,n)}catch(e){Promise.reject(e)}try{await this.#n.config.onSettled?.(void 0,t,this.state.variables,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onSettled?.(void 0,t,e,this.state.context,n)}catch(e){Promise.reject(e)}throw this.#i({type:`error`,error:t}),t}finally{this.#n.runNext(this)}}#i(e){let t=t=>{switch(e.type){case`failed`:return{...t,failureCount:e.failureCount,failureReason:e.error};case`pause`:return{...t,isPaused:!0};case`continue`:return{...t,isPaused:!1};case`pending`:return{...t,context:e.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:e.isPaused,status:`pending`,variables:e.variables,submittedAt:Date.now()};case`success`:return{...t,data:e.data,failureCount:0,failureReason:null,error:null,status:`success`,isPaused:!1};case`error`:return{...t,data:void 0,error:e.error,failureCount:t.failureCount+1,failureReason:e.error,isPaused:!1,status:`error`}}};this.state=t(this.state),mr.batch(()=>{this.#t.forEach(t=>{t.onMutationUpdate(e)}),this.#n.notify({mutation:this,type:`updated`,action:e})})}};function kr(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:`idle`,variables:void 0,submittedAt:0}}var Ar=class extends Nn{constructor(e={}){super(),this.config=e,this.#e=new Set,this.#t=new Map,this.#n=0}#e;#t;#n;build(e,t,n){let r=new Or({client:e,mutationCache:this,mutationId:++this.#n,options:e.defaultMutationOptions(t),state:n});return this.add(r),r}add(e){this.#e.add(e);let t=jr(e);if(typeof t==`string`){let n=this.#t.get(t);n?n.push(e):this.#t.set(t,[e])}this.notify({type:`added`,mutation:e})}remove(e){if(this.#e.delete(e)){let t=jr(e);if(typeof t==`string`){let n=this.#t.get(t);if(n)if(n.length>1){let t=n.indexOf(e);t!==-1&&n.splice(t,1)}else n[0]===e&&this.#t.delete(t)}}this.notify({type:`removed`,mutation:e})}canRun(e){let t=jr(e);if(typeof t==`string`){let n=this.#t.get(t)?.find(e=>e.state.status===`pending`);return!n||n===e}else return!0}runNext(e){let t=jr(e);return typeof t==`string`?(this.#t.get(t)?.find(t=>t!==e&&t.state.isPaused))?.continue()??Promise.resolve():Promise.resolve()}clear(){mr.batch(()=>{this.#e.forEach(e=>{this.notify({type:`removed`,mutation:e})}),this.#e.clear(),this.#t.clear()})}getAll(){return Array.from(this.#e)}find(e){let t={exact:!0,...e};return this.getAll().find(e=>Kn(t,e))}findAll(e={}){return this.getAll().filter(t=>Kn(e,t))}notify(e){mr.batch(()=>{this.listeners.forEach(t=>{t(e)})})}resumePausedMutations(){let e=this.getAll().filter(e=>e.state.isPaused);return mr.batch(()=>Promise.all(e.map(e=>e.continue().catch(zn))))}};function jr(e){return e.options.scope?.id}var Mr=class extends Nn{constructor(e={}){super(),this.config=e,this.#e=new Map}#e;build(e,t,n){let r=t.queryKey,i=t.queryHash??qn(r,t),a=this.get(i);return a||(a=new wr({client:e,queryKey:r,queryHash:i,options:e.defaultQueryOptions(t),state:n,defaultOptions:e.getQueryDefaults(r)}),this.add(a)),a}add(e){this.#e.has(e.queryHash)||(this.#e.set(e.queryHash,e),this.notify({type:`added`,query:e}))}remove(e){let t=this.#e.get(e.queryHash);t&&(e.destroy(),t===e&&this.#e.delete(e.queryHash),this.notify({type:`removed`,query:e}))}clear(){mr.batch(()=>{this.getAll().forEach(e=>{this.remove(e)})})}get(e){return this.#e.get(e)}getAll(){return[...this.#e.values()]}find(e){let t={exact:!0,...e};return this.getAll().find(e=>Gn(t,e))}findAll(e={}){let t=this.getAll();return Object.keys(e).length>0?t.filter(t=>Gn(e,t)):t}notify(e){mr.batch(()=>{this.listeners.forEach(t=>{t(e)})})}onFocus(){mr.batch(()=>{this.getAll().forEach(e=>{e.onFocus()})})}onOnline(){mr.batch(()=>{this.getAll().forEach(e=>{e.onOnline()})})}},Nr=class{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e={}){this.#e=e.queryCache||new Mr,this.#t=e.mutationCache||new Ar,this.#n=e.defaultOptions||{},this.#r=new Map,this.#i=new Map,this.#a=0}mount(){this.#a++,this.#a===1&&(this.#o=Pn.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onFocus())}),this.#s=hr.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onOnline())}))}unmount(){this.#a--,this.#a===0&&(this.#o?.(),this.#o=void 0,this.#s?.(),this.#s=void 0)}isFetching(e){return this.#e.findAll({...e,fetchStatus:`fetching`}).length}isMutating(e){return this.#t.findAll({...e,status:`pending`}).length}getQueryData(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state.data}ensureQueryData(e){let t=this.defaultQueryOptions(e),n=this.#e.build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(Un(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return this.#e.findAll(e).map(({queryKey:e,state:t})=>[e,t.data])}setQueryData(e,t,n){let r=this.defaultQueryOptions({queryKey:e}),i=this.#e.get(r.queryHash)?.state.data,a=Bn(t,i);if(a!==void 0)return this.#e.build(this,r).setData(a,{...n,manual:!0})}setQueriesData(e,t,n){return mr.batch(()=>this.#e.findAll(e).map(({queryKey:e})=>[e,this.setQueryData(e,t,n)]))}getQueryState(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state}removeQueries(e){let t=this.#e;mr.batch(()=>{t.findAll(e).forEach(e=>{t.remove(e)})})}resetQueries(e,t){let n=this.#e;return mr.batch(()=>(n.findAll(e).forEach(e=>{e.reset()}),this.refetchQueries({type:`active`,...e},t)))}cancelQueries(e,t={}){let n={revert:!0,...t},r=mr.batch(()=>this.#e.findAll(e).map(e=>e.cancel(n)));return Promise.all(r).then(zn).catch(zn)}invalidateQueries(e,t={}){return mr.batch(()=>(this.#e.findAll(e).forEach(e=>{e.invalidate()}),e?.refetchType===`none`?Promise.resolve():this.refetchQueries({...e,type:e?.refetchType??e?.type??`active`},t)))}refetchQueries(e,t={}){let n={...t,cancelRefetch:t.cancelRefetch??!0},r=mr.batch(()=>this.#e.findAll(e).filter(e=>!e.isDisabled()&&!e.isStatic()).map(e=>{let t=e.fetch(void 0,n);return n.throwOnError||(t=t.catch(zn)),e.state.fetchStatus===`paused`?Promise.resolve():t}));return Promise.all(r).then(zn)}fetchQuery(e){let t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);let n=this.#e.build(this,t);return n.isStaleByTime(Un(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(zn).catch(zn)}fetchInfiniteQuery(e){return e._type=`infinite`,this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(zn).catch(zn)}ensureInfiniteQueryData(e){return e._type=`infinite`,this.ensureQueryData(e)}resumePausedMutations(){return hr.isOnline()?this.#t.resumePausedMutations():Promise.resolve()}getQueryCache(){return this.#e}getMutationCache(){return this.#t}getDefaultOptions(){return this.#n}setDefaultOptions(e){this.#n=e}setQueryDefaults(e,t){this.#r.set(Jn(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){let t=[...this.#r.values()],n={};return t.forEach(t=>{Yn(e,t.queryKey)&&Object.assign(n,t.defaultOptions)}),n}setMutationDefaults(e,t){this.#i.set(Jn(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){let t=[...this.#i.values()],n={};return t.forEach(t=>{Yn(e,t.mutationKey)&&Object.assign(n,t.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;let t={...this.#n.queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||=qn(t.queryKey,t),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!==`always`),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode=`offlineFirst`),t.queryFn===or&&(t.enabled=!1),t}defaultMutationOptions(e){return e?._defaulted?e:{...this.#n.mutations,...e?.mutationKey&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){this.#e.clear(),this.#t.clear()}},U=n(r(),1),W=t(),Pr=U.createContext(void 0),Fr=e=>{let t=U.useContext(Pr);if(e)return e;if(!t)throw Error(`No QueryClient set, use QueryClientProvider to set one`);return t},Ir=({client:e,children:t})=>(U.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),(0,W.jsx)(Pr.Provider,{value:e,children:t}));function Lr(e,t=String){let n=new URLSearchParams;for(let r in e){let i=e[r];i!==void 0&&n.set(r,t(i))}return n.toString()}function Rr(e){return e?e===`false`?!1:e===`true`?!0:e*0==0&&+e+``===e?+e:e:``}function zr(e){let t=new URLSearchParams(e),n=Object.create(null);for(let[e,r]of t.entries()){let t=n[e];t==null?n[e]=Rr(r):Array.isArray(t)?t.push(Rr(r)):n[e]=[t,Rr(r)]}return n}var Br=Hr(JSON.parse),Vr=Ur(JSON.stringify,JSON.parse);function Hr(e){return t=>{t[0]===`?`&&(t=t.substring(1));let n=zr(t);for(let t in n){let r=n[t];if(typeof r==`string`)try{n[t]=e(r)}catch{}}return n}}function Ur(e,t){let n=typeof t==`function`;function r(r){if(typeof r==`object`&&r)try{return e(r)}catch{}else if(n&&typeof r==`string`)try{return t(r),e(r)}catch{}return r}return e=>{let t=Lr(e,r);return t?`?${t}`:``}}var Wr=e=>{if(!e.rendered)return e.rendered=!0,e.onReady?.()},Gr=e=>e.stores.matchesId.get().some(t=>e.stores.matchStores.get(t)?.get()._forcePending),Kr=(e,t)=>!!(e.preload&&!e.router.stores.matchStores.has(t)),qr=(e,t,n=!0)=>{let r={...e.router.options.context??{}},i=n?t:t-1;for(let t=0;t<=i;t++){let n=e.matches[t];if(!n)continue;let i=e.router.getMatch(n.id);i&&Object.assign(r,i.__routeContext,i.__beforeLoadContext)}return r},Jr=(e,t)=>{if(!e.matches.length)return;let n=t.routeId,r=e.matches.findIndex(t=>t.routeId===e.router.routeTree.id),i=r>=0?r:0,a=n?e.matches.findIndex(e=>e.routeId===n):e.firstBadMatchIndex??e.matches.length-1;a<0&&(a=i);for(let t=a;t>=0;t--){let n=e.matches[t];if(e.router.looseRoutesById[n.routeId].options.notFoundComponent)return t}return n?a:i},Yr=(e,t,n)=>{if(!(!he(n)&&!ue(n)))throw he(n)&&n.redirectHandled&&!n.options.reloadDocument?n:(t&&(t._nonReactive.beforeLoadPromise?.resolve(),t._nonReactive.loaderPromise?.resolve(),t._nonReactive.beforeLoadPromise=void 0,t._nonReactive.loaderPromise=void 0,t._nonReactive.error=n,e.updateMatch(t.id,r=>({...r,status:he(n)?`redirected`:ue(n)?`notFound`:r.status===`pending`?`success`:r.status,context:qr(e,t.index),isFetching:!1,error:n})),ue(n)&&!n.routeId&&(n.routeId=t.routeId),t._nonReactive.loadPromise?.resolve()),he(n)&&(e.rendered=!0,n.options._fromLocation=e.location,n.redirectHandled=!0,n=e.router.resolveRedirect(n)),n)},Xr=(e,t)=>{let n=e.router.getMatch(t);return!!(!n||n._nonReactive.dehydrated)},Zr=(e,t,n)=>{let r=qr(e,n);e.updateMatch(t,e=>({...e,context:r}))},Qr=(e,t,n)=>{let{id:r,routeId:i}=e.matches[t],a=e.router.looseRoutesById[i];if(n instanceof Promise)throw n;e.firstBadMatchIndex??=t,Yr(e,e.router.getMatch(r),n);try{a.options.onError?.(n)}catch(t){n=t,Yr(e,e.router.getMatch(r),n)}e.updateMatch(r,e=>(e._nonReactive.beforeLoadPromise?.resolve(),e._nonReactive.beforeLoadPromise=void 0,e._nonReactive.loadPromise?.resolve(),{...e,error:n,status:`error`,isFetching:!1,updatedAt:Date.now(),abortController:new AbortController})),!e.preload&&!he(n)&&!ue(n)&&(e.serialError??=n)},$r=(e,t,n,r)=>{if(r._nonReactive.pendingTimeout!==void 0)return;let i=n.options.pendingMs??e.router.options.defaultPendingMs;if(e.onReady&&!Kr(e,t)&&(n.options.loader||n.options.beforeLoad||ui(n))&&typeof i==`number`&&i!==1/0&&(n.options.pendingComponent??e.router.options?.defaultPendingComponent)){let t=setTimeout(()=>{Wr(e)},i);r._nonReactive.pendingTimeout=t}},ei=(e,t,n)=>{let r=e.router.getMatch(t);if(!r._nonReactive.beforeLoadPromise&&!r._nonReactive.loaderPromise)return;$r(e,t,n,r);let i=()=>{let n=e.router.getMatch(t);n.preload&&(n.status===`redirected`||n.status===`notFound`)&&Yr(e,n,n.error)};return r._nonReactive.beforeLoadPromise?r._nonReactive.beforeLoadPromise.then(i):i()},ti=(e,t,n,r)=>{let i=e.router.getMatch(t),a=i._nonReactive.loadPromise;i._nonReactive.loadPromise=_(()=>{a?.resolve(),a=void 0});let{paramsError:o,searchError:s}=i;o&&Qr(e,n,o),s&&Qr(e,n,s),$r(e,t,r,i);let c=new AbortController,l=!1,u=()=>{l||(l=!0,e.updateMatch(t,e=>({...e,isFetching:`beforeLoad`,fetchCount:e.fetchCount+1,abortController:c})))},d=()=>{i._nonReactive.beforeLoadPromise?.resolve(),i._nonReactive.beforeLoadPromise=void 0,e.updateMatch(t,e=>({...e,isFetching:!1}))};if(!r.options.beforeLoad){e.router.batch(()=>{u(),d()});return}i._nonReactive.beforeLoadPromise=_();let f={...qr(e,n,!1),...i.__routeContext},{search:p,params:h,cause:g}=i,v=Kr(e,t),y={search:p,abortController:c,params:h,preload:v,context:f,location:e.location,navigate:t=>e.router.navigate({...t,_fromLocation:e.location}),buildLocation:e.router.buildLocation,cause:v?`preload`:g,matches:e.matches,routeId:r.id,...e.router.options.additionalContext},b=r=>{if(r===void 0){e.router.batch(()=>{u(),d()});return}(he(r)||ue(r))&&(u(),Qr(e,n,r)),e.router.batch(()=>{u(),e.updateMatch(t,e=>({...e,__beforeLoadContext:r})),d()})},x;try{if(x=r.options.beforeLoad(y),m(x))return u(),x.catch(t=>{Qr(e,n,t)}).then(b)}catch(t){u(),Qr(e,n,t)}b(x)},ni=(e,t)=>{let{id:n,routeId:r}=e.matches[t],i=e.router.looseRoutesById[r],a=()=>s(),o=()=>ti(e,n,t,i),s=()=>{if(Xr(e,n))return;let t=ei(e,n,i);return m(t)?t.then(o):o()};return a()},ri=(e,t,n)=>{let r=e.router.getMatch(t);if(!r||!n.options.head&&!n.options.scripts&&!n.options.headers)return;let i={ssr:e.router.options.ssr,matches:e.matches,match:r,params:r.params,loaderData:r.loaderData};return Promise.all([n.options.head?.(i),n.options.scripts?.(i),n.options.headers?.(i)]).then(([e,t,n])=>({meta:e?.meta,links:e?.links,headScripts:e?.scripts,headers:n,scripts:t,styles:e?.styles}))},ii=(e,t,n,r,i)=>{let a=t[r-1],{params:o,loaderDeps:s,abortController:c,cause:l}=e.router.getMatch(n),u=qr(e,r),d=Kr(e,n);return{params:o,deps:s,preload:!!d,parentMatchPromise:a,abortController:c,context:u,location:e.location,navigate:t=>e.router.navigate({...t,_fromLocation:e.location}),cause:d?`preload`:l,route:i,...e.router.options.additionalContext}},ai=async(e,t,n,r,i)=>{try{let a=e.router.getMatch(n);try{li(i);let o=i.options.loader,s=typeof o==`function`?o:o?.handler,c=s?.(ii(e,t,n,r,i)),l=!!s&&m(c);if((l||i._lazyPromise||i._componentsPromise||i.options.head||i.options.scripts||i.options.headers||a._nonReactive.minPendingPromise)&&e.updateMatch(n,e=>({...e,isFetching:`loader`})),s){let t=l?await c:c;Yr(e,e.router.getMatch(n),t),t!==void 0&&e.updateMatch(n,e=>({...e,loaderData:t}))}i._lazyPromise&&await i._lazyPromise;let u=a._nonReactive.minPendingPromise;u&&await u,i._componentsPromise&&await i._componentsPromise,e.updateMatch(n,t=>({...t,error:void 0,context:qr(e,r),status:`success`,isFetching:!1,updatedAt:Date.now()}))}catch(t){let o=t;if(o?.name===`AbortError`){if(a.abortController.signal.aborted){a._nonReactive.loaderPromise?.resolve(),a._nonReactive.loaderPromise=void 0;return}e.updateMatch(n,t=>({...t,status:t.status===`pending`?`success`:t.status,isFetching:!1,context:qr(e,r)}));return}let s=a._nonReactive.minPendingPromise;s&&await s,ue(t)&&await i.options.notFoundComponent?.preload?.(),Yr(e,e.router.getMatch(n),t);try{i.options.onError?.(t)}catch(t){o=t,Yr(e,e.router.getMatch(n),t)}!he(o)&&!ue(o)&&await li(i,[`errorComponent`]),e.updateMatch(n,t=>({...t,error:o,context:qr(e,r),status:`error`,isFetching:!1}))}}catch(t){let r=e.router.getMatch(n);r&&(r._nonReactive.loaderPromise=void 0),Yr(e,r,t)}},oi=async(e,t,n)=>{async function r(r,a,c,l,d){let f=Date.now()-a.updatedAt,p=r?d.options.preloadStaleTime??e.router.options.defaultPreloadStaleTime??3e4:d.options.staleTime??e.router.options.defaultStaleTime??0,m=d.options.shouldReload,h=typeof m==`function`?m(ii(e,t,i,n,d)):m,{status:g,invalid:_}=l,v=f>=p&&(!!e.forceStaleReload||l.cause===`enter`||c!==void 0&&c!==l.id);o=g===`success`&&(_||(h??v)),r&&d.options.preload===!1||(o&&!e.sync&&u?(s=!0,(async()=>{try{await ai(e,t,i,n,d);let r=e.router.getMatch(i);r._nonReactive.loaderPromise?.resolve(),r._nonReactive.loadPromise?.resolve(),r._nonReactive.loaderPromise=void 0,r._nonReactive.loadPromise=void 0}catch(t){he(t)&&await e.router.navigate(t.options)}})()):g!==`success`||o?await ai(e,t,i,n,d):Zr(e,i,n))}let{id:i,routeId:a}=e.matches[n],o=!1,s=!1,c=e.router.looseRoutesById[a],l=c.options.loader,u=((typeof l==`function`?void 0:l?.staleReloadMode)??e.router.options.defaultStaleReloadMode)!==`blocking`;if(Xr(e,i)){if(!e.router.getMatch(i))return e.matches[n];Zr(e,i,n)}else{let t=e.router.getMatch(i),o=e.router.stores.matchesId.get()[n],s=(o&&e.router.stores.matchStores.get(o)||null)?.routeId===a?o:e.router.stores.matches.get().find(e=>e.routeId===a)?.id,l=Kr(e,i);if(t._nonReactive.loaderPromise){if(t.status===`success`&&!e.sync&&!t.preload&&u)return t;await t._nonReactive.loaderPromise;let n=e.router.getMatch(i),a=n._nonReactive.error||n.error;a&&Yr(e,n,a),n.status===`pending`&&await r(l,t,s,n,c)}else{let n=l&&!e.router.stores.matchStores.has(i),a=e.router.getMatch(i);a._nonReactive.loaderPromise=_(),n!==a.preload&&e.updateMatch(i,e=>({...e,preload:n})),await r(l,t,s,a,c)}}let d=e.router.getMatch(i);s||(d._nonReactive.loaderPromise?.resolve(),d._nonReactive.loadPromise?.resolve(),d._nonReactive.loadPromise=void 0),clearTimeout(d._nonReactive.pendingTimeout),d._nonReactive.pendingTimeout=void 0,s||(d._nonReactive.loaderPromise=void 0),d._nonReactive.dehydrated=void 0;let f=s?d.isFetching:!1;return f!==d.isFetching||d.invalid!==!1?(e.updateMatch(i,e=>({...e,isFetching:f,invalid:!1})),e.router.getMatch(i)):d};async function si(e){let t=e,n=[];Gr(t.router)&&Wr(t);let r;for(let e=0;e({...e,...a?{status:`success`,globalNotFound:!0,error:void 0}:{status:`notFound`,error:l},isFetching:!1})),u=e,await li(r,[`notFoundComponent`])}else if(!t.preload){let e=t.matches[0];e.globalNotFound||t.router.getMatch(e.id)?.globalNotFound&&t.updateMatch(e.id,e=>({...e,globalNotFound:!1,error:void 0}))}if(t.serialError&&t.firstBadMatchIndex!==void 0){let e=t.router.looseRoutesById[t.matches[t.firstBadMatchIndex].routeId];await li(e,[`errorComponent`])}for(let e=0;e<=u;e++){let{id:n,routeId:r}=t.matches[e],i=t.router.looseRoutesById[r];try{let e=ri(t,n,i);if(e){let r=await e;t.updateMatch(n,e=>({...e,...r}))}}catch(e){console.error(`Error executing head for route ${r}:`,e)}}let d=Wr(t);if(m(d)&&await d,l)throw l;if(t.serialError&&!t.preload&&!t.onReady)throw t.serialError;return t.matches}function ci(e,t){let n=t.map(t=>e.options[t]?.preload?.()).filter(Boolean);if(n.length!==0)return Promise.all(n)}function li(e,t=di){!e._lazyLoaded&&e._lazyPromise===void 0&&(e.lazyFn?e._lazyPromise=e.lazyFn().then(t=>{let{id:n,...r}=t.options;Object.assign(e.options,r),e._lazyLoaded=!0,e._lazyPromise=void 0}):e._lazyLoaded=!0);let n=()=>e._componentsLoaded?void 0:t===di?(()=>{if(e._componentsPromise===void 0){let t=ci(e,di);t?e._componentsPromise=t.then(()=>{e._componentsLoaded=!0,e._componentsPromise=void 0}):e._componentsLoaded=!0}return e._componentsPromise})():ci(e,t);return e._lazyPromise?e._lazyPromise.then(n):n()}function ui(e){for(let t of di)if(e.options[t]?.preload)return!0;return!1}var di=[`component`,`errorComponent`,`pendingComponent`,`notFoundComponent`];function fi(e){return{input:({url:t})=>{for(let n of e)t=mi(n,t);return t},output:({url:t})=>{for(let n=e.length-1;n>=0;n--)t=hi(e[n],t);return t}}}function pi(e){let t=j(e.basepath),n=`/${t}`,r=e.caseSensitive?n:n.toLowerCase(),i=`${r}/`;return{input:({url:t})=>{let a=e.caseSensitive?t.pathname:t.pathname.toLowerCase();return a===r?t.pathname=`/`:a.startsWith(i)&&(t.pathname=t.pathname.slice(n.length)),t},output:({url:e})=>(e.pathname=k([`/`,t,e.pathname]),e)}}function mi(e,t){let n=e?.input?.({url:t});if(n){if(typeof n==`string`)return new URL(n);if(n instanceof URL)return n}return t}function hi(e,t){let n=e?.output?.({url:t});if(n){if(typeof n==`string`)return new URL(n);if(n instanceof URL)return n}return t}function gi(e,t){let{createMutableStore:n,createReadonlyStore:r,batch:i,init:a}=t,o=new Map,s=new Map,c=new Map,l=n(e.status),u=n(e.loadedAt),d=n(e.isLoading),f=n(e.isTransitioning),p=n(e.location),m=n(e.resolvedLocation),h=n(e.statusCode),g=n(e.redirect),_=n([]),v=n([]),y=n([]),b=r(()=>_i(o,_.get())),x=r(()=>_i(s,v.get())),S=r(()=>_i(c,y.get())),C=r(()=>_.get()[0]),w=r(()=>_.get().some(e=>o.get(e)?.get().status===`pending`)),T=r(()=>({locationHref:p.get().href,resolvedLocationHref:m.get()?.href,status:l.get()})),E=r(()=>({status:l.get(),loadedAt:u.get(),isLoading:d.get(),isTransitioning:f.get(),matches:b.get(),location:p.get(),resolvedLocation:m.get(),statusCode:h.get(),redirect:g.get()})),D=se(64);function O(e){let t=D.get(e);return t||(t=r(()=>{let t=_.get();for(let n of t){let t=o.get(n);if(t&&t.routeId===e)return t.get()}}),D.set(e,t)),t}let k={status:l,loadedAt:u,isLoading:d,isTransitioning:f,location:p,resolvedLocation:m,statusCode:h,redirect:g,matchesId:_,pendingIds:v,cachedIds:y,matches:b,pendingMatches:x,cachedMatches:S,firstId:C,hasPending:w,matchRouteDeps:T,matchStores:o,pendingMatchStores:s,cachedMatchStores:c,__store:E,getRouteMatchStore:O,setMatches:A,setPending:j,setCached:M};A(e.matches),a?.(k);function A(e){vi(e,o,_,n,i)}function j(e){vi(e,s,v,n,i)}function M(e){vi(e,c,y,n,i)}return k}function _i(e,t){let n=[];for(let r of t){let t=e.get(r);t&&n.push(t.get())}return n}function vi(e,t,n,r,i){let a=e.map(e=>e.id),o=new Set(a);i(()=>{for(let e of t.keys())o.has(e)||t.delete(e);for(let n of e){let e=t.get(n.id);if(!e){let e=r(n);e.routeId=n.routeId,t.set(n.id,e);continue}e.routeId=n.routeId,e.get()!==n&&e.set(n)}l(n.get(),a)||n.set(a)})}var yi=`__TSR_index`,bi=`popstate`,xi=`beforeunload`;function Si(e){let t=e.getLocation(),n=new Set,r=r=>{t=e.getLocation(),n.forEach(e=>e({location:t,action:r}))},i=n=>{e.notifyOnIndexChange??!0?r(n):t=e.getLocation()},a=async({task:n,navigateOpts:r,...i})=>{if(r?.ignoreBlocker??!1){n();return}let a=e.getBlockers?.()??[],o=i.type===`PUSH`||i.type===`REPLACE`;if(typeof document<`u`&&a.length&&o)for(let n of a){let r=Ei(i.path,i.state);if(await n.blockerFn({currentLocation:t,nextLocation:r,action:i.type})){e.onBlocked?.();return}}n()};return{get location(){return t},get length(){return e.getLength()},subscribers:n,subscribe:e=>(n.add(e),()=>{n.delete(e)}),push:(n,i,o)=>{let s=t.state[yi];i=Ci(s+1,i),a({task:()=>{e.pushState(n,i),r({type:`PUSH`})},navigateOpts:o,type:`PUSH`,path:n,state:i})},replace:(n,i,o)=>{let s=t.state[yi];i=Ci(s,i),a({task:()=>{e.replaceState(n,i),r({type:`REPLACE`})},navigateOpts:o,type:`REPLACE`,path:n,state:i})},go:(t,n)=>{a({task:()=>{e.go(t),i({type:`GO`,index:t})},navigateOpts:n,type:`GO`})},back:t=>{a({task:()=>{e.back(t?.ignoreBlocker??!1),i({type:`BACK`})},navigateOpts:t,type:`BACK`})},forward:t=>{a({task:()=>{e.forward(t?.ignoreBlocker??!1),i({type:`FORWARD`})},navigateOpts:t,type:`FORWARD`})},canGoBack:()=>t.state[yi]!==0,createHref:t=>e.createHref(t),block:t=>{if(!e.setBlockers)return()=>{};let n=e.getBlockers?.()??[];return e.setBlockers([...n,t]),()=>{let n=e.getBlockers?.()??[];e.setBlockers?.(n.filter(e=>e!==t))}},flush:()=>e.flush?.(),destroy:()=>e.destroy?.(),notify:r}}function Ci(e,t){t||={};let n=Di();return{...t,key:n,__TSR_key:n,[yi]:e}}function wi(e){let t=e?.window??(typeof document<`u`?window:void 0),n=t.history.pushState,r=t.history.replaceState,i=[],a=()=>i,o=e=>i=e,s=e?.createHref??(e=>e),c=e?.parseLocation??(()=>Ei(`${t.location.pathname}${t.location.search}${t.location.hash}`,t.history.state));if(!t.history.state?.__TSR_key&&!t.history.state?.key){let e=Di();t.history.replaceState({[yi]:0,key:e,__TSR_key:e},``)}let l=c(),u,d=!1,f=!1,p=!1,m=!1,h=()=>l,g,_,v=()=>{g&&(C._ignoreSubscribers=!0,(g.isPush?t.history.pushState:t.history.replaceState)(g.state,``,g.href),C._ignoreSubscribers=!1,g=void 0,_=void 0,u=void 0)},y=(e,t,n)=>{let r=s(t);_||(u=l),l=Ei(t,n),g={href:r,state:n,isPush:g?.isPush||e===`push`},_||=Promise.resolve().then(()=>v())},b=e=>{l=c(),C.notify({type:e})},x=async()=>{if(f){f=!1;return}let e=c(),n=e.state[yi]-l.state[yi],r=n===1,i=n===-1,o=!r&&!i||d;d=!1;let s=o?`GO`:i?`BACK`:`FORWARD`,u=o?{type:`GO`,index:n}:{type:i?`BACK`:`FORWARD`};if(p)p=!1;else{let n=a();if(typeof document<`u`&&n.length){for(let r of n)if(await r.blockerFn({currentLocation:l,nextLocation:e,action:s})){f=!0,t.history.go(1),C.notify(u);return}}}l=c(),C.notify(u)},S=e=>{if(m){m=!1;return}let t=!1,n=a();if(typeof document<`u`&&n.length)for(let e of n){let n=e.enableBeforeUnload??!0;if(n===!0){t=!0;break}if(typeof n==`function`&&n()===!0){t=!0;break}}if(t)return e.preventDefault(),e.returnValue=``},C=Si({getLocation:h,getLength:()=>t.history.length,pushState:(e,t)=>y(`push`,e,t),replaceState:(e,t)=>y(`replace`,e,t),back:e=>(e&&(p=!0),m=!0,t.history.back()),forward:e=>{e&&(p=!0),m=!0,t.history.forward()},go:e=>{d=!0,t.history.go(e)},createHref:e=>s(e),flush:v,destroy:()=>{t.history.pushState=n,t.history.replaceState=r,t.removeEventListener(xi,S,{capture:!0}),t.removeEventListener(bi,x)},onBlocked:()=>{u&&l!==u&&(l=u)},getBlockers:a,setBlockers:o,notifyOnIndexChange:!1});return t.addEventListener(xi,S,{capture:!0}),t.addEventListener(bi,x),t.history.pushState=function(...e){let r=n.apply(t.history,e);return C._ignoreSubscribers||b(`PUSH`),r},t.history.replaceState=function(...e){let n=r.apply(t.history,e);return C._ignoreSubscribers||b(`REPLACE`),n},C}function Ti(e){let t=e.replace(/[\x00-\x1f\x7f]/g,``);return t.startsWith(`//`)&&(t=`/`+t.replace(/^\/+/,``)),t}function Ei(e,t){let n=Ti(e),r=n.indexOf(`#`),i=n.indexOf(`?`),a=Di();return{href:n,pathname:n.substring(0,r>0?i>0?Math.min(r,i):r:i>0?i:n.length),hash:r>-1?n.substring(r):``,search:i>-1?n.slice(i,r===-1?void 0:r):``,state:t||{[yi]:0,key:a,__TSR_key:a}}}function Di(){return(Math.random()+1).toString(36).substring(7)}function G(e,t){let n=t,r=e;return{fromLocation:n,toLocation:r,pathChanged:n?.pathname!==r.pathname,hrefChanged:n?.href!==r.href,hashChanged:n?.hash!==r.hash}}var Oi=new WeakMap,ki=class{constructor(e,t){this.tempLocationKey=`${Math.round(Math.random()*1e7)}`,this.resetNextScroll=!0,this.shouldViewTransition=void 0,this.isViewTransitionTypesSupported=void 0,this.subscribers=new Set,this.isScrollRestoring=!1,this.isScrollRestorationSetup=!1,this.routeBranchCache=new WeakMap,this.startTransition=e=>e(),this.update=e=>{let t=this.options,n=this.basepath??t?.basepath??`/`,r=this.basepath===void 0,i=t?.rewrite;if(this.options={...t,...e},this.isServer=this.options.isServer??typeof document>`u`,this.protocolAllowlist=new Set(this.options.protocolAllowlist),this.options.pathParamsAllowedCharacters&&(this.pathParamsDecoder=P(this.options.pathParamsAllowedCharacters)),(!this.history||this.options.history&&this.options.history!==this.history)&&(this.options.history?this.history=this.options.history:this.history=wi()),this.origin=this.options.origin,this.origin||(window?.origin&&window.origin!==`null`?this.origin=window.origin:this.origin=`http://localhost`),this.history&&this.updateLatestLocation(),this.options.routeTree!==this.routeTree){this.routeTree=this.options.routeTree;let e;this.resolvePathCache=se(1e3),e=this.buildRouteTree(),this.setRoutes(e)}if(!this.stores&&this.latestLocation){let e=this.getStoreConfig(this);this.batch=e.batch,this.stores=gi(Mi(this.latestLocation),e),Qi(this)}let a=!1,o=this.options.basepath??`/`,s=this.options.rewrite;if(r||n!==o||i!==s){this.basepath=o;let e=[],t=j(o);t&&t!==`/`&&e.push(pi({basepath:o})),s&&e.push(s),this.rewrite=e.length===0?void 0:e.length===1?e[0]:fi(e),this.history&&this.updateLatestLocation(),a=!0}a&&this.stores&&this.stores.location.set(this.latestLocation),typeof window<`u`&&`CSS`in window&&typeof window.CSS?.supports==`function`&&(this.isViewTransitionTypesSupported=window.CSS.supports(`selector(:active-view-transition-type(a))`))},this.updateLatestLocation=()=>{this.latestLocation=this.parseLocation(this.history.location,this.latestLocation)},this.buildRouteTree=()=>{let e=c(this.routeTree,this.options.caseSensitive,(e,t)=>{e.init({originalIndex:t})});return this.options.routeMasks&&b(this.options.routeMasks,e.processedTree),e},this.subscribe=(e,t)=>{let n={eventType:e,fn:t};return this.subscribers.add(n),()=>{this.subscribers.delete(n)}},this.emit=e=>{this.subscribers.forEach(t=>{t.eventType===e.type&&t.fn(e)})},this.parseLocation=(e,t)=>{let n=({pathname:e,search:n,hash:r,href:i,state:a})=>{if(!this.rewrite&&!/[ \x00-\x1f\x7f\u0080-\uffff]/.test(e)){let i=this.options.parseSearch(n),o=this.options.stringifySearch(i);return{href:e+o+r,publicHref:e+o+r,pathname:N(e).path,external:!1,searchStr:o,search:le(t?.search,i),hash:N(r.slice(1)).path,state:s(t?.state,a)}}let o=new URL(i,this.origin),c=mi(this.rewrite,o),l=this.options.parseSearch(c.search),u=this.options.stringifySearch(l);return c.search=u,{href:c.href.replace(c.origin,``),publicHref:i,pathname:N(c.pathname).path,external:!!this.rewrite&&c.origin!==this.origin,searchStr:u,search:le(t?.search,l),hash:N(c.hash.slice(1)).path,state:s(t?.state,a)}},r=n(e),{__tempLocation:i,__tempKey:a}=r.state;if(i&&(!a||a===this.tempLocationKey)){let e=n(i);return e.state.key=r.state.key,e.state.__TSR_key=r.state.__TSR_key,delete e.state.__tempLocation,{...e,maskedLocation:r}}return r},this.resolvePathWithBase=(e,t)=>te({base:e,to:t.includes(`//`)?D(t):t,trailingSlash:this.options.trailingSlash,cache:this.resolvePathCache}),this.matchRoutes=(e,t,n)=>typeof e==`string`?this.matchRoutesInternal({pathname:e,search:t},n):this.matchRoutesInternal(e,t),this.getMatchedRoutes=e=>Pi({pathname:e,routesById:this.routesById,processedTree:this.processedTree}),this.cancelMatch=e=>{let t=this.getMatch(e);t&&(t.abortController.abort(),clearTimeout(t._nonReactive.pendingTimeout),t._nonReactive.pendingTimeout=void 0)},this.cancelMatches=()=>{this.stores.pendingIds.get().forEach(e=>{this.cancelMatch(e)}),this.stores.matchesId.get().forEach(e=>{if(this.stores.pendingMatchStores.has(e))return;let t=this.stores.matchStores.get(e)?.get();t&&(t.status===`pending`||t.isFetching===`loader`)&&this.cancelMatch(e)})},this.buildLocation=e=>{let t=(t={})=>{let n=t._fromLocation||this.pendingBuiltLocation||this.latestLocation,r=this.matchRoutesLightweight(n);t.from;let i=t.unsafeRelative===`path`?n.pathname:t.from??r.fullPath,a=t.to?`${t.to}`:void 0,o=r.search,c=Object.assign(Object.create(null),r.params),l=a?.charCodeAt(0)===47?`/`:this.resolvePathWithBase(i,`.`),u=a?this.resolvePathWithBase(l,a):l,d=t.params===!1||t.params===null?Object.create(null):(t.params??!0)===!0?c:Object.assign(c,g(t.params,c)),f=this.routesByPath[w(u)],p;if(f)p=this.getRouteBranch(f);else if(u.includes(`$`))p=[];else{let e=this.getMatchedRoutes(u);p=e.matchedRoutes,this.options.notFoundRoute&&(!e.foundRoute||e.foundRoute.path!==`/`&&e.routeParams[`**`])&&(p=[...p,this.options.notFoundRoute])}if(p.length&&v(d))for(let e of p){let t=e.options.params?.stringify??e.options.stringifyParams;if(t)try{Object.assign(d,t(d))}catch{}}let m=e.leaveParams?u:N(O({path:u,params:d,decoder:this.pathParamsDecoder,server:this.isServer}).interpolatedPath).path,h=o;if(e._includeValidateSearch&&this.options.search?.strict){let e={};p.forEach(t=>{if(t.options.validateSearch)try{Object.assign(e,Ni(t.options.validateSearch,{...e,...h}))}catch{}}),h=e}h=Fi({search:h,dest:t,destRoutes:p,_includeValidateSearch:e._includeValidateSearch}),h=le(o,h);let _=this.options.stringifySearch(h),y=t.hash===!0?n.hash:t.hash?g(t.hash,n.hash):void 0,b=y?`#${y}`:``,x=t.state===!0?n.state:t.state?g(t.state,n.state):{};x=s(n.state,x);let S=`${m}${_}${b}`,C,T,E=!1;if(this.rewrite){let e=new URL(S,this.origin),t=hi(this.rewrite,e);C=e.href.replace(e.origin,``),t.origin===this.origin?T=t.pathname+t.search+t.hash:(T=t.href,E=!0)}else C=ee(S),T=C;return{publicHref:T,href:C,pathname:m,search:h,searchStr:_,state:x,hash:y??``,external:E,unmaskOnReload:t.unmaskOnReload}},n=(n={},r)=>{let i=t(n),a=r?t(r):void 0;if(!a){let n=Object.create(null);if(this.options.routeMasks){let o=ce(i.pathname,this.processedTree);if(o){Object.assign(n,o.rawParams);let{from:i,params:s,...c}=o.route,l=s===!1||s===null?Object.create(null):(s??!0)===!0?n:Object.assign(n,g(s,n));r={from:e.from,...c,params:l},a=t(r)}}}return a&&(i.maskedLocation=a),i};return e.mask?n(e,{from:e.from,...e.mask}):n(e)},this.commitLocation=async({viewTransition:e,ignoreBlocker:t,...n})=>{let r,i=()=>{let e=[`key`,`__TSR_key`,`__TSR_index`,`__hashScrollIntoViewOptions`];e.forEach(e=>{n.state[e]=this.latestLocation.state[e]});let t=o(n.state,this.latestLocation.state);return e.forEach(e=>{delete n.state[e]}),t},a=w(this.latestLocation.href)===w(n.href),s=this.commitLocationPromise;if(this.commitLocationPromise=_(()=>{s?.resolve(),s=void 0}),a&&i())this.load();else{let{maskedLocation:i,hashScrollIntoView:a,...o}=n;i&&(o={...i,state:{...i.state,__tempKey:void 0,__tempLocation:{...o,search:o.searchStr,state:{...o.state,__tempKey:void 0,__tempLocation:void 0,__TSR_key:void 0,key:void 0}}}},(o.unmaskOnReload??this.options.unmaskOnReload??!1)&&(o.state.__tempKey=this.tempLocationKey)),o.state.__hashScrollIntoViewOptions=a??this.options.defaultHashScrollIntoView??!0,this.shouldViewTransition=e,r=n.replace?`REPLACE`:`PUSH`,this.history[r===`REPLACE`?`replace`:`push`](o.publicHref,o.state,{ignoreBlocker:t})}return this.resetNextScroll=n.resetScroll??!0,this.history.subscribers.size||this.load(r?{action:{type:r}}:void 0),this.commitLocationPromise},this.buildAndCommitLocation=({replace:e,resetScroll:t,hashScrollIntoView:n,viewTransition:r,ignoreBlocker:i,href:a,...o}={})=>{if(a){let t=this.history.location.state.__TSR_index,n=Ei(a,{__TSR_index:e?t:t+1}),r=new URL(n.pathname,this.origin);o.to=mi(this.rewrite,r).pathname,o.search=this.options.parseSearch(n.search),o.hash=n.hash.slice(1)}let s=this.buildLocation({...o,_includeValidateSearch:!0});this.pendingBuiltLocation=s;let c=this.commitLocation({...s,viewTransition:r,replace:e,resetScroll:t,hashScrollIntoView:n,ignoreBlocker:i});return Promise.resolve().then(()=>{this.pendingBuiltLocation===s&&(this.pendingBuiltLocation=void 0)}),c},this.navigate=async({to:e,reloadDocument:t,href:n,publicHref:r,...i})=>{let a=!1;if(n)try{new URL(`${n}`),a=!0}catch{}if(a&&!t&&(t=!0),t){if(e!==void 0||!n){let t=this.buildLocation({to:e,...i});n??=t.publicHref,r??=t.publicHref}let t=!a&&r?r:n;if(d(t,this.protocolAllowlist))return Promise.resolve();if(!i.ignoreBlocker){let e=this.history.getBlockers?.()??[];for(let t of e)if(t?.blockerFn&&await t.blockerFn({currentLocation:this.latestLocation,nextLocation:this.latestLocation,action:`PUSH`}))return Promise.resolve()}return i.replace?window.location.replace(t):window.location.href=t,Promise.resolve()}return this.buildAndCommitLocation({...i,href:n,to:e,_isNavigate:!0})},this.beforeLoad=()=>{this.cancelMatches(),this.updateLatestLocation();let e=this.matchRoutes(this.latestLocation),t=this.stores.cachedMatches.get().filter(t=>!e.some(e=>e.id===t.id));this.batch(()=>{this.stores.status.set(`pending`),this.stores.statusCode.set(200),this.stores.isLoading.set(!0),this.stores.location.set(this.latestLocation),this.stores.setPending(e),this.stores.setCached(t)})},this.load=async e=>{let t=e?.action?.type,n,r,i,a=this.stores.resolvedLocation.get()??this.stores.location.get();for(i=new Promise(o=>{this.startTransition(async()=>{try{this.beforeLoad(),t?Oi.set(this.latestLocation,t):Oi.delete(this.latestLocation);let n=this.latestLocation,r=G(n,this.stores.resolvedLocation.get());this.stores.redirect.get()||this.emit({type:`onBeforeNavigate`,...r}),this.emit({type:`onBeforeLoad`,...r}),await si({router:this,sync:e?.sync,forceStaleReload:a.href===n.href,matches:this.stores.pendingMatches.get(),location:n,updateMatch:this.updateMatch,onReady:async()=>{this.startTransition(()=>{this.startViewTransition(async()=>{let e=null,t=null,n=null,r=null;this.batch(()=>{let i=this.stores.pendingMatches.get(),a=i.length,o=this.stores.matches.get();e=a?o.filter(e=>!this.stores.pendingMatchStores.has(e.id)):null;let s=new Set;for(let e of this.stores.pendingMatchStores.values())e.routeId&&s.add(e.routeId);let c=new Set;for(let e of this.stores.matchStores.values())e.routeId&&c.add(e.routeId);t=a?o.filter(e=>!s.has(e.routeId)):null,n=a?i.filter(e=>!c.has(e.routeId)):null,r=a?i.filter(e=>c.has(e.routeId)):o,this.stores.isLoading.set(!1),this.stores.loadedAt.set(Date.now()),a&&(this.stores.setMatches(i),this.stores.setPending([]),this.stores.setCached([...this.stores.cachedMatches.get(),...e.filter(e=>e.status!==`error`&&e.status!==`notFound`&&e.status!==`redirected`)]),this.clearExpiredCache())});for(let[e,i]of[[t,`onLeave`],[n,`onEnter`],[r,`onStay`]])if(e)for(let t of e)this.looseRoutesById[t.routeId].options[i]?.(t)})})}})}catch(e){he(e)?(n=e,this.navigate({...n.options,replace:!0,ignoreBlocker:!0})):ue(e)&&(r=e);let t=n?n.status:r?404:this.stores.matches.get().some(e=>e.status===`error`)?500:200;this.batch(()=>{this.stores.statusCode.set(t),this.stores.redirect.set(n)})}this.latestLoadPromise===i&&(this.commitLocationPromise?.resolve(),this.latestLoadPromise=void 0,this.commitLocationPromise=void 0),o()})}),this.latestLoadPromise=i,await i;this.latestLoadPromise&&i!==this.latestLoadPromise;)await this.latestLoadPromise;let o;this.hasNotFoundMatch()?o=404:this.stores.matches.get().some(e=>e.status===`error`)&&(o=500),o!==void 0&&this.stores.statusCode.set(o)},this.startViewTransition=e=>{let t=this.shouldViewTransition??this.options.defaultViewTransition;if(this.shouldViewTransition=void 0,t&&typeof document<`u`&&`startViewTransition`in document&&typeof document.startViewTransition==`function`){let n;if(typeof t==`object`&&this.isViewTransitionTypesSupported){let r=this.latestLocation,i=this.stores.resolvedLocation.get(),a=typeof t.types==`function`?t.types(G(r,i)):t.types;if(a===!1){e();return}n={update:e,types:a}}else n=e;document.startViewTransition(n)}else e()},this.updateMatch=(e,t)=>{this.startTransition(()=>{let n=this.stores.pendingMatchStores.get(e);if(n){n.set(t);return}let r=this.stores.matchStores.get(e);if(r){r.set(t);return}let i=this.stores.cachedMatchStores.get(e);if(i){let n=t(i.get());n.status===`redirected`?this.stores.cachedMatchStores.delete(e)&&this.stores.cachedIds.set(t=>t.filter(t=>t!==e)):i.set(n)}})},this.getMatch=e=>this.stores.cachedMatchStores.get(e)?.get()??this.stores.pendingMatchStores.get(e)?.get()??this.stores.matchStores.get(e)?.get(),this.invalidate=e=>{let t=t=>e?.filter?.(t)??!0?{...t,invalid:!0,...e?.forcePending||t.status===`error`||t.status===`notFound`?{status:`pending`,error:void 0}:void 0}:t;return this.batch(()=>{this.stores.setMatches(this.stores.matches.get().map(t)),this.stores.setCached(this.stores.cachedMatches.get().map(t)),this.stores.setPending(this.stores.pendingMatches.get().map(t))}),this.shouldViewTransition=!1,this.load({sync:e?.sync})},this.getParsedLocationHref=e=>e.publicHref||`/`,this.resolveRedirect=e=>{let t=e.headers.get(`Location`);if(!e.options.href||e.options._builtLocation){let t=e.options._builtLocation??this.buildLocation(e.options),n=this.getParsedLocationHref(t);e.options.href=n,e.headers.set(`Location`,n)}else if(t)try{let n=new URL(t);if(this.origin&&n.origin===this.origin){let t=n.pathname+n.search+n.hash;e.options.href=t,e.headers.set(`Location`,t)}}catch{}if(e.options.href&&!e.options._builtLocation&&d(e.options.href,this.protocolAllowlist))throw Error(`Redirect blocked: unsafe protocol`);return e.headers.get(`Location`)||e.headers.set(`Location`,e.options.href),e},this.clearCache=e=>{let t=e?.filter;t===void 0?this.stores.setCached([]):this.stores.setCached(this.stores.cachedMatches.get().filter(e=>!t(e)))},this.clearExpiredCache=()=>{let e=Date.now();this.clearCache({filter:t=>{let n=this.looseRoutesById[t.routeId];if(!n.options.loader)return!0;let r=(t.preload?n.options.preloadGcTime??this.options.defaultPreloadGcTime:n.options.gcTime??this.options.defaultGcTime)??300*1e3;return t.status===`error`?!0:e-t.updatedAt>=r}})},this.loadRouteChunk=li,this.preloadRoute=async e=>{let t=e._builtLocation??this.buildLocation(e),n=this.matchRoutes(t,{throwOnError:!0,preload:!0,dest:e}),r=new Set([...this.stores.matchesId.get(),...this.stores.pendingIds.get()]),i=new Set([...r,...this.stores.cachedIds.get()]),a=n.filter(e=>!i.has(e.id));if(a.length){let e=this.stores.cachedMatches.get();this.stores.setCached([...e,...a])}try{return n=await si({router:this,matches:n,location:t,preload:!0,updateMatch:(e,t)=>{r.has(e)?n=n.map(n=>n.id===e?t(n):n):this.updateMatch(e,t)}}),n}catch(e){if(he(e))return e.options.reloadDocument?void 0:await this.preloadRoute({...e.options,_fromLocation:t});ue(e)||console.error(e);return}},this.matchRoute=(e,t)=>{let n={...e,to:e.to?this.resolvePathWithBase(e.from||``,e.to):void 0,params:e.params||{},leaveParams:!0},r=this.buildLocation(n);if(t?.pending&&this.stores.status.get()!==`pending`)return!1;let i=(t?.pending===void 0?!this.stores.isLoading.get():t.pending)?this.latestLocation:this.stores.resolvedLocation.get()||this.stores.location.get(),a=I(r.pathname,t?.caseSensitive??!1,t?.fuzzy??!1,i.pathname,this.processedTree);return!a||e.params&&!o(a.rawParams,e.params,{partial:!0})?!1:t?.includeSearch??!0?o(i.search,r.search,{partial:!0})?a.rawParams:!1:a.rawParams},this.hasNotFoundMatch=()=>this.stores.matches.get().some(e=>e.status===`notFound`||e.globalNotFound),this.getStoreConfig=t,this.update({defaultPreloadDelay:50,defaultPendingMs:1e3,defaultPendingMinMs:500,context:void 0,...e,caseSensitive:e.caseSensitive??!1,notFoundMode:e.notFoundMode??`fuzzy`,stringifySearch:e.stringifySearch??Vr,parseSearch:e.parseSearch??Br,protocolAllowlist:e.protocolAllowlist??u}),typeof document<`u`&&(self.__TSR_ROUTER__=this)}isShell(){return!!this.options.isShell}isPrerendering(){return!!this.options.isPrerendering}get state(){return this.stores.__store.get()}setRoutes({routesById:e,routesByPath:t,processedTree:n}){this.routesById=e,this.routesByPath=t,this.processedTree=n;let r=this.options.notFoundRoute;r&&(r.init({originalIndex:99999999999}),this.routesById[r.id]=r)}getRouteBranch(e){let t=this.routeBranchCache.get(e);return t||(t=oe(e),this.routeBranchCache.set(e,t)),t}get looseRoutesById(){return this.routesById}getParentContext(e){return e?.id?e.context??this.options.context??void 0:this.options.context??void 0}matchRoutesInternal(e,t){let n=this.getMatchedRoutes(e.pathname),{foundRoute:r,routeParams:i}=n,{matchedRoutes:a}=n,o=!1;(r?r.path!==`/`&&i[`**`]:w(e.pathname))&&(this.options.notFoundRoute?a=[...a,this.options.notFoundRoute]:o=!0);let c=o?Li(this.options.notFoundMode,a):void 0,l=Array(a.length),u=new Map;for(let e of this.stores.matchStores.values())e.routeId&&u.set(e.routeId,e.get());for(let n=0;nthis.navigate({...t,_fromLocation:e}),buildLocation:this.buildLocation,cause:n.cause,abortController:n.abortController,preload:!!n.preload,matches:l,routeId:r.id};n.__routeContext=r.options.context(t)??void 0}n.context={...a,...n.__routeContext,...n.__beforeLoadContext}}}return l}matchRoutesLightweight(e){let{matchedRoutes:t,routeParams:n}=this.getMatchedRoutes(e.pathname),r=y(t),i={...e.search};for(let e of t)try{Object.assign(i,Ni(e.options.validateSearch,i))}catch{}let a=y(this.stores.matchesId.get()),o=a&&this.stores.matchStores.get(a)?.get(),s=o&&o.routeId===r.id&&o.pathname===e.pathname,c;if(s)c=o.params;else{let e=Object.assign(Object.create(null),n);for(let n of t)try{Ri(n,e)}catch{}c=e}return{matchedRoutes:t,fullPath:r.fullPath,search:i,params:c}}},Ai=class extends Error{},ji=class extends Error{};function Mi(e){return{loadedAt:0,isLoading:!1,isTransitioning:!1,status:`idle`,resolvedLocation:void 0,location:e,matches:[],statusCode:200}}function Ni(e,t){if(e==null)return{};if(`~standard`in e){let n=e[`~standard`].validate(t);if(n instanceof Promise)throw new Ai(`Async validation not supported`);if(n.issues)throw new Ai(JSON.stringify(n.issues,void 0,2),{cause:n});return n.value}return`parse`in e?e.parse(t):typeof e==`function`?e(t):{}}function Pi({pathname:e,routesById:t,processedTree:n}){let r=Object.create(null),i=w(e),a,o=E(i,n,!0);return o&&(a=o.route,Object.assign(r,o.rawParams)),{matchedRoutes:o?.branch||[t.__root__],routeParams:r,foundRoute:a}}function Fi({search:e,dest:t,destRoutes:n,_includeValidateSearch:r}){return Ii(n)(e,t,r??!1)}function Ii(e){let t,n,r=[];for(let t of e){let e=t.options;`search`in e?e.search?.middlewares&&r.push(...e.search.middlewares):(e.preSearchFilters||e.postSearchFilters)&&r.push(({search:t,next:n})=>{let r=n(e.preSearchFilters?e.preSearchFilters.reduce((e,t)=>t(e),t):t);return e.postSearchFilters?e.postSearchFilters.reduce((e,t)=>t(e),r):r});let i=e.validateSearch;i&&r.push(({search:e,next:t,meta:r})=>{let a=t(e);if(n)try{let e=Ni(i,a);if(r&&e)for(let t in e)t in a||(r.defaulted||=new Map).set(t,e[t]);return{...a,...e}}catch{}return a})}let i=(e,n,a)=>e>=r.length?t.search?t.search===!0?n:g(t.search,n):{}:r[e]({search:n,next:(t,n)=>{if(n){let n=a||{};return{search:i(e+1,t,n),meta:n}}return i(e+1,t,a)},meta:a});return function(e,r,a){return t=r,n=a,i(0,e)}}function Li(e,t){if(e!==`root`)for(let e=t.length-1;e>=0;e--){let n=t[e];if(n.children)return n.id}return fe}function Ri(e,t){let n=e.options.params?.parse??e.options.parseParams;if(n){let e=n(t);if(e===!1)throw Error(`Route params.parse returned false for a matched route`);Object.assign(t,e)}}function zi(){try{return sessionStorage}catch{return}}var Bi=`tsr-scroll-restoration-v1_3`,Vi=zi();function Hi(){try{return JSON.parse(Vi?.getItem(`tsr-scroll-restoration-v1_3`)||`{}`)}catch{return{}}}function Ui(){try{Vi?.setItem(Bi,JSON.stringify(Wi))}catch{}}var Wi=Hi(),Gi=`data-scroll-restoration-id`,Ki=e=>e.state.__TSR_key||e.href;function qi(e){let t=e.getAttribute(Gi);if(t)return`[${Gi}="${t}"]`;let n=``,r=e,i;for(;i=r.parentNode;){let e=1,t=r;for(;t=t.previousElementSibling;)e++;let a=`${r.localName}:nth-child(${e})`;n=n?`${a} > ${n}`:a,r=i}return n}var Ji=!1,Yi=`window`;function Xi(e){try{return typeof e==`function`?e():document.querySelector(e)}catch{}}function Zi(e){let t=[];for(let n of e){if(n===Yi)continue;let e=Xi(n);e&&t.push(e)}return t}function Qi(e,t){if((t??e.options.scrollRestoration)&&(e.isScrollRestoring=!0),e.isScrollRestorationSetup)return;e.isScrollRestorationSetup=!0,Ji=!1;let n=e.options.getScrollRestorationKey||Ki,r=new Map,i=(e,t,n)=>{let i=r.get(e)||{};i.scrollX=t,i.scrollY=n,r.set(e,i)};history.scrollRestoration=`manual`;let a=t=>{if(!(Ji||!e.isScrollRestoring))if(t.target===document)i(Yi,scrollX,scrollY);else{let e=t.target;i(e,e.scrollLeft,e.scrollTop)}},o=t=>{if(!e.isScrollRestoring)return;let n=Wi[t]||={};for(let[e,t]of r)e===Yi?n[Yi]=t:e.isConnected&&(n[qi(e)]=t)};document.addEventListener(`scroll`,a,!0),e.subscribe(`onBeforeLoad`,e=>{e.fromLocation&&o(n(e.fromLocation)),r.clear()}),addEventListener(`pagehide`,()=>{o(n(e.stores.resolvedLocation.get()??e.stores.location.get())),Ui()}),e.subscribe(`onRendered`,t=>{let i=e.options.scrollRestorationBehavior,a=e.options.scrollToTopSelectors,o=e.resetNextScroll,s;if(r.clear(),o||(e.resetNextScroll=!0),typeof e.options.scrollRestoration==`function`&&!e.options.scrollRestoration({location:e.latestLocation}))return;let c=n(t.toLocation),l=t.fromLocation&&n(t.fromLocation);if(e.isScrollRestoring&&l&&l!==c){let e=Wi[l];if(e){let t=Wi[c];for(let n in e){if(n===Yi){if(o)continue}else{let e=Xi(n);if(!e||o&&a&&(s??=Zi(a),s.includes(e)))continue}t||=Wi[c]={},t[n]??=e[n]}}}Ji=!0;try{let n=t.toLocation.hash,r=t.toLocation.state.__hashScrollIntoViewOptions??!0,l=!1;if(o){let o=Oi.get(t.toLocation),u=n&&r&&(o===`PUSH`||o===`REPLACE`),d=e.isScrollRestoring?Wi[c]:void 0;if(d)for(let e in d){let{scrollX:t,scrollY:n}=d[e];if(e===Yi){if(u)continue;scrollTo({top:n,left:t,behavior:i}),l=!0}else{let r=Xi(e);r&&(r.scrollLeft=t,r.scrollTop=n)}}if(!l&&!n){let e={top:0,left:0,behavior:i};if(scrollTo(e),a){s??=Zi(a);for(let t of s)t.scrollTo(e)}}}!l&&n&&r&&document.getElementById(n)?.scrollIntoView(r)}finally{Ji=!1}})}function $i(e,t){if(e)return typeof e==`string`?e:e[t]}function ea(e){return e?.scriptFormat??`module`}function ta(e,t,n){let r=na(t),i=$i(n,`script`)??r.crossOrigin;return{...ea(e)===`iife`?{rel:`preload`,as:`script`}:{rel:`modulepreload`},href:r.href,...i?{crossOrigin:i}:{}}}function na(e){return typeof e==`string`?{href:e,crossOrigin:void 0}:e}function ra(e,t){if(t.length===0)return;if(t.length===1){e.push(t[0]);return}let n=new Set;for(let r of t){let t=JSON.stringify(r);n.has(t)||(n.add(t),e.push(r))}}function ia(e){return typeof e==`string`?{href:e,crossOrigin:void 0}:e}var aa=class{get to(){return this._to}get id(){return this._id}get path(){return this._path}get fullPath(){return this._fullPath}constructor(e){if(this.init=e=>{this.originalIndex=e.originalIndex;let t=this.options,n=!t?.path&&!t?.id;this.parentRoute=this.options.getParentRoute?.(),n?this._path=fe:this.parentRoute||x();let r=n?fe:t?.path;r&&r!==`/`&&(r=A(r));let i=t?.id||r,a=n?fe:k([this.parentRoute.id===`__root__`?``:this.parentRoute.id,i]);r===`__root__`&&(r=`/`),a!==`__root__`&&(a=k([`/`,a]));let o=a===`__root__`?`/`:k([this.parentRoute.fullPath,r]);this._path=r,this._id=a,this._fullPath=o,this._to=w(o)},this.addChildren=e=>this._addFileChildren(e),this._addFileChildren=e=>(Array.isArray(e)&&(this.children=e),typeof e==`object`&&e&&(this.children=Object.values(e)),this),this._addFileTypes=()=>this,this.updateLoader=e=>(Object.assign(this.options,e),this),this.update=e=>(Object.assign(this.options,e),this),this.lazy=e=>(this.lazyFn=e,this),this.redirect=e=>me({from:this.fullPath,...e}),this.options=e||{},this.isRoot=!e?.getParentRoute,e?.id&&e?.path)throw Error(`Route cannot have both an 'id' and a 'path' option.`)}},oa=class extends aa{constructor(e){super(e)}};function sa(e){let t=e.errorComponent??la;return(0,W.jsx)(ca,{getResetKey:e.getResetKey,onCatch:e.onCatch,children:({error:n,reset:r})=>n?U.createElement(t,{error:n,reset:r}):e.children})}var ca=class extends U.Component{constructor(...e){super(...e),this.state={error:null}}static getDerivedStateFromProps(e,t){let n=e.getResetKey();return t.error&&t.resetKey!==n?{resetKey:n,error:null}:{resetKey:n}}static getDerivedStateFromError(e){return{error:e}}reset(){this.setState({error:null})}componentDidCatch(e,t){this.props.onCatch&&this.props.onCatch(e,t)}render(){return this.props.children({error:this.state.error,reset:()=>{this.reset()}})}};function la({error:e}){let[t,n]=U.useState(!1);return(0,W.jsxs)(`div`,{style:{padding:`.5rem`,maxWidth:`100%`},children:[(0,W.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:`.5rem`},children:[(0,W.jsx)(`strong`,{style:{fontSize:`1rem`},children:`Something went wrong!`}),(0,W.jsx)(`button`,{style:{appearance:`none`,fontSize:`.6em`,border:`1px solid currentColor`,padding:`.1rem .2rem`,fontWeight:`bold`,borderRadius:`.25rem`},onClick:()=>n(e=>!e),children:t?`Hide Error`:`Show Error`})]}),(0,W.jsx)(`div`,{style:{height:`.25rem`}}),t?(0,W.jsx)(`div`,{children:(0,W.jsx)(`pre`,{style:{fontSize:`.7em`,border:`1px solid red`,borderRadius:`.25rem`,padding:`.3rem`,color:`red`,overflow:`auto`},children:e.message?(0,W.jsx)(`code`,{children:e.message}):null})}):null]})}var ua=(e=>(e[e.None=0]=`None`,e[e.Mutable=1]=`Mutable`,e[e.Watching=2]=`Watching`,e[e.RecursedCheck=4]=`RecursedCheck`,e[e.Recursed=8]=`Recursed`,e[e.Dirty=16]=`Dirty`,e[e.Pending=32]=`Pending`,e))(ua||{});function da({update:e,notify:t,unwatched:n}){return{link:r,unlink:i,propagate:a,checkDirty:o,shallowPropagate:s};function r(e,t,n){let r=t.depsTail;if(r!==void 0&&r.dep===e)return;let i=r===void 0?t.deps:r.nextDep;if(i!==void 0&&i.dep===e){i.version=n,t.depsTail=i;return}let a=e.subsTail;if(a!==void 0&&a.version===n&&a.sub===t)return;let o=t.depsTail=e.subsTail={version:n,dep:e,sub:t,prevDep:r,nextDep:i,prevSub:a,nextSub:void 0};i!==void 0&&(i.prevDep=o),r===void 0?t.deps=o:r.nextDep=o,a===void 0?e.subs=o:a.nextSub=o}function i(e,t=e.sub){let r=e.dep,i=e.prevDep,a=e.nextDep,o=e.nextSub,s=e.prevSub;return a===void 0?t.depsTail=i:a.prevDep=i,i===void 0?t.deps=a:i.nextDep=a,o===void 0?r.subsTail=s:o.prevSub=s,s===void 0?(r.subs=o)===void 0&&n(r):s.nextSub=o,a}function a(e){let n=e.nextSub,r;top:do{let i=e.sub,a=i.flags;if(a&60?a&12?a&4?!(a&48)&&c(e,i)?(i.flags=a|40,a&=1):a=0:i.flags=a&-9|32:a=0:i.flags=a|32,a&2&&t(i),a&1){let t=i.subs;if(t!==void 0){let i=(e=t).nextSub;i!==void 0&&(r={value:n,prev:r},n=i);continue}}if((e=n)!==void 0){n=e.nextSub;continue}for(;r!==void 0;)if(e=r.value,r=r.prev,e!==void 0){n=e.nextSub;continue top}break}while(!0)}function o(t,n){let r,i=0,a=!1;top:do{let o=t.dep,c=o.flags;if(n.flags&16)a=!0;else if((c&17)==17){if(e(o)){let e=o.subs;e.nextSub!==void 0&&s(e),a=!0}}else if((c&33)==33){(t.nextSub!==void 0||t.prevSub!==void 0)&&(r={value:t,prev:r}),t=o.deps,n=o,++i;continue}if(!a){let e=t.nextDep;if(e!==void 0){t=e;continue}}for(;i--;){let i=n.subs,o=i.nextSub!==void 0;if(o?(t=r.value,r=r.prev):t=i,a){if(e(n)){o&&s(i),n=t.sub;continue}a=!1}else n.flags&=-33;n=t.sub;let c=t.nextDep;if(c!==void 0){t=c;continue top}}return a}while(!0)}function s(e){do{let n=e.sub,r=n.flags;(r&48)==32&&(n.flags=r|16,(r&6)==2&&t(n))}while((e=e.nextSub)!==void 0)}function c(e,t){let n=t.depsTail;for(;n!==void 0;){if(n===e)return!0;n=n.prevDep}return!1}}function fa(e,t,n){let r=typeof e==`object`,i=r?e:void 0;return{next:(r?e.next:e)?.bind(i),error:(r?e.error:t)?.bind(i),complete:(r?e.complete:n)?.bind(i)}}var pa=[],ma=0,{link:ha,unlink:ga,propagate:_a,checkDirty:va,shallowPropagate:ya}=da({update(e){return e._update()},notify(e){pa[xa++]=e,e.flags&=~ua.Watching},unwatched(e){e.depsTail!==void 0&&(e.depsTail=void 0,e.flags=ua.Mutable|ua.Dirty,Ta(e))}}),ba=0,xa=0,Sa,Ca=0;function wa(e){try{++Ca,e()}finally{--Ca||Ea()}}function Ta(e){let t=e.depsTail,n=t===void 0?e.deps:t.nextDep;for(;n!==void 0;)n=ga(n,e)}function Ea(){if(!(Ca>0)){for(;ba{i.get(),n.current?t.next?.(i._snapshot):n.current=!0});return{unsubscribe:()=>{r.stop()}}},_update(e){let a=Sa,o=t?.compare??Object.is;if(n)Sa=i,++ma,i.depsTail=void 0;else if(e===void 0)return!1;n&&(i.flags=ua.Mutable|ua.RecursedCheck);try{let t=i._snapshot,a=typeof e==`function`?e(t):e===void 0&&n?r(t):e;return t===void 0||!o(t,a)?(i._snapshot=a,!0):!1}finally{Sa=a,n&&(i.flags&=~ua.RecursedCheck),Ta(i)}}};return n?(i.flags=ua.Mutable|ua.Dirty,i.get=function(){let e=i.flags;if(e&ua.Dirty||e&ua.Pending&&va(i.deps,i)){if(i._update()){let e=i.subs;e!==void 0&&ya(e)}}else e&ua.Pending&&(i.flags=e&~ua.Pending);return Sa!==void 0&&ha(i,Sa,ma),i._snapshot}):i.set=function(e){if(i._update(e)){let e=i.subs;e!==void 0&&(_a(e),ya(e),Ea())}},i}function Oa(e){let t=()=>{let t=Sa;Sa=n,++ma,n.depsTail=void 0,n.flags=ua.Watching|ua.RecursedCheck;try{return e()}finally{Sa=t,n.flags&=~ua.RecursedCheck,Ta(n)}},n={deps:void 0,depsTail:void 0,subs:void 0,subsTail:void 0,flags:ua.Watching|ua.RecursedCheck,notify(){let e=this.flags;e&ua.Dirty||e&ua.Pending&&va(this.deps,this)?t():this.flags=ua.Watching},stop(){this.flags=ua.None,this.depsTail=void 0,Ta(this)}};return t(),n}var ka={get:()=>void 0,subscribe:()=>({unsubscribe:()=>{}})};function Aa(e){let t=M(),n=U.useContext(e.from?pe:de),r=e.from??n,i=r?e.from?t.stores.getRouteMatchStore(r):t.stores.matchStores.get(r):void 0,a=U.useRef(void 0);return F(i??ka,n=>{if((e.shouldThrow??!0)&&!n&&x(),n===void 0)return;let r=e.select?e.select(n):n;if(e.structuralSharing??t.options.defaultStructuralSharing){let e=s(a.current,r);return a.current=e,e}return r})}function ja(e){return Aa({from:e.from,strict:e.strict,structuralSharing:e.structuralSharing,select:t=>e.select?e.select(t.loaderData):t.loaderData})}function Ma(e){let{select:t,...n}=e;return Aa({...n,select:e=>t?t(e.loaderDeps):e.loaderDeps})}function Na(e){return Aa({from:e.from,shouldThrow:e.shouldThrow,structuralSharing:e.structuralSharing,strict:e.strict,select:t=>{let n=e.strict===!1?t.params:t._strictParams;return e.select?e.select(n):n}})}function Pa(e){return Aa({from:e.from,strict:e.strict,shouldThrow:e.shouldThrow,structuralSharing:e.structuralSharing,select:t=>e.select?e.select(t.search):t.search})}function Fa(e){let t=M();return U.useCallback(n=>t.navigate({...n,from:n.from??e?.from}),[e?.from,t])}function Ia(e){return Aa({...e,select:t=>e.select?e.select(t.context):t.context})}var La=class extends aa{constructor(e){super(e),this.useMatch=e=>Aa({select:e?.select,from:this.id,structuralSharing:e?.structuralSharing}),this.useRouteContext=e=>Ia({...e,from:this.id}),this.useSearch=e=>Pa({select:e?.select,structuralSharing:e?.structuralSharing,from:this.id}),this.useParams=e=>Na({select:e?.select,structuralSharing:e?.structuralSharing,from:this.id}),this.useLoaderDeps=e=>Ma({...e,from:this.id}),this.useLoaderData=e=>ja({...e,from:this.id}),this.useNavigate=()=>Fa({from:this.fullPath}),this.Link=U.forwardRef((e,t)=>(0,W.jsx)(Fe,{ref:t,from:this.fullPath,...e}))}};function Ra(e){return new La(e)}function za(){return e=>Va(e)}var Ba=class extends oa{constructor(e){super(e),this.useMatch=e=>Aa({select:e?.select,from:this.id,structuralSharing:e?.structuralSharing}),this.useRouteContext=e=>Ia({...e,from:this.id}),this.useSearch=e=>Pa({select:e?.select,structuralSharing:e?.structuralSharing,from:this.id}),this.useParams=e=>Na({select:e?.select,structuralSharing:e?.structuralSharing,from:this.id}),this.useLoaderDeps=e=>Ma({...e,from:this.id}),this.useLoaderData=e=>ja({...e,from:this.id}),this.useNavigate=()=>Fa({from:this.fullPath}),this.Link=U.forwardRef((e,t)=>(0,W.jsx)(Fe,{ref:t,from:this.fullPath,...e}))}};function Va(e){return new Ba(e)}function K(e){return new Ha(e,{silent:!0}).createRoute}var Ha=class{constructor(e,t){this.path=e,this.createRoute=e=>{let t=Ra(e);return t.isRoot=!1,t},this.silent=t?.silent}};function q(e,t){let n,r,i,a,o=()=>(n||=e().then(e=>{n=void 0,r=e[t??`default`]}).catch(e=>{if(i=e,p(i)&&i instanceof Error&&typeof window<`u`&&typeof sessionStorage<`u`){let e=`tanstack_router_reload:${i.message}`;sessionStorage.getItem(e)||(sessionStorage.setItem(e,`1`),a=!0)}}),n),s=function(e){if(a)throw window.location.reload(),new Promise(()=>{});if(i)throw i;if(!r)if(S)S(o());else throw o();return U.createElement(r,e)};return s.preload=o,s}function Ua(e){let t=M(),n=`not-found-${F(t.stores.location,e=>e.pathname)}-${F(t.stores.status,e=>e)}`;return(0,W.jsx)(sa,{getResetKey:()=>n,onCatch:(t,n)=>{if(ue(t))e.onCatch?.(t,n);else throw t},errorComponent:({error:t})=>{if(ue(t))return e.fallback?.(t);throw t},children:e.children})}function Wa(){return(0,W.jsx)(`p`,{children:`Not Found`})}function Ga(e){return(0,W.jsx)(W.Fragment,{children:e.children})}function Ka(e,t,n){return t.options.notFoundComponent?(0,W.jsx)(t.options.notFoundComponent,{...n}):e.options.defaultNotFoundComponent?(0,W.jsx)(e.options.defaultNotFoundComponent,{...n}):(0,W.jsx)(Wa,{})}var qa=U.memo(function({matchId:e}){let t=M(),n=t.stores.matchStores.get(e);n||x();let r=F(t.stores.loadedAt,e=>e),i=F(n,e=>e);return(0,W.jsx)(Ja,{router:t,matchId:e,resetKey:r,matchState:U.useMemo(()=>{let e=i.routeId,n=t.routesById[e].parentRoute?.id;return{routeId:e,ssr:i.ssr,_displayPending:i._displayPending,parentRouteId:n}},[i._displayPending,i.routeId,i.ssr,t.routesById])})});function Ja({router:e,matchId:t,resetKey:n,matchState:r}){let i=e.routesById[r.routeId],a=i.options.pendingComponent??e.options.defaultPendingComponent,o=a?(0,W.jsx)(a,{}):null,s=i.options.errorComponent??e.options.defaultErrorComponent,c=i.options.onCatch??e.options.defaultOnCatch,l=i.isRoot?i.options.notFoundComponent??e.options.notFoundRoute?.options.component:i.options.notFoundComponent,u=r.ssr===!1||r.ssr===`data-only`,d=(!i.isRoot||i.options.wrapInSuspense||u)&&(i.options.wrapInSuspense??a??(i.options.errorComponent?.preload||u))?U.Suspense:Ga,f=s?sa:Ga,p=l?Ua:Ga;return(0,W.jsxs)(i.isRoot?i.options.shellComponent??Ga:Ga,{children:[(0,W.jsx)(de.Provider,{value:t,children:(0,W.jsx)(d,{fallback:o,children:(0,W.jsx)(f,{getResetKey:()=>n,errorComponent:s||la,onCatch:(e,t)=>{if(ue(e))throw e.routeId??=r.routeId,e;c?.(e,t)},children:(0,W.jsx)(p,{fallback:e=>{if(e.routeId??=r.routeId,!l||e.routeId&&e.routeId!==r.routeId||!e.routeId&&!i.isRoot)throw e;return U.createElement(l,e)},children:u||r._displayPending?(0,W.jsx)(re,{fallback:o,children:(0,W.jsx)(Xa,{matchId:t})}):(0,W.jsx)(Xa,{matchId:t})})})})}),r.parentRouteId===`__root__`?(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(Ya,{resetKey:n}),(e.options.scrollRestoration,null)]}):null]})}function Ya({resetKey:e}){let t=M(),n=U.useRef(void 0);return C(()=>{let e=t.latestLocation.href;(n.current===void 0||n.current!==e)&&(t.emit({type:`onRendered`,...G(t.stores.location.get(),t.stores.resolvedLocation.get())}),n.current=e)},[t.latestLocation.state.__TSR_key,e,t]),null}var Xa=U.memo(function({matchId:e}){let t=M(),n=(e,n)=>t.getMatch(e.id)?._nonReactive[n]??e._nonReactive[n],r=t.stores.matchStores.get(e);r||x();let i=F(r,e=>e),a=i.routeId,o=t.routesById[a],s=U.useMemo(()=>{let e=(t.routesById[a].options.remountDeps??t.options.defaultRemountDeps)?.({routeId:a,loaderDeps:i.loaderDeps,params:i._strictParams,search:i._strictSearch});return e?JSON.stringify(e):void 0},[a,i.loaderDeps,i._strictParams,i._strictSearch,t.options.defaultRemountDeps,t.routesById]),c=U.useMemo(()=>{let e=o.options.component??t.options.defaultComponent;return e?(0,W.jsx)(e,{},s):(0,W.jsx)(Za,{})},[s,o.options.component,t.options.defaultComponent]);if(i._displayPending)throw n(i,`displayPendingPromise`);if(i._forcePending)throw n(i,`minPendingPromise`);if(i.status===`pending`){let e=o.options.pendingMinMs??t.options.defaultPendingMinMs;if(e){let n=t.getMatch(i.id);if(n&&!n._nonReactive.minPendingPromise){let t=_();n._nonReactive.minPendingPromise=t,setTimeout(()=>{t.resolve(),n._nonReactive.minPendingPromise=void 0},e)}}throw n(i,`loadPromise`)}if(i.status===`notFound`)return ue(i.error)||x(),Ka(t,o,i.error);if(i.status===`redirected`)throw he(i.error)||x(),n(i,`loadPromise`);if(i.status===`error`)throw i.error;return c}),Za=U.memo(function(){let e=M(),t=U.useContext(de),n,r=!1,i;{let a=t?e.stores.matchStores.get(t):void 0;[n,r]=F(a,e=>[e?.routeId,e?.globalNotFound??!1]),i=F(e.stores.matchesId,e=>e[e.findIndex(e=>e===t)+1])}let a=n?e.routesById[n]:void 0,o=e.options.defaultPendingComponent?(0,W.jsx)(e.options.defaultPendingComponent,{}):null;if(r)return a||x(),Ka(e,a,void 0);if(!i)return null;let s=(0,W.jsx)(qa,{matchId:i});return n===`__root__`?(0,W.jsx)(U.Suspense,{fallback:o,children:s}):s});function Qa(){let e=M(),t=U.useRef({router:e,mounted:!1}),[n,r]=U.useState(!1),i=F(e.stores.isLoading,e=>e),a=F(e.stores.hasPending,e=>e),o=f(i),s=i||n||a,c=f(s),l=i||a,u=f(l);return e.startTransition=e=>{r(!0),U.startTransition(()=>{e(),r(!1)})},U.useEffect(()=>{let t=e.history.subscribe(e.load),n=e.buildLocation({to:e.latestLocation.pathname,search:!0,params:!0,hash:!0,state:!0,_includeValidateSearch:!0});return w(e.latestLocation.publicHref)!==w(n.publicHref)&&e.commitLocation({...n,replace:!0}),()=>{t()}},[e,e.history]),C(()=>{typeof window<`u`&&e.ssr||t.current.router===e&&t.current.mounted||(t.current={router:e,mounted:!0},(async()=>{try{await e.load()}catch(e){console.error(e)}})())},[e]),C(()=>{o&&!i&&e.emit({type:`onLoad`,...G(e.stores.location.get(),e.stores.resolvedLocation.get())})},[o,e,i]),C(()=>{u&&!l&&e.emit({type:`onBeforeRouteMount`,...G(e.stores.location.get(),e.stores.resolvedLocation.get())})},[l,u,e]),C(()=>{if(c&&!s){let t=G(e.stores.location.get(),e.stores.resolvedLocation.get());e.emit({type:`onResolved`,...t}),wa(()=>{e.stores.status.set(`idle`),e.stores.resolvedLocation.set(e.stores.location.get())})}},[s,c,e]),null}function $a(){let e=M(),t=e.routesById.__root__.options.pendingComponent??e.options.defaultPendingComponent,n=t?(0,W.jsx)(t,{}):null,r=(0,W.jsxs)(typeof document<`u`&&e.ssr?Ga:U.Suspense,{fallback:n,children:[(0,W.jsx)(Qa,{}),(0,W.jsx)(eo,{})]});return e.options.InnerWrap?(0,W.jsx)(e.options.InnerWrap,{children:r}):r}function eo(){let e=M(),t=F(e.stores.firstId,e=>e),n=F(e.stores.loadedAt,e=>e),r=t?(0,W.jsx)(qa,{matchId:t}):null;return(0,W.jsx)(de.Provider,{value:t,children:e.options.disableGlobalCatchBoundary?r:(0,W.jsx)(sa,{getResetKey:()=>n,errorComponent:la,onCatch:void 0,children:r})})}var to=e=>({createMutableStore:Da,createReadonlyStore:Da,batch:wa}),no=e=>new ro(e),ro=class extends ki{constructor(e){super(e,to)}};function J({router:e,children:t,...n}){v(n)&&e.update({...e.options,...n,context:{...e.options.context,...n.context}});let r=(0,W.jsx)(T.Provider,{value:e,children:t});return e.options.Wrap?(0,W.jsx)(e.options.Wrap,{children:r}):r}function io({router:e,...t}){return(0,W.jsx)(J,{router:e,...t,children:(0,W.jsx)($a,{})})}function ao(e){let t=M(),n=(0,U.useRef)(void 0);return F(t.stores.location,r=>{let i=e?.select?e.select(r):r;if(e?.structuralSharing??t.options.defaultStructuralSharing){let e=s(n.current,i);return n.current=e,e}return i})}function oo(e,t){if(t)for(let[n,r]of Object.entries(t))n!==`suppressHydrationWarning`&&r!==void 0&&r!==!1&&e.setAttribute(n,typeof r==`boolean`?``:String(r))}function so(e){let{attrs:t,children:n,nonce:r,preventScriptHoist:i}=e;switch(e.tag){case`title`:return(0,W.jsx)(`title`,{...t,suppressHydrationWarning:!0,children:n});case`meta`:return(0,W.jsx)(`meta`,{...t,suppressHydrationWarning:!0});case`link`:return(0,W.jsx)(`link`,{...t,precedence:t?.precedence??(t?.rel===`stylesheet`?`default`:void 0),nonce:r,suppressHydrationWarning:!0});case`style`:return e.inlineCss,(0,W.jsx)(`style`,{...t,dangerouslySetInnerHTML:{__html:n},nonce:r});case`script`:return(0,W.jsx)(co,{attrs:t,preventScriptHoist:i,children:n});default:return null}}function co({attrs:e,children:t,preventScriptHoist:n}){M();let r=ae(),i=typeof e?.type==`string`&&e.type!==``&&e.type!==`text/javascript`&&e.type!==`module`;if(U.useEffect(()=>{if(!i){if(e?.src){let t=(()=>{try{let t=document.baseURI||window.location.href;return new URL(e.src,t).href}catch{return e.src}})();for(let e of document.querySelectorAll(`script[src]`))if(e.src===t)return;let n=document.createElement(`script`);return oo(n,e),document.head.appendChild(n),()=>n.remove()}if(typeof t==`string`){let n=typeof e?.type==`string`?e.type:`text/javascript`,r=typeof e?.nonce==`string`?e.nonce:void 0;for(let e of document.querySelectorAll(`script:not([src])`)){if(!(e instanceof HTMLScriptElement))continue;let i=e.getAttribute(`type`)??`text/javascript`,a=e.getAttribute(`nonce`)??void 0;if(e.textContent===t&&i===n&&a===r)return}let i=document.createElement(`script`);return i.textContent=t,oo(i,e),document.head.appendChild(i),()=>i.remove()}}},[e,t,i]),i&&typeof t==`string`)return(0,W.jsx)(`script`,{...e,suppressHydrationWarning:!0,dangerouslySetInnerHTML:{__html:t}});if(!r){if(e?.src)return(0,W.jsx)(`script`,{...e,suppressHydrationWarning:!0});if(typeof t==`string`)return(0,W.jsx)(`script`,{...e,dangerouslySetInnerHTML:{__html:t},suppressHydrationWarning:!0})}return null}var lo=e=>{let t=M(),n=t.options.ssr?.nonce,r=F(t.stores.matches,e=>e.map(e=>e.meta).filter(e=>e!==void 0),o),i=U.useMemo(()=>{let e=[],t={},i;for(let a=r.length-1;a>=0;a--){let o=r[a];for(let r=o.length-1;r>=0;r--){let a=o[r];if(a)if(a.title)i||={tag:`title`,children:a.title};else if(`script:ld+json`in a)try{let t=JSON.stringify(a[`script:ld+json`]);e.push({tag:`script`,attrs:{type:`application/ld+json`},children:h(t)})}catch{}else{let r=a.name??a.property;if(r){if(t[r])continue;t[r]=!0}e.push({tag:`meta`,attrs:{...a,nonce:n}})}}}return i&&e.push(i),n&&e.push({tag:`meta`,attrs:{property:`csp-nonce`,content:n}}),e.reverse(),e},[r,n]),a=F(t.stores.matches,e=>e.flatMap(e=>e.links??[]).filter(e=>e!==void 0).map(e=>({tag:`link`,attrs:{...e,nonce:n}})),o),s=F(t.stores.matches,r=>{let i=t.ssr?.manifest,a=[];return i?(r.forEach(t=>{i.routes[t.routeId]?.css?.forEach(t=>{let r=ia(t);a.push({tag:`link`,attrs:{rel:`stylesheet`,...r,crossOrigin:$i(e,`stylesheet`)??r.crossOrigin,suppressHydrationWarning:!0,nonce:n}})})}),i.inlineStyle&&a.push({tag:`style`,attrs:{...i.inlineStyle.attrs,nonce:n},children:i.inlineStyle.children,inlineCss:!0}),a):a},o),c=F(t.stores.matches,r=>{let i=[],a=t.ssr?.manifest;return a&&r.forEach(t=>{a.routes[t.routeId]?.preloads?.forEach(t=>{i.push({tag:`link`,attrs:{...ta(a,t,e),nonce:n}})})}),i},o),l=F(t.stores.matches,e=>e.flatMap(e=>e.styles??[]).filter(e=>e!==void 0).map(({children:e,...t})=>({tag:`style`,attrs:{...t,nonce:n},children:e})),o),u=F(t.stores.matches,e=>e.flatMap(e=>e.headScripts??[]).filter(e=>e!==void 0).map(({children:e,...t})=>({tag:`script`,attrs:{...t,nonce:n},children:e})),o),d=[];return ra(d,i),d.push(...c),ra(d,a),d.push(...s),ra(d,l),ra(d,u),d};function uo(e){let t=lo(e.assetCrossOrigin),n=M().options.ssr?.nonce;return(0,W.jsx)(W.Fragment,{children:t.map(e=>(0,U.createElement)(so,{...e,key:`tsr-meta-${JSON.stringify(e)}`,nonce:n}))})}var fo=e((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,O());else{var t=n(l);t!==null&&j(x,t.startTime-e)}}var S=!1,C=-1,w=5,T=-1;function E(){return g?!0:!(e.unstable_now()-Tt&&E());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&j(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?O():S=!1}}}var O;if(typeof y==`function`)O=function(){y(D)};else if(typeof MessageChannel<`u`){var k=new MessageChannel,A=k.port2;k.port1.onmessage=D,O=function(){A.postMessage(null)}}else O=function(){_(D,0)};function j(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,j(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,O()))),r},e.unstable_shouldYield=E,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),po=e(((e,t)=>{t.exports=fo()})),mo=e((e=>{var t=po(),n=r(),i=dt();function a(e){var t=`https://react.dev/errors/`+e;if(1re||(e.current=ne[re],ne[re]=null,re--)}function F(e,t){re++,ne[re]=e.current,e.current=t}var oe=ie(null),se=ie(null),I=ie(null),ce=ie(null);function le(e,t){switch(F(I,t),F(se,e),F(oe,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Ud(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Ud(t),e=Wd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}ae(oe),F(oe,e)}function ue(){ae(oe),ae(se),ae(I)}function de(e){e.memoizedState!==null&&F(ce,e);var t=oe.current,n=Wd(t,e.type);t!==n&&(F(se,e),F(oe,n))}function fe(e){se.current===e&&(ae(oe),ae(se)),ce.current===e&&(ae(ce),$f._currentValue=te)}var pe,me;function he(e){if(pe===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);pe=t&&t[1]||``,me=-1)`:-1i||c[r]!==l[i]){var u=` -`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{ve=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?_e(n):``}function be(e,t){switch(e.tag){case 26:case 27:case 5:return _e(e.type);case 16:return _e(`Lazy`);case 13:return e.child!==t&&t!==null?_e(`Suspense Fallback`):_e(`Suspense`);case 19:return _e(`SuspenseList`);case 0:case 15:return ye(e.type,!1);case 11:return ye(e.type.render,!1);case 1:return ye(e.type,!0);case 31:return _e(`Activity`);default:return``}}function xe(e){try{var t=``,n=null;do t+=be(e,n),n=e,e=e.return;while(e);return t}catch(e){return` +`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{ge=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?he(n):``}function ve(e,t){switch(e.tag){case 26:case 27:case 5:return he(e.type);case 16:return he(`Lazy`);case 13:return e.child!==t&&t!==null?he(`Suspense Fallback`):he(`Suspense`);case 19:return he(`SuspenseList`);case 0:case 15:return _e(e.type,!1);case 11:return _e(e.type.render,!1);case 1:return _e(e.type,!0);case 31:return he(`Activity`);default:return``}}function L(e){try{var t=``,n=null;do t+=ve(e,n),n=e,e=e.return;while(e);return t}catch(e){return` Error generating stack: `+e.message+` -`+e.stack}}var Se=Object.prototype.hasOwnProperty,Ce=t.unstable_scheduleCallback,we=t.unstable_cancelCallback,Te=t.unstable_shouldYield,Ee=t.unstable_requestPaint,De=t.unstable_now,Oe=t.unstable_getCurrentPriorityLevel,ke=t.unstable_ImmediatePriority,Ae=t.unstable_UserBlockingPriority,Me=t.unstable_NormalPriority,Ne=t.unstable_LowPriority,P=t.unstable_IdlePriority,F=t.log,Pe=t.unstable_setDisableYieldValue,Fe=null,Ie=null;function I(e){if(typeof F==`function`&&Pe(e),Ie&&typeof Ie.setStrictMode==`function`)try{Ie.setStrictMode(Fe,e)}catch{}}var Le=Math.clz32?Math.clz32:Be,Re=Math.log,ze=Math.LN2;function Be(e){return e>>>=0,e===0?32:31-(Re(e)/ze|0)|0}var Ve=256,He=262144,Ue=4194304;function We(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ge(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=We(n))):i=We(o):i=We(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=We(n))):i=We(o)):i=We(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function Ke(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function qe(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Je(){var e=Ue;return Ue<<=1,!(Ue&62914560)&&(Ue=4194304),e}function Ye(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function Xe(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Ze(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),un=!1;if(ln)try{var dn={};Object.defineProperty(dn,`passive`,{get:function(){un=!0}}),window.addEventListener(`test`,dn,dn),window.removeEventListener(`test`,dn,dn)}catch{un=!1}var fn=null,pn=null,mn=null;function hn(){if(mn)return mn;var e,t=pn,n=t.length,r,i=`value`in fn?fn.value:fn.textContent,a=i.length;for(e=0;e=qn),Xn=` `,Zn=!1;function Qn(e,t){switch(e){case`keyup`:return Gn.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function $n(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var er=!1;function tr(e,t){switch(e){case`compositionend`:return $n(t);case`keypress`:return t.which===32?(Zn=!0,Xn):null;case`textInput`:return e=t.data,e===Xn&&Zn?null:e;default:return null}}function nr(e,t){if(er)return e===`compositionend`||!Kn&&Qn(e,t)?(e=hn(),mn=pn=fn=null,er=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=wr(n)}}function Er(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Er(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Dr(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=It(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=It(e.document)}return t}function Or(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var kr=ln&&`documentMode`in document&&11>=document.documentMode,Ar=null,jr=null,Mr=null,Nr=!1;function Pr(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Nr||Ar==null||Ar!==It(r)||(r=Ar,`selectionStart`in r&&Or(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Mr&&Cr(Mr,r)||(Mr=r,r=Rd(jr,`onSelect`),0>=o,i-=o,Di=1<<32-Le(t)+i|n<m?(h=d,d=null):h=d.sibling;var g=p(i,d,s[m],c);if(g===null){d===null&&(d=h);break}e&&d&&g.alternate===null&&t(i,d),a=o(g,a,m),u===null?l=g:u.sibling=g,u=g,d=h}if(m===s.length)return n(i,d),R&&ki(i,m),l;if(d===null){for(;mh?(g=m,m=null):g=m.sibling;var y=p(a,m,v.value,l);if(y===null){m===null&&(m=g);break}e&&m&&y.alternate===null&&t(a,m),s=o(y,s,h),d===null?u=y:d.sibling=y,d=y,m=g}if(v.done)return n(a,m),R&&ki(a,h),u;if(m===null){for(;!v.done;h++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,h),d===null?u=v:d.sibling=v,d=v);return R&&ki(a,h),u}for(m=r(m);!v.done;h++,v=c.next())v=_(m,a,h,v.value,l),v!==null&&(e&&v.alternate!==null&&m.delete(v.key===null?h:v.key),s=o(v,s,h),d===null?u=v:d.sibling=v,d=v);return e&&m.forEach(function(e){return t(a,e)}),R&&ki(a,h),u}function x(e,r,o,c){if(typeof o==`object`&&o&&o.type===g&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case m:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===g){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===T&&ka(l)===r.type){n(e,r.sibling),c=a(r,o.props),Ia(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===g?(c=pi(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=fi(o.type,o.key,o.props,null,e.mode,c),Ia(c,o),c.return=e,e=c)}return s(e);case h:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=_i(o,e.mode,c),c.return=e,e=c}return s(e);case T:return o=ka(o),x(e,r,o,c)}if(re(o))return v(e,r,o,c);if(O(o)){if(l=O(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),y(e,r,o,c)}if(typeof o.then==`function`)return x(e,r,Fa(o),c);if(o.$$typeof===b)return x(e,r,na(e,o),c);La(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=hi(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{Pa=0;var i=x(e,t,n,r);return Na=null,i}catch(t){if(t===Ca||t===Ta)throw t;var a=ci(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var za=Ra(!0),Ba=Ra(!1),Va=!1;function Ha(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Ua(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Wa(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Ga(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,Wl&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=ai(e),ii(e,null,n),t}return ti(e,r,t,n),ai(e)}function Ka(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,$e(e,n)}}function qa(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Ja=!1;function Ya(){if(Ja){var e=ma;if(e!==null)throw e}}function Xa(e,t,n,r){Ja=!1;var i=e.updateQueue;Va=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var p=s.lane&-536870913,m=p!==s.lane;if(m?(ql&p)===p:(r&p)===p){p!==0&&p===pa&&(Ja=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;p=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,p);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,p=typeof h==`function`?h.call(_,d,p):h,p==null)break a;d=f({},d,p);break a;case 2:Va=!0}}p=s.callback,p!==null&&(e.flags|=64,m&&(e.flags|=8192),m=i.callbacks,m===null?i.callbacks=[p]:m.push(p))}else m={lane:p,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=m,c=d):u=u.next=m,o|=p;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;m=s,s=m.next,m.next=null,i.lastBaseUpdate=m,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),tu|=o,e.lanes=o,e.memoizedState=d}}function Za(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function Qa(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=k.T,s={};k.T=s,zs(e,!1,t,n);try{var c=i(),l=k.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Rs(e,t,_a(c,r),Cu(e)):Rs(e,t,r,Cu(e))}catch(n){Rs(e,t,{then:function(){},status:`rejected`,reason:n},Cu())}finally{A.p=a,o!==null&&s.types!==null&&(o.types=s.types),k.T=o}}function Os(){}function ks(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=As(e).queue;Ds(e,a,t,ie,n===null?Os:function(){return js(e),n(r)})}function As(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:ie,baseState:ie,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Bo,lastRenderedState:ie},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Bo,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function js(e){var t=As(e);t.next===null&&(t=e.alternate.memoizedState),Rs(e,t.next.queue,{},Cu())}function Ms(){return ta(dp)}function Ns(){return Fo().memoizedState}function Ps(){return Fo().memoizedState}function Fs(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=Cu();e=Wa(n);var r=Ga(t,e,n);r!==null&&(Tu(r,t,n),Ka(r,t,n)),t={cache:la()},e.payload=t;return}t=t.return}}function Is(e,t,n){var r=Cu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Bs(e)?Vs(t,n):(n=ni(e,t,n,r),n!==null&&(Tu(n,e,r),Hs(n,t,r)))}function Ls(e,t,n){Rs(e,t,n,Cu())}function Rs(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Bs(e))Vs(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,Sr(s,o))return ti(e,t,i,0),Gl===null&&ei(),!1}catch{}if(n=ni(e,t,i,r),n!==null)return Tu(n,e,r),Hs(n,t,r),!0}return!1}function zs(e,t,n,r){if(r={lane:2,revertLane:Sd(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Bs(e)){if(t)throw Error(i(479))}else t=ni(e,n,r,2),t!==null&&Tu(t,e,2)}function Bs(e){var t=e.alternate;return e===ho||t!==null&&t===ho}function Vs(e,t){yo=vo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Hs(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,$e(e,n)}}var Us={readContext:ta,use:Ro,useCallback:To,useContext:To,useEffect:To,useImperativeHandle:To,useLayoutEffect:To,useInsertionEffect:To,useMemo:To,useReducer:To,useRef:To,useState:To,useDebugValue:To,useDeferredValue:To,useTransition:To,useSyncExternalStore:To,useId:To,useHostTransitionStatus:To,useFormState:To,useActionState:To,useOptimistic:To,useMemoCache:To,useCacheRefresh:To};Us.useEffectEvent=To;var Ws={readContext:ta,use:Ro,useCallback:function(e,t){return Po().memoizedState=[e,t===void 0?null:t],e},useContext:ta,useEffect:ms,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),fs(4194308,4,bs.bind(null,t,e),n)},useLayoutEffect:function(e,t){return fs(4194308,4,e,t)},useInsertionEffect:function(e,t){fs(4,2,e,t)},useMemo:function(e,t){var n=Po();t=t===void 0?null:t;var r=e();if(bo){I(!0);try{e()}finally{I(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=Po();if(n!==void 0){var i=n(t);if(bo){I(!0);try{n(t)}finally{I(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=Is.bind(null,ho,e),[r.memoizedState,e]},useRef:function(e){var t=Po();return e={current:e},t.memoizedState=e},useState:function(e){e=Xo(e);var t=e.queue,n=Ls.bind(null,ho,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:Ss,useDeferredValue:function(e,t){return Ts(Po(),e,t)},useTransition:function(){var e=Xo(!1);return e=Ds.bind(null,ho,e.queue,!0,!1),Po().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=ho,a=Po();if(R){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),Gl===null)throw Error(i(349));ql&127||Go(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,ms(qo.bind(null,r,o,e),[e]),r.flags|=2048,us(9,{destroy:void 0},Ko.bind(null,r,o,n,t),null),n},useId:function(){var e=Po(),t=Gl.identifierPrefix;if(R){var n=Oi,r=Di;n=(r&~(1<<32-Le(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=xo++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[ot]=t,o[st]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(qd(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Lc(t)}}return Vc(t),Rc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Lc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=ue.current,Vi(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=L,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[ot]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Wd(e.nodeValue,n)),e||Ri(t,!0)}else e=$d(e).createTextNode(r),e[ot]=t,t.stateNode=e}return Vc(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=Vi(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[ot]=t}else Hi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Vc(t),e=!1}else n=Ui(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(uo(t),t):(uo(t),null);if(t.flags&128)throw Error(i(558))}return Vc(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=Vi(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[ot]=t}else Hi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Vc(t),a=!1}else a=Ui(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(uo(t),t):(uo(t),null)}return uo(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),zc(t,t.updateQueue),Vc(t),null);case 4:return fe(),e===null&&Pd(t.stateNode.containerInfo),Vc(t),null;case 10:return Yi(t.type),Vc(t),null;case 19:if(se(fo),r=t.memoizedState,r===null)return Vc(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)Bc(r,!1);else{if(eu!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=po(e),o!==null){for(t.flags|=128,Bc(r,!1),e=o.updateQueue,t.updateQueue=e,zc(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)di(n,e),n=n.sibling;return ce(fo,fo.current&1|2),R&&ki(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&De()>du&&(t.flags|=128,a=!0,Bc(r,!1),t.lanes=4194304)}else{if(!a)if(e=po(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,zc(t,e),Bc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!R)return Vc(t),null}else 2*De()-r.renderingStartTime>du&&n!==536870912&&(t.flags|=128,a=!0,Bc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(Vc(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=De(),e.sibling=null,n=fo.current,ce(fo,a?n&1|2:n&1),R&&ki(t,r.treeForkCount),e);case 22:case 23:return uo(t),ro(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(Vc(t),t.subtreeFlags&6&&(t.flags|=8192)):Vc(t),n=t.updateQueue,n!==null&&zc(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&se(ya),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),Yi(ca),Vc(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function Uc(e,t){switch(Mi(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Yi(ca),fe(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return me(t),null;case 31:if(t.memoizedState!==null){if(uo(t),t.alternate===null)throw Error(i(340));Hi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(uo(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));Hi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return se(fo),null;case 4:return fe(),null;case 10:return Yi(t.type),null;case 22:case 23:return uo(t),ro(),e!==null&&se(ya),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return Yi(ca),null;case 25:return null;default:return null}}function Wc(e,t){switch(Mi(t),t.tag){case 3:Yi(ca),fe();break;case 26:case 27:case 5:me(t);break;case 4:fe();break;case 31:t.memoizedState!==null&&uo(t);break;case 13:uo(t);break;case 19:se(fo);break;case 10:Yi(t.type);break;case 22:case 23:uo(t),ro(),e!==null&&se(ya);break;case 24:Yi(ca)}}function Gc(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){td(t,t.return,e)}}function Kc(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){td(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){td(t,t.return,e)}}function qc(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Qa(t,n)}catch(t){td(e,e.return,t)}}}function Jc(e,t,n){n.props=Zs(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){td(e,t,n)}}function Yc(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){td(e,t,n)}}function Xc(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){td(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){td(e,t,n)}else n.current=null}function Zc(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){td(e,e.return,t)}}function Qc(e,t,n){try{var r=e.stateNode;Jd(r,e.type,n,t),r[st]=t}catch(t){td(e,e.return,t)}}function $c(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&df(e.type)||e.tag===4}function el(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||$c(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&df(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function tl(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=$t));else if(r!==4&&(r===27&&df(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(tl(e,t,n),e=e.sibling;e!==null;)tl(e,t,n),e=e.sibling}function nl(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&df(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(nl(e,t,n),e=e.sibling;e!==null;)nl(e,t,n),e=e.sibling}function rl(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);qd(t,r,n),t[ot]=e,t[st]=n}catch(t){td(e,e.return,t)}}var il=!1,al=!1,ol=!1,sl=typeof WeakSet==`function`?WeakSet:Set,cl=null;function ll(e,t){if(e=e.containerInfo,Zd=bp,e=Dr(e),Or(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(Qd={focusedElem:e,selectionRange:n},bp=!1,cl=t;cl!==null;)if(t=cl,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,cl=e;else for(;cl!==null;){switch(t=cl,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),qd(o,r,n),o[ot]=e,yt(o),r=o;break a;case`link`:var s=ep(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=Tr(s,h),v=Tr(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,k.T=null,n=yu,yu=null;var o=hu,s=_u;if(mu=0,gu=hu=null,_u=0,Wl&6)throw Error(i(331));var c=Wl;if(Wl|=4,zl(o.current),jl(o,o.current,s,n),Wl=c,hd(0,!1),Ie&&typeof Ie.onPostCommitFiberRoot==`function`)try{Ie.onPostCommitFiberRoot(Fe,o)}catch{}return!0}finally{A.p=a,k.T=r,Zu(e,t)}}function ed(e,t,n){t=yi(n,t),t=rc(e.stateNode,t,2),e=Ga(e,t,2),e!==null&&(Xe(e,2),md(e))}function td(e,t,n){if(e.tag===3)ed(e,e,n);else for(;t!==null;){if(t.tag===3){ed(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(pu===null||!pu.has(r))){e=yi(n,e),n=ic(2),r=Ga(t,n,2),r!==null&&(ac(n,r,t,e),Xe(r,2),md(r));break}}t=t.return}}function nd(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new Ul;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(Ql=!0,i.add(n),e=rd.bind(null,e,t,n),t.then(e,e))}function rd(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,Gl===e&&(ql&n)===n&&(eu===4||eu===3&&(ql&62914560)===ql&&300>De()-lu?!(Wl&2)&&Mu(e,0):ru|=n,au===ql&&(au=0)),md(e)}function id(e,t){t===0&&(t=Je()),e=ri(e,t),e!==null&&(Xe(e,t),md(e))}function ad(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),id(e,n)}function od(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),id(e,n)}function sd(e,t){return Ce(e,t)}var cd=null,ld=null,ud=!1,dd=!1,fd=!1,pd=0;function md(e){e!==ld&&e.next===null&&(ld===null?cd=ld=e:ld=ld.next=e),dd=!0,ud||(ud=!0,xd())}function hd(e,t){if(!fd&&dd){fd=!0;do for(var n=!1,r=cd;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Le(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,bd(r,a))}else a=ql,a=Ge(r,r===Gl?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||Ke(r,a)||(n=!0,bd(r,a));r=r.next}while(n);fd=!1}}function gd(){_d()}function _d(){dd=ud=!1;var e=0;pd!==0&&af()&&(e=pd);for(var t=De(),n=null,r=cd;r!==null;){var i=r.next,a=vd(r,t);a===0?(r.next=null,n===null?cd=i:n.next=i,i===null&&(ld=n)):(n=r,(e!==0||a&3)&&(dd=!0)),r=i}mu!==0&&mu!==5||hd(e,!1),pd!==0&&(pd=0)}function vd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&Yd(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function Pf(e,t,n){var r=Nf;if(r&&typeof t==`string`&&t){var i=Rt(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),Of.has(i)||(Of.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),qd(t,`link`,e),yt(t),r.head.appendChild(t)))}}function Ff(e){Af.D(e),Pf(`dns-prefetch`,e,null)}function If(e,t){Af.C(e,t),Pf(`preconnect`,e,t)}function Lf(e,t,n){Af.L(e,t,n);var r=Nf;if(r&&e&&t){var i=`link[rel="preload"][as="`+Rt(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Rt(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Rt(n.imageSizes)+`"]`)):i+=`[href="`+Rt(e)+`"]`;var a=i;switch(t){case`style`:a=Uf(e);break;case`script`:a=qf(e)}Df.has(a)||(e=f({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),Df.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(Wf(a))||t===`script`&&r.querySelector(Jf(a))||(t=r.createElement(`link`),qd(t,`link`,e),yt(t),r.head.appendChild(t)))}}function Rf(e,t){Af.m(e,t);var n=Nf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Rt(r)+`"][href="`+Rt(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=qf(e)}if(!Df.has(a)&&(e=f({rel:`modulepreload`,href:e},t),Df.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Jf(a)))return}r=n.createElement(`link`),qd(r,`link`,e),yt(r),n.head.appendChild(r)}}}function zf(e,t,n){Af.S(e,t,n);var r=Nf;if(r&&e){var i=vt(r).hoistableStyles,a=Uf(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(Wf(a)))s.loading=5;else{e=f({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=Df.get(a))&&Zf(e,n);var c=o=r.createElement(`link`);yt(c),qd(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Xf(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Bf(e,t){Af.X(e,t);var n=Nf;if(n&&e){var r=vt(n).hoistableScripts,i=qf(e),a=r.get(i);a||(a=n.querySelector(Jf(i)),a||(e=f({src:e,async:!0},t),(t=Df.get(i))&&Qf(e,t),a=n.createElement(`script`),yt(a),qd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Vf(e,t){Af.M(e,t);var n=Nf;if(n&&e){var r=vt(n).hoistableScripts,i=qf(e),a=r.get(i);a||(a=n.querySelector(Jf(i)),a||(e=f({src:e,async:!0,type:`module`},t),(t=Df.get(i))&&Qf(e,t),a=n.createElement(`script`),yt(a),qd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Hf(e,t,n,r){var a=(a=ue.current)?kf(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Uf(n.href),n=vt(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Uf(n.href);var o=vt(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(Wf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),Df.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},Df.set(e,n),o||Kf(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=qf(n),n=vt(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Uf(e){return`href="`+Rt(e)+`"`}function Wf(e){return`link[rel="stylesheet"][`+e+`]`}function Gf(e){return f({},e,{"data-precedence":e.precedence,precedence:null})}function Kf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),qd(t,`link`,n),yt(t),e.head.appendChild(t))}function qf(e){return`[src="`+Rt(e)+`"]`}function Jf(e){return`script[async]`+e}function Yf(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Rt(n.href)+`"]`);if(r)return t.instance=r,yt(r),r;var a=f({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),yt(r),qd(r,`style`,a),Xf(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Uf(n.href);var o=e.querySelector(Wf(a));if(o)return t.state.loading|=4,t.instance=o,yt(o),o;r=Gf(n),(a=Df.get(a))&&Zf(r,a),o=(e.ownerDocument||e).createElement(`link`),yt(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),qd(o,`link`,r),t.state.loading|=4,Xf(o,n.precedence,e),t.instance=o;case`script`:return o=qf(n.src),(a=e.querySelector(Jf(o)))?(t.instance=a,yt(a),a):(r=n,(a=Df.get(o))&&(r=f({},n),Qf(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),yt(a),qd(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Xf(r,n.precedence,e));return t.instance}function Xf(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function np(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function rp(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function ip(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Uf(r.href),a=t.querySelector(Wf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=sp.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,yt(a);return}a=t.ownerDocument||t,r=Gf(r),(i=Df.get(i))&&Zf(r,i),a=a.createElement(`link`),yt(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),qd(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=sp.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var ap=0;function op(e,t){return e.stylesheets&&e.count===0&&lp(e,e.stylesheets),0ap?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function sp(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)lp(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var cp=null;function lp(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,cp=new Map,t.forEach(up,e),cp=null,sp.call(e))}function up(e,t){if(!(t.state.loading&4)){var n=cp.get(e);if(n)var r=n.get(null);else{n=new Map,cp.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=oa()})),ca=o((e=>{var t=je().__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;e.c=function(e){return t.H.useMemoCache(e)}})),la=o(((e,t)=>{t.exports=ca()})),ua=c(sa(),1),da=la(),fa=(0,P.createContext)(void 0);function pa(e,t){try{let t=localStorage.getItem(e);if(t===`light`||t===`dark`||t===`system`)return t}catch{}return t}function ma(e){let t=(0,da.c)(14),{children:n,defaultTheme:r,storageKey:i}=e,a=r===void 0?`dark`:r,o=i===void 0?`ui-theme`:i,s;t[0]!==a||t[1]!==o?(s=()=>pa(o,a),t[0]=a,t[1]=o,t[2]=s):s=t[2];let[c,l]=(0,P.useState)(s),u,d;t[3]===c?(u=t[4],d=t[5]):(u=()=>{let e=window.document.documentElement;if(e.classList.remove(`light`,`dark`),c===`system`){let t=window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`;e.classList.add(t);return}e.classList.add(c)},d=[c],t[3]=c,t[4]=u,t[5]=d),(0,P.useEffect)(u,d);let f;t[6]===o?f=t[7]:(f=e=>{try{localStorage.setItem(o,e)}catch{}l(e)},t[6]=o,t[7]=f);let p;t[8]!==f||t[9]!==c?(p={theme:c,setTheme:f},t[8]=f,t[9]=c,t[10]=p):p=t[10];let m=p,h;return t[11]!==n||t[12]!==m?(h=(0,F.jsx)(fa.Provider,{value:m,children:n}),t[11]=n,t[12]=m,t[13]=h):h=t[13],h}function ha(){let e=(0,P.useContext)(fa);if(e===void 0)throw Error(`useTheme must be used within a ThemeProvider`);return e}function ga(e,t){if(e&&!t)return e;if(!e&&t)return t;if(e||t)return{...e,...t}}var _a={};function va(e,t,n,r,i){if(!n&&!r&&!i&&!e)return ba(t);let a=ba(e);return t&&(a=xa(a,t)),n&&(a=xa(a,n)),r&&(a=xa(a,r)),i&&(a=xa(a,i)),a}function ya(e){if(e.length===0)return _a;if(e.length===1)return ba(e[0]);let t=ba(e[0]);for(let n=1;n=65&&i<=90&&(typeof t==`function`||t===void 0)}function Ta(e){return typeof e==`function`}function Ea(e,t){return Ta(e)?e(t):e??_a}function Da(e,t){return t?e?(...n)=>{let r=n[0];if(ja(r)){let i=r;ka(i);let a=t(...n);return i.baseUIHandlerPrevented||e?.(...n),a}let i=t(...n);return e?.(...n),i}:Oa(t):e}function Oa(e){return e&&((...t)=>{let n=t[0];return ja(n)&&ka(n),e(...t)})}function ka(e){return e.preventBaseUIHandler=()=>{e.baseUIHandlerPrevented=!0},e}function Aa(e,t){return t?e?t+` `+e:t:e}function ja(e){return typeof e==`object`&&!!e&&`nativeEvent`in e}function Ma(e,t){return function(n,...r){let i=new URL(e);return i.searchParams.set(`code`,n.toString()),r.forEach(e=>i.searchParams.append(`args[]`,e)),`${t} error #${n}; visit ${i} for the full message.`}}var Na=Ma(`https://base-ui.com/production-error`,`Base UI`),Pa={};function Fa(e,t){let n=P.useRef(Pa);return n.current===Pa&&(n.current=e(t)),n}function Ia(e,t,n,r){let i=Fa(Ra).current;return za(i,e,t,n,r)&&Va(i,[e,t,n,r]),i.callback}function La(e){let t=Fa(Ra).current;return Ba(t,e)&&Va(t,e),t.callback}function Ra(){return{callback:null,cleanup:null,refs:[]}}function za(e,t,n,r,i){return e.refs[0]!==t||e.refs[1]!==n||e.refs[2]!==r||e.refs[3]!==i}function Ba(e,t){return e.refs.length!==t.length||e.refs.some((e,n)=>e!==t[n])}function Va(e,t){if(e.refs=t,t.every(e=>e==null)){e.callback=null;return}e.callback=n=>{if(e.cleanup&&=(e.cleanup(),null),n!=null){let r=Array(t.length).fill(null);for(let e=0;e{for(let e=0;e=e}function Wa(e){if(!P.isValidElement(e))return null;let t=e,n=t.props;return(Ua(19)?n?.ref:t.ref)??null}function Ga(){}var Ka=Object.freeze([]),qa=Object.freeze({});function Ja(e,t){let n={};for(let r in e){let i=e[r];if(t?.hasOwnProperty(r)){let e=t[r](i);e!=null&&Object.assign(n,e);continue}i===!0?n[`data-${r.toLowerCase()}`]=``:i&&(n[`data-${r.toLowerCase()}`]=i.toString())}return n}function Ya(e,t){return typeof e==`function`?e(t):e}function Xa(e,t){return typeof e==`function`?e(t):e}function Za(e,t,n={}){let r=t.render,i=Qa(t,n);return n.enabled===!1?null:to(e,r,i,n.state??qa)}function Qa(e,t={}){let{className:n,style:r,render:i}=e,{state:a=qa,ref:o,props:s,stateAttributesMapping:c,enabled:l=!0}=t,u=l?Ya(n,a):void 0,d=l?Xa(r,a):void 0,f=l?Ja(a,c):qa,p=l&&s?$a(s):void 0,m=l?ga(f,p)??{}:qa;return typeof document<`u`&&(l?Array.isArray(o)?m.ref=La([m.ref,Wa(i),...o]):m.ref=Ia(m.ref,Wa(i),o):Ia(null,null)),l?(u!==void 0&&(m.className=Aa(m.className,u)),d!==void 0&&(m.style=ga(m.style,d)),m):qa}function $a(e){return Array.isArray(e)?ya(e):va(void 0,e)}var eo=Symbol.for(`react.lazy`);function to(e,t,n,r){if(t){if(typeof t==`function`)return t(n,r);let e=va(n,t.props);e.ref=n.ref;let i=t;return i?.$$typeof===eo&&(i=P.Children.toArray(t)[0]),P.cloneElement(i,e)}if(e&&typeof e==`string`)return no(e,n);throw Error(Na(8))}function no(e,t){return e===`button`?(0,P.createElement)(`button`,{type:`button`,...t,key:t.key}):e===`img`?(0,P.createElement)(`img`,{alt:``,...t,key:t.key}):P.createElement(e,t)}function ro(e){return Za(e.defaultTagName??`div`,e,e)}function io(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`)if(Array.isArray(e)){var i=e.length;for(t=0;ttypeof e==`boolean`?`${e}`:e===0?`0`:e,so=ao,co=(e,t)=>n=>{if(t?.variants==null)return so(e,n?.class,n?.className);let{variants:r,defaultVariants:i}=t,a=Object.keys(r).map(e=>{let t=n?.[e],a=i?.[e];if(t===null)return null;let o=oo(t)||oo(a);return r[e][o]}),o=n&&Object.entries(n).reduce((e,t)=>{let[n,r]=t;return r===void 0||(e[n]=r),e},{});return so(e,a,t?.compoundVariants?.reduce((e,t)=>{let{class:n,className:r,...a}=t;return Object.entries(a).every(e=>{let[t,n]=e;return Array.isArray(n)?n.includes({...i,...o}[t]):{...i,...o}[t]===n})?[...e,n,r]:e},[]),n?.class,n?.className)},lo=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),uo=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),fo=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),po=e=>{let t=fo(e);return t.charAt(0).toUpperCase()+t.slice(1)},mo={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`},ho=e=>{for(let t in e)if(t.startsWith(`aria-`)||t===`role`||t===`title`)return!0;return!1},go=(0,P.createContext)({}),_o=()=>(0,P.useContext)(go),vo=(0,P.forwardRef)(({color:e,size:t,strokeWidth:n,absoluteStrokeWidth:r,className:i=``,children:a,iconNode:o,...s},c)=>{let{size:l=24,strokeWidth:u=2,absoluteStrokeWidth:d=!1,color:f=`currentColor`,className:p=``}=_o()??{},m=r??d?Number(n??u)*24/Number(t??l):n??u;return(0,P.createElement)(`svg`,{ref:c,...mo,width:t??l??mo.width,height:t??l??mo.height,stroke:e??f,strokeWidth:m,className:lo(`lucide`,p,i),...!a&&!ho(s)&&{"aria-hidden":`true`},...s},[...o.map(([e,t])=>(0,P.createElement)(e,t)),...Array.isArray(a)?a:[a]])}),yo=(e,t)=>{let n=(0,P.forwardRef)(({className:n,...r},i)=>(0,P.createElement)(vo,{ref:i,iconNode:t,className:lo(`lucide-${uo(po(e))}`,`lucide-${e}`,n),...r}));return n.displayName=po(e),n},bo=yo(`activity`,[[`path`,{d:`M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2`,key:`169zse`}]]),xo=yo(`book-open`,[[`path`,{d:`M12 7v14`,key:`1akyts`}],[`path`,{d:`M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z`,key:`ruj8y`}]]),So=yo(`camera`,[[`path`,{d:`M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z`,key:`18u6gg`}],[`circle`,{cx:`12`,cy:`13`,r:`3`,key:`1vg3eu`}]]),Co=yo(`circle-alert`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`12`,x2:`12`,y1:`8`,y2:`12`,key:`1pkeuh`}],[`line`,{x1:`12`,x2:`12.01`,y1:`16`,y2:`16`,key:`4dfq90`}]]),wo=yo(`crosshair`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`22`,x2:`18`,y1:`12`,y2:`12`,key:`l9bcsi`}],[`line`,{x1:`6`,x2:`2`,y1:`12`,y2:`12`,key:`13hhkx`}],[`line`,{x1:`12`,x2:`12`,y1:`6`,y2:`2`,key:`10w3f3`}],[`line`,{x1:`12`,x2:`12`,y1:`22`,y2:`18`,key:`15g9kq`}]]),To=yo(`globe`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20`,key:`13o1zl`}],[`path`,{d:`M2 12h20`,key:`9i4pu4`}]]),Eo=yo(`hard-drive`,[[`path`,{d:`M10 16h.01`,key:`1bzywj`}],[`path`,{d:`M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`18tbho`}],[`path`,{d:`M21.946 12.013H2.054`,key:`zqlbp7`}],[`path`,{d:`M6 16h.01`,key:`1pmjb7`}]]),Do=yo(`house`,[[`path`,{d:`M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8`,key:`5wwlr5`}],[`path`,{d:`M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z`,key:`r6nss1`}]]),Oo=yo(`image`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,ry:`2`,key:`1m3agn`}],[`circle`,{cx:`9`,cy:`9`,r:`2`,key:`af1f0g`}],[`path`,{d:`m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21`,key:`1xmnt7`}]]),ko=yo(`key`,[[`path`,{d:`m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4`,key:`g0fldk`}],[`path`,{d:`m21 2-9.6 9.6`,key:`1j0ho8`}],[`circle`,{cx:`7.5`,cy:`15.5`,r:`5.5`,key:`yqb3hr`}]]),Ao=yo(`layers`,[[`path`,{d:`M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z`,key:`zw3jo`}],[`path`,{d:`M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12`,key:`1wduqc`}],[`path`,{d:`M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17`,key:`kqbvx6`}]]),jo=yo(`layout-grid`,[[`rect`,{width:`7`,height:`7`,x:`3`,y:`3`,rx:`1`,key:`1g98yp`}],[`rect`,{width:`7`,height:`7`,x:`14`,y:`3`,rx:`1`,key:`6d4xhi`}],[`rect`,{width:`7`,height:`7`,x:`14`,y:`14`,rx:`1`,key:`nxv5o0`}],[`rect`,{width:`7`,height:`7`,x:`3`,y:`14`,rx:`1`,key:`1bb6yr`}]]),Mo=yo(`log-out`,[[`path`,{d:`m16 17 5-5-5-5`,key:`1bji2h`}],[`path`,{d:`M21 12H9`,key:`dn1m92`}],[`path`,{d:`M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4`,key:`1uf3rs`}]]),No=yo(`map-pin`,[[`path`,{d:`M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0`,key:`1r0f0z`}],[`circle`,{cx:`12`,cy:`10`,r:`3`,key:`ilqhr7`}]]),Po=yo(`moon`,[[`path`,{d:`M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401`,key:`kfwtm`}]]),Fo=yo(`network`,[[`rect`,{x:`16`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`4q2zg0`}],[`rect`,{x:`2`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`8cvhb9`}],[`rect`,{x:`9`,y:`2`,width:`6`,height:`6`,rx:`1`,key:`1egb70`}],[`path`,{d:`M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3`,key:`1jsf9p`}],[`path`,{d:`M12 12V8`,key:`2874zd`}]]),Io=yo(`panel-left`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}]]),Lo=yo(`route`,[[`circle`,{cx:`6`,cy:`19`,r:`3`,key:`1kj8tv`}],[`path`,{d:`M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15`,key:`1d8sl`}],[`circle`,{cx:`18`,cy:`5`,r:`3`,key:`gq8acd`}]]),Ro=yo(`router`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,key:`w68u3i`}],[`path`,{d:`M6.01 18H6`,key:`19vcac`}],[`path`,{d:`M10.01 18H10`,key:`uamcmx`}],[`path`,{d:`M15 10v4`,key:`qjz1xs`}],[`path`,{d:`M17.84 7.17a4 4 0 0 0-5.66 0`,key:`1rif40`}],[`path`,{d:`M20.66 4.34a8 8 0 0 0-11.31 0`,key:`6a5xfq`}]]),zo=yo(`server`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`2`,rx:`2`,ry:`2`,key:`ngkwjq`}],[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,ry:`2`,key:`iecqi9`}],[`line`,{x1:`6`,x2:`6.01`,y1:`6`,y2:`6`,key:`16zg32`}],[`line`,{x1:`6`,x2:`6.01`,y1:`18`,y2:`18`,key:`nzw8ys`}]]),Bo=yo(`shield-check`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),Vo=yo(`shield`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}]]),Ho=yo(`sun`,[[`circle`,{cx:`12`,cy:`12`,r:`4`,key:`4exip2`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`m4.93 4.93 1.41 1.41`,key:`149t6j`}],[`path`,{d:`m17.66 17.66 1.41 1.41`,key:`ptbguv`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`m6.34 17.66-1.41 1.41`,key:`1m8zz5`}],[`path`,{d:`m19.07 4.93-1.41 1.41`,key:`1shlcs`}]]),Uo=yo(`users`,[[`path`,{d:`M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2`,key:`1yyitq`}],[`path`,{d:`M16 3.128a4 4 0 0 1 0 7.744`,key:`16gr8j`}],[`path`,{d:`M22 21v-2a4 4 0 0 0-3-3.87`,key:`kshegd`}],[`circle`,{cx:`9`,cy:`7`,r:`4`,key:`nufk8`}]]),Wo=yo(`waypoints`,[[`path`,{d:`m10.586 5.414-5.172 5.172`,key:`4mc350`}],[`path`,{d:`m18.586 13.414-5.172 5.172`,key:`8c96vv`}],[`path`,{d:`M6 12h12`,key:`8npq4p`}],[`circle`,{cx:`12`,cy:`20`,r:`2`,key:`144qzu`}],[`circle`,{cx:`12`,cy:`4`,r:`2`,key:`muu5ef`}],[`circle`,{cx:`20`,cy:`12`,r:`2`,key:`1xzzfp`}],[`circle`,{cx:`4`,cy:`12`,r:`2`,key:`1hvhnz`}]]),Go=yo(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]);function Ko(){return typeof window<`u`}function qo(e){return Xo(e)?(e.nodeName||``).toLowerCase():`#document`}function Jo(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function Yo(e){return((Xo(e)?e.ownerDocument:e.document)||window.document)?.documentElement}function Xo(e){return Ko()?e instanceof Node||e instanceof Jo(e).Node:!1}function Zo(e){return Ko()?e instanceof Element||e instanceof Jo(e).Element:!1}function Qo(e){return Ko()?e instanceof HTMLElement||e instanceof Jo(e).HTMLElement:!1}function $o(e){return!Ko()||typeof ShadowRoot>`u`?!1:e instanceof ShadowRoot||e instanceof Jo(e).ShadowRoot}function es(e){let{overflow:t,overflowX:n,overflowY:r,display:i}=ds(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&i!==`inline`&&i!==`contents`}function ts(e){return/^(table|td|th)$/.test(qo(e))}function ns(e){try{if(e.matches(`:popover-open`))return!0}catch{}try{return e.matches(`:modal`)}catch{return!1}}var rs=/transform|translate|scale|rotate|perspective|filter/,is=/paint|layout|strict|content/,as=e=>!!e&&e!==`none`,os;function ss(e){let t=Zo(e)?ds(e):e;return as(t.transform)||as(t.translate)||as(t.scale)||as(t.rotate)||as(t.perspective)||!ls()&&(as(t.backdropFilter)||as(t.filter))||rs.test(t.willChange||``)||is.test(t.contain||``)}function cs(e){let t=ps(e);for(;Qo(t)&&!us(t);){if(ss(t))return t;if(ns(t))return null;t=ps(t)}return null}function ls(){return os??=typeof CSS<`u`&&CSS.supports&&CSS.supports(`-webkit-backdrop-filter`,`none`),os}function us(e){return/^(html|body|#document)$/.test(qo(e))}function ds(e){return Jo(e).getComputedStyle(e)}function fs(e){return Zo(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function ps(e){if(qo(e)===`html`)return e;let t=e.assignedSlot||e.parentNode||$o(e)&&e.host||Yo(e);return $o(t)?t.host:t}function ms(e){let t=ps(e);return us(t)?e.ownerDocument?e.ownerDocument.body:e.body:Qo(t)&&es(t)?t:ms(t)}function hs(e,t,n){t===void 0&&(t=[]),n===void 0&&(n=!0);let r=ms(e),i=r===e.ownerDocument?.body,a=Jo(r);if(i){let e=gs(a);return t.concat(a,a.visualViewport||[],es(r)?r:[],e&&n?hs(e):[])}else return t.concat(r,hs(r,[],n))}function gs(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}var _s=P[`useInsertionEffect${Math.random().toFixed(1)}`.slice(0,-3)],vs=_s&&_s!==P.useLayoutEffect?_s:e=>e();function ys(e){let t=Fa(bs).current;return t.next=e,vs(t.effect),t.trampoline}function bs(){let e={next:void 0,callback:xs,trampoline:(...t)=>e.callback?.(...t),effect:()=>{e.callback=e.next}};return e}function xs(){}var Ss={...P},Cs=typeof document<`u`?P.useLayoutEffect:()=>{},ws=P.createContext(void 0);function Ts(e=!1){let t=P.useContext(ws);if(t===void 0&&!e)throw Error(Na(16));return t}function Es(e){let{focusableWhenDisabled:t,disabled:n,composite:r=!1,tabIndex:i=0,isNativeButton:a}=e,o=r&&t!==!1,s=r&&t===!1;return{props:P.useMemo(()=>{let e={onKeyDown(e){n&&t&&e.key!==`Tab`&&e.preventDefault()}};return r||(e.tabIndex=i,!a&&n&&(e.tabIndex=t?i:-1)),(a&&(t||o)||!a&&n)&&(e[`aria-disabled`]=n),a&&(!t||s)&&(e.disabled=n),e},[r,n,t,o,s,a,i])}}function Ds(e={}){let{disabled:t=!1,focusableWhenDisabled:n,tabIndex:r=0,native:i=!0,composite:a}=e,o=P.useRef(null),s=Ts(!0),c=a??s!==void 0,{props:l}=Es({focusableWhenDisabled:n,disabled:t,composite:c,tabIndex:r,isNativeButton:i}),u=P.useCallback(()=>{let e=o.current;Os(e)&&c&&t&&l.disabled===void 0&&e.disabled&&(e.disabled=!1)},[t,l.disabled,c]);return Cs(u,[u]),{getButtonProps:P.useCallback((e={})=>{let{onClick:n,onMouseDown:r,onKeyUp:a,onKeyDown:o,onPointerDown:s,...u}=e;return va({type:i?`button`:void 0,onClick(e){if(t){e.preventDefault();return}n?.(e)},onMouseDown(e){t||r?.(e)},onKeyDown(e){if(t||(ka(e),o?.(e),e.baseUIHandlerPrevented))return;let r=e.target===e.currentTarget,a=e.currentTarget,s=Os(a),l=!i&&ks(a),u=r&&(i?s:!l),d=e.key===`Enter`,f=e.key===` `,p=a.getAttribute(`role`),m=p?.startsWith(`menuitem`)||p===`option`||p===`gridcell`;if(r&&c&&f){if(e.defaultPrevented&&m)return;e.preventDefault(),l||i&&s?(a.click(),e.preventBaseUIHandler()):u&&(n?.(e),e.preventBaseUIHandler());return}u&&(!i&&(f||d)&&e.preventDefault(),!i&&d&&n?.(e))},onKeyUp(e){if(!t){if(ka(e),a?.(e),e.target===e.currentTarget&&i&&c&&Os(e.currentTarget)&&e.key===` `){e.preventDefault();return}e.baseUIHandlerPrevented||e.target===e.currentTarget&&!i&&!c&&e.key===` `&&n?.(e)}},onPointerDown(e){if(t){e.preventDefault();return}s?.(e)}},i?void 0:{role:`button`},l,u)},[t,l,c,i]),buttonRef:ys(e=>{o.current=e,u()})}}function Os(e){return Qo(e)&&e.tagName===`BUTTON`}function ks(e){return!!(e?.tagName===`A`&&e?.href)}var As=P.forwardRef(function(e,t){let{render:n,className:r,disabled:i=!1,focusableWhenDisabled:a=!1,nativeButton:o=!0,style:s,...c}=e,{getButtonProps:l,buttonRef:u}=Ds({disabled:i,focusableWhenDisabled:a,native:o});return Za(`button`,e,{state:{disabled:i},ref:[t,u],props:[c,l]})}),js=(e,t)=>{let n=Array(e.length+t.length);for(let t=0;t({classGroupId:e,validator:t}),Ns=(e=new Map,t=null,n)=>({nextPart:e,validators:t,classGroupId:n}),Ps=`-`,Fs=[],Is=`arbitrary..`,Ls=e=>{let t=Bs(e),{conflictingClassGroups:n,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:e=>{if(e.startsWith(`[`)&&e.endsWith(`]`))return zs(e);let n=e.split(Ps);return Rs(n,+(n[0]===``&&n.length>1),t)},getConflictingClassGroupIds:(e,t)=>{if(t){let t=r[e],i=n[e];return t?i?js(i,t):t:i||Fs}return n[e]||Fs}}},Rs=(e,t,n)=>{if(e.length-t===0)return n.classGroupId;let r=e[t],i=n.nextPart.get(r);if(i){let n=Rs(e,t+1,i);if(n)return n}let a=n.validators;if(a===null)return;let o=t===0?e.join(Ps):e.slice(t).join(Ps),s=a.length;for(let e=0;ee.slice(1,-1).indexOf(`:`)===-1?void 0:(()=>{let t=e.slice(1,-1),n=t.indexOf(`:`),r=t.slice(0,n);return r?Is+r:void 0})(),Bs=e=>{let{theme:t,classGroups:n}=e;return Vs(n,t)},Vs=(e,t)=>{let n=Ns();for(let r in e){let i=e[r];Hs(i,n,r,t)}return n},Hs=(e,t,n,r)=>{let i=e.length;for(let a=0;a{if(typeof e==`string`){Ws(e,t,n);return}if(typeof e==`function`){Gs(e,t,n,r);return}Ks(e,t,n,r)},Ws=(e,t,n)=>{let r=e===``?t:qs(t,e);r.classGroupId=n},Gs=(e,t,n,r)=>{if(Js(e)){Hs(e(r),t,n,r);return}t.validators===null&&(t.validators=[]),t.validators.push(Ms(n,e))},Ks=(e,t,n,r)=>{let i=Object.entries(e),a=i.length;for(let e=0;e{let n=e,r=t.split(Ps),i=r.length;for(let e=0;e`isThemeGetter`in e&&e.isThemeGetter===!0,Ys=e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let t=0,n=Object.create(null),r=Object.create(null),i=(i,a)=>{n[i]=a,t++,t>e&&(t=0,r=n,n=Object.create(null))};return{get(e){let t=n[e];if(t!==void 0)return t;if((t=r[e])!==void 0)return i(e,t),t},set(e,t){e in n?n[e]=t:i(e,t)}}},Xs=`!`,Zs=`:`,Qs=[],$s=(e,t,n,r,i)=>({modifiers:e,hasImportantModifier:t,baseClassName:n,maybePostfixModifierPosition:r,isExternal:i}),ec=e=>{let{prefix:t,experimentalParseClassName:n}=e,r=e=>{let t=[],n=0,r=0,i=0,a,o=e.length;for(let s=0;si?a-i:void 0;return $s(t,l,c,u)};if(t){let e=t+Zs,n=r;r=t=>t.startsWith(e)?n(t.slice(e.length)):$s(Qs,!1,t,void 0,!0)}if(n){let e=r;r=t=>n({className:t,parseClassName:e})}return r},tc=e=>{let t=new Map;return e.orderSensitiveModifiers.forEach((e,n)=>{t.set(e,1e6+n)}),e=>{let n=[],r=[];for(let i=0;i0&&(r.sort(),n.push(...r),r=[]),n.push(a)):r.push(a)}return r.length>0&&(r.sort(),n.push(...r)),n}},nc=e=>({cache:Ys(e.cacheSize),parseClassName:ec(e),sortModifiers:tc(e),postfixLookupClassGroupIds:rc(e),...Ls(e)}),rc=e=>{let t=Object.create(null),n=e.postfixLookupClassGroups;if(n)for(let e=0;e{let{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:i,sortModifiers:a,postfixLookupClassGroupIds:o}=t,s=[],c=e.trim().split(ic),l=``;for(let e=c.length-1;e>=0;--e){let t=c[e],{isExternal:u,modifiers:d,hasImportantModifier:f,baseClassName:p,maybePostfixModifierPosition:m}=n(t);if(u){l=t+(l.length>0?` `+l:l);continue}let h=!!m,g;if(h){g=r(p.substring(0,m));let e=g&&o[g]?r(p):void 0;e&&e!==g&&(g=e,h=!1)}else g=r(p);if(!g){if(!h){l=t+(l.length>0?` `+l:l);continue}if(g=r(p),!g){l=t+(l.length>0?` `+l:l);continue}h=!1}let _=d.length===0?``:d.length===1?d[0]:a(d).join(`:`),v=f?_+Xs:_,y=v+g;if(s.indexOf(y)>-1)continue;s.push(y);let b=i(g,h);for(let e=0;e0?` `+l:l)}return l},oc=(...e)=>{let t=0,n,r,i=``;for(;t{if(typeof e==`string`)return e;let t,n=``;for(let r=0;r{let n,r,i,a,o=o=>(n=nc(t.reduce((e,t)=>t(e),e())),r=n.cache.get,i=n.cache.set,a=s,s(o)),s=e=>{let t=r(e);if(t)return t;let a=ac(e,n);return i(e,a),a};return a=o,(...e)=>a(oc(...e))},lc=[],uc=e=>{let t=t=>t[e]||lc;return t.isThemeGetter=!0,t},dc=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,fc=/^\((?:(\w[\w-]*):)?(.+)\)$/i,pc=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,mc=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,hc=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,gc=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,_c=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,vc=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,yc=e=>pc.test(e),bc=e=>!!e&&!Number.isNaN(Number(e)),xc=e=>!!e&&Number.isInteger(Number(e)),Sc=e=>e.endsWith(`%`)&&bc(e.slice(0,-1)),Cc=e=>mc.test(e),wc=()=>!0,Tc=e=>hc.test(e)&&!gc.test(e),Ec=()=>!1,Dc=e=>_c.test(e),Oc=e=>vc.test(e),kc=e=>!z(e)&&!B(e),Ac=e=>e.startsWith(`@container`)&&(e[10]===`/`&&e[11]!==void 0||e[11]===`s`&&e[16]!==void 0&&e.startsWith(`-size/`,10)||e[11]===`n`&&e[18]!==void 0&&e.startsWith(`-normal/`,10)),jc=e=>Kc(e,Xc,Ec),z=e=>dc.test(e),Mc=e=>Kc(e,Zc,Tc),Nc=e=>Kc(e,Qc,bc),Pc=e=>Kc(e,el,wc),Fc=e=>Kc(e,$c,Ec),Ic=e=>Kc(e,Jc,Ec),Lc=e=>Kc(e,Yc,Oc),Rc=e=>Kc(e,tl,Dc),B=e=>fc.test(e),zc=e=>qc(e,Zc),Bc=e=>qc(e,$c),Vc=e=>qc(e,Jc),Hc=e=>qc(e,Xc),Uc=e=>qc(e,Yc),Wc=e=>qc(e,tl,!0),Gc=e=>qc(e,el,!0),Kc=(e,t,n)=>{let r=dc.exec(e);return r?r[1]?t(r[1]):n(r[2]):!1},qc=(e,t,n=!1)=>{let r=fc.exec(e);return r?r[1]?t(r[1]):n:!1},Jc=e=>e===`position`||e===`percentage`,Yc=e=>e===`image`||e===`url`,Xc=e=>e===`length`||e===`size`||e===`bg-size`,Zc=e=>e===`length`,Qc=e=>e===`number`,$c=e=>e===`family-name`,el=e=>e===`number`||e===`weight`,tl=e=>e===`shadow`,nl=cc(()=>{let e=uc(`color`),t=uc(`font`),n=uc(`text`),r=uc(`font-weight`),i=uc(`tracking`),a=uc(`leading`),o=uc(`breakpoint`),s=uc(`container`),c=uc(`spacing`),l=uc(`radius`),u=uc(`shadow`),d=uc(`inset-shadow`),f=uc(`text-shadow`),p=uc(`drop-shadow`),m=uc(`blur`),h=uc(`perspective`),g=uc(`aspect`),_=uc(`ease`),v=uc(`animate`),y=()=>[`auto`,`avoid`,`all`,`avoid-page`,`page`,`left`,`right`,`column`],b=()=>[`center`,`top`,`bottom`,`left`,`right`,`top-left`,`left-top`,`top-right`,`right-top`,`bottom-right`,`right-bottom`,`bottom-left`,`left-bottom`],x=()=>[...b(),B,z],S=()=>[`auto`,`hidden`,`clip`,`visible`,`scroll`],C=()=>[`auto`,`contain`,`none`],w=()=>[B,z,c],T=()=>[yc,`full`,`auto`,...w()],E=()=>[xc,`none`,`subgrid`,B,z],ee=()=>[`auto`,{span:[`full`,xc,B,z]},xc,B,z],D=()=>[xc,`auto`,B,z],O=()=>[`auto`,`min`,`max`,`fr`,B,z],te=()=>[`start`,`end`,`center`,`between`,`around`,`evenly`,`stretch`,`baseline`,`center-safe`,`end-safe`],ne=()=>[`start`,`end`,`center`,`stretch`,`center-safe`,`end-safe`],re=()=>[`auto`,...w()],k=()=>[yc,`auto`,`full`,`dvw`,`dvh`,`lvw`,`lvh`,`svw`,`svh`,`min`,`max`,`fit`,...w()],A=()=>[yc,`screen`,`full`,`dvw`,`lvw`,`svw`,`min`,`max`,`fit`,...w()],ie=()=>[yc,`screen`,`full`,`lh`,`dvh`,`lvh`,`svh`,`min`,`max`,`fit`,...w()],j=()=>[e,B,z],ae=()=>[...b(),Vc,Ic,{position:[B,z]}],oe=()=>[`no-repeat`,{repeat:[``,`x`,`y`,`space`,`round`]}],se=()=>[`auto`,`cover`,`contain`,Hc,jc,{size:[B,z]}],ce=()=>[Sc,zc,Mc],M=()=>[``,`none`,`full`,l,B,z],le=()=>[``,bc,zc,Mc],ue=()=>[`solid`,`dashed`,`dotted`,`double`],N=()=>[`normal`,`multiply`,`screen`,`overlay`,`darken`,`lighten`,`color-dodge`,`color-burn`,`hard-light`,`soft-light`,`difference`,`exclusion`,`hue`,`saturation`,`color`,`luminosity`],de=()=>[bc,Sc,Vc,Ic],fe=()=>[``,`none`,m,B,z],pe=()=>[`none`,bc,B,z],me=()=>[`none`,bc,B,z],he=()=>[bc,B,z],ge=()=>[yc,`full`,...w()];return{cacheSize:500,theme:{animate:[`spin`,`ping`,`pulse`,`bounce`],aspect:[`video`],blur:[Cc],breakpoint:[Cc],color:[wc],container:[Cc],"drop-shadow":[Cc],ease:[`in`,`out`,`in-out`],font:[kc],"font-weight":[`thin`,`extralight`,`light`,`normal`,`medium`,`semibold`,`bold`,`extrabold`,`black`],"inset-shadow":[Cc],leading:[`none`,`tight`,`snug`,`normal`,`relaxed`,`loose`],perspective:[`dramatic`,`near`,`normal`,`midrange`,`distant`,`none`],radius:[Cc],shadow:[Cc],spacing:[`px`,bc],text:[Cc],"text-shadow":[Cc],tracking:[`tighter`,`tight`,`normal`,`wide`,`wider`,`widest`]},classGroups:{aspect:[{aspect:[`auto`,`square`,yc,z,B,g]}],container:[`container`],"container-type":[{"@container":[``,`normal`,`size`,B,z]}],"container-named":[Ac],columns:[{columns:[bc,z,B,s]}],"break-after":[{"break-after":y()}],"break-before":[{"break-before":y()}],"break-inside":[{"break-inside":[`auto`,`avoid`,`avoid-page`,`avoid-column`]}],"box-decoration":[{"box-decoration":[`slice`,`clone`]}],box:[{box:[`border`,`content`]}],display:[`block`,`inline-block`,`inline`,`flex`,`inline-flex`,`table`,`inline-table`,`table-caption`,`table-cell`,`table-column`,`table-column-group`,`table-footer-group`,`table-header-group`,`table-row-group`,`table-row`,`flow-root`,`grid`,`inline-grid`,`contents`,`list-item`,`hidden`],sr:[`sr-only`,`not-sr-only`],float:[{float:[`right`,`left`,`none`,`start`,`end`]}],clear:[{clear:[`left`,`right`,`both`,`none`,`start`,`end`]}],isolation:[`isolate`,`isolation-auto`],"object-fit":[{object:[`contain`,`cover`,`fill`,`none`,`scale-down`]}],"object-position":[{object:x()}],overflow:[{overflow:S()}],"overflow-x":[{"overflow-x":S()}],"overflow-y":[{"overflow-y":S()}],overscroll:[{overscroll:C()}],"overscroll-x":[{"overscroll-x":C()}],"overscroll-y":[{"overscroll-y":C()}],position:[`static`,`fixed`,`absolute`,`relative`,`sticky`],inset:[{inset:T()}],"inset-x":[{"inset-x":T()}],"inset-y":[{"inset-y":T()}],start:[{"inset-s":T(),start:T()}],end:[{"inset-e":T(),end:T()}],"inset-bs":[{"inset-bs":T()}],"inset-be":[{"inset-be":T()}],top:[{top:T()}],right:[{right:T()}],bottom:[{bottom:T()}],left:[{left:T()}],visibility:[`visible`,`invisible`,`collapse`],z:[{z:[xc,`auto`,B,z]}],basis:[{basis:[yc,`full`,`auto`,s,...w()]}],"flex-direction":[{flex:[`row`,`row-reverse`,`col`,`col-reverse`]}],"flex-wrap":[{flex:[`nowrap`,`wrap`,`wrap-reverse`]}],flex:[{flex:[bc,yc,`auto`,`initial`,`none`,z]}],grow:[{grow:[``,bc,B,z]}],shrink:[{shrink:[``,bc,B,z]}],order:[{order:[xc,`first`,`last`,`none`,B,z]}],"grid-cols":[{"grid-cols":E()}],"col-start-end":[{col:ee()}],"col-start":[{"col-start":D()}],"col-end":[{"col-end":D()}],"grid-rows":[{"grid-rows":E()}],"row-start-end":[{row:ee()}],"row-start":[{"row-start":D()}],"row-end":[{"row-end":D()}],"grid-flow":[{"grid-flow":[`row`,`col`,`dense`,`row-dense`,`col-dense`]}],"auto-cols":[{"auto-cols":O()}],"auto-rows":[{"auto-rows":O()}],gap:[{gap:w()}],"gap-x":[{"gap-x":w()}],"gap-y":[{"gap-y":w()}],"justify-content":[{justify:[...te(),`normal`]}],"justify-items":[{"justify-items":[...ne(),`normal`]}],"justify-self":[{"justify-self":[`auto`,...ne()]}],"align-content":[{content:[`normal`,...te()]}],"align-items":[{items:[...ne(),{baseline:[``,`last`]}]}],"align-self":[{self:[`auto`,...ne(),{baseline:[``,`last`]}]}],"place-content":[{"place-content":te()}],"place-items":[{"place-items":[...ne(),`baseline`]}],"place-self":[{"place-self":[`auto`,...ne()]}],p:[{p:w()}],px:[{px:w()}],py:[{py:w()}],ps:[{ps:w()}],pe:[{pe:w()}],pbs:[{pbs:w()}],pbe:[{pbe:w()}],pt:[{pt:w()}],pr:[{pr:w()}],pb:[{pb:w()}],pl:[{pl:w()}],m:[{m:re()}],mx:[{mx:re()}],my:[{my:re()}],ms:[{ms:re()}],me:[{me:re()}],mbs:[{mbs:re()}],mbe:[{mbe:re()}],mt:[{mt:re()}],mr:[{mr:re()}],mb:[{mb:re()}],ml:[{ml:re()}],"space-x":[{"space-x":w()}],"space-x-reverse":[`space-x-reverse`],"space-y":[{"space-y":w()}],"space-y-reverse":[`space-y-reverse`],size:[{size:k()}],"inline-size":[{inline:[`auto`,...A()]}],"min-inline-size":[{"min-inline":[`auto`,...A()]}],"max-inline-size":[{"max-inline":[`none`,...A()]}],"block-size":[{block:[`auto`,...ie()]}],"min-block-size":[{"min-block":[`auto`,...ie()]}],"max-block-size":[{"max-block":[`none`,...ie()]}],w:[{w:[s,`screen`,...k()]}],"min-w":[{"min-w":[s,`screen`,`none`,...k()]}],"max-w":[{"max-w":[s,`screen`,`none`,`prose`,{screen:[o]},...k()]}],h:[{h:[`screen`,`lh`,...k()]}],"min-h":[{"min-h":[`screen`,`lh`,`none`,...k()]}],"max-h":[{"max-h":[`screen`,`lh`,...k()]}],"font-size":[{text:[`base`,n,zc,Mc]}],"font-smoothing":[`antialiased`,`subpixel-antialiased`],"font-style":[`italic`,`not-italic`],"font-weight":[{font:[r,Gc,Pc]}],"font-stretch":[{"font-stretch":[`ultra-condensed`,`extra-condensed`,`condensed`,`semi-condensed`,`normal`,`semi-expanded`,`expanded`,`extra-expanded`,`ultra-expanded`,Sc,z]}],"font-family":[{font:[Bc,Fc,t]}],"font-features":[{"font-features":[z]}],"fvn-normal":[`normal-nums`],"fvn-ordinal":[`ordinal`],"fvn-slashed-zero":[`slashed-zero`],"fvn-figure":[`lining-nums`,`oldstyle-nums`],"fvn-spacing":[`proportional-nums`,`tabular-nums`],"fvn-fraction":[`diagonal-fractions`,`stacked-fractions`],tracking:[{tracking:[i,B,z]}],"line-clamp":[{"line-clamp":[bc,`none`,B,Nc]}],leading:[{leading:[a,...w()]}],"list-image":[{"list-image":[`none`,B,z]}],"list-style-position":[{list:[`inside`,`outside`]}],"list-style-type":[{list:[`disc`,`decimal`,`none`,B,z]}],"text-alignment":[{text:[`left`,`center`,`right`,`justify`,`start`,`end`]}],"placeholder-color":[{placeholder:j()}],"text-color":[{text:j()}],"text-decoration":[`underline`,`overline`,`line-through`,`no-underline`],"text-decoration-style":[{decoration:[...ue(),`wavy`]}],"text-decoration-thickness":[{decoration:[bc,`from-font`,`auto`,B,Mc]}],"text-decoration-color":[{decoration:j()}],"underline-offset":[{"underline-offset":[bc,`auto`,B,z]}],"text-transform":[`uppercase`,`lowercase`,`capitalize`,`normal-case`],"text-overflow":[`truncate`,`text-ellipsis`,`text-clip`],"text-wrap":[{text:[`wrap`,`nowrap`,`balance`,`pretty`]}],indent:[{indent:w()}],"tab-size":[{tab:[xc,B,z]}],"vertical-align":[{align:[`baseline`,`top`,`middle`,`bottom`,`text-top`,`text-bottom`,`sub`,`super`,B,z]}],whitespace:[{whitespace:[`normal`,`nowrap`,`pre`,`pre-line`,`pre-wrap`,`break-spaces`]}],break:[{break:[`normal`,`words`,`all`,`keep`]}],wrap:[{wrap:[`break-word`,`anywhere`,`normal`]}],hyphens:[{hyphens:[`none`,`manual`,`auto`]}],content:[{content:[`none`,B,z]}],"bg-attachment":[{bg:[`fixed`,`local`,`scroll`]}],"bg-clip":[{"bg-clip":[`border`,`padding`,`content`,`text`]}],"bg-origin":[{"bg-origin":[`border`,`padding`,`content`]}],"bg-position":[{bg:ae()}],"bg-repeat":[{bg:oe()}],"bg-size":[{bg:se()}],"bg-image":[{bg:[`none`,{linear:[{to:[`t`,`tr`,`r`,`br`,`b`,`bl`,`l`,`tl`]},xc,B,z],radial:[``,B,z],conic:[xc,B,z]},Uc,Lc]}],"bg-color":[{bg:j()}],"gradient-from-pos":[{from:ce()}],"gradient-via-pos":[{via:ce()}],"gradient-to-pos":[{to:ce()}],"gradient-from":[{from:j()}],"gradient-via":[{via:j()}],"gradient-to":[{to:j()}],rounded:[{rounded:M()}],"rounded-s":[{"rounded-s":M()}],"rounded-e":[{"rounded-e":M()}],"rounded-t":[{"rounded-t":M()}],"rounded-r":[{"rounded-r":M()}],"rounded-b":[{"rounded-b":M()}],"rounded-l":[{"rounded-l":M()}],"rounded-ss":[{"rounded-ss":M()}],"rounded-se":[{"rounded-se":M()}],"rounded-ee":[{"rounded-ee":M()}],"rounded-es":[{"rounded-es":M()}],"rounded-tl":[{"rounded-tl":M()}],"rounded-tr":[{"rounded-tr":M()}],"rounded-br":[{"rounded-br":M()}],"rounded-bl":[{"rounded-bl":M()}],"border-w":[{border:le()}],"border-w-x":[{"border-x":le()}],"border-w-y":[{"border-y":le()}],"border-w-s":[{"border-s":le()}],"border-w-e":[{"border-e":le()}],"border-w-bs":[{"border-bs":le()}],"border-w-be":[{"border-be":le()}],"border-w-t":[{"border-t":le()}],"border-w-r":[{"border-r":le()}],"border-w-b":[{"border-b":le()}],"border-w-l":[{"border-l":le()}],"divide-x":[{"divide-x":le()}],"divide-x-reverse":[`divide-x-reverse`],"divide-y":[{"divide-y":le()}],"divide-y-reverse":[`divide-y-reverse`],"border-style":[{border:[...ue(),`hidden`,`none`]}],"divide-style":[{divide:[...ue(),`hidden`,`none`]}],"border-color":[{border:j()}],"border-color-x":[{"border-x":j()}],"border-color-y":[{"border-y":j()}],"border-color-s":[{"border-s":j()}],"border-color-e":[{"border-e":j()}],"border-color-bs":[{"border-bs":j()}],"border-color-be":[{"border-be":j()}],"border-color-t":[{"border-t":j()}],"border-color-r":[{"border-r":j()}],"border-color-b":[{"border-b":j()}],"border-color-l":[{"border-l":j()}],"divide-color":[{divide:j()}],"outline-style":[{outline:[...ue(),`none`,`hidden`]}],"outline-offset":[{"outline-offset":[bc,B,z]}],"outline-w":[{outline:[``,bc,zc,Mc]}],"outline-color":[{outline:j()}],shadow:[{shadow:[``,`none`,u,Wc,Rc]}],"shadow-color":[{shadow:j()}],"inset-shadow":[{"inset-shadow":[`none`,d,Wc,Rc]}],"inset-shadow-color":[{"inset-shadow":j()}],"ring-w":[{ring:le()}],"ring-w-inset":[`ring-inset`],"ring-color":[{ring:j()}],"ring-offset-w":[{"ring-offset":[bc,Mc]}],"ring-offset-color":[{"ring-offset":j()}],"inset-ring-w":[{"inset-ring":le()}],"inset-ring-color":[{"inset-ring":j()}],"text-shadow":[{"text-shadow":[`none`,f,Wc,Rc]}],"text-shadow-color":[{"text-shadow":j()}],opacity:[{opacity:[bc,B,z]}],"mix-blend":[{"mix-blend":[...N(),`plus-darker`,`plus-lighter`]}],"bg-blend":[{"bg-blend":N()}],"mask-clip":[{"mask-clip":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]},`mask-no-clip`],"mask-composite":[{mask:[`add`,`subtract`,`intersect`,`exclude`]}],"mask-image-linear-pos":[{"mask-linear":[bc]}],"mask-image-linear-from-pos":[{"mask-linear-from":de()}],"mask-image-linear-to-pos":[{"mask-linear-to":de()}],"mask-image-linear-from-color":[{"mask-linear-from":j()}],"mask-image-linear-to-color":[{"mask-linear-to":j()}],"mask-image-t-from-pos":[{"mask-t-from":de()}],"mask-image-t-to-pos":[{"mask-t-to":de()}],"mask-image-t-from-color":[{"mask-t-from":j()}],"mask-image-t-to-color":[{"mask-t-to":j()}],"mask-image-r-from-pos":[{"mask-r-from":de()}],"mask-image-r-to-pos":[{"mask-r-to":de()}],"mask-image-r-from-color":[{"mask-r-from":j()}],"mask-image-r-to-color":[{"mask-r-to":j()}],"mask-image-b-from-pos":[{"mask-b-from":de()}],"mask-image-b-to-pos":[{"mask-b-to":de()}],"mask-image-b-from-color":[{"mask-b-from":j()}],"mask-image-b-to-color":[{"mask-b-to":j()}],"mask-image-l-from-pos":[{"mask-l-from":de()}],"mask-image-l-to-pos":[{"mask-l-to":de()}],"mask-image-l-from-color":[{"mask-l-from":j()}],"mask-image-l-to-color":[{"mask-l-to":j()}],"mask-image-x-from-pos":[{"mask-x-from":de()}],"mask-image-x-to-pos":[{"mask-x-to":de()}],"mask-image-x-from-color":[{"mask-x-from":j()}],"mask-image-x-to-color":[{"mask-x-to":j()}],"mask-image-y-from-pos":[{"mask-y-from":de()}],"mask-image-y-to-pos":[{"mask-y-to":de()}],"mask-image-y-from-color":[{"mask-y-from":j()}],"mask-image-y-to-color":[{"mask-y-to":j()}],"mask-image-radial":[{"mask-radial":[B,z]}],"mask-image-radial-from-pos":[{"mask-radial-from":de()}],"mask-image-radial-to-pos":[{"mask-radial-to":de()}],"mask-image-radial-from-color":[{"mask-radial-from":j()}],"mask-image-radial-to-color":[{"mask-radial-to":j()}],"mask-image-radial-shape":[{"mask-radial":[`circle`,`ellipse`]}],"mask-image-radial-size":[{"mask-radial":[{closest:[`side`,`corner`],farthest:[`side`,`corner`]}]}],"mask-image-radial-pos":[{"mask-radial-at":b()}],"mask-image-conic-pos":[{"mask-conic":[bc]}],"mask-image-conic-from-pos":[{"mask-conic-from":de()}],"mask-image-conic-to-pos":[{"mask-conic-to":de()}],"mask-image-conic-from-color":[{"mask-conic-from":j()}],"mask-image-conic-to-color":[{"mask-conic-to":j()}],"mask-mode":[{mask:[`alpha`,`luminance`,`match`]}],"mask-origin":[{"mask-origin":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]}],"mask-position":[{mask:ae()}],"mask-repeat":[{mask:oe()}],"mask-size":[{mask:se()}],"mask-type":[{"mask-type":[`alpha`,`luminance`]}],"mask-image":[{mask:[`none`,B,z]}],filter:[{filter:[``,`none`,B,z]}],blur:[{blur:fe()}],brightness:[{brightness:[bc,B,z]}],contrast:[{contrast:[bc,B,z]}],"drop-shadow":[{"drop-shadow":[``,`none`,p,Wc,Rc]}],"drop-shadow-color":[{"drop-shadow":j()}],grayscale:[{grayscale:[``,bc,B,z]}],"hue-rotate":[{"hue-rotate":[bc,B,z]}],invert:[{invert:[``,bc,B,z]}],saturate:[{saturate:[bc,B,z]}],sepia:[{sepia:[``,bc,B,z]}],"backdrop-filter":[{"backdrop-filter":[``,`none`,B,z]}],"backdrop-blur":[{"backdrop-blur":fe()}],"backdrop-brightness":[{"backdrop-brightness":[bc,B,z]}],"backdrop-contrast":[{"backdrop-contrast":[bc,B,z]}],"backdrop-grayscale":[{"backdrop-grayscale":[``,bc,B,z]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[bc,B,z]}],"backdrop-invert":[{"backdrop-invert":[``,bc,B,z]}],"backdrop-opacity":[{"backdrop-opacity":[bc,B,z]}],"backdrop-saturate":[{"backdrop-saturate":[bc,B,z]}],"backdrop-sepia":[{"backdrop-sepia":[``,bc,B,z]}],"border-collapse":[{border:[`collapse`,`separate`]}],"border-spacing":[{"border-spacing":w()}],"border-spacing-x":[{"border-spacing-x":w()}],"border-spacing-y":[{"border-spacing-y":w()}],"table-layout":[{table:[`auto`,`fixed`]}],caption:[{caption:[`top`,`bottom`]}],transition:[{transition:[``,`all`,`colors`,`opacity`,`shadow`,`transform`,`none`,B,z]}],"transition-behavior":[{transition:[`normal`,`discrete`]}],duration:[{duration:[bc,`initial`,B,z]}],ease:[{ease:[`linear`,`initial`,_,B,z]}],delay:[{delay:[bc,B,z]}],animate:[{animate:[`none`,v,B,z]}],backface:[{backface:[`hidden`,`visible`]}],perspective:[{perspective:[h,B,z]}],"perspective-origin":[{"perspective-origin":x()}],rotate:[{rotate:pe()}],"rotate-x":[{"rotate-x":pe()}],"rotate-y":[{"rotate-y":pe()}],"rotate-z":[{"rotate-z":pe()}],scale:[{scale:me()}],"scale-x":[{"scale-x":me()}],"scale-y":[{"scale-y":me()}],"scale-z":[{"scale-z":me()}],"scale-3d":[`scale-3d`],skew:[{skew:he()}],"skew-x":[{"skew-x":he()}],"skew-y":[{"skew-y":he()}],transform:[{transform:[B,z,``,`none`,`gpu`,`cpu`]}],"transform-origin":[{origin:x()}],"transform-style":[{transform:[`3d`,`flat`]}],translate:[{translate:ge()}],"translate-x":[{"translate-x":ge()}],"translate-y":[{"translate-y":ge()}],"translate-z":[{"translate-z":ge()}],"translate-none":[`translate-none`],zoom:[{zoom:[xc,B,z]}],accent:[{accent:j()}],appearance:[{appearance:[`none`,`auto`]}],"caret-color":[{caret:j()}],"color-scheme":[{scheme:[`normal`,`dark`,`light`,`light-dark`,`only-dark`,`only-light`]}],cursor:[{cursor:[`auto`,`default`,`pointer`,`wait`,`text`,`move`,`help`,`not-allowed`,`none`,`context-menu`,`progress`,`cell`,`crosshair`,`vertical-text`,`alias`,`copy`,`no-drop`,`grab`,`grabbing`,`all-scroll`,`col-resize`,`row-resize`,`n-resize`,`e-resize`,`s-resize`,`w-resize`,`ne-resize`,`nw-resize`,`se-resize`,`sw-resize`,`ew-resize`,`ns-resize`,`nesw-resize`,`nwse-resize`,`zoom-in`,`zoom-out`,B,z]}],"field-sizing":[{"field-sizing":[`fixed`,`content`]}],"pointer-events":[{"pointer-events":[`auto`,`none`]}],resize:[{resize:[`none`,``,`y`,`x`]}],"scroll-behavior":[{scroll:[`auto`,`smooth`]}],"scrollbar-thumb-color":[{"scrollbar-thumb":j()}],"scrollbar-track-color":[{"scrollbar-track":j()}],"scrollbar-gutter":[{"scrollbar-gutter":[`auto`,`stable`,`both`]}],"scrollbar-w":[{scrollbar:[`auto`,`thin`,`none`]}],"scroll-m":[{"scroll-m":w()}],"scroll-mx":[{"scroll-mx":w()}],"scroll-my":[{"scroll-my":w()}],"scroll-ms":[{"scroll-ms":w()}],"scroll-me":[{"scroll-me":w()}],"scroll-mbs":[{"scroll-mbs":w()}],"scroll-mbe":[{"scroll-mbe":w()}],"scroll-mt":[{"scroll-mt":w()}],"scroll-mr":[{"scroll-mr":w()}],"scroll-mb":[{"scroll-mb":w()}],"scroll-ml":[{"scroll-ml":w()}],"scroll-p":[{"scroll-p":w()}],"scroll-px":[{"scroll-px":w()}],"scroll-py":[{"scroll-py":w()}],"scroll-ps":[{"scroll-ps":w()}],"scroll-pe":[{"scroll-pe":w()}],"scroll-pbs":[{"scroll-pbs":w()}],"scroll-pbe":[{"scroll-pbe":w()}],"scroll-pt":[{"scroll-pt":w()}],"scroll-pr":[{"scroll-pr":w()}],"scroll-pb":[{"scroll-pb":w()}],"scroll-pl":[{"scroll-pl":w()}],"snap-align":[{snap:[`start`,`end`,`center`,`align-none`]}],"snap-stop":[{snap:[`normal`,`always`]}],"snap-type":[{snap:[`none`,`x`,`y`,`both`]}],"snap-strictness":[{snap:[`mandatory`,`proximity`]}],touch:[{touch:[`auto`,`none`,`manipulation`]}],"touch-x":[{"touch-pan":[`x`,`left`,`right`]}],"touch-y":[{"touch-pan":[`y`,`up`,`down`]}],"touch-pz":[`touch-pinch-zoom`],select:[{select:[`none`,`text`,`all`,`auto`]}],"will-change":[{"will-change":[`auto`,`scroll`,`contents`,`transform`,B,z]}],fill:[{fill:[`none`,...j()]}],"stroke-w":[{stroke:[bc,zc,Mc,Nc]}],stroke:[{stroke:[`none`,...j()]}],"forced-color-adjust":[{"forced-color-adjust":[`auto`,`none`]}]},conflictingClassGroups:{"container-named":[`container-type`],overflow:[`overflow-x`,`overflow-y`],overscroll:[`overscroll-x`,`overscroll-y`],inset:[`inset-x`,`inset-y`,`inset-bs`,`inset-be`,`start`,`end`,`top`,`right`,`bottom`,`left`],"inset-x":[`right`,`left`],"inset-y":[`top`,`bottom`],flex:[`basis`,`grow`,`shrink`],gap:[`gap-x`,`gap-y`],p:[`px`,`py`,`ps`,`pe`,`pbs`,`pbe`,`pt`,`pr`,`pb`,`pl`],px:[`pr`,`pl`],py:[`pt`,`pb`],m:[`mx`,`my`,`ms`,`me`,`mbs`,`mbe`,`mt`,`mr`,`mb`,`ml`],mx:[`mr`,`ml`],my:[`mt`,`mb`],size:[`w`,`h`],"font-size":[`leading`],"fvn-normal":[`fvn-ordinal`,`fvn-slashed-zero`,`fvn-figure`,`fvn-spacing`,`fvn-fraction`],"fvn-ordinal":[`fvn-normal`],"fvn-slashed-zero":[`fvn-normal`],"fvn-figure":[`fvn-normal`],"fvn-spacing":[`fvn-normal`],"fvn-fraction":[`fvn-normal`],"line-clamp":[`display`,`overflow`],rounded:[`rounded-s`,`rounded-e`,`rounded-t`,`rounded-r`,`rounded-b`,`rounded-l`,`rounded-ss`,`rounded-se`,`rounded-ee`,`rounded-es`,`rounded-tl`,`rounded-tr`,`rounded-br`,`rounded-bl`],"rounded-s":[`rounded-ss`,`rounded-es`],"rounded-e":[`rounded-se`,`rounded-ee`],"rounded-t":[`rounded-tl`,`rounded-tr`],"rounded-r":[`rounded-tr`,`rounded-br`],"rounded-b":[`rounded-br`,`rounded-bl`],"rounded-l":[`rounded-tl`,`rounded-bl`],"border-spacing":[`border-spacing-x`,`border-spacing-y`],"border-w":[`border-w-x`,`border-w-y`,`border-w-s`,`border-w-e`,`border-w-bs`,`border-w-be`,`border-w-t`,`border-w-r`,`border-w-b`,`border-w-l`],"border-w-x":[`border-w-r`,`border-w-l`],"border-w-y":[`border-w-t`,`border-w-b`],"border-color":[`border-color-x`,`border-color-y`,`border-color-s`,`border-color-e`,`border-color-bs`,`border-color-be`,`border-color-t`,`border-color-r`,`border-color-b`,`border-color-l`],"border-color-x":[`border-color-r`,`border-color-l`],"border-color-y":[`border-color-t`,`border-color-b`],translate:[`translate-x`,`translate-y`,`translate-none`],"translate-none":[`translate`,`translate-x`,`translate-y`,`translate-z`],"scroll-m":[`scroll-mx`,`scroll-my`,`scroll-ms`,`scroll-me`,`scroll-mbs`,`scroll-mbe`,`scroll-mt`,`scroll-mr`,`scroll-mb`,`scroll-ml`],"scroll-mx":[`scroll-mr`,`scroll-ml`],"scroll-my":[`scroll-mt`,`scroll-mb`],"scroll-p":[`scroll-px`,`scroll-py`,`scroll-ps`,`scroll-pe`,`scroll-pbs`,`scroll-pbe`,`scroll-pt`,`scroll-pr`,`scroll-pb`,`scroll-pl`],"scroll-px":[`scroll-pr`,`scroll-pl`],"scroll-py":[`scroll-pt`,`scroll-pb`],touch:[`touch-x`,`touch-y`,`touch-pz`],"touch-x":[`touch`],"touch-y":[`touch`],"touch-pz":[`touch`]},conflictingClassGroupModifiers:{"font-size":[`leading`]},postfixLookupClassGroups:[`container-type`],orderSensitiveModifiers:[`*`,`**`,`after`,`backdrop`,`before`,`details-content`,`file`,`first-letter`,`first-line`,`marker`,`placeholder`,`selection`]}});function rl(...e){return nl(ao(e))}var il=new Intl.DateTimeFormat(void 0,{year:`numeric`,month:`long`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`});function al(e){return e?il.format(new Date(e)):`Unknown`}function ol(e){return`${Math.round(e/1024)} GiB`}var sl=[`B`,`KB`,`MB`,`GB`,`TB`];function cl(e){if(e===0)return`0 B`;let t=Math.floor(Math.log(e)/Math.log(1024));return`${(e/1024**t).toFixed(2)} ${sl[t]}`}function ll(e){return e?.find(e=>e.Key===`Name`)?.Value}var ul=/\/$/;function dl(e){return e.replace(ul,``)}function fl(e){return e&&(e.endsWith(`/`)?e:`${e}/`)}function pl(e,t){return e.startsWith(t)?e:`${t}${e}`}function ml(e,t){return e.startsWith(t)?e.slice(t.length):e}var hl=co(`group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-xs/relaxed font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-[2px] focus-visible:ring-ring/30 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[2px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,{variants:{variant:{default:`bg-primary text-primary-foreground hover:bg-primary/80`,outline:`border-border hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-input/30`,secondary:`bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground`,ghost:`hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50`,destructive:`bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40`,link:`text-primary underline-offset-4 hover:underline`},size:{default:`h-7 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5`,xs:`h-5 gap-1 rounded-sm px-2 text-[0.625rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-2.5`,sm:`h-6 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3`,lg:`h-8 gap-1 px-2.5 text-xs/relaxed has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4`,icon:`size-7 [&_svg:not([class*='size-'])]:size-3.5`,"icon-xs":`size-5 rounded-sm [&_svg:not([class*='size-'])]:size-2.5`,"icon-sm":`size-6 [&_svg:not([class*='size-'])]:size-3`,"icon-lg":`size-8 [&_svg:not([class*='size-'])]:size-4`}},defaultVariants:{variant:`default`,size:`default`}});function gl(e){let t=(0,da.c)(12),n,r,i,a;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4]):({className:n,variant:i,size:a,...r}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a);let o=i===void 0?`default`:i,s=a===void 0?`default`:a,c;t[5]!==n||t[6]!==s||t[7]!==o?(c=rl(hl({variant:o,size:s,className:n})),t[5]=n,t[6]=s,t[7]=o,t[8]=c):c=t[8];let l;return t[9]!==r||t[10]!==c?(l=(0,F.jsx)(As,{className:c,"data-slot":`button`,...r}),t[9]=r,t[10]=c,t[11]=l):l=t[11],l}var _l=function(e){return e.disabled=`data-disabled`,e.valid=`data-valid`,e.invalid=`data-invalid`,e.touched=`data-touched`,e.dirty=`data-dirty`,e.filled=`data-filled`,e.focused=`data-focused`,e}({}),vl={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},yl={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},bl={disabled:!1,...yl},xl={valid(e){return e===null?null:e?{[_l.valid]:``}:{[_l.invalid]:``}}},Sl=P.createContext({invalid:void 0,name:void 0,validityData:{state:vl,errors:[],error:``,value:``,initialValue:null},setValidityData:Ga,disabled:void 0,touched:yl.touched,setTouched:Ga,dirty:yl.dirty,setDirty:Ga,filled:yl.filled,setFilled:Ga,focused:yl.focused,setFocused:Ga,validate:()=>null,validationMode:`onSubmit`,validationDebounceTime:0,shouldValidateOnChange:()=>!1,state:bl,markedDirtyRef:{current:!1},registerFieldControl:Ga,validation:{getValidationProps:(e=qa)=>e,getInputValidationProps:(e=qa)=>e,inputRef:{current:null},commit:async()=>{}}});function Cl(e=!0){let t=P.useContext(Sl);if(t.setValidityData===Ga&&!e)throw Error(Na(28));return t}var wl=0;function Tl(e,t=`mui`){let[n,r]=P.useState(e),i=e||n;return P.useEffect(()=>{n??(wl+=1,r(`${t}-${wl}`))},[n,t]),i}var El=Ss.useId;function Dl(e,t){if(El!==void 0){let n=El();return e??(t?`${t}-${n}`:n)}return Tl(e,t)}function Ol(e){return Dl(e,`base-ui`)}var kl=P.createContext({controlId:void 0,registerControlId:Ga,labelId:void 0,setLabelId:Ga,messageIds:[],setMessageIds:Ga,getDescriptionProps:e=>e});function Al(){return P.useContext(kl)}function jl(e={}){let{id:t,implicit:n=!1,controlRef:r}=e,{controlId:i,registerControlId:a}=Al(),o=Ol(t),s=n?i:void 0,c=Fa(()=>Symbol(`labelable-control`)),l=P.useRef(!1),u=P.useRef(t!=null),d=ys(()=>{!l.current||a===Ga||(l.current=!1,a(c.current,void 0))});return Cs(()=>{if(a===Ga)return;let e;if(n){let n=r?.current;e=Zo(n)&&n.closest(`label`)!=null?t??null:s??o}else if(t!=null)u.current=!0,e=t;else if(u.current)e=o;else{d();return}if(e===void 0){d();return}l.current=!0,a(c.current,e)},[t,r,s,a,n,o,c,d]),P.useEffect(()=>d,[d]),i??o}function Ml(e){return e?.ownerDocument||document}var Nl=typeof navigator<`u`,Pl=Ul(),Fl=Gl(),Il=Wl(),Ll=typeof CSS>`u`||!CSS.supports?!1:CSS.supports(`-webkit-backdrop-filter:none`),Rl=Pl.platform===`MacIntel`&&Pl.maxTouchPoints>1?!0:/iP(hone|ad|od)|iOS/.test(Pl.platform);Nl&&/firefox/i.test(Il);var zl=Nl&&/apple/i.test(navigator.vendor);Nl&&/Edg/i.test(Il);var Bl=Nl&&/android/i.test(Fl)||/android/i.test(Il),Vl=Nl&&Fl.toLowerCase().startsWith(`mac`)&&!navigator.maxTouchPoints,Hl=Il.includes(`jsdom/`);function Ul(){if(!Nl)return{platform:``,maxTouchPoints:-1};let e=navigator.userAgentData;return e?.platform?{platform:e.platform,maxTouchPoints:navigator.maxTouchPoints}:{platform:navigator.platform??``,maxTouchPoints:navigator.maxTouchPoints??-1}}function Wl(){if(!Nl)return``;let e=navigator.userAgentData;return e&&Array.isArray(e.brands)?e.brands.map(({brand:e,version:t})=>`${e}/${t}`).join(` `):navigator.userAgent}function Gl(){if(!Nl)return``;let e=navigator.userAgentData;return e?.platform?e.platform:navigator.platform??``}var Kl=`data-base-ui-focusable`,ql=`input:not([type='hidden']):not([disabled]),[contenteditable]:not([contenteditable='false']),textarea:not([disabled])`,Jl=`ArrowLeft`,Yl=`ArrowRight`,Xl=`ArrowUp`,Zl=`ArrowDown`;function Ql(e){let t=e.activeElement;for(;t?.shadowRoot?.activeElement!=null;)t=t.shadowRoot.activeElement;return t}function $l(e,t){if(!e||!t)return!1;let n=t.getRootNode?.();if(e.contains(t))return!0;if(n&&$o(n)){let n=t;for(;n;){if(e===n)return!0;n=n.parentNode||n.host}}return!1}function eu(e){return`composedPath`in e?e.composedPath()[0]:e.target}function tu(e,t){if(!Zo(e))return!1;let n=e;if(t.hasElement(n))return!n.hasAttribute(`data-trigger-disabled`);for(let[,e]of t.entries())if($l(e,n))return!e.hasAttribute(`data-trigger-disabled`);return!1}function nu(e,t){if(t==null)return!1;if(`composedPath`in e)return e.composedPath().includes(t);let n=e;return n.target!=null&&t.contains(n.target)}function ru(e){return e.matches(`html,body`)}function iu(e){return Qo(e)&&e.matches(`input:not([type='hidden']):not([disabled]),[contenteditable]:not([contenteditable='false']),textarea:not([disabled])`)}function au(e){return e?.closest(`button,a[href],[role="button"],select,[tabindex]:not([tabindex="-1"]),${ql}`)!=null}function ou(e){return e?e.getAttribute(`role`)===`combobox`&&iu(e):!1}function su(e){if(!e||Hl)return!0;try{return e.matches(`:focus-visible`)}catch{return!0}}function cu(e){return e?e.hasAttribute(`data-base-ui-focusable`)?e:e.querySelector(`[data-base-ui-focusable]`)||e:null}function lu(e,t,n=!0){return e.filter(e=>e.parentId===t).flatMap(t=>[...!n||t.context?.open?[t]:[],...lu(e,t.id,n)])}function uu(e,t){let n=[],r=e.find(e=>e.id===t)?.parentId;for(;r;){let t=e.find(e=>e.id===r);r=t?.parentId,t&&(n=n.concat(t))}return n}function du(e){e.preventDefault(),e.stopPropagation()}function fu(e){return`nativeEvent`in e}function pu(e){return e.pointerType===``&&e.isTrusted?!0:Bl&&e.pointerType?e.type===`click`&&e.buttons===1:e.detail===0&&!e.pointerType}function mu(e){return Hl?!1:!Bl&&e.width===0&&e.height===0||Bl&&e.width===1&&e.height===1&&e.pressure===0&&e.detail===0&&e.pointerType===`mouse`||e.width<1&&e.height<1&&e.pressure===0&&e.detail===0&&e.pointerType===`touch`}function hu(e,t){let n=[`mouse`,`pen`];return t||n.push(``,void 0),n.includes(e)}function gu(e){let t=e.type;return t===`click`||t===`mousedown`||t===`keydown`||t===`keyup`}var _u=[`top`,`right`,`bottom`,`left`],vu=Math.min,yu=Math.max,bu=Math.round,xu=Math.floor,Su=e=>({x:e,y:e}),Cu={left:`right`,right:`left`,bottom:`top`,top:`bottom`};function wu(e,t,n){return yu(e,vu(t,n))}function Tu(e,t){return typeof e==`function`?e(t):e}function Eu(e){return e.split(`-`)[0]}function Du(e){return e.split(`-`)[1]}function Ou(e){return e===`x`?`y`:`x`}function ku(e){return e===`y`?`height`:`width`}function Au(e){let t=e[0];return t===`t`||t===`b`?`y`:`x`}function ju(e){return Ou(Au(e))}function Mu(e,t,n){n===void 0&&(n=!1);let r=Du(e),i=ju(e),a=ku(i),o=i===`x`?r===(n?`end`:`start`)?`right`:`left`:r===`start`?`bottom`:`top`;return t.reference[a]>t.floating[a]&&(o=Vu(o)),[o,Vu(o)]}function Nu(e){let t=Vu(e);return[Pu(e),t,Pu(t)]}function Pu(e){return e.includes(`start`)?e.replace(`start`,`end`):e.replace(`end`,`start`)}var Fu=[`left`,`right`],Iu=[`right`,`left`],Lu=[`top`,`bottom`],Ru=[`bottom`,`top`];function zu(e,t,n){switch(e){case`top`:case`bottom`:return n?t?Iu:Fu:t?Fu:Iu;case`left`:case`right`:return t?Lu:Ru;default:return[]}}function Bu(e,t,n,r){let i=Du(e),a=zu(Eu(e),n===`start`,r);return i&&(a=a.map(e=>e+`-`+i),t&&(a=a.concat(a.map(Pu)))),a}function Vu(e){let t=Eu(e);return Cu[t]+e.slice(t.length)}function Hu(e){return{top:0,right:0,bottom:0,left:0,...e}}function Uu(e){return typeof e==`number`?{top:e,right:e,bottom:e,left:e}:Hu(e)}function Wu(e){let{x:t,y:n,width:r,height:i}=e;return{width:r,height:i,top:n,left:t,right:t+r,bottom:n+i,x:t,y:n}}function Gu(e,t,n){return Math.floor(e/t)!==n}function Ku(e,t){return t<0||t>=e.length}function qu(e,t){return Yu(e.current,{disabledIndices:t})}function Ju(e,t){return Yu(e.current,{decrement:!0,startingIndex:e.current.length,disabledIndices:t})}function Yu(e,{startingIndex:t=-1,decrement:n=!1,disabledIndices:r,amount:i=1}={}){let a=t;do a+=n?-i:i;while(a>=0&&a<=e.length-1&&ed(e,a,r));return a}function Xu(e,{event:t,orientation:n,loopFocus:r,onLoop:i,rtl:a,cols:o,disabledIndices:s,minIndex:c,maxIndex:l,prevIndex:u,stopEvent:d=!1}){let f=u,p;if(t.key===`ArrowUp`?p=`up`:t.key===`ArrowDown`&&(p=`down`),p){let n=[],a=[],m=!1,h=0;{let t=null,r=-1;e.forEach((e,i)=>{if(e==null)return;h+=1;let o=e.closest(`[role="row"]`);o&&(m=!0),(o!==t||r===-1)&&(t=o,r+=1,n[r]=[]),n[r].push(i),a[i]=r})}let g=!1,_=0;if(m)for(let e of n){let t=e.length;t>_&&(_=t),t!==o&&(g=!0)}let v=g&&h{if(!g||u===-1)return;let c=a[u];if(c==null)return;let l=n[c].indexOf(u),d=o===`up`?-1:1;for(let o=c+d,f=0;f=n.length){if(!r||v)return;if(o=o<0?n.length-1:0,i){let e=Math.min(l,n[o].length-1);o=a[i(t,u,n[o][e]??n[o][0])]??o}}let c=n[o];for(let t=Math.min(l,c.length-1);t>=0;--t){let n=c[t];if(!ed(e,n,s))return n}}},x=t=>{if(!v||u===-1)return;let n=u%y,i=t===`up`?-y:y,a=l-l%y,o=xu(l/y)+1;for(let t=u-n+i,c=0;cl){if(!r)return;t=t<0?a:0}let i=Math.min(t+y-1,l);for(let r=Math.min(t+n,i);r>=t;--r)if(!ed(e,r,s))return r}};d&&du(t);let S=b(p)??x(p);if(S!==void 0)f=S;else if(u===-1)f=p===`up`?l:c;else if(f=Yu(e,{startingIndex:u,amount:y,decrement:p===`up`,disabledIndices:s}),r){if(p===`up`&&(u-ye?r:r-y,i&&(f=i(t,u,f))}p===`down`&&u+y>l&&(f=Yu(e,{startingIndex:u%y-y,amount:y,disabledIndices:s}),i&&(f=i(t,u,f)))}Ku(e,f)&&(f=u)}if(n===`both`){let n=xu(u/o);t.key===(a?`ArrowLeft`:`ArrowRight`)&&(d&&du(t),u%o===o-1?r&&(f=Yu(e,{startingIndex:u-u%o-1,disabledIndices:s}),i&&(f=i(t,u,f))):(f=Yu(e,{startingIndex:u,disabledIndices:s}),r&&Gu(f,o,n)&&(f=Yu(e,{startingIndex:u-u%o-1,disabledIndices:s}),i&&(f=i(t,u,f)))),Gu(f,o,n)&&(f=u)),t.key===(a?`ArrowRight`:`ArrowLeft`)&&(d&&du(t),u%o===0?r&&(f=Yu(e,{startingIndex:u+(o-u%o),decrement:!0,disabledIndices:s}),i&&(f=i(t,u,f))):(f=Yu(e,{startingIndex:u,decrement:!0,disabledIndices:s}),r&&Gu(f,o,n)&&(f=Yu(e,{startingIndex:u+(o-u%o),decrement:!0,disabledIndices:s}),i&&(f=i(t,u,f)))),Gu(f,o,n)&&(f=u));let c=xu(l/o)===n;Ku(e,f)&&(r&&c?(f=t.key===(a?`ArrowRight`:`ArrowLeft`)?l:Yu(e,{startingIndex:u-u%o-1,disabledIndices:s}),i&&(f=i(t,u,f))):f=u)}return f}function Zu(e,t,n){let r=[],i=0;return e.forEach(({width:e,height:a},o)=>{let s=!1;for(n&&(i=0);!s;){let n=[];for(let r=0;rr[e]==null)?(n.forEach(e=>{r[e]=o}),s=!0):i+=1}}),[...r]}function Qu(e,t,n,r,i){if(e===-1)return-1;let a=n.indexOf(e),o=t[e];switch(i){case`tl`:return a;case`tr`:return o?a+o.width-1:a;case`bl`:return o?a+(o.height-1)*r:a;case`br`:return n.lastIndexOf(e);default:return-1}}function $u(e,t){return t.flatMap((t,n)=>e.includes(t)?[n]:[])}function ed(e,t,n){if(typeof n==`function`?n(t):n?.includes(t)??!1)return!0;let r=e[t];return r?nd(r)?!n&&(r.hasAttribute(`disabled`)||r.getAttribute(`aria-disabled`)===`true`):!0:!1}function td(e){return e.visibility===`hidden`||e.visibility===`collapse`}function nd(e,t=e?ds(e):null){return!e||!e.isConnected||!t||td(t)?!1:typeof e.checkVisibility==`function`?e.checkVisibility():t.display!==`none`&&t.display!==`contents`}var rd=`a[href],button,input,select,textarea,summary,details,iframe,object,embed,[tabindex],[contenteditable]:not([contenteditable="false"]),audio[controls],video[controls]`;function id(e){let t=e.assignedSlot;if(t)return t;if(e.parentElement)return e.parentElement;let n=e.getRootNode();return $o(n)?n.host:null}function ad(e){for(let t of Array.from(e.children))if(qo(t)===`summary`)return t;return null}function od(e,t){let n=ad(t);return!!n&&(e===n||$l(n,e))}function sd(e){let t=e?qo(e):``;return e!=null&&e.matches(rd)&&(t!==`summary`||e.parentElement!=null&&qo(e.parentElement)===`details`&&ad(e.parentElement)===e)&&(t!==`details`||ad(e)==null)&&(t!==`input`||e.type!==`hidden`)}function cd(e){if(!sd(e)||!e.isConnected||e.matches(`:disabled`))return!1;for(let t=e;t;t=id(t)){let n=t!==e,r=qo(t)===`slot`;if(t.hasAttribute(`inert`)||n&&qo(t)===`details`&&!t.open&&!od(e,t)||t.hasAttribute(`hidden`)||!r&&!ld(t,n))return!1}return!0}function ld(e,t){let n=ds(e);return t?n.display!==`none`:nd(e,n)}function ud(e){let t=e.tabIndex;if(t<0){let t=qo(e);if(t===`details`||t===`audio`||t===`video`||Qo(e)&&e.isContentEditable)return 0}return t}function dd(e){if(qo(e)!==`input`)return null;let t=e;return t.type===`radio`&&t.name!==``?t:null}function fd(e,t){let n=dd(e);if(!n)return!0;let r=t.find(e=>{let t=dd(e);return t?.name===n.name&&t.form===n.form&&t.checked});return r?r===n:t.find(e=>{let t=dd(e);return t?.name===n.name&&t.form===n.form})===n}function pd(e){if(Qo(e)&&qo(e)===`slot`){let t=e.assignedElements({flatten:!0});if(t.length>0)return t}return Qo(e)&&e.shadowRoot?Array.from(e.shadowRoot.children):Array.from(e.children)}function md(e,t){pd(e).forEach(e=>{sd(e)&&t.push(e),md(e,t)})}function hd(e,t,n){pd(e).forEach(e=>{Qo(e)&&e.matches(t)&&n.push(e),hd(e,t,n)})}function gd(e){return cd(e)&&ud(e)>=0}function _d(e){let t=[];return md(e,t),t.filter(cd)}function vd(e){let t=_d(e);return t.filter(e=>ud(e)>=0&&fd(e,t))}function yd(e,t){let n=vd(e),r=n.length;if(r===0)return;let i=Ql(Ml(e)),a=n.indexOf(i);return n[a===-1?t===1?0:r-1:a+t]}function bd(e){return yd(Ml(e).body,1)||e}function xd(e){return yd(Ml(e).body,-1)||e}function Sd(e,t){let n=t||e.currentTarget,r=e.relatedTarget;return!r||!$l(n,r)}function Cd(e){vd(e).forEach(e=>{e.dataset.tabindex=e.getAttribute(`tabindex`)||``,e.setAttribute(`tabindex`,`-1`)})}function wd(e){let t=[];hd(e,`[data-tabindex]`,t),t.forEach(e=>{let t=e.dataset.tabindex;delete e.dataset.tabindex,t?e.setAttribute(`tabindex`,t):e.removeAttribute(`tabindex`)})}var Td=[];function Ed(e){P.useEffect(e,Td)}var Dd=0,Od=class e{static create(){return new e}currentId=Dd;start(e,t){this.clear(),this.currentId=setTimeout(()=>{this.currentId=Dd,t()},e)}isStarted(){return this.currentId!==Dd}clear=()=>{this.currentId!==Dd&&(clearTimeout(this.currentId),this.currentId=Dd)};disposeEffect=()=>this.clear};function kd(){let e=Fa(Od.create).current;return Ed(e.disposeEffect),e}var Ad=null;globalThis.requestAnimationFrame;var jd=new class{callbacks=[];callbacksCount=0;nextId=1;startId=1;isScheduled=!1;tick=e=>{this.isScheduled=!1;let t=this.callbacks,n=this.callbacksCount;if(this.callbacks=[],this.callbacksCount=0,this.startId=this.nextId,n>0)for(let n=0;n=this.callbacks.length||(this.callbacks[t]=null,--this.callbacksCount)}},Md=class e{static create(){return new e}static request(e){return jd.request(e)}static cancel(e){return jd.cancel(e)}currentId=Ad;request(e){this.cancel(),this.currentId=jd.request(()=>{this.currentId=Ad,e()})}cancel=()=>{this.currentId!==Ad&&(jd.cancel(this.currentId),this.currentId=Ad)};disposeEffect=()=>this.cancel};function Nd(){let e=Fa(Md.create).current;return Ed(e.disposeEffect),e}function Pd(e){return e==null?e:`current`in e?e.current:e}var Fd=function(e){return e.startingStyle=`data-starting-style`,e.endingStyle=`data-ending-style`,e}({}),Id={[Fd.startingStyle]:``},Ld={[Fd.endingStyle]:``},Rd={transitionStatus(e){return e===`starting`?Id:e===`ending`?Ld:null}};function zd(e,t=!1,n=!0){let r=Nd();return ys((i,a=null)=>{r.cancel();let o=Pd(e);if(o==null)return;let s=o,c=()=>{hi.flushSync(i)};if(typeof s.getAnimations!=`function`||globalThis.BASE_UI_ANIMATIONS_DISABLED){i();return}function l(){Promise.all(s.getAnimations().map(e=>e.finished)).then(()=>{a?.aborted||c()}).catch(()=>{if(n){a?.aborted||c();return}let e=s.getAnimations();!a?.aborted&&e.length>0&&e.some(e=>e.pending||e.playState!==`finished`)&&l()})}if(t){let e=Fd.startingStyle;if(!s.hasAttribute(e)){r.request(l);return}let t=new MutationObserver(()=>{s.hasAttribute(e)||(t.disconnect(),l())});t.observe(s,{attributes:!0,attributeFilter:[e]}),a?.addEventListener(`abort`,()=>t.disconnect(),{once:!0});return}r.request(l)})}function Bd(e){let{enabled:t=!0,open:n,ref:r,onComplete:i}=e,a=ys(i),o=zd(r,n,!1);P.useEffect(()=>{if(!t)return;let e=new AbortController;return o(a,e.signal),()=>{e.abort()}},[t,n,a,o])}function Vd(e,t=!1,n=!1){let[r,i]=P.useState(e&&t?`idle`:void 0),[a,o]=P.useState(e);return e&&!a&&(o(!0),i(`starting`)),!e&&a&&r!==`ending`&&!n&&i(`ending`),!e&&!a&&r===`ending`&&i(void 0),Cs(()=>{if(!e&&a&&r!==`ending`&&n){let e=Md.request(()=>{i(`ending`)});return()=>{Md.cancel(e)}}},[e,a,r,n]),Cs(()=>{if(!e||t)return;let n=Md.request(()=>{i(void 0)});return()=>{Md.cancel(n)}},[t,e]),Cs(()=>{if(!e||!t)return;e&&a&&r!==`idle`&&i(`starting`);let n=Md.request(()=>{i(`idle`)});return()=>{Md.cancel(n)}},[t,e,a,r]),{mounted:a,setMounted:o,transitionStatus:r}}function Hd({controlled:e,default:t,name:n,state:r=`value`}){let{current:i}=P.useRef(e!==void 0),[a,o]=P.useState(t);return[i?e:a,P.useCallback(e=>{i||o(e)},[])]}function Ud(e,t){let{enabled:n=!0,getValue:r,id:i,value:a}=t,{registerFieldControl:o}=Cl(),s=P.useRef(null);s.current||=Symbol(),Cs(()=>{let t=s.current;if(!(!t||!n))return o(t,{controlRef:e,getValue:r,id:i,value:a}),()=>{o(t,void 0)}},[e,n,r,i,o,a])}var Wd=`none`,Gd=`trigger-press`,Kd=`trigger-hover`,qd=`trigger-focus`,Jd=`outside-press`,Yd=`item-press`,Xd=`close-press`,Zd=`focus-out`,Qd=`escape-key`,$d=`list-navigation`,ef=`cancel-open`,tf=`disabled`,nf=`imperative-action`,rf=`window-resize`;function af(e,t,n,r){let i=!1,a=!1,o=r??qa;return{reason:e,event:t??new Event(`base-ui`),cancel(){i=!0},allowPropagation(){a=!0},get isCanceled(){return i},get isPropagationAllowed(){return a},trigger:n,...o}}var of=P.forwardRef(function(e,t){let{render:n,className:r,id:i,name:a,value:o,disabled:s=!1,onValueChange:c,defaultValue:l,autoFocus:u=!1,style:d,...f}=e,{state:p,name:m,disabled:h,setTouched:g,setDirty:_,validityData:v,setFocused:y,setFilled:b,validationMode:x,validation:S}=Cl(),C=h||s,w=m??a,T={...p,disabled:C},{labelId:E}=Al(),ee=jl({id:i});Cs(()=>{let e=o!=null;S.inputRef.current?.value||e&&o!==``?b(!0):e&&o===``&&b(!1)},[S.inputRef,b,o]);let D=P.useRef(null);Cs(()=>{u&&D.current===Ql(Ml(D.current))&&y(!0)},[u,y]);let[O]=Hd({controlled:o,default:l,name:`FieldControl`,state:`value`}),te=o!==void 0,ne=te?O:void 0,re=ys(()=>S.inputRef.current?.value);return Ud(S.inputRef,{id:ee,value:ne,getValue:re}),Za(`input`,e,{ref:[t,D],state:T,props:[{id:ee,disabled:C,name:w,ref:S.inputRef,"aria-labelledby":E,autoFocus:u,...te?{value:ne}:{defaultValue:l},onChange(e){let t=e.currentTarget.value;c?.(t,af(Wd,e.nativeEvent)),_(t!==v.initialValue),b(t!==``)},onFocus(){y(!0)},onBlur(e){g(!0),y(!1),x===`onBlur`&&S.commit(e.currentTarget.value)},onKeyDown(e){e.currentTarget.tagName===`INPUT`&&e.key===`Enter`&&(g(!0),S.commit(e.currentTarget.value))}},S.getInputValidationProps(),f],stateAttributesMapping:xl})}),sf=P.forwardRef(function(e,t){return(0,F.jsx)(of,{ref:t,...e})});function cf(e){let t=(0,da.c)(10),n,r,i;t[0]===e?(n=t[1],r=t[2],i=t[3]):({className:n,type:i,...r}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i);let a;t[4]===n?a=t[5]:(a=rl(`h-7 w-full min-w-0 rounded-md border border-input bg-input/20 px-2 py-0.5 text-sm transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-xs/relaxed file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-xs/relaxed dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40`,n),t[4]=n,t[5]=a);let o;return t[6]!==r||t[7]!==a||t[8]!==i?(o=(0,F.jsx)(sf,{className:a,"data-slot":`input`,type:i,...r}),t[6]=r,t[7]=a,t[8]=i,t[9]=o):o=t[9],o}var lf=P.forwardRef(function(e,t){let{className:n,render:r,orientation:i=`horizontal`,style:a,...o}=e;return Za(`div`,e,{state:{orientation:i},ref:t,props:[{role:`separator`,"aria-orientation":i},o]})});function uf(e){let t=(0,da.c)(10),n,r,i;t[0]===e?(n=t[1],r=t[2],i=t[3]):({className:n,orientation:i,...r}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i);let a=i===void 0?`horizontal`:i,o;t[4]===n?o=t[5]:(o=rl(`shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch`,n),t[4]=n,t[5]=o);let s;return t[6]!==a||t[7]!==r||t[8]!==o?(s=(0,F.jsx)(lf,{className:o,"data-slot":`separator`,orientation:a,...r}),t[6]=a,t[7]=r,t[8]=o,t[9]=s):s=t[9],s}var df=P.createContext(void 0);function ff(e){let t=P.useContext(df);if(e===!1&&t===void 0)throw Error(Na(27));return t}var pf=function(e){return e.open=`data-open`,e.closed=`data-closed`,e[e.startingStyle=Fd.startingStyle]=`startingStyle`,e[e.endingStyle=Fd.endingStyle]=`endingStyle`,e.anchorHidden=`data-anchor-hidden`,e.side=`data-side`,e.align=`data-align`,e}({}),mf=function(e){return e.popupOpen=`data-popup-open`,e.pressed=`data-pressed`,e}({}),hf={[mf.popupOpen]:``},gf={[mf.popupOpen]:``,[mf.pressed]:``},_f={[pf.open]:``},vf={[pf.closed]:``},yf={[pf.anchorHidden]:``},bf={open(e){return e?hf:null}},xf={open(e){return e?gf:null}},Sf={open(e){return e?_f:vf},anchorHidden(e){return e?yf:null}},Cf={...Sf,...Rd},wf=P.forwardRef(function(e,t){let{render:n,className:r,style:i,forceRender:a=!1,...o}=e,{store:s}=ff(),c=s.useState(`open`),l=s.useState(`nested`),u=s.useState(`mounted`);return Za(`div`,e,{state:{open:c,transitionStatus:s.useState(`transitionStatus`)},ref:[s.context.backdropRef,t],stateAttributesMapping:Cf,props:[{role:`presentation`,hidden:!u,style:{userSelect:`none`,WebkitUserSelect:`none`}},o],enabled:a||!l})}),Tf=P.forwardRef(function(e,t){let{render:n,className:r,disabled:i=!1,nativeButton:a=!0,style:o,...s}=e,{store:c}=ff(),l=c.useState(`open`);function u(e){l&&c.setOpen(!1,af(Xd,e.nativeEvent))}let{getButtonProps:d,buttonRef:f}=Ds({disabled:i,native:a});return Za(`button`,e,{state:{disabled:i},ref:[t,f],props:[{onClick:u},s,d]})}),Ef=P.forwardRef(function(e,t){let{render:n,className:r,style:i,id:a,...o}=e,{store:s}=ff(),c=Ol(a);return s.useSyncedValueWithCleanup(`descriptionElementId`,c),Za(`p`,e,{ref:t,props:[{id:c},o]})});function Df(e,t){return t!=null&&!hu(t)?0:typeof e==`function`?e():e}function Of(e,t,n){let r=Df(e,n);return typeof r==`number`?r:r?.[t]}function kf(e){return typeof e==`function`?e():e}function Af(e,t){return t||e===`click`||e===`mousedown`}var jf=P.createContext({hasProvider:!1,timeoutMs:0,delayRef:{current:0},initialDelayRef:{current:0},timeout:new Od,currentIdRef:{current:null},currentContextRef:{current:null}});function Mf(e){let{children:t,delay:n,timeoutMs:r=0}=e,i=P.useRef(n),a=P.useRef(n),o=P.useRef(null),s=P.useRef(null),c=kd();return(0,F.jsx)(jf.Provider,{value:P.useMemo(()=>({hasProvider:!0,delayRef:i,initialDelayRef:a,currentIdRef:o,timeoutMs:r,currentContextRef:s,timeout:c}),[r,c]),children:t})}function Nf(e,t={open:!1}){let n=`rootStore`in e?e.rootStore:e,r=n.useState(`floatingId`),{open:i}=t,{currentIdRef:a,delayRef:o,timeoutMs:s,initialDelayRef:c,currentContextRef:l,hasProvider:u,timeout:d}=P.useContext(jf),[f,p]=P.useState(!1);return Cs(()=>{function e(){p(!1),l.current?.setIsInstantPhase(!1),a.current=null,l.current=null,o.current=c.current}if(a.current&&!i&&a.current===r){if(p(!1),s){let t=r;return d.start(s,()=>{n.select(`open`)||a.current&&a.current!==t||e()}),()=>{d.clear()}}e()}},[i,r,a,o,s,c,l,d,n]),Cs(()=>{if(!i)return;let e=l.current,t=a.current;d.clear(),l.current={onOpenChange:n.setOpen,setIsInstantPhase:p},a.current=r,o.current={open:0,close:Of(c.current,`close`)},t!==null&&t!==r?(p(!0),e?.setIsInstantPhase(!0),e?.onOpenChange(!1,af(Wd))):(p(!1),e?.setIsInstantPhase(!1))},[i,r,n,a,o,s,c,l,d]),Cs(()=>()=>{l.current=null},[l]),P.useMemo(()=>({hasProvider:u,delayRef:o,isInstantPhase:f}),[u,o,f])}function Pf(e,t,n,r){return e.addEventListener(t,n,r),()=>{e.removeEventListener(t,n,r)}}function Ff(...e){return()=>{for(let t=0;t{t.current=t.next}};return t}var Rf={clipPath:`inset(50%)`,overflow:`hidden`,whiteSpace:`nowrap`,border:0,padding:0,width:1,height:1,margin:-1},zf={...Rf,position:`fixed`,top:0,left:0},Bf={...Rf,position:`absolute`},Vf=P.forwardRef(function(e,t){let[n,r]=P.useState();Cs(()=>{zl&&r(`button`)},[]);let i={tabIndex:0,role:n};return(0,F.jsx)(`span`,{...e,ref:t,style:zf,"aria-hidden":n?void 0:!0,...i,"data-base-ui-focus-guard":``})});function Hf(e){return`data-base-ui-${e}`}var Uf=0;function Wf(e,t={}){let{preventScroll:n=!1,cancelPrevious:r=!0,sync:i=!1}=t;r&&cancelAnimationFrame(Uf);let a=()=>e?.focus({preventScroll:n});if(i)return a(),Ga;let o=requestAnimationFrame(a);return Uf=o,()=>{Uf===o&&(cancelAnimationFrame(o),Uf=0)}}var Gf={inert:new WeakMap,"aria-hidden":new WeakMap},Kf=`data-base-ui-inert`,qf={inert:new WeakSet,"aria-hidden":new WeakSet},Jf=new WeakMap,Yf=0;function Xf(e){return qf[e]}function Zf(e){return e?$o(e)?e.host:Zf(e.parentNode):null}var Qf=(e,t)=>t.map(t=>{if(e.contains(t))return t;let n=Zf(t);return e.contains(n)?n:null}).filter(e=>e!=null),$f=e=>{let t=new Set;return e.forEach(e=>{let n=e;for(;n&&!t.has(n);)t.add(n),n=n.parentNode}),t},ep=(e,t,n)=>{let r=[],i=e=>{!e||n.has(e)||Array.from(e.children).forEach(e=>{qo(e)!==`script`&&(t.has(e)?i(e):r.push(e))})};return i(e),r};function tp(e,t,n,r,{mark:i=!0,markerIgnoreElements:a=[]}){let o=r?`inert`:n?`aria-hidden`:null,s=null,c=null,l=Qf(t,e),u=i?Qf(t,a):[],d=new Set(u),f=i?ep(t,$f(l),new Set(l)).filter(e=>!d.has(e)):[],p=[],m=[];if(o){let e=Gf[o],n=Xf(o);c=n,s=e;let r=Qf(t,Array.from(t.querySelectorAll(`[aria-live]`))),i=l.concat(r);ep(t,$f(i),new Set(i)).forEach(t=>{let r=t.getAttribute(o),i=r!==null&&r!==`false`,a=(e.get(t)||0)+1;e.set(t,a),p.push(t),a===1&&i&&n.add(t),i||t.setAttribute(o,o===`inert`?``:`true`)})}return i&&f.forEach(e=>{let t=(Jf.get(e)||0)+1;Jf.set(e,t),m.push(e),t===1&&e.setAttribute(Kf,``)}),Yf+=1,()=>{s&&p.forEach(e=>{let t=(s.get(e)||0)-1;s.set(e,t),t||(!c?.has(e)&&o&&e.removeAttribute(o),c?.delete(e))}),i&&m.forEach(e=>{let t=(Jf.get(e)||0)-1;Jf.set(e,t),t||e.removeAttribute(Kf)}),--Yf,Yf||(Gf.inert=new WeakMap,Gf[`aria-hidden`]=new WeakMap,qf.inert=new WeakSet,qf[`aria-hidden`]=new WeakSet,Jf=new WeakMap)}}function np(e,t={}){let{ariaHidden:n=!1,inert:r=!1,mark:i=!0,markerIgnoreElements:a=[]}=t,o=Ml(e[0]).body;return tp(e,o,n,r,{mark:i,markerIgnoreElements:a})}var rp={style:{transition:`none`}},ip={fallbackAxisSide:`none`},ap={fallbackAxisSide:`end`},op={clipPath:`inset(50%)`,position:`fixed`,top:0,left:0},sp=P.createContext(null),cp=()=>P.useContext(sp),lp=Hf(`portal`);function up(e={}){let{ref:t,container:n,componentProps:r=qa,elementProps:i}=e,a=Dl(),o=cp()?.portalNode,[s,c]=P.useState(null),[l,u]=P.useState(null),d=ys(e=>{e!==null&&u(e)}),f=P.useRef(null);Cs(()=>{if(n===null){f.current&&(f.current=null,u(null),c(null));return}if(a==null)return;let e=(n&&(Xo(n)?n:n.current))??o??document.body;if(e==null){f.current&&(f.current=null,u(null),c(null));return}f.current!==e&&(f.current=e,u(null),c(e))},[n,o,a]);let p=Za(`div`,r,{ref:[t,d],props:[{id:a,[lp]:``},i]});return{portalNode:l,portalSubtree:s&&p?hi.createPortal(p,s):null}}var dp=P.forwardRef(function(e,t){let{children:n,container:r,className:i,render:a,renderGuards:o,style:s,...c}=e,{portalNode:l,portalSubtree:u}=up({container:r,ref:t,componentProps:e,elementProps:c}),d=P.useRef(null),f=P.useRef(null),p=P.useRef(null),m=P.useRef(null),[h,g]=P.useState(null),_=P.useRef(!1),v=h?.modal,y=h?.open,b=typeof o==`boolean`?o:!!h&&!h.modal&&h.open&&!!l;P.useEffect(()=>{if(!l||v)return;function e(e){l&&e.relatedTarget&&Sd(e)&&(e.type===`focusin`?_.current&&=(wd(l),!1):(Cd(l),_.current=!0))}return Ff(Pf(l,`focusin`,e,!0),Pf(l,`focusout`,e,!0))},[l,v]),P.useEffect(()=>{!l||y!==!1||(wd(l),_.current=!1)},[y,l]);let x=P.useMemo(()=>({beforeOutsideRef:d,afterOutsideRef:f,beforeInsideRef:p,afterInsideRef:m,portalNode:l,setFocusManagerState:g}),[l]);return(0,F.jsxs)(P.Fragment,{children:[u,(0,F.jsxs)(sp.Provider,{value:x,children:[b&&l&&(0,F.jsx)(Vf,{"data-type":`outside`,ref:d,onFocus:e=>{Sd(e,l)?p.current?.focus():xd(h?h.domReference:null)?.focus()}}),b&&l&&(0,F.jsx)(`span`,{"aria-owns":l.id,style:op}),l&&hi.createPortal(n,l),b&&l&&(0,F.jsx)(Vf,{"data-type":`outside`,ref:f,onFocus:e=>{Sd(e,l)?m.current?.focus():(bd(h?h.domReference:null)?.focus(),h?.closeOnFocusOut&&h?.onOpenChange(!1,af(`focus-out`,e.nativeEvent)))}})]})]})});function fp(){let e=new Map;return{emit(t,n){e.get(t)?.forEach(e=>e(n))},on(t,n){e.has(t)||e.set(t,new Set),e.get(t).add(n)},off(t,n){e.get(t)?.delete(n)}}}var pp=P.createContext(null),mp=P.createContext(null),hp=()=>P.useContext(pp)?.id||null,gp=e=>{let t=P.useContext(mp);return e??t};function _p(e,t){let n=Jo(eu(e));return e instanceof n.KeyboardEvent?`keyboard`:e instanceof n.FocusEvent?t||`keyboard`:`pointerType`in e?e.pointerType||`keyboard`:`touches`in e?`touch`:e instanceof n.MouseEvent?t||(e.detail===0?`keyboard`:`mouse`):``}var vp=20,yp=[];function bp(){yp=yp.filter(e=>e.deref()?.isConnected)}function xp(e){bp(),e&&qo(e)!==`body`&&(yp.push(new WeakRef(e)),yp.length>vp&&(yp=yp.slice(-vp)))}function Sp(){return bp(),yp[yp.length-1]?.deref()}function Cp(e){return e?gd(e)?e:vd(e)[0]||e:null}function wp(e,t){if(e.hasAttribute(`tabindex`)&&!e.hasAttribute(`data-tabindex`)||!t.current.includes(`floating`)&&!e.getAttribute(`role`)?.includes(`dialog`))return;let n=_d(e).filter(e=>{let t=e.getAttribute(`data-tabindex`)||``;return gd(e)||e.hasAttribute(`data-tabindex`)&&!t.startsWith(`-`)}),r=e.getAttribute(`tabindex`);t.current.includes(`floating`)||n.length===0?r!==`0`&&e.setAttribute(`tabindex`,`0`):(r!==`-1`||e.hasAttribute(`data-tabindex`)&&e.getAttribute(`data-tabindex`)!==`-1`)&&(e.setAttribute(`tabindex`,`-1`),e.setAttribute(`data-tabindex`,`-1`))}function Tp(e){let{context:t,children:n,disabled:r=!1,initialFocus:i=!0,returnFocus:a=!0,restoreFocus:o=!1,modal:s=!0,closeOnFocusOut:c=!0,openInteractionType:l=``,nextFocusableElement:u,previousFocusableElement:d,beforeContentFocusGuardRef:f,externalTree:p,getInsideElements:m}=e,h=`rootStore`in t?t.rootStore:t,g=h.useState(`open`),_=h.useState(`domReferenceElement`),v=h.useState(`floatingElement`),{events:y,dataRef:b}=h.context,x=ys(()=>b.current.floatingContext?.nodeId),S=i===!1,C=ou(_)&&S,w=P.useRef([`content`]),T=If(i),E=If(a),ee=If(l),D=gp(p),O=cp(),te=P.useRef(!1),ne=P.useRef(!1),re=P.useRef(!1),k=P.useRef(null),A=P.useRef(``),ie=P.useRef(``),j=P.useRef(null),ae=P.useRef(null),oe=Ia(j,f,O?.beforeInsideRef),se=Ia(ae,O?.afterInsideRef),ce=kd(),M=kd(),le=Nd(),ue=O!=null,N=cu(v),de=ys((e=N)=>e?vd(e):[]),fe=ys(()=>m?.().filter(e=>e!=null)??[]);P.useEffect(()=>{if(r||!s)return;function e(e){e.key===`Tab`&&$l(N,Ql(Ml(N)))&&de().length===0&&!C&&du(e)}return Pf(Ml(N),`keydown`,e)},[r,_,N,s,w,C,de]),P.useEffect(()=>{if(r||!g)return;let e=Ml(N);function t(){re.current=!1}function n(e){let t=eu(e),n=fe();re.current=!($l(v,t)||$l(_,t)||$l(O?.portalNode,t)||n.some(e=>e===t||$l(e,t))),ie.current=e.pointerType||`keyboard`,t?.closest(`[data-base-ui-click-trigger]`)&&(ne.current=!0)}function i(){ie.current=`keyboard`}return Ff(Pf(e,`pointerdown`,n,!0),Pf(e,`pointerup`,t,!0),Pf(e,`pointercancel`,t,!0),Pf(e,`keydown`,i,!0))},[r,v,_,N,g,O,fe]),P.useEffect(()=>{if(r||!c)return;let e=Ml(N);function t(){ne.current=!0,M.start(0,()=>{ne.current=!1})}function n(e){let t=eu(e);gd(t)&&(k.current=t)}function i(t){let n=t.relatedTarget,r=t.currentTarget,i=eu(t);queueMicrotask(()=>{let a=x(),c=h.context.triggerElements,l=fe(),f=n?.hasAttribute(Hf(`focus-guard`))&&[j.current,ae.current,O?.beforeInsideRef.current,O?.afterInsideRef.current,O?.beforeOutsideRef.current,O?.afterOutsideRef.current,Pd(d),Pd(u)].includes(n),p=!($l(_,n)||$l(v,n)||$l(n,v)||$l(O?.portalNode,n)||l.some(e=>e===n||$l(e,n))||n!=null&&c.hasElement(n)||c.hasMatchingElement(e=>$l(e,n))||f||D&&(lu(D.nodesRef.current,a).find(e=>$l(e.context?.elements.floating,n)||$l(e.context?.elements.domReference,n))||uu(D.nodesRef.current,a).find(e=>[e.context?.elements.floating,cu(e.context?.elements.floating)].includes(n)||e.context?.elements.domReference===n)));if(r===_&&N&&wp(N,w),o&&r!==_&&!nd(i)&&Ql(e)===e.body){if(Qo(N)&&(N.focus(),o===`popup`)){le.request(()=>{N.focus()});return}let e=de(),t=k.current,n=(t&&e.includes(t)?t:null)||e[e.length-1]||N;Qo(n)&&n.focus()}if(b.current.insideReactTree){b.current.insideReactTree=!1;return}(C||!s)&&n&&p&&!ne.current&&(C||n!==Sp())&&(te.current=!0,h.setOpen(!1,af(Zd,t)))})}function a(){re.current||(b.current.insideReactTree=!0,ce.start(0,()=>{b.current.insideReactTree=!1}))}let l=Qo(_)?_:null;if(!(!v&&!l))return Ff(l&&Pf(l,`focusout`,i),l&&Pf(l,`pointerdown`,t),v&&Pf(v,`focusin`,n),v&&Pf(v,`focusout`,i),v&&O&&Pf(v,`focusout`,a,!0))},[r,_,v,N,s,D,O,h,c,o,de,C,x,w,b,ce,M,le,u,d,fe]),P.useEffect(()=>{if(r||!v||!g)return;let e=Array.from(O?.portalNode?.querySelectorAll(`[${Hf(`portal`)}]`)||[]),t=(D?uu(D.nodesRef.current,x()):[]).find(e=>ou(e.context?.elements.domReference||null))?.context?.elements.domReference,n=np([v,...e,j.current,ae.current,O?.beforeOutsideRef.current,O?.afterOutsideRef.current,...fe(),t,Pd(d),Pd(u),C?_:null].filter(e=>e!=null),{ariaHidden:s||C,mark:!1}),i=np([v,...e].filter(e=>e!=null));return()=>{i(),n()}},[g,r,_,v,s,w,O,C,D,x,u,d,fe]),Cs(()=>{if(!g||r||!Qo(N))return;let e=Ql(Ml(N));queueMicrotask(()=>{let t=T.current,n=typeof t==`function`?t(ee.current||``):t;if(n===void 0||n===!1||$l(N,e))return;let r=null,i=()=>(r??=de(N),r[0]||N),a;a=n===!0||n===null?i():Pd(n),a||=i(),Wf(a,{preventScroll:a===N})})},[r,g,N,S,de,T,ee]),Cs(()=>{if(r||!N)return;let e=Ml(N);xp(Ql(e));function t(e){if(e.open||(A.current=_p(e.nativeEvent,ie.current)),e.reason===`trigger-hover`&&e.nativeEvent.type===`mouseleave`&&(te.current=!0),e.reason===`outside-press`)if(e.nested)te.current=!1;else if(pu(e.nativeEvent)||mu(e.nativeEvent))te.current=!1;else{let e=!1;Ml(N).createElement(`div`).focus({get preventScroll(){return e=!0,!1}}),e?te.current=!1:te.current=!0}}y.on(`openchange`,t);function n(){let e=E.current,t=typeof e==`function`?e(A.current):e;if(t===void 0||t===!1)return null;if(t===null&&(t=!0),typeof t==`boolean`){let e=_||Sp();return e&&e.isConnected?e:null}let n=_||Sp();return Pd(t)||n||null}return()=>{y.off(`openchange`,t);let r=Ql(e),i=fe(),a=$l(v,r)||i.some(e=>e===r||$l(e,r))||D&&lu(D.nodesRef.current,x(),!1).some(e=>$l(e.context?.elements.floating,r)),o=E.current,s=n();queueMicrotask(()=>{let t=Cp(s),n=typeof o!=`boolean`;o&&!te.current&&Qo(t)&&(!(!n&&t!==r&&r!==e.body)||a)&&t.focus({preventScroll:!0}),te.current=!1})}},[r,v,N,E,b,y,D,_,x,fe]),Cs(()=>{if(!Ll||g||!v)return;let e=Ql(Ml(v));!Qo(e)||!iu(e)||$l(v,e)&&e.blur()},[g,v]),Cs(()=>{if(!(r||!O))return O.setFocusManagerState({modal:s,closeOnFocusOut:c,open:g,onOpenChange:h.setOpen,domReference:_}),()=>{O.setFocusManagerState(null)}},[r,O,s,g,h,c,_]),Cs(()=>{if(!(r||!N))return wp(N,w),()=>{queueMicrotask(bp)}},[r,N,w]);let pe=!r&&(s?!C:!0)&&(ue||s);return(0,F.jsxs)(P.Fragment,{children:[pe&&(0,F.jsx)(Vf,{"data-type":`inside`,ref:oe,onFocus:e=>{if(s){let e=de();Wf(e[e.length-1])}else O?.portalNode&&(te.current=!1,Sd(e,O.portalNode)?bd(_)?.focus():Pd(d??O.beforeOutsideRef)?.focus())}}),n,pe&&(0,F.jsx)(Vf,{"data-type":`inside`,ref:se,onFocus:e=>{s?Wf(de()[0]):O?.portalNode&&(c&&(te.current=!0),Sd(e,O.portalNode)?xd(_)?.focus():Pd(u??O.afterOutsideRef)?.focus())}})]})}function Ep(e,t){let n=null,r=null,i=!1;return{contextElement:e||void 0,getBoundingClientRect(){let a=e?.getBoundingClientRect()||{width:0,height:0,x:0,y:0},o=t.axis===`x`||t.axis===`both`,s=t.axis===`y`||t.axis===`both`,c=[`mouseenter`,`mousemove`].includes(t.dataRef.current.openEvent?.type||``)&&t.pointerType!==`touch`,l=a.width,u=a.height,d=a.x,f=a.y;return n==null&&t.x&&o&&(n=a.x-t.x),r==null&&t.y&&s&&(r=a.y-t.y),d-=n||0,f-=r||0,l=0,u=0,!i||c?(l=t.axis===`y`?a.width:0,u=t.axis===`x`?a.height:0,d=o&&t.x!=null?t.x:d,f=s&&t.y!=null?t.y:f):i&&!c&&(u=t.axis===`x`?a.height:u,l=t.axis===`y`?a.width:l),i=!0,{width:l,height:u,x:d,y:f,top:f,right:d+l,bottom:f+u,left:d}}}}function Dp(e){return e!=null&&e.clientX!=null}function Op(e,t={}){let n=`rootStore`in e?e.rootStore:e,r=n.useState(`open`),i=n.useState(`floatingElement`),a=n.useState(`domReferenceElement`),o=n.context.dataRef,{enabled:s=!0,axis:c=`both`}=t,l=P.useRef(!1),u=P.useRef(null),[d,f]=P.useState(),[p,m]=P.useState([]),h=ys((e,t,r)=>{l.current||o.current.openEvent&&!Dp(o.current.openEvent)||n.set(`positionReference`,Ep(r??a,{x:e,y:t,axis:c,dataRef:o,pointerType:d}))}),g=ys(e=>{r?u.current||m([]):h(e.clientX,e.clientY,e.currentTarget)}),_=hu(d)?i:r,v=P.useCallback(()=>{if(!_||!s)return;let e=Jo(i);function t(e){$l(i,eu(e))?(u.current?.(),u.current=null):h(e.clientX,e.clientY)}if(!o.current.openEvent||Dp(o.current.openEvent))return u.current=Pf(e,`mousemove`,t),()=>{u.current?.(),u.current=null};n.set(`positionReference`,a)},[_,s,i,o,a,n,h]);P.useEffect(()=>v(),[v,p]),P.useEffect(()=>{s&&!i&&(l.current=!1)},[s,i]),P.useEffect(()=>{!s&&r&&(l.current=!0)},[s,r]);let y=P.useMemo(()=>{function e(e){f(e.pointerType)}return{onPointerDown:e,onPointerEnter:e,onMouseMove:g,onMouseEnter:g}},[g]);return P.useMemo(()=>s?{reference:y,trigger:y}:{},[s,y])}var kp={intentional:`onClick`,sloppy:`onPointerDown`};function Ap(){return!1}function jp(e){return{escapeKey:typeof e==`boolean`?e:e?.escapeKey??!1,outsidePress:typeof e==`boolean`?e:e?.outsidePress??!0}}function Mp(e,t={}){let n=`rootStore`in e?e.rootStore:e,r=n.useState(`open`),i=n.useState(`floatingElement`),{dataRef:a}=n.context,{enabled:o=!0,escapeKey:s=!0,outsidePress:c=!0,outsidePressEvent:l=`sloppy`,referencePress:u=Ap,referencePressEvent:d=`sloppy`,bubbles:f,externalTree:p}=t,m=gp(p),h=ys(typeof c==`function`?c:()=>!1),g=typeof c==`function`?h:c,_=g!==!1,v=ys(()=>l),y=P.useRef(!1),b=P.useRef(!1),x=P.useRef(!1),{escapeKey:S,outsidePress:C}=jp(f),w=P.useRef(null),T=kd(),E=kd(),ee=ys(()=>{E.clear(),a.current.insideReactTree=!1}),D=P.useRef(!1),O=P.useRef(``),te=ys(u),ne=ys(e=>{if(!r||!o||!s||e.key!==`Escape`||D.current)return;let t=a.current.floatingContext?.nodeId,i=m?lu(m.nodesRef.current,t):[];if(!S&&i.length>0){let e=!0;if(i.forEach(t=>{t.context?.open&&!t.context.dataRef.current.__escapeKeyBubbles&&(e=!1)}),!e)return}let c=af(Qd,fu(e)?e.nativeEvent:e);n.setOpen(!1,c),!S&&!c.isPropagationAllowed&&e.stopPropagation()}),re=ys(()=>{a.current.insideReactTree=!0,E.start(0,ee)});P.useEffect(()=>{if(!r||!o)return;a.current.__escapeKeyBubbles=S,a.current.__outsidePressBubbles=C;let e=new Od,t=new Od;function c(){e.clear(),D.current=!0}function l(){e.start(ls()?5:0,()=>{D.current=!1})}function u(){x.current=!0,t.start(0,()=>{x.current=!1})}function d(){y.current=!1,b.current=!1}function f(){let e=O.current,t=e===`pen`||!e?`mouse`:e,n=v(),r=typeof n==`function`?n():n;return typeof r==`string`?r:r[t]}function p(e){let t=f();return t===`intentional`&&e.type!==`click`||t===`sloppy`&&e.type===`click`}function h(e){let t=a.current.floatingContext?.nodeId,r=m&&lu(m.nodesRef.current,t).some(t=>nu(e,t.context?.elements.floating));return nu(e,n.select(`floatingElement`))||nu(e,n.select(`domReferenceElement`))||r}function E(e){if(p(e)){ee();return}if(a.current.insideReactTree){ee();return}let r=eu(e),i=`[${Hf(`inert`)}]`,o=Zo(r)?r.getRootNode():null,s=Array.from(($o(o)?o:Ml(n.select(`floatingElement`))).querySelectorAll(i)),c=n.context.triggerElements;if(r&&(c.hasElement(r)||c.hasMatchingElement(e=>$l(e,r))))return;let l=Zo(r)?r:null;for(;l&&!us(l);){let e=ps(l);if(us(e)||!Zo(e))break;l=e}if(s.length&&Zo(r)&&!ru(r)&&!$l(r,n.select(`floatingElement`))&&s.every(e=>!$l(l,e)))return;if(Qo(r)&&!(`touches`in e)){let t=us(r),n=ds(r),i=/auto|scroll/,a=t||i.test(n.overflowX),o=t||i.test(n.overflowY),s=a&&r.clientWidth>0&&r.scrollWidth>r.clientWidth,c=o&&r.clientHeight>0&&r.scrollHeight>r.clientHeight,l=n.direction===`rtl`,u=c&&(l?e.offsetX<=r.offsetWidth-r.clientWidth:e.offsetX>r.clientWidth),d=s&&e.offsetY>r.clientHeight;if(u||d)return}if(h(e))return;if(f()===`intentional`&&x.current){t.clear(),x.current=!1;return}if(typeof g==`function`&&!g(e))return;let u=a.current.floatingContext?.nodeId,d=m?lu(m.nodesRef.current,u):[];if(d.length>0){let e=!0;if(d.forEach(t=>{t.context?.open&&!t.context.dataRef.current.__outsidePressBubbles&&(e=!1)}),!e)return}n.setOpen(!1,af(Jd,e)),ee()}function te(e){f()!==`sloppy`||e.pointerType===`touch`||!n.select(`open`)||!o||nu(e,n.select(`floatingElement`))||nu(e,n.select(`domReferenceElement`))||E(e)}function re(e){if(f()!==`sloppy`||!n.select(`open`)||!o||nu(e,n.select(`floatingElement`))||nu(e,n.select(`domReferenceElement`)))return;let t=e.touches[0];t&&(w.current={startTime:Date.now(),startX:t.clientX,startY:t.clientY,dismissOnTouchEnd:!1,dismissOnMouseDown:!0},T.start(1e3,()=>{w.current&&(w.current.dismissOnTouchEnd=!1,w.current.dismissOnMouseDown=!1)}))}function k(e,t){let n=eu(e);if(!n)return;let r=Pf(n,e.type,()=>{t(e),r()})}function A(e){O.current=`touch`,k(e,re)}function ie(e){T.clear(),e.type===`pointerdown`&&(O.current=e.pointerType),!(e.type===`mousedown`&&w.current&&!w.current.dismissOnMouseDown)&&k(e,e=>{e.type===`pointerdown`?te(e):E(e)})}function j(e){if(!y.current)return;let n=b.current;if(d(),f()===`intentional`){if(e.type===`pointercancel`){n&&u();return}if(!h(e)){if(n){u();return}typeof g==`function`&&!g(e)||(t.clear(),x.current=!0,ee())}}}function ae(e){if(f()!==`sloppy`||!w.current||nu(e,n.select(`floatingElement`))||nu(e,n.select(`domReferenceElement`)))return;let t=e.touches[0];if(!t)return;let r=Math.abs(t.clientX-w.current.startX),i=Math.abs(t.clientY-w.current.startY),a=Math.sqrt(r*r+i*i);a>5&&(w.current.dismissOnTouchEnd=!0),a>10&&(E(e),T.clear(),w.current=null)}function oe(e){k(e,ae)}function se(e){f()!==`sloppy`||!w.current||nu(e,n.select(`floatingElement`))||nu(e,n.select(`domReferenceElement`))||(w.current.dismissOnTouchEnd&&E(e),T.clear(),w.current=null)}function ce(e){k(e,se)}let M=Ml(i),le=Ff(s&&Ff(Pf(M,`keydown`,ne),Pf(M,`compositionstart`,c),Pf(M,`compositionend`,l)),_&&Ff(Pf(M,`click`,ie,!0),Pf(M,`pointerdown`,ie,!0),Pf(M,`pointerup`,j,!0),Pf(M,`pointercancel`,j,!0),Pf(M,`mousedown`,ie,!0),Pf(M,`mouseup`,j,!0),Pf(M,`touchstart`,A,!0),Pf(M,`touchmove`,oe,!0),Pf(M,`touchend`,ce,!0)));return()=>{le(),e.clear(),t.clear(),d(),x.current=!1}},[a,i,s,_,g,r,o,S,C,ne,ee,v,m,n,T]),P.useEffect(ee,[g,ee]);let k=P.useMemo(()=>({onKeyDown:ne,[kp[d]]:e=>{te()&&n.setOpen(!1,af(Gd,e.nativeEvent))},...d!==`intentional`&&{onClick(e){te()&&n.setOpen(!1,af(`trigger-press`,e.nativeEvent))}}}),[ne,n,d,te]),A=ys(e=>{if(!r||!o||e.button!==0)return;let t=eu(e.nativeEvent);$l(n.select(`floatingElement`),t)&&(y.current||(y.current=!0,b.current=!1))}),ie=ys(e=>{!r||!o||(e.defaultPrevented||e.nativeEvent.defaultPrevented)&&y.current&&(b.current=!0)}),j=P.useMemo(()=>({onKeyDown:ne,onPointerDown:ie,onMouseDown:ie,onClickCapture:re,onMouseDownCapture(e){re(),A(e)},onPointerDownCapture(e){re(),A(e)},onMouseUpCapture:re,onTouchEndCapture:re,onTouchMoveCapture:re}),[ne,re,A,ie]);return P.useMemo(()=>o?{reference:k,floating:j,trigger:k}:{},[o,k,j])}function Np(e,t,n){let{reference:r,floating:i}=e,a=Au(t),o=ju(t),s=ku(o),c=Eu(t),l=a===`y`,u=r.x+r.width/2-i.width/2,d=r.y+r.height/2-i.height/2,f=r[s]/2-i[s]/2,p;switch(c){case`top`:p={x:u,y:r.y-i.height};break;case`bottom`:p={x:u,y:r.y+r.height};break;case`right`:p={x:r.x+r.width,y:d};break;case`left`:p={x:r.x-i.width,y:d};break;default:p={x:r.x,y:r.y}}switch(Du(t)){case`start`:p[o]-=f*(n&&l?-1:1);break;case`end`:p[o]+=f*(n&&l?-1:1);break}return p}async function Pp(e,t){t===void 0&&(t={});let{x:n,y:r,platform:i,rects:a,elements:o,strategy:s}=e,{boundary:c=`clippingAncestors`,rootBoundary:l=`viewport`,elementContext:u=`floating`,altBoundary:d=!1,padding:f=0}=Tu(t,e),p=Uu(f),m=o[d?u===`floating`?`reference`:`floating`:u],h=Wu(await i.getClippingRect({element:await(i.isElement==null?void 0:i.isElement(m))??!0?m:m.contextElement||await(i.getDocumentElement==null?void 0:i.getDocumentElement(o.floating)),boundary:c,rootBoundary:l,strategy:s})),g=u===`floating`?{x:n,y:r,width:a.floating.width,height:a.floating.height}:a.reference,_=await(i.getOffsetParent==null?void 0:i.getOffsetParent(o.floating)),v=await(i.isElement==null?void 0:i.isElement(_))&&await(i.getScale==null?void 0:i.getScale(_))||{x:1,y:1},y=Wu(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:o,rect:g,offsetParent:_,strategy:s}):g);return{top:(h.top-y.top+p.top)/v.y,bottom:(y.bottom-h.bottom+p.bottom)/v.y,left:(h.left-y.left+p.left)/v.x,right:(y.right-h.right+p.right)/v.x}}var Fp=50,Ip=async(e,t,n)=>{let{placement:r=`bottom`,strategy:i=`absolute`,middleware:a=[],platform:o}=n,s=o.detectOverflow?o:{...o,detectOverflow:Pp},c=await(o.isRTL==null?void 0:o.isRTL(t)),l=await o.getElementRects({reference:e,floating:t,strategy:i}),{x:u,y:d}=Np(l,r,c),f=r,p=0,m={};for(let n=0;ne<=0)){let e=(i.flip?.index||0)+1,t=S[e];if(t&&(!(u===`alignment`&&_!==Au(t))||T.every(e=>Au(e.placement)===_?e.overflows[0]>0:!0)))return{data:{index:e,overflows:T},reset:{placement:t}};let n=T.filter(e=>e.overflows[0]<=0).sort((e,t)=>e.overflows[1]-t.overflows[1])[0]?.placement;if(!n)switch(f){case`bestFit`:{let e=T.filter(e=>{if(x){let t=Au(e.placement);return t===_||t===`y`}return!0}).map(e=>[e.placement,e.overflows.filter(e=>e>0).reduce((e,t)=>e+t,0)]).sort((e,t)=>e[1]-t[1])[0]?.[0];e&&(n=e);break}case`initialPlacement`:n=o;break}if(r!==n)return{reset:{placement:n}}}return{}}}};function Rp(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function zp(e){return _u.some(t=>e[t]>=0)}var Bp=function(e){return e===void 0&&(e={}),{name:`hide`,options:e,async fn(t){let{rects:n,platform:r}=t,{strategy:i=`referenceHidden`,...a}=Tu(e,t);switch(i){case`referenceHidden`:{let e=Rp(await r.detectOverflow(t,{...a,elementContext:`reference`}),n.reference);return{data:{referenceHiddenOffsets:e,referenceHidden:zp(e)}}}case`escaped`:{let e=Rp(await r.detectOverflow(t,{...a,altBoundary:!0}),n.floating);return{data:{escapedOffsets:e,escaped:zp(e)}}}default:return{}}}}},Vp=new Set([`left`,`top`]);async function Hp(e,t){let{placement:n,platform:r,elements:i}=e,a=await(r.isRTL==null?void 0:r.isRTL(i.floating)),o=Eu(n),s=Du(n),c=Au(n)===`y`,l=Vp.has(o)?-1:1,u=a&&c?-1:1,d=Tu(t,e),{mainAxis:f,crossAxis:p,alignmentAxis:m}=typeof d==`number`?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:d.mainAxis||0,crossAxis:d.crossAxis||0,alignmentAxis:d.alignmentAxis};return s&&typeof m==`number`&&(p=s===`end`?m*-1:m),c?{x:p*u,y:f*l}:{x:f*l,y:p*u}}var Up=function(e){return e===void 0&&(e=0),{name:`offset`,options:e,async fn(t){var n;let{x:r,y:i,placement:a,middlewareData:o}=t,s=await Hp(t,e);return a===o.offset?.placement&&(n=o.arrow)!=null&&n.alignmentOffset?{}:{x:r+s.x,y:i+s.y,data:{...s,placement:a}}}}},Wp=function(e){return e===void 0&&(e={}),{name:`shift`,options:e,async fn(t){let{x:n,y:r,placement:i,platform:a}=t,{mainAxis:o=!0,crossAxis:s=!1,limiter:c={fn:e=>{let{x:t,y:n}=e;return{x:t,y:n}}},...l}=Tu(e,t),u={x:n,y:r},d=await a.detectOverflow(t,l),f=Au(Eu(i)),p=Ou(f),m=u[p],h=u[f];if(o){let e=p===`y`?`top`:`left`,t=p===`y`?`bottom`:`right`,n=m+d[e],r=m-d[t];m=wu(n,m,r)}if(s){let e=f===`y`?`top`:`left`,t=f===`y`?`bottom`:`right`,n=h+d[e],r=h-d[t];h=wu(n,h,r)}let g=c.fn({...t,[p]:m,[f]:h});return{...g,data:{x:g.x-n,y:g.y-r,enabled:{[p]:o,[f]:s}}}}}},Gp=function(e){return e===void 0&&(e={}),{options:e,fn(t){let{x:n,y:r,placement:i,rects:a,middlewareData:o}=t,{offset:s=0,mainAxis:c=!0,crossAxis:l=!0}=Tu(e,t),u={x:n,y:r},d=Au(i),f=Ou(d),p=u[f],m=u[d],h=Tu(s,t),g=typeof h==`number`?{mainAxis:h,crossAxis:0}:{mainAxis:0,crossAxis:0,...h};if(c){let e=f===`y`?`height`:`width`,t=a.reference[f]-a.floating[e]+g.mainAxis,n=a.reference[f]+a.reference[e]-g.mainAxis;pn&&(p=n)}if(l){let e=f===`y`?`width`:`height`,t=Vp.has(Eu(i)),n=a.reference[d]-a.floating[e]+(t&&o.offset?.[d]||0)+(t?0:g.crossAxis),r=a.reference[d]+a.reference[e]+(t?0:o.offset?.[d]||0)-(t?g.crossAxis:0);mr&&(m=r)}return{[f]:p,[d]:m}}}},Kp=function(e){return e===void 0&&(e={}),{name:`size`,options:e,async fn(t){var n,r;let{placement:i,rects:a,platform:o,elements:s}=t,{apply:c=()=>{},...l}=Tu(e,t),u=await o.detectOverflow(t,l),d=Eu(i),f=Du(i),p=Au(i)===`y`,{width:m,height:h}=a.floating,g,_;d===`top`||d===`bottom`?(g=d,_=f===(await(o.isRTL==null?void 0:o.isRTL(s.floating))?`start`:`end`)?`left`:`right`):(_=d,g=f===`end`?`top`:`bottom`);let v=h-u.top-u.bottom,y=m-u.left-u.right,b=vu(h-u[g],v),x=vu(m-u[_],y),S=!t.middlewareData.shift,C=b,w=x;if((n=t.middlewareData.shift)!=null&&n.enabled.x&&(w=y),(r=t.middlewareData.shift)!=null&&r.enabled.y&&(C=v),S&&!f){let e=yu(u.left,0),t=yu(u.right,0),n=yu(u.top,0),r=yu(u.bottom,0);p?w=m-2*(e!==0||t!==0?e+t:yu(u.left,u.right)):C=h-2*(n!==0||r!==0?n+r:yu(u.top,u.bottom))}await c({...t,availableWidth:w,availableHeight:C});let T=await o.getDimensions(s.floating);return m!==T.width||h!==T.height?{reset:{rects:!0}}:{}}}};function qp(e){let t=ds(e),n=parseFloat(t.width)||0,r=parseFloat(t.height)||0,i=Qo(e),a=i?e.offsetWidth:n,o=i?e.offsetHeight:r,s=bu(n)!==a||bu(r)!==o;return s&&(n=a,r=o),{width:n,height:r,$:s}}function Jp(e){return Zo(e)?e:e.contextElement}function Yp(e){let t=Jp(e);if(!Qo(t))return Su(1);let n=t.getBoundingClientRect(),{width:r,height:i,$:a}=qp(t),o=(a?bu(n.width):n.width)/r,s=(a?bu(n.height):n.height)/i;return(!o||!Number.isFinite(o))&&(o=1),(!s||!Number.isFinite(s))&&(s=1),{x:o,y:s}}var Xp=Su(0);function Zp(e){let t=Jo(e);return!ls()||!t.visualViewport?Xp:{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}}function Qp(e,t,n){return t===void 0&&(t=!1),!n||t&&n!==Jo(e)?!1:t}function $p(e,t,n,r){t===void 0&&(t=!1),n===void 0&&(n=!1);let i=e.getBoundingClientRect(),a=Jp(e),o=Su(1);t&&(r?Zo(r)&&(o=Yp(r)):o=Yp(e));let s=Qp(a,n,r)?Zp(a):Su(0),c=(i.left+s.x)/o.x,l=(i.top+s.y)/o.y,u=i.width/o.x,d=i.height/o.y;if(a){let e=Jo(a),t=r&&Zo(r)?Jo(r):r,n=e,i=gs(n);for(;i&&r&&t!==n;){let e=Yp(i),t=i.getBoundingClientRect(),r=ds(i),a=t.left+(i.clientLeft+parseFloat(r.paddingLeft))*e.x,o=t.top+(i.clientTop+parseFloat(r.paddingTop))*e.y;c*=e.x,l*=e.y,u*=e.x,d*=e.y,c+=a,l+=o,n=Jo(i),i=gs(n)}}return Wu({width:u,height:d,x:c,y:l})}function em(e,t){let n=fs(e).scrollLeft;return t?t.left+n:$p(Yo(e)).left+n}function tm(e,t){let n=e.getBoundingClientRect();return{x:n.left+t.scrollLeft-em(e,n),y:n.top+t.scrollTop}}function nm(e){let{elements:t,rect:n,offsetParent:r,strategy:i}=e,a=i===`fixed`,o=Yo(r),s=t?ns(t.floating):!1;if(r===o||s&&a)return n;let c={scrollLeft:0,scrollTop:0},l=Su(1),u=Su(0),d=Qo(r);if((d||!d&&!a)&&((qo(r)!==`body`||es(o))&&(c=fs(r)),d)){let e=$p(r);l=Yp(r),u.x=e.x+r.clientLeft,u.y=e.y+r.clientTop}let f=o&&!d&&!a?tm(o,c):Su(0);return{width:n.width*l.x,height:n.height*l.y,x:n.x*l.x-c.scrollLeft*l.x+u.x+f.x,y:n.y*l.y-c.scrollTop*l.y+u.y+f.y}}function rm(e){return Array.from(e.getClientRects())}function eee(e){let t=Yo(e),n=fs(e),r=e.ownerDocument.body,i=yu(t.scrollWidth,t.clientWidth,r.scrollWidth,r.clientWidth),a=yu(t.scrollHeight,t.clientHeight,r.scrollHeight,r.clientHeight),o=-n.scrollLeft+em(e),s=-n.scrollTop;return ds(r).direction===`rtl`&&(o+=yu(t.clientWidth,r.clientWidth)-i),{width:i,height:a,x:o,y:s}}var im=25;function am(e,t){let n=Jo(e),r=Yo(e),i=n.visualViewport,a=r.clientWidth,o=r.clientHeight,s=0,c=0;if(i){a=i.width,o=i.height;let e=ls();(!e||e&&t===`fixed`)&&(s=i.offsetLeft,c=i.offsetTop)}let l=em(r);if(l<=0){let e=r.ownerDocument,t=e.body,n=getComputedStyle(t),i=e.compatMode===`CSS1Compat`&&parseFloat(n.marginLeft)+parseFloat(n.marginRight)||0,o=Math.abs(r.clientWidth-t.clientWidth-i);o<=im&&(a-=o)}else l<=im&&(a+=l);return{width:a,height:o,x:s,y:c}}function om(e,t){let n=$p(e,!0,t===`fixed`),r=n.top+e.clientTop,i=n.left+e.clientLeft,a=Qo(e)?Yp(e):Su(1);return{width:e.clientWidth*a.x,height:e.clientHeight*a.y,x:i*a.x,y:r*a.y}}function sm(e,t,n){let r;if(t===`viewport`)r=am(e,n);else if(t===`document`)r=eee(Yo(e));else if(Zo(t))r=om(t,n);else{let n=Zp(e);r={x:t.x-n.x,y:t.y-n.y,width:t.width,height:t.height}}return Wu(r)}function cm(e,t){let n=ps(e);return n===t||!Zo(n)||us(n)?!1:ds(n).position===`fixed`||cm(n,t)}function lm(e,t){let n=t.get(e);if(n)return n;let r=hs(e,[],!1).filter(e=>Zo(e)&&qo(e)!==`body`),i=null,a=ds(e).position===`fixed`,o=a?ps(e):e;for(;Zo(o)&&!us(o);){let t=ds(o),n=ss(o);!n&&t.position===`fixed`&&(i=null),(a?!n&&!i:!n&&t.position===`static`&&i&&(i.position===`absolute`||i.position===`fixed`)||es(o)&&!n&&cm(e,o))?r=r.filter(e=>e!==o):i=t,o=ps(o)}return t.set(e,r),r}function um(e){let{element:t,boundary:n,rootBoundary:r,strategy:i}=e,a=[...n===`clippingAncestors`?ns(t)?[]:lm(t,this._c):[].concat(n),r],o=sm(t,a[0],i),s=o.top,c=o.right,l=o.bottom,u=o.left;for(let e=1;e{o(!1,1e-7)},1e3)}n===1&&!_m(l,e.getBoundingClientRect())&&o(),y=!1}try{n=new IntersectionObserver(b,{...v,root:i.ownerDocument})}catch{n=new IntersectionObserver(b,v)}n.observe(e)}return o(!0),a}function ym(e,t,n,r){r===void 0&&(r={});let{ancestorScroll:i=!0,ancestorResize:a=!0,elementResize:o=typeof ResizeObserver==`function`,layoutShift:s=typeof IntersectionObserver==`function`,animationFrame:c=!1}=r,l=Jp(e),u=i||a?[...l?hs(l):[],...t?hs(t):[]]:[];u.forEach(e=>{i&&e.addEventListener(`scroll`,n,{passive:!0}),a&&e.addEventListener(`resize`,n)});let d=l&&s?vm(l,n):null,f=-1,p=null;o&&(p=new ResizeObserver(e=>{let[r]=e;r&&r.target===l&&p&&t&&(p.unobserve(t),cancelAnimationFrame(f),f=requestAnimationFrame(()=>{var e;(e=p)==null||e.observe(t)})),n()}),l&&!c&&p.observe(l),t&&p.observe(t));let m,h=c?$p(e):null;c&&g();function g(){let t=$p(e);h&&!_m(h,t)&&n(),h=t,m=requestAnimationFrame(g)}return n(),()=>{var e;u.forEach(e=>{i&&e.removeEventListener(`scroll`,n),a&&e.removeEventListener(`resize`,n)}),d?.(),(e=p)==null||e.disconnect(),p=null,c&&cancelAnimationFrame(m)}}var bm=Up,xm=Wp,Sm=Lp,Cm=Kp,wm=Bp,Tm=Gp,Em=(e,t,n)=>{let r=new Map,i={platform:gm,...n},a={...i.platform,_c:r};return Ip(e,t,{...i,platform:a})},Dm=typeof document<`u`?P.useLayoutEffect:function(){};function Om(e,t){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e==`function`&&e.toString()===t.toString())return!0;let n,r,i;if(e&&t&&typeof e==`object`){if(Array.isArray(e)){if(n=e.length,n!==t.length)return!1;for(r=n;r--!==0;)if(!Om(e[r],t[r]))return!1;return!0}if(i=Object.keys(e),n=i.length,n!==Object.keys(t).length)return!1;for(r=n;r--!==0;)if(!{}.hasOwnProperty.call(t,i[r]))return!1;for(r=n;r--!==0;){let n=i[r];if(!(n===`_owner`&&e.$$typeof)&&!Om(e[n],t[n]))return!1}return!0}return e!==e&&t!==t}function km(e){return typeof window>`u`?1:(e.ownerDocument.defaultView||window).devicePixelRatio||1}function Am(e,t){let n=km(e);return Math.round(t*n)/n}function jm(e){let t=P.useRef(e);return Dm(()=>{t.current=e}),t}function ree(e){e===void 0&&(e={});let{placement:t=`bottom`,strategy:n=`absolute`,middleware:r=[],platform:i,elements:{reference:a,floating:o}={},transform:s=!0,whileElementsMounted:c,open:l}=e,[u,d]=P.useState({x:0,y:0,strategy:n,placement:t,middlewareData:{},isPositioned:!1}),[f,p]=P.useState(r);Om(f,r)||p(r);let[m,h]=P.useState(null),[g,_]=P.useState(null),v=P.useCallback(e=>{e!==S.current&&(S.current=e,h(e))},[]),y=P.useCallback(e=>{e!==C.current&&(C.current=e,_(e))},[]),b=a||m,x=o||g,S=P.useRef(null),C=P.useRef(null),w=P.useRef(u),T=c!=null,E=jm(c),ee=jm(i),D=jm(l),O=P.useCallback(()=>{if(!S.current||!C.current)return;let e={placement:t,strategy:n,middleware:f};ee.current&&(e.platform=ee.current),Em(S.current,C.current,e).then(e=>{let t={...e,isPositioned:D.current!==!1};te.current&&!Om(w.current,t)&&(w.current=t,hi.flushSync(()=>{d(t)}))})},[f,t,n,ee,D]);Dm(()=>{l===!1&&w.current.isPositioned&&(w.current.isPositioned=!1,d(e=>({...e,isPositioned:!1})))},[l]);let te=P.useRef(!1);Dm(()=>(te.current=!0,()=>{te.current=!1}),[]),Dm(()=>{if(b&&(S.current=b),x&&(C.current=x),b&&x){if(E.current)return E.current(b,x,O);O()}},[b,x,O,E,T]);let ne=P.useMemo(()=>({reference:S,floating:C,setReference:v,setFloating:y}),[v,y]),re=P.useMemo(()=>({reference:b,floating:x}),[b,x]),k=P.useMemo(()=>{let e={position:n,left:0,top:0};if(!re.floating)return e;let t=Am(re.floating,u.x),r=Am(re.floating,u.y);return s?{...e,transform:`translate(`+t+`px, `+r+`px)`,...km(re.floating)>=1.5&&{willChange:`transform`}}:{position:n,left:t,top:r}},[n,s,re.floating,u.x,u.y]);return P.useMemo(()=>({...u,update:O,refs:ne,elements:re,floatingStyles:k}),[u,O,ne,re,k])}var iee=(e,t)=>{let n=bm(e);return{name:n.name,fn:n.fn,options:[e,t]}},aee=(e,t)=>{let n=xm(e);return{name:n.name,fn:n.fn,options:[e,t]}},oee=(e,t)=>({fn:Tm(e).fn,options:[e,t]}),see=(e,t)=>{let n=Sm(e);return{name:n.name,fn:n.fn,options:[e,t]}},cee=(e,t)=>{let n=Cm(e);return{name:n.name,fn:n.fn,options:[e,t]}},Mm=(e,t)=>{let n=wm(e);return{name:n.name,fn:n.fn,options:[e,t]}},Nm=(e,t,n,r,i,a,...o)=>{if(o.length>0)throw Error(Na(1));let s;if(e&&t&&n&&r&&i&&a)s=(o,s,c,l)=>a(e(o,s,c,l),t(o,s,c,l),n(o,s,c,l),r(o,s,c,l),i(o,s,c,l),s,c,l);else if(e&&t&&n&&r&&i)s=(a,o,s,c)=>i(e(a,o,s,c),t(a,o,s,c),n(a,o,s,c),r(a,o,s,c),o,s,c);else if(e&&t&&n&&r)s=(i,a,o,s)=>r(e(i,a,o,s),t(i,a,o,s),n(i,a,o,s),a,o,s);else if(e&&t&&n)s=(r,i,a,o)=>n(e(r,i,a,o),t(r,i,a,o),i,a,o);else if(e&&t)s=(n,r,i,a)=>t(e(n,r,i,a),r,i,a);else if(e)s=e;else throw Error(`Missing arguments`);return s},Pm=[],Fm=void 0;function Im(){return Fm}function Lm(e){Pm.push(e)}function Rm(e){let t=(t,n)=>{let r=Fa(Bm).current,i;try{Fm=r;for(let e of Pm)e.before(r);i=e(t,n);for(let e of Pm)e.after(r);r.didInitialize=!0}finally{Fm=void 0}return i};return t.displayName=e.displayName||e.name,t}function zm(e){return P.forwardRef(Rm(e))}function Bm(){return{didInitialize:!1}}var Vm=ei(),Hm=Ua(19)?Gm:Km;function Um(e,t,n,r,i){return Hm(e,t,n,r,i)}function Wm(e,t,n,r,i){let a=P.useCallback(()=>t(e.getSnapshot(),n,r,i),[e,t,n,r,i]);return(0,Vm.useSyncExternalStore)(e.subscribe,a,a)}Lm({before(e){e.syncIndex=0,e.didInitialize||(e.syncTick=1,e.syncHooks=[],e.didChangeStore=!0,e.getSnapshot=()=>{let t=!1;for(let n=0;n0&&(e.didChangeStore&&(e.didChangeStore=!1,e.subscribe=t=>{let n=new Set;for(let t of e.syncHooks)n.add(t.store);let r=[];for(let e of n)r.push(e.subscribe(t));return()=>{for(let e of r)e()}}),(0,Vm.useSyncExternalStore)(e.subscribe,e.getSnapshot,e.getSnapshot))}});function Gm(e,t,n,r,i){let a=Im();if(!a)return Wm(e,t,n,r,i);let o=a.syncIndex;a.syncIndex+=1;let s;return a.didInitialize?(s=a.syncHooks[o],(s.store!==e||s.selector!==t||!Object.is(s.a1,n)||!Object.is(s.a2,r)||!Object.is(s.a3,i))&&(s.store!==e&&(a.didChangeStore=!0),s.store=e,s.selector=t,s.a1=n,s.a2=r,s.a3=i,s.didChange=!0)):(s={store:e,selector:t,a1:n,a2:r,a3:i,value:t(e.getSnapshot(),n,r,i),didChange:!1},a.syncHooks.push(s)),s.value}function Km(e,t,n,r,i){return(0,ni.useSyncExternalStoreWithSelector)(e.subscribe,e.getSnapshot,e.getSnapshot,e=>t(e,n,r,i))}var qm=class{constructor(e){this.state=e,this.listeners=new Set,this.updateTick=0}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});getSnapshot=()=>this.state;setState(e){if(this.state===e)return;this.state=e,this.updateTick+=1;let t=this.updateTick;for(let n of this.listeners){if(t!==this.updateTick)return;n(e)}}update(e){for(let t in e)if(!Object.is(this.state[t],e[t])){this.setState({...this.state,...e});return}}set(e,t){Object.is(this.state[e],t)||this.setState({...this.state,[e]:t})}notifyAll(){let e={...this.state};this.setState(e)}use(e,t,n,r){return Um(this,e,t,n,r)}},Jm=class extends qm{constructor(e,t={},n){super(e),this.context=t,this.selectors=n}useSyncedValue(e,t){P.useDebugValue(e),Cs(()=>{this.state[e]!==t&&this.set(e,t)},[e,t])}useSyncedValueWithCleanup(e,t){let n=this;Cs(()=>(n.state[e]!==t&&n.set(e,t),()=>{n.set(e,void 0)}),[n,e,t])}useSyncedValues(e){let t=this;Cs(()=>{t.update(e)},[t,...Object.values(e)])}useControlledProp(e,t){P.useDebugValue(e);let n=t!==void 0;Cs(()=>{n&&!Object.is(this.state[e],t)&&super.setState({...this.state,[e]:t})},[e,t,n])}select(e,t,n,r){let i=this.selectors[e];return i(this.state,t,n,r)}useState(e,t,n,r){return P.useDebugValue(e),Um(this,this.selectors[e],t,n,r)}useContextCallback(e,t){P.useDebugValue(e);let n=ys(t??Ga);this.context[e]=n}useStateSetter(e){let t=P.useRef(void 0);return t.current===void 0&&(t.current=t=>{this.set(e,t)}),t.current}observe(e,t){let n;n=typeof e==`function`?e:this.selectors[e];let r=n(this.state);return t(r,r,this),this.subscribe(e=>{let i=n(e);if(!Object.is(r,i)){let e=r;r=i,t(i,e,this)}})}},Ym={open:Nm(e=>e.open),transitionStatus:Nm(e=>e.transitionStatus),domReferenceElement:Nm(e=>e.domReferenceElement),referenceElement:Nm(e=>e.positionReference??e.referenceElement),floatingElement:Nm(e=>e.floatingElement),floatingId:Nm(e=>e.floatingId)},Xm=class extends Jm{constructor(e){let{syncOnly:t,nested:n,onOpenChange:r,triggerElements:i,...a}=e;super({...a,positionReference:a.referenceElement,domReferenceElement:a.referenceElement},{onOpenChange:r,dataRef:{current:{}},events:fp(),nested:n,triggerElements:i},Ym),this.syncOnly=t}syncOpenEvent=(e,t)=>{(!e||!this.state.open||t!=null&&gu(t))&&(this.context.dataRef.current.openEvent=e?t:void 0)};dispatchOpenChange=(e,t)=>{this.syncOpenEvent(e,t.event);let n={open:e,reason:t.reason,nativeEvent:t.event,nested:this.context.nested,triggerElement:t.trigger};this.context.events.emit(`openchange`,n)};setOpen=(e,t)=>{if(this.syncOnly){this.context.onOpenChange?.(e,t);return}this.dispatchOpenChange(e,t),this.context.onOpenChange?.(e,t)}};function Zm(e,t){let n=P.useRef(null),r=P.useRef(null);return P.useCallback(i=>{if(e!==void 0){if(n.current!==null){let e=n.current,i=r.current,a=t.context.triggerElements.getById(e);i&&a===i&&t.context.triggerElements.delete(e),n.current=null,r.current=null}i!==null&&(n.current=e,r.current=i,t.context.triggerElements.add(e,i))}},[t,e])}function Qm(e,t,n,r){let i=n.useState(`isMountedByTrigger`,e),a=Zm(e,n),o=ys(t=>{if(a(t),!t||!n.select(`open`))return;let i=n.select(`activeTriggerId`);if(i===e){n.update({activeTriggerElement:t,...r});return}i??n.update({activeTriggerId:e,activeTriggerElement:t,...r})});return Cs(()=>{i&&n.update({activeTriggerElement:t.current,...r})},[i,n,t,...Object.values(r)]),{registerTrigger:o,isMountedByThisTrigger:i}}function $m(e){let t=e.useState(`open`);Cs(()=>{if(t&&!e.select(`activeTriggerId`)&&e.context.triggerElements.size===1){let t=e.context.triggerElements.entries().next();if(!t.done){let[n,r]=t.value;e.update({activeTriggerId:n,activeTriggerElement:r})}}},[t,e])}function eh(e,t,n){let{mounted:r,setMounted:i,transitionStatus:a}=Vd(e);t.useSyncedValues({mounted:r,transitionStatus:a});let o=ys(()=>{i(!1),t.update({activeTriggerId:null,activeTriggerElement:null,mounted:!1}),n?.(),t.context.onOpenChangeComplete?.(!1)});return Bd({enabled:!t.useState(`preventUnmountingOnClose`),open:e,ref:t.context.popupRef,onComplete(){e||o()}}),{forceUnmount:o,transitionStatus:a}}var th=class{constructor(){this.elementsSet=new Set,this.idMap=new Map}add(e,t){let n=this.idMap.get(e);n!==t&&(n!==void 0&&this.elementsSet.delete(n),this.elementsSet.add(t),this.idMap.set(e,t))}delete(e){let t=this.idMap.get(e);t&&(this.elementsSet.delete(t),this.idMap.delete(e))}hasElement(e){return this.elementsSet.has(e)}hasMatchingElement(e){for(let t of this.elementsSet)if(e(t))return!0;return!1}getById(e){return this.idMap.get(e)}entries(){return this.idMap.entries()}elements(){return this.elementsSet.values()}get size(){return this.idMap.size}};function nh(){return new Xm({open:!1,transitionStatus:void 0,floatingElement:null,referenceElement:null,triggerElements:new th,floatingId:``,syncOnly:!1,nested:!1,onOpenChange:void 0})}function rh(){return{open:!1,openProp:void 0,mounted:!1,transitionStatus:void 0,floatingRootContext:nh(),preventUnmountingOnClose:!1,payload:void 0,activeTriggerId:null,activeTriggerElement:null,triggerIdProp:void 0,popupElement:null,positionerElement:null,activeTriggerProps:qa,inactiveTriggerProps:qa,popupProps:qa}}var ih=Nm(e=>e.triggerIdProp??e.activeTriggerId),ah={open:Nm(e=>e.openProp??e.open),mounted:Nm(e=>e.mounted),transitionStatus:Nm(e=>e.transitionStatus),floatingRootContext:Nm(e=>e.floatingRootContext),preventUnmountingOnClose:Nm(e=>e.preventUnmountingOnClose),payload:Nm(e=>e.payload),activeTriggerId:ih,activeTriggerElement:Nm(e=>e.mounted?e.activeTriggerElement:null),isTriggerActive:Nm((e,t)=>t!==void 0&&ih(e)===t),isOpenedByTrigger:Nm((e,t)=>t!==void 0&&ih(e)===t&&e.open),isMountedByTrigger:Nm((e,t)=>t!==void 0&&ih(e)===t&&e.mounted),triggerProps:Nm((e,t)=>t?e.activeTriggerProps:e.inactiveTriggerProps),popupProps:Nm(e=>e.popupProps),popupElement:Nm(e=>e.popupElement),positionerElement:Nm(e=>e.positionerElement)};function oh(e){let{open:t=!1,onOpenChange:n,elements:r={}}=e,i=Dl(),a=hp()!=null,o=Fa(()=>new Xm({open:t,transitionStatus:void 0,onOpenChange:n,referenceElement:r.reference??null,floatingElement:r.floating??null,triggerElements:new th,floatingId:i,syncOnly:!1,nested:a})).current;return Cs(()=>{let e={open:t,floatingId:i};r.reference!==void 0&&(e.referenceElement=r.reference,e.domReferenceElement=Zo(r.reference)?r.reference:null),r.floating!==void 0&&(e.floatingElement=r.floating),o.update(e)},[t,i,r.reference,r.floating,o]),o.context.onOpenChange=n,o.context.nested=a,o}function sh(e={}){let{nodeId:t,externalTree:n}=e,r=oh(e),i=e.rootContext||r,a={reference:i.useState(`referenceElement`),floating:i.useState(`floatingElement`),domReference:i.useState(`domReferenceElement`)},[o,s]=P.useState(null),c=P.useRef(null),l=gp(n);Cs(()=>{a.domReference&&(c.current=a.domReference)},[a.domReference]);let u=ree({...e,elements:{...a,...o&&{reference:o}}}),d=P.useCallback(e=>{let t=Zo(e)?{getBoundingClientRect:()=>e.getBoundingClientRect(),getClientRects:()=>e.getClientRects(),contextElement:e}:e;s(t),u.refs.setReference(t)},[u.refs]),[f,p]=P.useState(void 0),[m,h]=P.useState(null);i.useSyncedValue(`referenceElement`,f??null);let g=Zo(f)?f:null;i.useSyncedValue(`domReferenceElement`,f===void 0?a.domReference:g),i.useSyncedValue(`floatingElement`,m);let _=P.useCallback(e=>{(Zo(e)||e===null)&&(c.current=e,p(e)),(Zo(u.refs.reference.current)||u.refs.reference.current===null||e!==null&&!Zo(e))&&u.refs.setReference(e)},[u.refs,p]),v=P.useCallback(e=>{h(e),u.refs.setFloating(e)},[u.refs]),y=P.useMemo(()=>({...u.refs,setReference:_,setFloating:v,setPositionReference:d,domReference:c}),[u.refs,_,v,d]),b=P.useMemo(()=>({...u.elements,domReference:a.domReference}),[u.elements,a.domReference]),x=i.useState(`open`),S=i.useState(`floatingId`),C=P.useMemo(()=>({...u,dataRef:i.context.dataRef,open:x,onOpenChange:i.setOpen,events:i.context.events,floatingId:S,refs:y,elements:b,nodeId:t,rootStore:i}),[u,y,b,t,i,x,S]);return Cs(()=>{i.context.dataRef.current.floatingContext=C;let e=l?.nodesRef.current.find(e=>e.id===t);e&&(e.context=C)}),P.useMemo(()=>({...u,context:C,refs:y,elements:b,rootStore:i}),[u,y,b,C,i])}function ch(e){let{popupStore:t,treatPopupAsFloatingElement:n=!1,onOpenChange:r}=e,i=Dl(),a=hp()!=null,o=t.useState(`open`),s=t.useState(`activeTriggerElement`),c=t.useState(n?`popupElement`:`positionerElement`),l=t.context.triggerElements,u=Fa(()=>new Xm({open:o,transitionStatus:void 0,referenceElement:s,floatingElement:c,triggerElements:l,onOpenChange:r,floatingId:i,syncOnly:!0,nested:a})).current;return Cs(()=>{let e={open:o,floatingId:i,referenceElement:s,floatingElement:c};Zo(s)&&(e.domReferenceElement=s),u.state.positionReference===u.state.referenceElement&&(e.positionReference=s),u.update(e)},[o,i,s,c,u]),u.context.onOpenChange=r,u.context.nested=a,u}var lh=Vl&&zl;function lee(e,t={}){let n=`rootStore`in e?e.rootStore:e,{events:r,dataRef:i}=n.context,{enabled:a=!0,delay:o}=t,s=P.useRef(!1),c=P.useRef(null),l=kd(),u=P.useRef(!0);P.useEffect(()=>{let e=n.select(`domReferenceElement`);if(!a)return;let t=Jo(e);function r(){let e=n.select(`domReferenceElement`);!n.select(`open`)&&Qo(e)&&e===Ql(Ml(e))&&(s.current=!0)}function i(){u.current=!0}function o(){u.current=!1}return Ff(Pf(t,`blur`,r),lh&&Pf(t,`keydown`,i,!0),lh&&Pf(t,`pointerdown`,o,!0))},[n,a]),P.useEffect(()=>{if(!a)return;function e(e){if(e.reason===`trigger-press`||e.reason===`escape-key`){let e=n.select(`domReferenceElement`);Zo(e)&&(c.current=e,s.current=!0)}}return r.on(`openchange`,e),()=>{r.off(`openchange`,e)}},[r,a,n]);let d=P.useMemo(()=>({onMouseLeave(){s.current=!1,c.current=null},onFocus(e){let t=e.currentTarget;if(s.current){if(c.current===t)return;s.current=!1,c.current=null}let r=eu(e.nativeEvent);if(Zo(r)){if(lh&&!e.relatedTarget){if(!u.current&&!iu(r))return}else if(!su(r))return}let i=tu(e.relatedTarget,n.context.triggerElements),{nativeEvent:a,currentTarget:d}=e,f=typeof o==`function`?o():o;if(n.select(`open`)&&i||f===0||f===void 0){n.setOpen(!0,af(qd,a,d));return}l.start(f,()=>{s.current||n.setOpen(!0,af(qd,a,d))})},onBlur(e){s.current=!1,c.current=null;let t=e.relatedTarget,r=e.nativeEvent,a=Zo(t)&&t.hasAttribute(Hf(`focus-guard`))&&t.getAttribute(`data-type`)===`outside`;l.start(0,()=>{let e=n.select(`domReferenceElement`),o=Ql(Ml(e));!t&&o===e||$l(i.current.floatingContext?.refs.floating.current,o)||$l(e,o)||a||tu(t??o,n.context.triggerElements)||n.setOpen(!1,af(qd,r))})}}),[i,n,l,o]);return P.useMemo(()=>a?{reference:d,trigger:d}:{},[a,d])}var uee=class e{constructor(){this.pointerType=void 0,this.interactedInside=!1,this.handler=void 0,this.blockMouseMove=!0,this.performedPointerEventsMutation=!1,this.pointerEventsScopeElement=null,this.pointerEventsReferenceElement=null,this.pointerEventsFloatingElement=null,this.restTimeoutPending=!1,this.openChangeTimeout=new Od,this.restTimeout=new Od,this.handleCloseOptions=void 0}static create(){return new e}dispose=()=>{this.openChangeTimeout.clear(),this.restTimeout.clear()};disposeEffect=()=>this.dispose},uh=new WeakMap;function dh(e){if(!e.performedPointerEventsMutation)return;let t=e.pointerEventsScopeElement;t&&uh.get(t)===e&&(e.pointerEventsScopeElement?.style.removeProperty(`pointer-events`),e.pointerEventsReferenceElement?.style.removeProperty(`pointer-events`),e.pointerEventsFloatingElement?.style.removeProperty(`pointer-events`),uh.delete(t)),e.performedPointerEventsMutation=!1,e.pointerEventsScopeElement=null,e.pointerEventsReferenceElement=null,e.pointerEventsFloatingElement=null}function fh(e,t){let{scopeElement:n,referenceElement:r,floatingElement:i}=t,a=uh.get(n);a&&a!==e&&dh(a),dh(e),e.performedPointerEventsMutation=!0,e.pointerEventsScopeElement=n,e.pointerEventsReferenceElement=r,e.pointerEventsFloatingElement=i,uh.set(n,e),n.style.pointerEvents=`none`,r.style.pointerEvents=`auto`,i.style.pointerEvents=`auto`}function ph(e){let t=Fa(uee.create).current,n=e.context.dataRef.current;return n.hoverInteractionState||=t,Ed(n.hoverInteractionState.disposeEffect),n.hoverInteractionState}function dee(e,t={}){let n=`rootStore`in e?e.rootStore:e,r=n.useState(`open`),i=n.useState(`floatingElement`),a=n.useState(`domReferenceElement`),{dataRef:o}=n.context,{enabled:s=!0,closeDelay:c=0,nodeId:l}=t,u=ph(n),d=gp(),f=hp(),p=ys(()=>Af(o.current.openEvent?.type,u.interactedInside)),m=ys(()=>{let e=o.current.openEvent?.type;return e?.includes(`mouse`)&&e!==`mousedown`}),h=ys(e=>tu(e,n.context.triggerElements)),g=P.useCallback(e=>{let t=Of(c,`close`,u.pointerType),r=()=>{n.setOpen(!1,af(Kd,e)),d?.events.emit(`floating.closed`,e)};t?u.openChangeTimeout.start(t,r):(u.openChangeTimeout.clear(),r())},[c,n,u,d]),_=ys(()=>{dh(u)}),v=ys(e=>{let t=eu(e);if(!au(t)){u.interactedInside=!1;return}u.interactedInside=t?.closest(`[aria-haspopup]`)!=null});Cs(()=>{r||(u.pointerType=void 0,u.restTimeoutPending=!1,u.interactedInside=!1,_())},[r,u,_]),P.useEffect(()=>_,[_]),Cs(()=>{if(s&&r&&u.handleCloseOptions?.blockPointerEvents&&m()&&Zo(a)&&i){let e=a,t=i,n=Ml(i),r=d?.nodesRef.current.find(e=>e.id===f)?.context?.elements.floating;return r&&(r.style.pointerEvents=``),fh(u,{scopeElement:u.handleCloseOptions?.getScope?.()??u.pointerEventsScopeElement??r??e.closest(`[data-rootownerid]`)??n.body,referenceElement:e,floatingElement:t}),()=>{_()}}},[s,r,a,i,u,m,d,f,_]);let y=kd();P.useEffect(()=>{if(!s)return;function e(){u.openChangeTimeout.clear(),y.clear(),d?.events.off(`floating.closed`,r),_()}function t(e){if(d&&f&&lu(d.nodesRef.current,f).length>0){d.events.on(`floating.closed`,r);return}if(h(e.relatedTarget))return;let t=o.current.floatingContext?.nodeId??l,n=e.relatedTarget;if(!(d&&t&&Zo(n)&&lu(d.nodesRef.current,t,!1).some(e=>$l(e.context?.elements.floating,n)))){if(u.handler){u.handler(e);return}_(),p()||g(e)}}function r(e){!d||!f||lu(d.nodesRef.current,f).length>0||y.start(0,()=>{d.events.off(`floating.closed`,r),n.setOpen(!1,af(Kd,e)),d.events.emit(`floating.closed`,e)})}let a=i;return Ff(a&&Pf(a,`mouseenter`,e),a&&Pf(a,`mouseleave`,t),a&&Pf(a,`pointerdown`,v,!0),()=>{d?.events.off(`floating.closed`,r)})},[s,i,n,o,l,p,h,g,_,v,u,d,f,y])}var fee={current:null};function pee(e,t={}){let n=`rootStore`in e?e.rootStore:e,{dataRef:r,events:i}=n.context,{enabled:a=!0,delay:o=0,handleClose:s=null,mouseOnly:c=!1,restMs:l=0,move:u=!0,triggerElementRef:d=fee,externalTree:f,isActiveTrigger:p=!0,getHandleCloseContext:m,isClosing:h}=t,g=gp(f),_=ph(n),v=P.useRef(!1),y=If(s),b=If(o),x=If(l),S=If(a),C=If(h);p&&(_.handleCloseOptions=y.current?.__options);let w=ys(()=>Af(r.current.openEvent?.type,_.interactedInside)),T=ys(e=>tu(e,n.context.triggerElements)),E=ys((e,t,r)=>{let i=n.context.triggerElements;if(i.hasElement(t))return!e||!$l(e,t);if(!Zo(r))return!1;let a=r;return i.hasMatchingElement(e=>$l(e,a))&&(!e||!$l(e,a))}),ee=ys((e,t=!0)=>{let r=Of(b.current,`close`,_.pointerType);r?_.openChangeTimeout.start(r,()=>{n.setOpen(!1,af(Kd,e)),g?.events.emit(`floating.closed`,e)}):t&&(_.openChangeTimeout.clear(),n.setOpen(!1,af(Kd,e)),g?.events.emit(`floating.closed`,e))}),D=ys(()=>{_.handler&&=(Ml(n.select(`domReferenceElement`)).removeEventListener(`mousemove`,_.handler),void 0)}),O=ys(()=>{dh(_)});return P.useEffect(()=>D,[D]),P.useEffect(()=>{if(!a)return;function e(e){e.open?v.current=!1:(v.current=e.reason===Kd,D(),_.openChangeTimeout.clear(),_.restTimeout.clear(),_.blockMouseMove=!0,_.restTimeoutPending=!1)}return i.on(`openchange`,e),()=>{i.off(`openchange`,e)}},[a,i,_,D]),P.useEffect(()=>{if(!a)return;let e=d.current??(p?n.select(`domReferenceElement`):null);if(!Zo(e))return;function t(e){if(_.openChangeTimeout.clear(),_.blockMouseMove=!1,c&&!hu(_.pointerType))return;let t=kf(x.current),r=Of(b.current,`open`,_.pointerType),i=eu(e),a=e.currentTarget??null,o=n.select(`domReferenceElement`),s=a;if(Zo(i)&&!n.context.triggerElements.hasElement(i)){for(let e of n.context.triggerElements.elements())if($l(e,i)){s=e;break}}Zo(a)&&Zo(o)&&!n.context.triggerElements.hasElement(a)&&$l(a,o)&&(s=o);let l=s==null?!1:E(o,s,i),u=n.select(`open`),d=C.current?.()??n.select(`transitionStatus`)===`ending`,f=!u&&d&&v.current,p=!l&&Zo(s)&&Zo(o)&&$l(o,s)&&f,m=t>0&&!r,h=l&&(u||f)||p,g=!u||l;if(h){n.setOpen(!0,af(Kd,e,s));return}m||(r?_.openChangeTimeout.start(r,()=>{g&&n.setOpen(!0,af(Kd,e,s))}):g&&n.setOpen(!0,af(Kd,e,s)))}function i(e){if(w()){O();return}D();let t=Ml(n.select(`domReferenceElement`));_.restTimeout.clear(),_.restTimeoutPending=!1;let i=r.current.floatingContext??m?.();if(!T(e.relatedTarget)){if(y.current&&i){n.select(`open`)||_.openChangeTimeout.clear();let r=d.current;_.handler=y.current({...i,tree:g,x:e.clientX,y:e.clientY,onClose(){O(),D(),S.current&&!w()&&r===n.select(`domReferenceElement`)&&ee(e,!0)}}),t.addEventListener(`mousemove`,_.handler),_.handler(e);return}(_.pointerType!==`touch`||!$l(n.select(`floatingElement`),e.relatedTarget))&&ee(e)}}return u?Ff(Pf(e,`mousemove`,t,{once:!0}),Pf(e,`mouseenter`,t),Pf(e,`mouseleave`,i)):Ff(Pf(e,`mouseenter`,t),Pf(e,`mouseleave`,i))},[D,O,r,b,ee,n,a,y,_,p,E,w,T,c,u,x,d,g,S,m,C]),P.useMemo(()=>{if(!a)return;function e(e){_.pointerType=e.pointerType}return{onPointerDown:e,onPointerEnter:e,onMouseMove(e){let{nativeEvent:t}=e,r=e.currentTarget,i=n.select(`domReferenceElement`),a=n.select(`open`),o=E(i,r,e.target);if(c&&!hu(_.pointerType))return;if(a&&o&&_.handleCloseOptions?.blockPointerEvents){let e=n.select(`floatingElement`);e&&fh(_,{scopeElement:_.handleCloseOptions?.getScope?.()??r.ownerDocument.body,referenceElement:r,floatingElement:e})}let s=kf(x.current);if(a&&!o||s===0||!o&&_.restTimeoutPending&&e.movementX**2+e.movementY**2<2)return;_.restTimeout.clear();function l(){if(_.restTimeoutPending=!1,w())return;let e=n.select(`open`);!_.blockMouseMove&&(!e||o)&&n.setOpen(!0,af(Kd,t,r))}_.pointerType===`touch`?hi.flushSync(()=>{l()}):o&&a?l():(_.restTimeoutPending=!0,_.restTimeout.start(s,l))}}},[a,_,w,E,c,n,x])}function mh(e=[]){let t=e.map(e=>e?.reference),n=e.map(e=>e?.floating),r=e.map(e=>e?.item),i=e.map(e=>e?.trigger),a=P.useCallback(t=>hh(t,e,`reference`),t),o=P.useCallback(t=>hh(t,e,`floating`),n),s=P.useCallback(t=>hh(t,e,`item`),r),c=P.useCallback(t=>hh(t,e,`trigger`),i);return P.useMemo(()=>({getReferenceProps:a,getFloatingProps:o,getItemProps:s,getTriggerProps:c}),[a,o,s,c])}function hh(e,t,n){let r=new Map,i=n===`item`,a={};n===`floating`&&(a.tabIndex=-1,a[Kl]=``);for(let t in e)i&&e&&(t===`active`||t===`selected`)||(a[t]=e[t]);for(let o=0;or.get(i)?.map(t=>t(...e)).find(e=>e!==void 0))):e[i]=a)}}var mee=new Map([[`select`,`listbox`],[`combobox`,`listbox`],[`label`,!1]]);function hee(e,t={}){let n=`rootStore`in e?e.rootStore:e,r=n.useState(`open`),i=n.useState(`floatingId`),a=n.useState(`domReferenceElement`),o=n.useState(`floatingElement`),{role:s=`dialog`}=t,c=Dl(),l=a?.id||c,u=P.useMemo(()=>cu(o)?.id||i,[o,i]),d=mee.get(s)??s,f=hp()!=null,p=P.useMemo(()=>d===`tooltip`||s===`label`?qa:{"aria-haspopup":d===`alertdialog`?`dialog`:d,"aria-expanded":`false`,...d===`listbox`&&{role:`combobox`},...d===`menu`&&f&&{role:`menuitem`},...s===`select`&&{"aria-autocomplete":`none`},...s===`combobox`&&{"aria-autocomplete":`list`}},[d,f,s]),m=P.useMemo(()=>d===`tooltip`||s===`label`?{[`aria-${s===`label`?`labelledby`:`describedby`}`]:r?u:void 0}:{...p,"aria-expanded":r?`true`:`false`,"aria-controls":r?u:void 0,...d===`menu`&&{id:l}},[d,u,r,l,s,p]),h=P.useMemo(()=>{let e={id:u,...d&&{role:d}};return d===`tooltip`||s===`label`?e:{...e,...d===`menu`&&{"aria-labelledby":l}}},[d,u,l,s]),g=P.useCallback(({active:e,selected:t})=>{let n={role:`option`,...e&&{id:`${u}-fui-option`}};switch(s){case`select`:case`combobox`:return{...n,"aria-selected":t};default:}return{}},[u,s]);return P.useMemo(()=>({reference:m,floating:h,item:g,trigger:p}),[m,h,p,g])}var _h=.1,vh=_h*_h,yh=.5;function bh(e,t,n,r,i,a){return r>=t!=a>=t&&e<=(i-n)*(t-r)/(a-r)+n}function xh(e,t,n,r,i,a,o,s,c,l){let u=!1;return bh(e,t,n,r,i,a)&&(u=!u),bh(e,t,i,a,o,s)&&(u=!u),bh(e,t,o,s,c,l)&&(u=!u),bh(e,t,c,l,n,r)&&(u=!u),u}function Sh(e,t,n){return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}function Ch(e,t,n,r,i,a){return e>=Math.min(n,i)&&e<=Math.max(n,i)&&t>=Math.min(r,a)&&t<=Math.max(r,a)}function wh(e={}){let{blockPointerEvents:t=!1}=e,n=new Od,r=({x:e,y:t,placement:r,elements:i,onClose:a,nodeId:o,tree:s})=>{let c=r?.split(`-`)[0],l=!1,u=null,d=null,f=typeof performance<`u`?performance.now():0;function p(e,t){let n=performance.now(),r=n-f;if(u===null||d===null||r===0)return u=e,d=t,f=n,!1;let i=e-u,a=t-d,o=i*i+a*a,s=r*r*vh;return u=e,d=t,f=n,o0)}function b(){y()||m()}if(y())return;let x=a.getBoundingClientRect(),S=u.getBoundingClientRect(),C=e>S.right-S.width/2,w=t>S.bottom-S.height/2,T=S.width>x.width,E=S.height>x.height,ee=(T?x:S).left,D=(T?x:S).right,O=(E?x:S).top,te=(E?x:S).bottom;if(c===`top`&&t>=x.bottom-1||c===`bottom`&&t<=x.top+1||c===`left`&&e>=x.right-1||c===`right`&&e<=x.left+1){b();return}let ne=!1;switch(c){case`top`:ne=Ch(d,f,ee,x.top+1,D,S.bottom-1);break;case`bottom`:ne=Ch(d,f,ee,S.top+1,D,x.bottom-1);break;case`left`:ne=Ch(d,f,S.right-1,te,x.left+1,O);break;case`right`:ne=Ch(d,f,x.right-1,te,S.left+1,O);break;default:}if(ne)return;if(l&&!Sh(d,f,x)){b();return}if(!g&&p(d,f)){b();return}let re=!1;switch(c){case`top`:{let n=T?yh/2:yh*4,r=T||C?e+n:e-n,i=T?e-n:C?e+n:e-n,a=t+yh+1,o=C||T?S.bottom-yh:S.top,s=C?T?S.bottom-yh:S.top:S.bottom-yh;re=xh(d,f,r,a,i,a,S.left,o,S.right,s);break}case`bottom`:{let n=T?yh/2:yh*4,r=T||C?e+n:e-n,i=T?e-n:C?e+n:e-n,a=t-yh,o=C||T?S.top+yh:S.bottom,s=C?T?S.top+yh:S.bottom:S.top+yh;re=xh(d,f,r,a,i,a,S.left,o,S.right,s);break}case`left`:{let n=E?yh/2:yh*4,r=E||w?t+n:t-n,i=E?t-n:w?t+n:t-n,a=e+yh+1,o=w||E?S.right-yh:S.left,s=w?E?S.right-yh:S.left:S.right-yh;re=xh(d,f,o,S.top,s,S.bottom,a,r,a,i);break}case`right`:{let n=E?yh/2:yh*4,r=E||w?t+n:t-n,i=E?t-n:w?t+n:t-n,a=e-yh,o=w||E?S.left+yh:S.right,s=w?E?S.left+yh:S.right:S.left+yh;re=xh(d,f,a,r,a,i,o,S.top,s,S.bottom);break}default:}re?l||n.start(40,b):b()}};return r.__options={...e,blockPointerEvents:t},r}var Th=function(e){return e.nestedDialogs=`--nested-dialogs`,e}({}),Eh=function(e){return e[e.open=pf.open]=`open`,e[e.closed=pf.closed]=`closed`,e[e.startingStyle=pf.startingStyle]=`startingStyle`,e[e.endingStyle=pf.endingStyle]=`endingStyle`,e.nested=`data-nested`,e.nestedDialogOpen=`data-nested-dialog-open`,e}({}),Dh=P.createContext(void 0);function Oh(){let e=P.useContext(Dh);if(e===void 0)throw Error(Na(26));return e}var kh=`ArrowUp`,Ah=`ArrowDown`,jh=`ArrowLeft`,Mh=`ArrowRight`,Nh=`Home`,Ph=new Set([jh,Mh]),Fh=new Set([jh,Mh,Nh,`End`]),Ih=new Set([kh,Ah]),Lh=new Set([kh,Ah,Nh,`End`]),Rh=new Set([...Ph,...Ih]),zh=new Set([...Rh,Nh,`End`]),Bh=new Set([kh,Ah,jh,Mh,Nh,`End`]),Vh=new Set([`Shift`,`Control`,`Alt`,`Meta`]);function Hh(e){return Qo(e)&&e.tagName===`INPUT`}function Uh(e){return!!(Hh(e)&&e.selectionStart!=null||Qo(e)&&e.tagName===`TEXTAREA`)}function Wh(e,t,n,r){if(!e||!t||!t.scrollTo)return;let i=e.scrollLeft,a=e.scrollTop,o=e.clientWidthe.scrollLeft+e.clientWidth-a.scrollPaddingRight?i=r+t.offsetWidth+o.scrollMarginRight-e.clientWidth+a.scrollPaddingRight:r-o.scrollMarginLefte.scrollLeft+e.clientWidth-a.scrollPaddingRight&&(i=r+t.offsetWidth+o.scrollMarginRight-e.clientWidth+a.scrollPaddingRight))}if(s&&r!==`horizontal`){let n=Gh(e,t,`top`),r=Kh(e),i=Kh(t);n-i.scrollMarginTope.scrollTop+e.clientHeight-r.scrollPaddingBottom&&(a=n+t.offsetHeight+i.scrollMarginBottom-e.clientHeight+r.scrollPaddingBottom)}e.scrollTo({left:i,top:a,behavior:`auto`})}function Gh(e,t,n){let r=n===`left`?`offsetLeft`:`offsetTop`,i=0;for(;t.offsetParent&&(i+=t[r],t.offsetParent!==e);)t=t.offsetParent;return i}function Kh(e){let t=getComputedStyle(e);return{scrollMarginTop:parseFloat(t.scrollMarginTop)||0,scrollMarginRight:parseFloat(t.scrollMarginRight)||0,scrollMarginBottom:parseFloat(t.scrollMarginBottom)||0,scrollMarginLeft:parseFloat(t.scrollMarginLeft)||0,scrollPaddingTop:parseFloat(t.scrollPaddingTop)||0,scrollPaddingRight:parseFloat(t.scrollPaddingRight)||0,scrollPaddingBottom:parseFloat(t.scrollPaddingBottom)||0,scrollPaddingLeft:parseFloat(t.scrollPaddingLeft)||0}}var qh={...Sf,...Rd,nestedDialogOpen(e){return e?{[Eh.nestedDialogOpen]:``}:null}},Jh=P.forwardRef(function(e,t){let{className:n,finalFocus:r,initialFocus:i,render:a,style:o,...s}=e,{store:c}=ff(),l=c.useState(`descriptionElementId`),u=c.useState(`disablePointerDismissal`),d=c.useState(`floatingRootContext`),f=c.useState(`popupProps`),p=c.useState(`modal`),m=c.useState(`mounted`),h=c.useState(`nested`),g=c.useState(`nestedOpenDialogCount`),_=c.useState(`open`),v=c.useState(`openMethod`),y=c.useState(`titleElementId`),b=c.useState(`transitionStatus`),x=c.useState(`role`);Oh(),Bd({open:_,ref:c.context.popupRef,onComplete(){_&&c.context.onOpenChangeComplete?.(!0)}});function S(e){return e===`touch`?c.context.popupRef.current:!0}let C=i===void 0?S:i,w=Za(`div`,e,{state:{open:_,nested:h,transitionStatus:b,nestedDialogOpen:g>0},props:[f,{"aria-labelledby":y??void 0,"aria-describedby":l??void 0,role:x,tabIndex:-1,hidden:!m,onKeyDown(e){Bh.has(e.key)&&e.stopPropagation()},style:{[Th.nestedDialogs]:g}},s],ref:[t,c.context.popupRef,c.useStateSetter(`popupElement`)],stateAttributesMapping:qh});return(0,F.jsx)(Tp,{context:d,openInteractionType:v,disabled:!m,closeOnFocusOut:!u,initialFocus:C,returnFocus:r,modal:p!==!1,restoreFocus:`popup`,children:w})});function Yh(e){return Ua(19)?e:e?`true`:void 0}var Xh=P.forwardRef(function(e,t){let{cutout:n,...r}=e,i;if(n){let e=n.getBoundingClientRect();i=`polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${e.left}px ${e.top}px,${e.left}px ${e.bottom}px,${e.right}px ${e.bottom}px,${e.right}px ${e.top}px,${e.left}px ${e.top}px)`}return(0,F.jsx)(`div`,{ref:t,role:`presentation`,"data-base-ui-inert":``,...r,style:{position:`fixed`,inset:0,userSelect:`none`,WebkitUserSelect:`none`,clipPath:i}})}),Zh=P.forwardRef(function(e,t){let{keepMounted:n=!1,...r}=e,{store:i}=ff(),a=i.useState(`mounted`),o=i.useState(`modal`),s=i.useState(`open`);return a||n?(0,F.jsx)(Dh.Provider,{value:n,children:(0,F.jsxs)(dp,{ref:t,...r,children:[a&&o===!0&&(0,F.jsx)(Xh,{ref:i.context.internalBackdropRef,inert:Yh(!s)}),e.children]})}):null});function Qh(e){let t=P.useRef(!0);t.current&&(t.current=!1,e())}var $h={},eg={},tg=``;function ng(e){if(typeof document>`u`)return!1;let t=Ml(e);return Jo(t).innerWidth-t.documentElement.clientWidth>0}function rg(e){if(!(typeof CSS<`u`&&CSS.supports&&CSS.supports(`scrollbar-gutter`,`stable`))||typeof document>`u`)return!1;let t=Ml(e),n=t.documentElement,r=t.body,i=es(n)?n:r,a=i.style.overflowY,o=n.style.scrollbarGutter;n.style.scrollbarGutter=`stable`,i.style.overflowY=`scroll`;let s=i.offsetWidth;i.style.overflowY=`hidden`;let c=i.offsetWidth;return i.style.overflowY=a,n.style.scrollbarGutter=o,s===c}function ig(e){let t=Ml(e),n=t.documentElement,r=t.body,i=es(n)?n:r,a={overflowY:i.style.overflowY,overflowX:i.style.overflowX};return Object.assign(i.style,{overflowY:`hidden`,overflowX:`hidden`}),()=>{Object.assign(i.style,a)}}function ag(e){let t=Ml(e),n=t.documentElement,r=t.body,i=Jo(n),a=0,o=0,s=!1,c=Md.create();if(Ll&&(i.visualViewport?.scale??1)!==1)return()=>{};function l(){let t=i.getComputedStyle(n),c=i.getComputedStyle(r),l=(t.scrollbarGutter||``).includes(`both-edges`)?`stable both-edges`:`stable`;a=n.scrollTop,o=n.scrollLeft,$h={scrollbarGutter:n.style.scrollbarGutter,overflowY:n.style.overflowY,overflowX:n.style.overflowX},tg=n.style.scrollBehavior,eg={position:r.style.position,height:r.style.height,width:r.style.width,boxSizing:r.style.boxSizing,overflowY:r.style.overflowY,overflowX:r.style.overflowX,scrollBehavior:r.style.scrollBehavior};let u=n.scrollHeight>n.clientHeight,d=n.scrollWidth>n.clientWidth,f=t.overflowY===`scroll`||c.overflowY===`scroll`,p=t.overflowX===`scroll`||c.overflowX===`scroll`,m=Math.max(0,i.innerWidth-r.clientWidth),h=Math.max(0,i.innerHeight-r.clientHeight),g=parseFloat(c.marginTop)+parseFloat(c.marginBottom),_=parseFloat(c.marginLeft)+parseFloat(c.marginRight),v=es(n)?n:r;if(s=rg(e),s){n.style.scrollbarGutter=l,v.style.overflowY=`hidden`,v.style.overflowX=`hidden`;return}Object.assign(n.style,{scrollbarGutter:l,overflowY:`hidden`,overflowX:`hidden`}),(u||f)&&(n.style.overflowY=`scroll`),(d||p)&&(n.style.overflowX=`scroll`),Object.assign(r.style,{position:`relative`,height:g||h?`calc(100dvh - ${g+h}px)`:`100dvh`,width:_||m?`calc(100vw - ${_+m}px)`:`100vw`,boxSizing:`border-box`,overflow:`hidden`,scrollBehavior:`unset`}),r.scrollTop=a,r.scrollLeft=o,n.setAttribute(`data-base-ui-scroll-locked`,``),n.style.scrollBehavior=`unset`}function u(){Object.assign(n.style,$h),Object.assign(r.style,eg),s||(n.scrollTop=a,n.scrollLeft=o,n.removeAttribute(`data-base-ui-scroll-locked`),n.style.scrollBehavior=tg)}function d(){u(),c.request(l)}l();let f=Pf(i,`resize`,d);return()=>{c.cancel(),u(),typeof i.removeEventListener==`function`&&f()}}var og=new class{lockCount=0;restore=null;timeoutLock=Od.create();timeoutUnlock=Od.create();acquire(e){return this.lockCount+=1,this.lockCount===1&&this.restore===null&&this.timeoutLock.start(0,()=>this.lock(e)),this.release}release=()=>{--this.lockCount,this.lockCount===0&&this.restore&&this.timeoutUnlock.start(0,this.unlock)};unlock=()=>{this.lockCount===0&&this.restore&&(this.restore?.(),this.restore=null)};lock(e){if(this.lockCount===0||this.restore!==null)return;let t=Ml(e).documentElement,n=Jo(t).getComputedStyle(t).overflowY;if(n===`hidden`||n===`clip`){this.restore=Ga;return}let r=Rl||!ng(e);this.restore=r?ig(e):ag(e)}};function sg(e=!0,t=null){Cs(()=>{if(e)return og.acquire(t)},[e,t])}function cg(e){let t=P.useRef(``),n=P.useCallback(n=>{n.defaultPrevented||(t.current=n.pointerType,e(n,n.pointerType))},[e]);return{onClick:P.useCallback(n=>{if(n.detail===0){e(n,`keyboard`);return}`pointerType`in n?e(n,n.pointerType):e(n,t.current),t.current=``},[e]),onPointerDown:n}}function lg(e,t){let n=P.useRef(e),r=ys(t);Cs(()=>{n.current!==e&&r(n.current)},[e,r]),Cs(()=>{n.current=e},[e])}function ug(e){let[t,n]=P.useState(null),r=ys((t,r)=>{e||n(r||(Rl?`touch`:``))});lg(e,t=>{t&&!e&&n(null)});let{onClick:i,onPointerDown:a}=cg(r);return P.useMemo(()=>({openMethod:t,triggerProps:{onClick:i,onPointerDown:a}}),[t,i,a])}function dg(e){let{store:t,parentContext:n,actionsRef:r,isDrawer:i}=e,a=t.useState(`open`),o=t.useState(`disablePointerDismissal`),s=t.useState(`modal`),c=t.useState(`popupElement`),{openMethod:l,triggerProps:u}=ug(a);$m(t);let{forceUnmount:d}=eh(a,t),f=P.useCallback(()=>{t.setOpen(!1,af(nf))},[t]);P.useImperativeHandle(r,()=>({unmount:d,close:f}),[d,f]);let p=ch({popupStore:t,onOpenChange:t.setOpen,treatPopupAsFloatingElement:!0}),[m,h]=P.useState(0),[g,_]=P.useState(0),v=m===0,y=hee(p),b=Mp(p,{outsidePressEvent(){return t.context.internalBackdropRef.current||t.context.backdropRef.current?`intentional`:{mouse:s===`trap-focus`?`sloppy`:`intentional`,touch:`sloppy`}},outsidePress(e){if(!t.context.outsidePressEnabledRef.current||`button`in e&&e.button!==0||`touches`in e&&e.touches.length!==1)return!1;let n=eu(e);if(v&&!o){let e=n;return s&&(t.context.internalBackdropRef.current||t.context.backdropRef.current)?t.context.internalBackdropRef.current===e||t.context.backdropRef.current===e||$l(e,c)&&!e?.hasAttribute(`data-base-ui-portal`):!0}return!1},escapeKey:v});sg(a&&s===!0,c);let{getReferenceProps:x,getFloatingProps:S,getTriggerProps:C}=mh([y,b]);t.useContextCallback(`onNestedDialogOpen`,(e,t)=>{h(e),_(t)}),t.useContextCallback(`onNestedDialogClose`,()=>{h(0),_(0)}),P.useEffect(()=>(n?.onNestedDialogOpen&&a&&n.onNestedDialogOpen(m+1,g+ +!!i),n?.onNestedDialogClose&&!a&&n.onNestedDialogClose(),()=>{n?.onNestedDialogClose&&a&&n.onNestedDialogClose()}),[i,a,m,g,n]);let w=P.useMemo(()=>x(u),[x,u]),T=P.useMemo(()=>C(u),[C,u]),E=P.useMemo(()=>S(),[S]);t.useSyncedValues({openMethod:l,activeTriggerProps:w,inactiveTriggerProps:T,popupProps:E,floatingRootContext:p,nestedOpenDialogCount:m,nestedOpenDrawerCount:g})}var fg={...ah,modal:Nm(e=>e.modal),nested:Nm(e=>e.nested),nestedOpenDialogCount:Nm(e=>e.nestedOpenDialogCount),nestedOpenDrawerCount:Nm(e=>e.nestedOpenDrawerCount),disablePointerDismissal:Nm(e=>e.disablePointerDismissal),openMethod:Nm(e=>e.openMethod),descriptionElementId:Nm(e=>e.descriptionElementId),titleElementId:Nm(e=>e.titleElementId),viewportElement:Nm(e=>e.viewportElement),role:Nm(e=>e.role)},pg=class e extends Jm{constructor(e){super(mg(e),{popupRef:P.createRef(),backdropRef:P.createRef(),internalBackdropRef:P.createRef(),outsidePressEnabledRef:{current:!0},triggerElements:new th,onOpenChange:void 0,onOpenChangeComplete:void 0},fg)}setOpen=(e,t)=>{if(t.preventUnmountOnClose=()=>{this.set(`preventUnmountingOnClose`,!0)},!e&&t.trigger==null&&this.state.activeTriggerId!=null&&(t.trigger=this.state.activeTriggerElement??void 0),this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let n={open:e},r=t.trigger?.id??null;(r||e)&&(n.activeTriggerId=r,n.activeTriggerElement=t.trigger??null),this.update(n)};static useStore(t,n){let r=Fa(()=>new e(n)).current;return t??r}};function mg(e={}){return{...rh(),modal:!0,disablePointerDismissal:!1,popupElement:null,viewportElement:null,descriptionElementId:void 0,titleElementId:void 0,openMethod:null,nested:!1,nestedOpenDialogCount:0,nestedOpenDrawerCount:0,role:`dialog`,...e}}var hg=P.createContext(!1);function gg(e){let{children:t,open:n,defaultOpen:r=!1,onOpenChange:i,onOpenChangeComplete:a,disablePointerDismissal:o=!1,modal:s=!0,actionsRef:c,handle:l,triggerId:u,defaultTriggerId:d=null}=e,f=ff(!0),p=P.useContext(hg),m=!!f,h=pg.useStore(l?.store,{open:r,openProp:n,activeTriggerId:d,triggerIdProp:u,modal:s,disablePointerDismissal:o,nested:m});Qh(()=>{n===void 0&&h.state.open===!1&&r===!0&&h.update({open:!0,activeTriggerId:d})}),h.useControlledProp(`openProp`,n),h.useControlledProp(`triggerIdProp`,u),h.useSyncedValues({disablePointerDismissal:o,nested:m,modal:s}),h.useContextCallback(`onOpenChange`,i),h.useContextCallback(`onOpenChangeComplete`,a);let g=h.useState(`payload`);dg({store:h,actionsRef:c,parentContext:f?.store.context,isDrawer:p,onOpenChange:i,triggerIdProp:u});let _=P.useMemo(()=>({store:h}),[h]);return(0,F.jsx)(hg.Provider,{value:!1,children:(0,F.jsx)(df.Provider,{value:_,children:typeof t==`function`?t({payload:g}):t})})}var _g=P.forwardRef(function(e,t){let{render:n,className:r,style:i,id:a,...o}=e,{store:s}=ff(),c=Ol(a);return s.useSyncedValueWithCleanup(`titleElementId`,c),Za(`h2`,e,{ref:t,props:[{id:c},o]})});function vg(e){let t=(0,da.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,F.jsx)(gg,{"data-slot":`sheet`,...n}),t[2]=n,t[3]=r),r}function yg(e){let t=(0,da.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,F.jsx)(Zh,{"data-slot":`sheet-portal`,...n}),t[2]=n,t[3]=r),r}function bg(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`data-closed:fade-out-0 data-open:fade-in-0 data-closed:animate-out data-open:animate-in fixed inset-0 z-50 bg-black/80 duration-100 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(wf,{className:i,"data-slot":`sheet-overlay`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function xg(e){let t=(0,da.c)(17),n,r,i,a,o;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5]):({className:r,children:n,side:a,showCloseButton:o,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o);let s=a===void 0?`right`:a,c=o===void 0?!0:o,l;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,F.jsx)(bg,{}),t[6]=l):l=t[6];let u;t[7]===r?u=t[8]:(u=rl(`data-[side=right]:data-closed:slide-out-to-right-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=top]:data-closed:slide-out-to-top-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:fade-out-0 data-open:fade-in-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=bottom]:data-open:slide-in-from-bottom-10 data-closed:animate-out data-open:animate-in fixed z-50 flex flex-col bg-background bg-clip-padding text-xs/relaxed shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm`,r),t[7]=r,t[8]=u);let d;t[9]===c?d=t[10]:(d=c&&(0,F.jsxs)(Tf,{"data-slot":`sheet-close`,render:(0,F.jsx)(gl,{className:`absolute top-4 right-4`,size:`icon-sm`,variant:`ghost`}),children:[(0,F.jsx)(Go,{}),(0,F.jsx)(`span`,{className:`sr-only`,children:`Close`})]}),t[9]=c,t[10]=d);let f;return t[11]!==n||t[12]!==i||t[13]!==s||t[14]!==u||t[15]!==d?(f=(0,F.jsxs)(yg,{children:[l,(0,F.jsxs)(Jh,{className:u,"data-side":s,"data-slot":`sheet-content`,...i,children:[n,d]})]}),t[11]=n,t[12]=i,t[13]=s,t[14]=u,t[15]=d,t[16]=f):f=t[16],f}function Sg(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`flex flex-col gap-1.5 p-6`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(`div`,{className:i,"data-slot":`sheet-header`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function gee(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`text-sm font-medium text-foreground`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(_g,{className:i,"data-slot":`sheet-title`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function _ee(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`text-xs/relaxed text-muted-foreground`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(Ef,{className:i,"data-slot":`sheet-description`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}var Cg=P.createContext(void 0);function wg(e){let t=P.useContext(Cg);if(t===void 0&&!e)throw Error(Na(72));return t}var vee={...ah,disabled:Nm(e=>e.disabled),instantType:Nm(e=>e.instantType),isInstantPhase:Nm(e=>e.isInstantPhase),trackCursorAxis:Nm(e=>e.trackCursorAxis),disableHoverablePopup:Nm(e=>e.disableHoverablePopup),lastOpenChangeReason:Nm(e=>e.openChangeReason),closeOnClick:Nm(e=>e.closeOnClick),closeDelay:Nm(e=>e.closeDelay),hasViewport:Nm(e=>e.hasViewport)},yee=class e extends Jm{constructor(e){super({...bee(),...e},{popupRef:P.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerElements:new th},vee)}setOpen=(e,t)=>{let n=t.reason,r=n===Kd,i=e&&n===`trigger-focus`,a=!e&&(n===`trigger-press`||n===`escape-key`);if(t.preventUnmountOnClose=()=>{this.set(`preventUnmountingOnClose`,!0)},this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let o=()=>{let r={open:e,openChangeReason:n};i?r.instantType=`focus`:a?r.instantType=`dismiss`:n===`trigger-hover`&&(r.instantType=void 0);let o=t.trigger?.id??null;(o||e)&&(r.activeTriggerId=o,r.activeTriggerElement=t.trigger??null),this.update(r)};r?hi.flushSync(o):o()};static useStore(t,n){let r=Fa(()=>new e(n)).current,i=t??r,a=ch({popupStore:i,onOpenChange:i.setOpen});return i.state.floatingRootContext=a,i}};function bee(){return{...rh(),disabled:!1,instantType:void 0,isInstantPhase:!1,trackCursorAxis:`none`,disableHoverablePopup:!1,openChangeReason:null,closeOnClick:!0,closeDelay:0,hasViewport:!1}}var xee=Rm(function(e){let{disabled:t=!1,defaultOpen:n=!1,open:r,disableHoverablePopup:i=!1,trackCursorAxis:a=`none`,actionsRef:o,onOpenChange:s,onOpenChangeComplete:c,handle:l,triggerId:u,defaultTriggerId:d=null,children:f}=e,p=yee.useStore(l?.store,{open:n,openProp:r,activeTriggerId:d,triggerIdProp:u});Qh(()=>{r===void 0&&p.state.open===!1&&n===!0&&p.update({open:!0,activeTriggerId:d})}),p.useControlledProp(`openProp`,r),p.useControlledProp(`triggerIdProp`,u),p.useContextCallback(`onOpenChange`,s),p.useContextCallback(`onOpenChangeComplete`,c);let m=p.useState(`open`),h=!t&&m,g=p.useState(`activeTriggerId`),_=p.useState(`payload`);p.useSyncedValues({trackCursorAxis:a,disableHoverablePopup:i}),Cs(()=>{m&&t&&p.setOpen(!1,af(tf))},[m,t,p]),p.useSyncedValue(`disabled`,t),$m(p);let{forceUnmount:v,transitionStatus:y}=eh(h,p),b=p.select(`floatingRootContext`),x=p.useState(`isInstantPhase`),S=p.useState(`instantType`),C=p.useState(`lastOpenChangeReason`),w=P.useRef(null);Cs(()=>{y===`ending`&&C===`none`||y!==`ending`&&x?(S!==`delay`&&(w.current=S),p.set(`instantType`,`delay`)):w.current!==null&&(p.set(`instantType`,w.current),w.current=null)},[y,x,C,S,p]),Cs(()=>{h&&(g??p.set(`payload`,void 0))},[p,g,h]);let T=P.useCallback(()=>{p.setOpen(!1,af(nf))},[p]);P.useImperativeHandle(o,()=>({unmount:v,close:T}),[v,T]);let{getReferenceProps:E,getFloatingProps:ee,getTriggerProps:D}=mh([Mp(b,{enabled:!t,referencePress:()=>p.select(`closeOnClick`)}),Op(b,{enabled:!t&&a!==`none`,axis:a===`none`?void 0:a})]),O=P.useMemo(()=>E(),[E]),te=P.useMemo(()=>D(),[D]),ne=P.useMemo(()=>ee(),[ee]);return p.useSyncedValues({activeTriggerProps:O,inactiveTriggerProps:te,popupProps:ne}),(0,F.jsx)(Cg.Provider,{value:p,children:typeof f==`function`?f({payload:_}):f})}),Tg=P.createContext(void 0);function See(){return P.useContext(Tg)}var Cee=function(e){return e[e.popupOpen=mf.popupOpen]=`popupOpen`,e.triggerDisabled=`data-trigger-disabled`,e}({}),wee=zm(function(e,t){let{className:n,render:r,handle:i,payload:a,disabled:o,delay:s,closeOnClick:c=!0,closeDelay:l,id:u,style:d,...f}=e,p=wg(!0),m=i?.store??p;if(!m)throw Error(Na(82));let h=Ol(u),g=m.useState(`isTriggerActive`,h),_=m.useState(`isOpenedByTrigger`,h),v=m.useState(`floatingRootContext`),y=P.useRef(null),b=s??600,x=l??0,{registerTrigger:S,isMountedByThisTrigger:C}=Qm(h,y,m,{payload:a,closeOnClick:c,closeDelay:x}),w=See(),{delayRef:T,isInstantPhase:E,hasProvider:ee}=Nf(v,{open:_});m.useSyncedValue(`isInstantPhase`,E);let D=m.useState(`disabled`),O=o??D,te=m.useState(`trackCursorAxis`),ne=m.useState(`disableHoverablePopup`),re=pee(v,{enabled:!O,mouseOnly:!0,move:!1,handleClose:!ne&&te!==`both`?wh():null,restMs(){let e=w?.delay,t=typeof T.current==`object`?T.current.open:void 0,n=b;return ee&&(n=t===0?0:s??e??b),n},delay(){let e=typeof T.current==`object`?T.current.close:void 0,t=x;return l==null&&ee&&(t=e),{close:t}},triggerElementRef:y,isActiveTrigger:g,isClosing:()=>m.select(`transitionStatus`)===`ending`}),k=lee(v,{enabled:!O}).reference,A={open:_},ie=m.useState(`triggerProps`,C);return Za(`button`,e,{state:A,ref:[t,S,y],props:[re,k,ie,{onPointerDown(){m.set(`closeOnClick`,c)},id:h,[Cee.triggerDisabled]:O?``:void 0},f],stateAttributesMapping:bf})}),Eg=P.createContext(void 0);function Tee(){let e=P.useContext(Eg);if(e===void 0)throw Error(Na(70));return e}var Eee=P.forwardRef(function(e,t){let{children:n,container:r,className:i,render:a,style:o,...s}=e,{portalNode:c,portalSubtree:l}=up({container:r,ref:t,componentProps:e,elementProps:s});return!l&&!c?null:(0,F.jsxs)(P.Fragment,{children:[l,c&&hi.createPortal(n,c)]})}),Dee=P.forwardRef(function(e,t){let{keepMounted:n=!1,...r}=e;return wg().useState(`mounted`)||n?(0,F.jsx)(Eg.Provider,{value:n,children:(0,F.jsx)(Eee,{ref:t,...r})}):null}),Dg=P.createContext(void 0);function Og(){let e=P.useContext(Dg);if(e===void 0)throw Error(Na(71));return e}var Oee=P.createContext(void 0);function kg(){return P.useContext(Oee)?.direction??`ltr`}var kee=e=>({name:`arrow`,options:e,async fn(t){let{x:n,y:r,placement:i,rects:a,platform:o,elements:s,middlewareData:c}=t,{element:l,padding:u=0,offsetParent:d=`real`}=Tu(e,t)||{};if(l==null)return{};let f=Uu(u),p={x:n,y:r},m=ju(i),h=ku(m),g=await o.getDimensions(l),_=m===`y`,v=_?`top`:`left`,y=_?`bottom`:`right`,b=_?`clientHeight`:`clientWidth`,x=a.reference[h]+a.reference[m]-p[m]-a.floating[h],S=p[m]-a.reference[m],C=d===`real`?await o.getOffsetParent?.(l):s.floating,w=s.floating[b]||a.floating[h];(!w||!await o.isElement?.(C))&&(w=s.floating[b]||a.floating[h]);let T=x/2-S/2,E=w/2-g[h]/2-1,ee=Math.min(f[v],E),D=Math.min(f[y],E),O=ee,te=w-g[h]-D,ne=w/2-g[h]/2+T,re=wu(O,ne,te),k=!c.arrow&&Du(i)!=null&&ne!==re&&a.reference[h]/2-(ne({...kee(e),options:[e,t]}),jee={name:`hide`,async fn(e){let{width:t,height:n,x:r,y:i}=e.rects.reference,a=t===0&&n===0&&r===0&&i===0;return{data:{referenceHidden:(await Mm().fn(e)).data?.referenceHidden||a}}}},Ag={sideX:`left`,sideY:`top`},Mee={name:`adaptiveOrigin`,async fn(e){let{x:t,y:n,rects:{floating:r},elements:{floating:i},platform:a,strategy:o,placement:s}=e,c=Jo(i),l=c.getComputedStyle(i);if(!(l.transitionDuration!==`0s`&&l.transitionDuration!==``))return{x:t,y:n,data:Ag};let u=await a.getOffsetParent?.(i),d={width:0,height:0};if(o===`fixed`&&c?.visualViewport)d={width:c.visualViewport.width,height:c.visualViewport.height};else if(u===c){let e=Ml(i);d={width:e.documentElement.clientWidth,height:e.documentElement.clientHeight}}else await a.isElement?.(u)&&(d=await a.getDimensions(u));let f=Eu(s),p=t,m=n;f===`left`&&(p=d.width-(t+r.width)),f===`top`&&(m=d.height-(n+r.height));let h=f===`left`?`right`:Ag.sideX,g=f===`top`?`bottom`:Ag.sideY;return{x:p,y:m,data:{sideX:h,sideY:g}}}};function jg(e,t,n){let r=e===`inline-start`||e===`inline-end`;return{top:`top`,right:r?n?`inline-start`:`inline-end`:`right`,bottom:`bottom`,left:r?n?`inline-end`:`inline-start`:`left`}[t]}function Mg(e,t,n){let{rects:r,placement:i}=e;return{side:jg(t,Eu(i),n),align:Du(i)||`center`,anchor:{width:r.reference.width,height:r.reference.height},positioner:{width:r.floating.width,height:r.floating.height}}}function Ng(e){let{anchor:t,positionMethod:n=`absolute`,side:r=`bottom`,sideOffset:i=0,align:a=`center`,alignOffset:o=0,collisionBoundary:s,collisionPadding:c=5,sticky:l=!1,arrowPadding:u=5,disableAnchorTracking:d=!1,keepMounted:f=!1,floatingRootContext:p,mounted:m,collisionAvoidance:h,shiftCrossAxis:g=!1,nodeId:_,adaptiveOrigin:v,lazyFlip:y=!1,externalTree:b}=e,[x,S]=P.useState(null);!m&&x!==null&&S(null);let C=h.side||`flip`,w=h.align||`flip`,T=h.fallbackAxisSide||`end`,E=typeof t==`function`?t:void 0,ee=ys(E),D=E?ee:t,O=If(t),te=If(m),ne=kg()===`rtl`,re=x||{top:`top`,right:`right`,bottom:`bottom`,left:`left`,"inline-end":ne?`left`:`right`,"inline-start":ne?`right`:`left`}[r],k=a===`center`?re:`${re}-${a}`,A=c,ie=+(r===`bottom`),j=+(r===`top`),ae=+(r===`right`),oe=+(r===`left`);typeof A==`number`?A={top:A+ie,right:A+oe,bottom:A+j,left:A+ae}:A&&={top:(A.top||0)+ie,right:(A.right||0)+oe,bottom:(A.bottom||0)+j,left:(A.left||0)+ae};let se={boundary:s===`clipping-ancestors`?`clippingAncestors`:s,padding:A},ce=P.useRef(null),M=If(i),le=If(o),ue=[iee(e=>{let t=Mg(e,r,ne),n=typeof M.current==`function`?M.current(t):M.current,i=typeof le.current==`function`?le.current(t):le.current;return{mainAxis:n,crossAxis:i,alignmentAxis:i}},[typeof i==`function`?0:i,typeof o==`function`?0:o,ne,r])],N=w===`none`&&C!==`shift`,de=!N&&(l||g||C===`shift`),fe=C===`none`?null:see({...se,padding:{top:A.top+1,right:A.right+1,bottom:A.bottom+1,left:A.left+1},mainAxis:!g&&C===`flip`,crossAxis:w===`flip`?`alignment`:!1,fallbackAxisSideDirection:T}),pe=N?null:aee(e=>{let t=Ml(e.elements.floating).documentElement;return{...se,rootBoundary:g?{x:0,y:0,width:t.clientWidth,height:t.clientHeight}:void 0,mainAxis:w!==`none`,crossAxis:de,limiter:l||g?void 0:oee(e=>{if(!ce.current)return{};let{width:t,height:n}=ce.current.getBoundingClientRect(),r=Au(Eu(e.placement)),i=r===`y`?t:n,a=r===`y`?A.left+A.right:A.top+A.bottom;return{offset:i/2+a/2}})}},[se,l,g,A,w]);C===`shift`||w===`shift`||a===`center`?ue.push(pe,fe):ue.push(fe,pe),ue.push(cee({...se,apply({elements:{floating:e},availableWidth:t,availableHeight:n,rects:r}){if(!te.current)return;let i=e.style;i.setProperty(`--available-width`,`${t}px`),i.setProperty(`--available-height`,`${n}px`);let a=Jo(e).devicePixelRatio||1,{x:o,y:s,width:c,height:l}=r.reference,u=(Math.round((o+c)*a)-Math.round(o*a))/a,d=(Math.round((s+l)*a)-Math.round(s*a))/a;i.setProperty(`--anchor-width`,`${u}px`),i.setProperty(`--anchor-height`,`${d}px`)}}),Aee(()=>({element:ce.current||Ml(ce.current).createElement(`div`),padding:u,offsetParent:`floating`}),[u]),{name:`transformOrigin`,fn(e){let{elements:t,middlewareData:n,placement:a,rects:o,y:s}=e,c=Eu(a),l=Au(c),u=ce.current,d=n.arrow?.x||0,f=n.arrow?.y||0,p=u?.clientWidth||0,m=u?.clientHeight||0,h=d+p/2,g=f+m/2,_=Math.abs(n.shift?.y||0),v=o.reference.height/2,y=typeof i==`function`?i(Mg(e,r,ne)):i,b=_>y,x={top:`${h}px calc(100% + ${y}px)`,bottom:`${h}px ${-y}px`,left:`calc(100% + ${y}px) ${g}px`,right:`${-y}px ${g}px`}[c],S=`${h}px ${o.reference.y+v-s}px`;return t.floating.style.setProperty(`--transform-origin`,de&&l===`y`&&b?S:x),{}}},jee,v),Cs(()=>{!m&&p&&p.update({referenceElement:null,floatingElement:null,domReferenceElement:null,positionReference:null})},[m,p]);let me=P.useMemo(()=>({elementResize:!d&&typeof ResizeObserver<`u`,layoutShift:!d&&typeof IntersectionObserver<`u`}),[d]),{refs:he,elements:ge,x:_e,y:ve,middlewareData:ye,update:be,placement:xe,context:Se,isPositioned:Ce,floatingStyles:we}=sh({rootContext:p,open:f?m:void 0,placement:k,middleware:ue,strategy:n,whileElementsMounted:f?void 0:(...e)=>ym(...e,me),nodeId:_,externalTree:b}),{sideX:Te,sideY:Ee}=ye.adaptiveOrigin||Ag,De=Ce?n:`fixed`,Oe=P.useMemo(()=>{let e=v?{position:De,[Te]:_e,[Ee]:ve}:{position:De,...we};return Ce||(e.opacity=0),e},[v,De,Te,_e,Ee,ve,we,Ce]),ke=P.useRef(null);Cs(()=>{if(!m)return;let e=O.current,t=typeof e==`function`?e():e,n=(Pg(t)?t.current:t)||null;n!==ke.current&&(he.setPositionReference(n),ke.current=n)},[m,he,D,O]),P.useEffect(()=>{if(!m)return;let e=O.current;typeof e!=`function`&&Pg(e)&&e.current!==ke.current&&(he.setPositionReference(e.current),ke.current=e.current)},[m,he,D,O]),P.useEffect(()=>{if(f&&m&&ge.domReference&&ge.floating)return ym(ge.domReference,ge.floating,be,me)},[f,m,ge,be,me]);let Ae=Eu(xe),je=jg(r,Ae,ne),Me=Du(xe)||`center`,Ne=!!ye.hide?.referenceHidden;Cs(()=>{y&&m&&Ce&&S(Ae)},[y,m,Ce,Ae]);let F=P.useMemo(()=>({position:`absolute`,top:ye.arrow?.y,left:ye.arrow?.x}),[ye.arrow]),Pe=ye.arrow?.centerOffset!==0;return P.useMemo(()=>({positionerStyles:Oe,arrowStyles:F,arrowRef:ce,arrowUncentered:Pe,side:je,align:Me,physicalSide:Ae,anchorHidden:Ne,refs:he,context:Se,isPositioned:Ce,update:be}),[Oe,F,ce,Pe,je,Me,Ae,Ne,he,Se,Ce,be])}function Pg(e){return e!=null&&`current`in e}function Fg(e){return e===`starting`?rp:qa}function Ig(e,t,{styles:n,transitionStatus:r,props:i,refs:a,hidden:o,inert:s=!1}){let c={...n};return s&&(c.pointerEvents=`none`),Za(`div`,e,{state:t,ref:a,props:[{role:`presentation`,hidden:o,style:c},Fg(r),i],stateAttributesMapping:Sf})}var Nee=P.forwardRef(function(e,t){let{render:n,className:r,anchor:i,positionMethod:a=`absolute`,side:o=`top`,align:s=`center`,sideOffset:c=0,alignOffset:l=0,collisionBoundary:u=`clipping-ancestors`,collisionPadding:d=5,arrowPadding:f=5,sticky:p=!1,disableAnchorTracking:m=!1,collisionAvoidance:h=ap,style:g,..._}=e,v=wg(),y=Tee(),b=v.useState(`open`),x=v.useState(`mounted`),S=v.useState(`trackCursorAxis`),C=v.useState(`disableHoverablePopup`),w=v.useState(`floatingRootContext`),T=v.useState(`instantType`),E=v.useState(`transitionStatus`),ee=Ng({anchor:i,positionMethod:a,floatingRootContext:w,mounted:x,side:o,sideOffset:c,align:s,alignOffset:l,collisionBoundary:u,collisionPadding:d,sticky:p,arrowPadding:f,disableAnchorTracking:m,keepMounted:y,collisionAvoidance:h,adaptiveOrigin:v.useState(`hasViewport`)?Mee:void 0}),D=Ig(e,P.useMemo(()=>({open:b,side:ee.side,align:ee.align,anchorHidden:ee.anchorHidden,instant:S===`none`?T:`tracking-cursor`}),[b,ee.side,ee.align,ee.anchorHidden,S,T]),{styles:ee.positionerStyles,transitionStatus:E,props:_,refs:[t,v.useStateSetter(`positionerElement`)],hidden:!x,inert:!b||S===`both`||C});return(0,F.jsx)(Dg.Provider,{value:ee,children:D})}),Pee={...Sf,...Rd},Fee=P.forwardRef(function(e,t){let{className:n,render:r,style:i,...a}=e,o=wg(),{side:s,align:c}=Og(),l=o.useState(`open`),u=o.useState(`instantType`),d=o.useState(`transitionStatus`),f=o.useState(`popupProps`),p=o.useState(`floatingRootContext`);Bd({open:l,ref:o.context.popupRef,onComplete(){l&&o.context.onOpenChangeComplete?.(!0)}});let m=o.useState(`disabled`),h=o.useState(`closeDelay`);return dee(p,{enabled:!m,closeDelay:h}),Za(`div`,e,{state:{open:l,side:s,align:c,instant:u,transitionStatus:d},ref:[t,o.context.popupRef,o.useStateSetter(`popupElement`)],props:[f,Fg(d),a],stateAttributesMapping:Pee})}),Iee=P.forwardRef(function(e,t){let{className:n,render:r,style:i,...a}=e,o=wg(),s=o.useState(`open`),c=o.useState(`instantType`),{arrowRef:l,side:u,align:d,arrowUncentered:f,arrowStyles:p}=Og();return Za(`div`,e,{state:{open:s,side:u,align:d,uncentered:f,instant:c},ref:[t,l],props:[{style:p,"aria-hidden":!0},a],stateAttributesMapping:Sf})}),Lee=function(e){let{delay:t,closeDelay:n,timeout:r=400}=e,i=P.useMemo(()=>({delay:t,closeDelay:n}),[t,n]),a=P.useMemo(()=>({open:t,close:n}),[t,n]);return(0,F.jsx)(Tg.Provider,{value:i,children:(0,F.jsx)(Mf,{delay:a,timeoutMs:r,children:e.children})})};function Ree(e){let t=(0,da.c)(6),n,r;t[0]===e?(n=t[1],r=t[2]):({delay:r,...n}=e,t[0]=e,t[1]=n,t[2]=r);let i=r===void 0?0:r,a;return t[3]!==i||t[4]!==n?(a=(0,F.jsx)(Lee,{"data-slot":`tooltip-provider`,delay:i,...n}),t[3]=i,t[4]=n,t[5]=a):a=t[5],a}function zee(e){let t=(0,da.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,F.jsx)(Ree,{children:(0,F.jsx)(xee,{"data-slot":`tooltip`,...n})}),t[2]=n,t[3]=r),r}function Bee(e){let t=(0,da.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,F.jsx)(wee,{"data-slot":`tooltip-trigger`,...n}),t[2]=n,t[3]=r),r}function Vee(e){let t=(0,da.c)(21),n,r,i,a,o,s,c;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6],c=t[7]):({className:r,side:a,sideOffset:o,align:s,alignOffset:c,children:n,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o,t[6]=s,t[7]=c);let l=a===void 0?`top`:a,u=o===void 0?4:o,d=s===void 0?`center`:s,f=c===void 0?0:c,p;t[8]===r?p=t[9]:(p=rl(`data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:fade-out-0 data-closed:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=delayed-open]:animate-in data-closed:animate-out data-open:animate-in z-50 w-fit max-w-xs origin-(--transform-origin) rounded-md bg-foreground px-3 py-1.5 text-xs text-background **:data-[slot=kbd]:rounded-md`,r),t[8]=r,t[9]=p);let m;t[10]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,F.jsx)(Iee,{className:`z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5`}),t[10]=m):m=t[10];let h;t[11]!==n||t[12]!==i||t[13]!==p?(h=(0,F.jsxs)(Fee,{className:p,"data-slot":`tooltip-content`,...i,children:[n,m]}),t[11]=n,t[12]=i,t[13]=p,t[14]=h):h=t[14];let g;return t[15]!==d||t[16]!==f||t[17]!==l||t[18]!==u||t[19]!==h?(g=(0,F.jsx)(Dee,{children:(0,F.jsx)(Nee,{align:d,alignOffset:f,className:`isolate z-50`,side:l,sideOffset:u,children:h})}),t[15]=d,t[16]=f,t[17]=l,t[18]=u,t[19]=h,t[20]=g):g=t[20],g}var Lg=window.matchMedia(`(max-width: 767px)`);function Hee(e){return Lg.addEventListener(`change`,e),()=>Lg.removeEventListener(`change`,e)}function Uee(){return Lg.matches}function Wee(){return(0,P.useSyncExternalStore)(Hee,Uee)}var Gee=`sidebar_state`,Kee=3600*24*7,qee=`12rem`,Jee=`16rem`,Yee=`3rem`,Xee=`b`,Rg=P.createContext(null);function zg(){let e=P.useContext(Rg);if(!e)throw Error(`useSidebar must be used within a SidebarProvider.`);return e}function Bg(e){let t=(0,da.c)(36),n,r,i,a,o,s,c;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6],c=t[7]):({defaultOpen:c,open:i,onOpenChange:o,className:r,style:s,children:n,...a}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o,t[6]=s,t[7]=c);let l=c===void 0?!0:c,u=Wee(),[d,f]=P.useState(!1),[p,m]=P.useState(l),h=i??p,g;t[8]!==h||t[9]!==o?(g=e=>{let t=typeof e==`function`?e(h):e;o?o(t):m(t),document.cookie=`${Gee}=${t}; path=/; max-age=${Kee}`},t[8]=h,t[9]=o,t[10]=g):g=t[10];let _=g,v;t[11]!==u||t[12]!==_?(v=()=>u?f(Hg):_(Vg),t[11]=u,t[12]=_,t[13]=v):v=t[13];let y=v,b,x;t[14]===y?(b=t[15],x=t[16]):(b=()=>{let e=e=>{e.key===Xee&&(e.metaKey||e.ctrlKey)&&(e.preventDefault(),y())};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},x=[y],t[14]=y,t[15]=b,t[16]=x),P.useEffect(b,x);let S=h?`expanded`:`collapsed`,C;t[17]!==u||t[18]!==h||t[19]!==d||t[20]!==_||t[21]!==S||t[22]!==y?(C={state:S,open:h,setOpen:_,isMobile:u,openMobile:d,setOpenMobile:f,toggleSidebar:y},t[17]=u,t[18]=h,t[19]=d,t[20]=_,t[21]=S,t[22]=y,t[23]=C):C=t[23];let w=C,T;t[24]===r?T=t[25]:(T=rl(`group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar`,r),t[24]=r,t[25]=T);let E;t[26]===s?E=t[27]:(E={"--sidebar-width":qee,"--sidebar-width-icon":Yee,...s},t[26]=s,t[27]=E);let ee=E,D;t[28]!==n||t[29]!==a||t[30]!==T||t[31]!==ee?(D=(0,F.jsx)(`div`,{className:T,"data-slot":`sidebar-wrapper`,style:ee,...a,children:n}),t[28]=n,t[29]=a,t[30]=T,t[31]=ee,t[32]=D):D=t[32];let O;return t[33]!==w||t[34]!==D?(O=(0,F.jsx)(Rg.Provider,{value:w,children:D}),t[33]=w,t[34]=D,t[35]=O):O=t[35],O}function Vg(e){return!e}function Hg(e){return!e}function Ug(e){let t=(0,da.c)(46),n,r,i,a,o,s;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6]):({side:a,variant:o,collapsible:s,className:r,children:n,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o,t[6]=s);let c=a===void 0?`left`:a,l=o===void 0?`sidebar`:o,u=s===void 0?`offExamples`:s,{isMobile:d,state:f,openMobile:p,setOpenMobile:m}=zg();if(u===`none`){let e;t[7]===r?e=t[8]:(e=rl(`flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground`,r),t[7]=r,t[8]=e);let a;return t[9]!==n||t[10]!==i||t[11]!==e?(a=(0,F.jsx)(`div`,{className:e,"data-slot":`sidebar`,...i,children:n}),t[9]=n,t[10]=i,t[11]=e,t[12]=a):a=t[12],a}if(d){let e;t[13]===Symbol.for(`react.memo_cache_sentinel`)?(e={"--sidebar-width":Jee},t[13]=e):e=t[13];let r=e,a;t[14]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,F.jsxs)(Sg,{className:`sr-only`,children:[(0,F.jsx)(gee,{children:`Sidebar`}),(0,F.jsx)(_ee,{children:`Displays the mobile sidebar.`})]}),t[14]=a):a=t[14];let o;t[15]===n?o=t[16]:(o=(0,F.jsx)(`div`,{className:`flex h-full w-full flex-col`,children:n}),t[15]=n,t[16]=o);let s;t[17]!==c||t[18]!==o?(s=(0,F.jsxs)(xg,{className:`w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden`,"data-mobile":`true`,"data-sidebar":`sidebar`,"data-slot":`sidebar`,side:c,style:r,children:[a,o]}),t[17]=c,t[18]=o,t[19]=s):s=t[19];let l;return t[20]!==p||t[21]!==i||t[22]!==m||t[23]!==s?(l=(0,F.jsx)(vg,{onOpenChange:m,open:p,...i,children:s}),t[20]=p,t[21]=i,t[22]=m,t[23]=s,t[24]=l):l=t[24],l}let h=f===`collapsed`?u:``,g=l===`floating`||l===`inset`?`group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]`:`group-data-[collapsible=icon]:w-(--sidebar-width-icon)`,_;t[25]===g?_=t[26]:(_=rl(`relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear`,`group-data-[collapsible=offExamples]:w-0`,`group-data-[side=right]:rotate-180`,g),t[25]=g,t[26]=_);let v;t[27]===_?v=t[28]:(v=(0,F.jsx)(`div`,{className:_,"data-slot":`sidebar-gap`}),t[27]=_,t[28]=v);let y=c===`left`?`left-0 group-data-[collapsible=offExamples]:left-[calc(var(--sidebar-width)*-1)]`:`right-0 group-data-[collapsible=offExamples]:right-[calc(var(--sidebar-width)*-1)]`,b=l===`floating`||l===`inset`?`p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]`:`group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l`,x;t[29]!==r||t[30]!==y||t[31]!==b?(x=rl(`fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex`,y,b,r),t[29]=r,t[30]=y,t[31]=b,t[32]=x):x=t[32];let S;t[33]===n?S=t[34]:(S=(0,F.jsx)(`div`,{className:`flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border`,"data-sidebar":`sidebar`,"data-slot":`sidebar-inner`,children:n}),t[33]=n,t[34]=S);let C;t[35]!==i||t[36]!==x||t[37]!==S?(C=(0,F.jsx)(`div`,{className:x,"data-slot":`sidebar-container`,...i,children:S}),t[35]=i,t[36]=x,t[37]=S,t[38]=C):C=t[38];let w;return t[39]!==c||t[40]!==f||t[41]!==C||t[42]!==h||t[43]!==v||t[44]!==l?(w=(0,F.jsxs)(`div`,{className:`group peer hidden text-sidebar-foreground md:block`,"data-collapsible":h,"data-side":c,"data-slot":`sidebar`,"data-state":f,"data-variant":l,children:[v,C]}),t[39]=c,t[40]=f,t[41]=C,t[42]=h,t[43]=v,t[44]=l,t[45]=w):w=t[45],w}function Wg(e){let t=(0,da.c)(15),n,r,i;t[0]===e?(n=t[1],r=t[2],i=t[3]):({className:n,onClick:r,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i);let{toggleSidebar:a}=zg(),o;t[4]===n?o=t[5]:(o=rl(n),t[4]=n,t[5]=o);let s;t[6]!==r||t[7]!==a?(s=e=>{r?.(e),a()},t[6]=r,t[7]=a,t[8]=s):s=t[8];let c,l;t[9]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,F.jsx)(Io,{}),l=(0,F.jsx)(`span`,{className:`sr-only`,children:`Toggle Sidebar`}),t[9]=c,t[10]=l):(c=t[9],l=t[10]);let u;return t[11]!==i||t[12]!==o||t[13]!==s?(u=(0,F.jsxs)(gl,{className:o,"data-sidebar":`trigger`,"data-slot":`sidebar-trigger`,onClick:s,size:`icon-lg`,variant:`ghost`,...i,children:[c,l]}),t[11]=i,t[12]=o,t[13]=s,t[14]=u):u=t[14],u}function Gg(e){let t=(0,da.c)(9),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let{toggleSidebar:i}=zg(),a;t[3]===n?a=t[4]:(a=rl(`absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex`,`in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize`,`[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize`,`group-data-[collapsible=offExamples]:translate-x-0 group-data-[collapsible=offExamples]:after:left-full hover:group-data-[collapsible=offExamples]:bg-sidebar`,`[[data-side=left][data-collapsible=offExamples]_&]:-right-2`,`[[data-side=right][data-collapsible=offExamples]_&]:-left-2`,n),t[3]=n,t[4]=a);let o;return t[5]!==r||t[6]!==a||t[7]!==i?(o=(0,F.jsx)(`button`,{"aria-label":`Toggle Sidebar`,className:a,"data-sidebar":`rail`,"data-slot":`sidebar-rail`,onClick:i,tabIndex:-1,title:`Toggle Sidebar`,...r}),t[5]=r,t[6]=a,t[7]=i,t[8]=o):o=t[8],o}function Kg(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`flex flex-col gap-2 p-2`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(`div`,{className:i,"data-sidebar":`header`,"data-slot":`sidebar-header`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function qg(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`flex flex-col gap-2 p-2`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(`div`,{className:i,"data-sidebar":`footer`,"data-slot":`sidebar-footer`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Jg(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`mx-2 w-auto bg-sidebar-border`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(uf,{className:i,"data-sidebar":`separator`,"data-slot":`sidebar-separator`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Yg(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`no-scrollbar flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(`div`,{className:i,"data-sidebar":`content`,"data-slot":`sidebar-content`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Xg(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`relative flex w-full min-w-0 flex-col px-2 py-1`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(`div`,{className:i,"data-sidebar":`group`,"data-slot":`sidebar-group`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Zg(e){let t=(0,da.c)(10),n,r,i,a;if(t[0]!==e){let{className:o,render:s,...c}=e;n=s,a=ro,r=`div`,i=va({className:rl(`flex h-8 shrink-0 items-center rounded-md px-2 text-xs text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0`,o)},c),t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a}else n=t[1],r=t[2],i=t[3],a=t[4];let o;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(o={slot:`sidebar-group-label`,sidebar:`group-label`},t[5]=o):o=t[5];let s;return t[6]!==n||t[7]!==r||t[8]!==i?(s={defaultTagName:r,props:i,render:n,state:o},t[6]=n,t[7]=r,t[8]=i,t[9]=s):s=t[9],a(s)}function Qg(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`flex w-full min-w-0 flex-col gap-px`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(`ul`,{className:i,"data-sidebar":`menu`,"data-slot":`sidebar-menu`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function $g(e){let t=(0,da.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=rl(`group/menu-item relative`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,F.jsx)(`li`,{className:i,"data-sidebar":`menu-item`,"data-slot":`sidebar-menu-item`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}var e_=co(`peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-[calc(var(--radius-sm)+2px)] p-2 text-left text-xs ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate`,{variants:{variant:{default:`hover:bg-sidebar-accent hover:text-sidebar-accent-foreground`,outline:`bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]`},size:{default:`h-8 text-xs`,sm:`h-7 text-xs`,lg:`h-12 text-xs group-data-[collapsible=icon]:p-0!`}},defaultVariants:{variant:`default`,size:`default`}});function t_(e){let t=(0,da.c)(15),{render:n,isActive:r,variant:i,size:a,tooltip:o,className:s,...c}=e,l=r===void 0?!1:r,u=i===void 0?`default`:i,d=a===void 0?`default`:a,{isMobile:f,state:p}=zg(),m=va({className:rl(e_({variant:u,size:d}),s)},c),h=o?Bee:n,g;t[0]!==l||t[1]!==d?(g={slot:`sidebar-menu-button`,sidebar:`menu-button`,size:d,active:l},t[0]=l,t[1]=d,t[2]=g):g=t[2];let _;t[3]!==m||t[4]!==h||t[5]!==g?(_={defaultTagName:`button`,props:m,render:h,state:g},t[3]=m,t[4]=h,t[5]=g,t[6]=_):_=t[6];let v=ro(_);if(!o)return v;let y;t[7]===o?y=t[8]:(y=typeof o==`string`?{children:o}:o,t[7]=o,t[8]=y);let b=y,x=p!==`collapsed`||f,S;t[9]!==x||t[10]!==b?(S=(0,F.jsx)(Vee,{align:`center`,hidden:x,side:`right`,...b}),t[9]=x,t[10]=b,t[11]=S):S=t[11];let C;return t[12]!==v||t[13]!==S?(C=(0,F.jsxs)(zee,{children:[v,S]}),t[12]=v,t[13]=S,t[14]=C):C=t[14],C}var n_;function V(e,t,n){function r(n,r){if(n._zod||Object.defineProperty(n,`_zod`,{value:{def:r,constr:o,traits:new Set},enumerable:!1}),n._zod.traits.has(e))return;n._zod.traits.add(e),t(n,r);let i=o.prototype,a=Object.keys(i);for(let e=0;en?.Parent&&t instanceof n.Parent?!0:t?._zod?.traits?.has(e)}),Object.defineProperty(o,`name`,{value:e}),o}var r_=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}},i_=class extends Error{constructor(e){super(`Encountered unidirectional transform during encode: ${e}`),this.name=`ZodEncodeError`}};(n_=globalThis).__zod_globalConfig??(n_.__zod_globalConfig={});var a_=globalThis.__zod_globalConfig;function o_(e){return e&&Object.assign(a_,e),a_}function s_(e){let t=Object.values(e).filter(e=>typeof e==`number`);return Object.entries(e).filter(([e,n])=>t.indexOf(+e)===-1).map(([e,t])=>t)}function c_(e,t){return typeof t==`bigint`?t.toString():t}function l_(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function u_(e){return e==null}function d_(e){let t=+!!e.startsWith(`^`),n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}function f_(e,t){let n=e/t,r=Math.round(n),i=2**-52*Math.max(Math.abs(n),1);return Math.abs(n-r){};function b_(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}var x_=l_(()=>{if(a_.jitless||typeof navigator<`u`&&navigator?.userAgent?.includes(`Cloudflare`))return!1;try{return Function(``),!0}catch{return!1}});function S_(e){if(b_(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(b_(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}function C_(e){return S_(e)?{...e}:Array.isArray(e)?[...e]:e instanceof Map?new Map(e):e instanceof Set?new Set(e):e}var w_=new Set([`string`,`number`,`symbol`]);function T_(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function E_(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function D_(e){let t=e;if(!t)return{};if(typeof t==`string`)return{error:()=>t};if(t?.message!==void 0){if(t?.error!==void 0)throw Error("Cannot specify both `message` and `error` params");t.error=t.message}return delete t.message,typeof t.error==`string`?{...t,error:()=>t.error}:t}function O_(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}var k_={safeint:[-(2**53-1),2**53-1],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};function A_(e,t){let n=e._zod.def,r=n.checks;if(r&&r.length>0)throw Error(`.pick() cannot be used on object schemas containing refinements`);return E_(e,g_(e._zod.def,{get shape(){let e={};for(let r in t){if(!(r in n.shape))throw Error(`Unrecognized key: "${r}"`);t[r]&&(e[r]=n.shape[r])}return h_(this,`shape`,e),e},checks:[]}))}function j_(e,t){let n=e._zod.def,r=n.checks;if(r&&r.length>0)throw Error(`.omit() cannot be used on object schemas containing refinements`);return E_(e,g_(e._zod.def,{get shape(){let r={...e._zod.def.shape};for(let e in t){if(!(e in n.shape))throw Error(`Unrecognized key: "${e}"`);t[e]&&delete r[e]}return h_(this,`shape`,r),r},checks:[]}))}function M_(e,t){if(!S_(t))throw Error(`Invalid input to extend: expected a plain object`);let n=e._zod.def.checks;if(n&&n.length>0){let n=e._zod.def.shape;for(let e in t)if(Object.getOwnPropertyDescriptor(n,e)!==void 0)throw Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}return E_(e,g_(e._zod.def,{get shape(){let n={...e._zod.def.shape,...t};return h_(this,`shape`,n),n}}))}function N_(e,t){if(!S_(t))throw Error(`Invalid input to safeExtend: expected a plain object`);return E_(e,g_(e._zod.def,{get shape(){let n={...e._zod.def.shape,...t};return h_(this,`shape`,n),n}}))}function P_(e,t){if(e._zod.def.checks?.length)throw Error(`.merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.`);return E_(e,g_(e._zod.def,{get shape(){let n={...e._zod.def.shape,...t._zod.def.shape};return h_(this,`shape`,n),n},get catchall(){return t._zod.def.catchall},checks:t._zod.def.checks??[]}))}function F_(e,t,n){let r=t._zod.def.checks;if(r&&r.length>0)throw Error(`.partial() cannot be used on object schemas containing refinements`);return E_(t,g_(t._zod.def,{get shape(){let r=t._zod.def.shape,i={...r};if(n)for(let t in n){if(!(t in r))throw Error(`Unrecognized key: "${t}"`);n[t]&&(i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t])}else for(let t in r)i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t];return h_(this,`shape`,i),i},checks:[]}))}function I_(e,t,n){return E_(t,g_(t._zod.def,{get shape(){let r=t._zod.def.shape,i={...r};if(n)for(let t in n){if(!(t in i))throw Error(`Unrecognized key: "${t}"`);n[t]&&(i[t]=new e({type:`nonoptional`,innerType:r[t]}))}else for(let t in r)i[t]=new e({type:`nonoptional`,innerType:r[t]});return h_(this,`shape`,i),i}}))}function L_(e,t=0){if(e.aborted===!0)return!0;for(let n=t;n{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function B_(e){return typeof e==`string`?e:e?.message}function V_(e,t,n){let r=e.message?e.message:B_(e.inst?._zod.def?.error?.(e))??B_(t?.error?.(e))??B_(n.customError?.(e))??B_(n.localeError?.(e))??`Invalid input`,{inst:i,continue:a,input:o,...s}=e;return s.path??=[],s.message=r,t?.reportInput&&(s.input=o),s}function H_(e){return Array.isArray(e)?`array`:typeof e==`string`?`string`:`unknown`}function U_(...e){let[t,n,r]=e;return typeof t==`string`?{message:t,code:`custom`,input:n,inst:r}:{...t}}var W_=(e,t)=>{e.name=`$ZodError`,Object.defineProperty(e,`_zod`,{value:e._zod,enumerable:!1}),Object.defineProperty(e,`issues`,{value:t,enumerable:!1}),e.message=JSON.stringify(t,c_,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},G_=V(`$ZodError`,W_),K_=V(`$ZodError`,W_,{Parent:Error});function q_(e,t=e=>e.message){let n={},r=[];for(let i of e.issues)i.path.length>0?(n[i.path[0]]=n[i.path[0]]||[],n[i.path[0]].push(t(i))):r.push(t(i));return{formErrors:r,fieldErrors:n}}function J_(e,t=e=>e.message){let n={_errors:[]},r=(e,i=[])=>{for(let a of e.issues)if(a.code===`invalid_union`&&a.errors.length)a.errors.map(e=>r({issues:e},[...i,...a.path]));else if(a.code===`invalid_key`)r({issues:a.issues},[...i,...a.path]);else if(a.code===`invalid_element`)r({issues:a.issues},[...i,...a.path]);else{let e=[...i,...a.path];if(e.length===0)n._errors.push(t(a));else{let r=n,i=0;for(;i(t,n,r,i)=>{let a=r?{...r,async:!1}:{async:!1},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise)throw new r_;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>V_(e,a,o_())));throw y_(t,i?.callee),t}return o.value},X_=Y_(K_),Z_=e=>async(t,n,r,i)=>{let a=r?{...r,async:!0}:{async:!0},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise&&(o=await o),o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>V_(e,a,o_())));throw y_(t,i?.callee),t}return o.value},Q_=Z_(K_),$_=e=>(t,n,r)=>{let i=r?{...r,async:!1}:{async:!1},a=t._zod.run({value:n,issues:[]},i);if(a instanceof Promise)throw new r_;return a.issues.length?{success:!1,error:new(e??G_)(a.issues.map(e=>V_(e,i,o_())))}:{success:!0,data:a.value}},ev=$_(K_),tv=e=>async(t,n,r)=>{let i=r?{...r,async:!0}:{async:!0},a=t._zod.run({value:n,issues:[]},i);return a instanceof Promise&&(a=await a),a.issues.length?{success:!1,error:new e(a.issues.map(e=>V_(e,i,o_())))}:{success:!0,data:a.value}},nv=tv(K_),rv=e=>(t,n,r)=>{let i=r?{...r,direction:`backward`}:{direction:`backward`};return Y_(e)(t,n,i)},iv=e=>(t,n,r)=>Y_(e)(t,n,r),av=e=>async(t,n,r)=>{let i=r?{...r,direction:`backward`}:{direction:`backward`};return Z_(e)(t,n,i)},ov=e=>async(t,n,r)=>Z_(e)(t,n,r),sv=e=>(t,n,r)=>{let i=r?{...r,direction:`backward`}:{direction:`backward`};return $_(e)(t,n,i)},cv=e=>(t,n,r)=>$_(e)(t,n,r),lv=e=>async(t,n,r)=>{let i=r?{...r,direction:`backward`}:{direction:`backward`};return tv(e)(t,n,i)},uv=e=>async(t,n,r)=>tv(e)(t,n,r),dv=/^[cC][0-9a-z]{6,}$/,fv=/^[0-9a-z]+$/,pv=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,mv=/^[0-9a-vA-V]{20}$/,hv=/^[A-Za-z0-9]{27}$/,gv=/^[a-zA-Z0-9_-]{21}$/,_v=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,vv=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,yv=e=>e?RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${e}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`):/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,bv=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/,xv=`^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;function Sv(){return new RegExp(xv,`u`)}var Cv=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,wv=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,Tv=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,Ev=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,Dv=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,Ov=/^[A-Za-z0-9_-]*$/,kv=/^https?$/,Av=/^\+[1-9]\d{6,14}$/,jv=`(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`,Mv=RegExp(`^${jv}$`);function Nv(e){let t=`(?:[01]\\d|2[0-3]):[0-5]\\d`;return typeof e.precision==`number`?e.precision===-1?`${t}`:e.precision===0?`${t}:[0-5]\\d`:`${t}:[0-5]\\d\\.\\d{${e.precision}}`:`${t}(?::[0-5]\\d(?:\\.\\d+)?)?`}function Pv(e){return RegExp(`^${Nv(e)}$`)}function Fv(e){let t=Nv({precision:e.precision}),n=[`Z`];e.local&&n.push(``),e.offset&&n.push(`([+-](?:[01]\\d|2[0-3]):[0-5]\\d)`);let r=`${t}(?:${n.join(`|`)})`;return RegExp(`^${jv}T(?:${r})$`)}var Iv=e=>{let t=e?`[\\s\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\s\\S]*`;return RegExp(`^${t}$`)},Lv=/^-?\d+$/,Rv=/^-?\d+(?:\.\d+)?$/,zv=/^(?:true|false)$/i,Bv=/^[^A-Z]*$/,Vv=/^[^a-z]*$/,Hv=V(`$ZodCheck`,(e,t)=>{var n;e._zod??={},e._zod.def=t,(n=e._zod).onattach??(n.onattach=[])}),Uv={number:`number`,bigint:`bigint`,object:`date`},Wv=V(`$ZodCheckLessThan`,(e,t)=>{Hv.init(e,t);let n=Uv[typeof t.value];e._zod.onattach.push(e=>{let n=e._zod.bag,r=(t.inclusive?n.maximum:n.exclusiveMaximum)??1/0;t.value{(t.inclusive?r.value<=t.value:r.value{Hv.init(e,t);let n=Uv[typeof t.value];e._zod.onattach.push(e=>{let n=e._zod.bag,r=(t.inclusive?n.minimum:n.exclusiveMinimum)??-1/0;t.value>r&&(t.inclusive?n.minimum=t.value:n.exclusiveMinimum=t.value)}),e._zod.check=r=>{(t.inclusive?r.value>=t.value:r.value>t.value)||r.issues.push({origin:n,code:`too_small`,minimum:typeof t.value==`object`?t.value.getTime():t.value,input:r.value,inclusive:t.inclusive,inst:e,continue:!t.abort})}}),Kv=V(`$ZodCheckMultipleOf`,(e,t)=>{Hv.init(e,t),e._zod.onattach.push(e=>{var n;(n=e._zod.bag).multipleOf??(n.multipleOf=t.value)}),e._zod.check=n=>{if(typeof n.value!=typeof t.value)throw Error(`Cannot mix number and bigint in multiple_of check.`);(typeof n.value==`bigint`?n.value%t.value===BigInt(0):f_(n.value,t.value)===0)||n.issues.push({origin:typeof n.value,code:`not_multiple_of`,divisor:t.value,input:n.value,inst:e,continue:!t.abort})}}),qv=V(`$ZodCheckNumberFormat`,(e,t)=>{Hv.init(e,t),t.format=t.format||`float64`;let n=t.format?.includes(`int`),r=n?`int`:`number`,[i,a]=k_[t.format];e._zod.onattach.push(e=>{let r=e._zod.bag;r.format=t.format,r.minimum=i,r.maximum=a,n&&(r.pattern=Lv)}),e._zod.check=o=>{let s=o.value;if(n){if(!Number.isInteger(s)){o.issues.push({expected:r,format:t.format,code:`invalid_type`,continue:!1,input:s,inst:e});return}if(!Number.isSafeInteger(s)){s>0?o.issues.push({input:s,code:`too_big`,maximum:2**53-1,note:`Integers must be within the safe integer range.`,inst:e,origin:r,inclusive:!0,continue:!t.abort}):o.issues.push({input:s,code:`too_small`,minimum:-(2**53-1),note:`Integers must be within the safe integer range.`,inst:e,origin:r,inclusive:!0,continue:!t.abort});return}}sa&&o.issues.push({origin:`number`,input:s,code:`too_big`,maximum:a,inclusive:!0,inst:e,continue:!t.abort})}}),Jv=V(`$ZodCheckMaxLength`,(e,t)=>{var n;Hv.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!u_(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag.maximum??1/0;t.maximum{let r=n.value;if(r.length<=t.maximum)return;let i=H_(r);n.issues.push({origin:i,code:`too_big`,maximum:t.maximum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),Yv=V(`$ZodCheckMinLength`,(e,t)=>{var n;Hv.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!u_(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag.minimum??-1/0;t.minimum>n&&(e._zod.bag.minimum=t.minimum)}),e._zod.check=n=>{let r=n.value;if(r.length>=t.minimum)return;let i=H_(r);n.issues.push({origin:i,code:`too_small`,minimum:t.minimum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),Xv=V(`$ZodCheckLengthEquals`,(e,t)=>{var n;Hv.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!u_(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag;n.minimum=t.length,n.maximum=t.length,n.length=t.length}),e._zod.check=n=>{let r=n.value,i=r.length;if(i===t.length)return;let a=H_(r),o=i>t.length;n.issues.push({origin:a,...o?{code:`too_big`,maximum:t.length}:{code:`too_small`,minimum:t.length},inclusive:!0,exact:!0,input:n.value,inst:e,continue:!t.abort})}}),Zv=V(`$ZodCheckStringFormat`,(e,t)=>{var n,r;Hv.init(e,t),e._zod.onattach.push(e=>{let n=e._zod.bag;n.format=t.format,t.pattern&&(n.patterns??=new Set,n.patterns.add(t.pattern))}),t.pattern?(n=e._zod).check??(n.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:t.format,input:n.value,...t.pattern?{pattern:t.pattern.toString()}:{},inst:e,continue:!t.abort})}):(r=e._zod).check??(r.check=()=>{})}),Qv=V(`$ZodCheckRegex`,(e,t)=>{Zv.init(e,t),e._zod.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:`regex`,input:n.value,pattern:t.pattern.toString(),inst:e,continue:!t.abort})}}),$v=V(`$ZodCheckLowerCase`,(e,t)=>{t.pattern??=Bv,Zv.init(e,t)}),ey=V(`$ZodCheckUpperCase`,(e,t)=>{t.pattern??=Vv,Zv.init(e,t)}),ty=V(`$ZodCheckIncludes`,(e,t)=>{Hv.init(e,t);let n=T_(t.includes),r=new RegExp(typeof t.position==`number`?`^.{${t.position}}${n}`:n);t.pattern=r,e._zod.onattach.push(e=>{let t=e._zod.bag;t.patterns??=new Set,t.patterns.add(r)}),e._zod.check=n=>{n.value.includes(t.includes,t.position)||n.issues.push({origin:`string`,code:`invalid_format`,format:`includes`,includes:t.includes,input:n.value,inst:e,continue:!t.abort})}}),ny=V(`$ZodCheckStartsWith`,(e,t)=>{Hv.init(e,t);let n=RegExp(`^${T_(t.prefix)}.*`);t.pattern??=n,e._zod.onattach.push(e=>{let t=e._zod.bag;t.patterns??=new Set,t.patterns.add(n)}),e._zod.check=n=>{n.value.startsWith(t.prefix)||n.issues.push({origin:`string`,code:`invalid_format`,format:`starts_with`,prefix:t.prefix,input:n.value,inst:e,continue:!t.abort})}}),ry=V(`$ZodCheckEndsWith`,(e,t)=>{Hv.init(e,t);let n=RegExp(`.*${T_(t.suffix)}$`);t.pattern??=n,e._zod.onattach.push(e=>{let t=e._zod.bag;t.patterns??=new Set,t.patterns.add(n)}),e._zod.check=n=>{n.value.endsWith(t.suffix)||n.issues.push({origin:`string`,code:`invalid_format`,format:`ends_with`,suffix:t.suffix,input:n.value,inst:e,continue:!t.abort})}}),iy=V(`$ZodCheckOverwrite`,(e,t)=>{Hv.init(e,t),e._zod.check=e=>{e.value=t.tx(e.value)}}),ay=class{constructor(e=[]){this.content=[],this.indent=0,this&&(this.args=e)}indented(e){this.indent+=1,e(this),--this.indent}write(e){if(typeof e==`function`){e(this,{execution:`sync`}),e(this,{execution:`async`});return}let t=e.split(` -`).filter(e=>e),n=Math.min(...t.map(e=>e.length-e.trimStart().length)),r=t.map(e=>e.slice(n)).map(e=>` `.repeat(this.indent*2)+e);for(let e of r)this.content.push(e)}compile(){let e=Function,t=this?.args,n=[...(this?.content??[``]).map(e=>` ${e}`)];return new e(...t,n.join(` -`))}},oy={major:4,minor:4,patch:3},sy=V(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=oy;let r=[...e._zod.def.checks??[]];e._zod.traits.has(`$ZodCheck`)&&r.unshift(e);for(let t of r)for(let n of t._zod.onattach)n(e);if(r.length===0)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let t=(e,t,n)=>{let r=L_(e),i;for(let a of t){if(a._zod.def.when){if(R_(e)||!a._zod.def.when(e))continue}else if(r)continue;let t=e.issues.length,o=a._zod.check(e);if(o instanceof Promise&&n?.async===!1)throw new r_;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=L_(e,t))});else{if(e.issues.length===t)continue;r||=L_(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(L_(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new r_;return o.then(t=>e._zod.parse(t,a))}return e._zod.parse(o,a)};e._zod.run=(i,a)=>{if(a.skipChecks)return e._zod.parse(i,a);if(a.direction===`backward`){let t=e._zod.parse({value:i.value,issues:[]},{...a,skipChecks:!0});return t instanceof Promise?t.then(e=>n(e,i,a)):n(t,i,a)}let o=e._zod.parse(i,a);if(o instanceof Promise){if(a.async===!1)throw new r_;return o.then(e=>t(e,r,a))}return t(o,r,a)}}m_(e,`~standard`,()=>({validate:t=>{try{let n=ev(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return nv(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),cy=V(`$ZodString`,(e,t)=>{sy.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??Iv(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch{}return typeof n.value==`string`||n.issues.push({expected:`string`,code:`invalid_type`,input:n.value,inst:e}),n}}),ly=V(`$ZodStringFormat`,(e,t)=>{Zv.init(e,t),cy.init(e,t)}),uy=V(`$ZodGUID`,(e,t)=>{t.pattern??=vv,ly.init(e,t)}),dy=V(`$ZodUUID`,(e,t)=>{if(t.version){let e={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8}[t.version];if(e===void 0)throw Error(`Invalid UUID version: "${t.version}"`);t.pattern??=yv(e)}else t.pattern??=yv();ly.init(e,t)}),fy=V(`$ZodEmail`,(e,t)=>{t.pattern??=bv,ly.init(e,t)}),py=V(`$ZodURL`,(e,t)=>{ly.init(e,t),e._zod.check=n=>{try{let r=n.value.trim();if(!t.normalize&&t.protocol?.source===kv.source&&!/^https?:\/\//i.test(r)){n.issues.push({code:`invalid_format`,format:`url`,note:`Invalid URL format`,input:n.value,inst:e,continue:!t.abort});return}let i=new URL(r);t.hostname&&(t.hostname.lastIndex=0,t.hostname.test(i.hostname)||n.issues.push({code:`invalid_format`,format:`url`,note:`Invalid hostname`,pattern:t.hostname.source,input:n.value,inst:e,continue:!t.abort})),t.protocol&&(t.protocol.lastIndex=0,t.protocol.test(i.protocol.endsWith(`:`)?i.protocol.slice(0,-1):i.protocol)||n.issues.push({code:`invalid_format`,format:`url`,note:`Invalid protocol`,pattern:t.protocol.source,input:n.value,inst:e,continue:!t.abort})),t.normalize?n.value=i.href:n.value=r;return}catch{n.issues.push({code:`invalid_format`,format:`url`,input:n.value,inst:e,continue:!t.abort})}}}),my=V(`$ZodEmoji`,(e,t)=>{t.pattern??=Sv(),ly.init(e,t)}),hy=V(`$ZodNanoID`,(e,t)=>{t.pattern??=gv,ly.init(e,t)}),gy=V(`$ZodCUID`,(e,t)=>{t.pattern??=dv,ly.init(e,t)}),_y=V(`$ZodCUID2`,(e,t)=>{t.pattern??=fv,ly.init(e,t)}),vy=V(`$ZodULID`,(e,t)=>{t.pattern??=pv,ly.init(e,t)}),yy=V(`$ZodXID`,(e,t)=>{t.pattern??=mv,ly.init(e,t)}),by=V(`$ZodKSUID`,(e,t)=>{t.pattern??=hv,ly.init(e,t)}),xy=V(`$ZodISODateTime`,(e,t)=>{t.pattern??=Fv(t),ly.init(e,t)}),Sy=V(`$ZodISODate`,(e,t)=>{t.pattern??=Mv,ly.init(e,t)}),Cy=V(`$ZodISOTime`,(e,t)=>{t.pattern??=Pv(t),ly.init(e,t)}),wy=V(`$ZodISODuration`,(e,t)=>{t.pattern??=_v,ly.init(e,t)}),Ty=V(`$ZodIPv4`,(e,t)=>{t.pattern??=Cv,ly.init(e,t),e._zod.bag.format=`ipv4`}),Ey=V(`$ZodIPv6`,(e,t)=>{t.pattern??=wv,ly.init(e,t),e._zod.bag.format=`ipv6`,e._zod.check=n=>{try{new URL(`http://[${n.value}]`)}catch{n.issues.push({code:`invalid_format`,format:`ipv6`,input:n.value,inst:e,continue:!t.abort})}}}),Dy=V(`$ZodCIDRv4`,(e,t)=>{t.pattern??=Tv,ly.init(e,t)}),Oy=V(`$ZodCIDRv6`,(e,t)=>{t.pattern??=Ev,ly.init(e,t),e._zod.check=n=>{let r=n.value.split(`/`);try{if(r.length!==2)throw Error();let[e,t]=r;if(!t)throw Error();let n=Number(t);if(`${n}`!==t||n<0||n>128)throw Error();new URL(`http://[${e}]`)}catch{n.issues.push({code:`invalid_format`,format:`cidrv6`,input:n.value,inst:e,continue:!t.abort})}}});function ky(e){if(e===``)return!0;if(/\s/.test(e)||e.length%4!=0)return!1;try{return atob(e),!0}catch{return!1}}var Ay=V(`$ZodBase64`,(e,t)=>{t.pattern??=Dv,ly.init(e,t),e._zod.bag.contentEncoding=`base64`,e._zod.check=n=>{ky(n.value)||n.issues.push({code:`invalid_format`,format:`base64`,input:n.value,inst:e,continue:!t.abort})}});function jy(e){if(!Ov.test(e))return!1;let t=e.replace(/[-_]/g,e=>e===`-`?`+`:`/`);return ky(t.padEnd(Math.ceil(t.length/4)*4,`=`))}var My=V(`$ZodBase64URL`,(e,t)=>{t.pattern??=Ov,ly.init(e,t),e._zod.bag.contentEncoding=`base64url`,e._zod.check=n=>{jy(n.value)||n.issues.push({code:`invalid_format`,format:`base64url`,input:n.value,inst:e,continue:!t.abort})}}),Ny=V(`$ZodE164`,(e,t)=>{t.pattern??=Av,ly.init(e,t)});function Py(e,t=null){try{let n=e.split(`.`);if(n.length!==3)return!1;let[r]=n;if(!r)return!1;let i=JSON.parse(atob(r));return!(`typ`in i&&i?.typ!==`JWT`||!i.alg||t&&(!(`alg`in i)||i.alg!==t))}catch{return!1}}var Fy=V(`$ZodJWT`,(e,t)=>{ly.init(e,t),e._zod.check=n=>{Py(n.value,t.alg)||n.issues.push({code:`invalid_format`,format:`jwt`,input:n.value,inst:e,continue:!t.abort})}}),Iy=V(`$ZodNumber`,(e,t)=>{sy.init(e,t),e._zod.pattern=e._zod.bag.pattern??Rv,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=Number(n.value)}catch{}let i=n.value;if(typeof i==`number`&&!Number.isNaN(i)&&Number.isFinite(i))return n;let a=typeof i==`number`?Number.isNaN(i)?`NaN`:Number.isFinite(i)?void 0:`Infinity`:void 0;return n.issues.push({expected:`number`,code:`invalid_type`,input:i,inst:e,...a?{received:a}:{}}),n}}),Ly=V(`$ZodNumberFormat`,(e,t)=>{qv.init(e,t),Iy.init(e,t)}),Ry=V(`$ZodBoolean`,(e,t)=>{sy.init(e,t),e._zod.pattern=zv,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=!!n.value}catch{}let i=n.value;return typeof i==`boolean`||n.issues.push({expected:`boolean`,code:`invalid_type`,input:i,inst:e}),n}}),zy=V(`$ZodUnknown`,(e,t)=>{sy.init(e,t),e._zod.parse=e=>e}),By=V(`$ZodNever`,(e,t)=>{sy.init(e,t),e._zod.parse=(t,n)=>(t.issues.push({expected:`never`,code:`invalid_type`,input:t.value,inst:e}),t)});function Vy(e,t,n){e.issues.length&&t.issues.push(...z_(n,e.issues)),t.value[n]=e.value}var Hy=V(`$ZodArray`,(e,t)=>{sy.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({expected:`array`,code:`invalid_type`,input:i,inst:e}),n;n.value=Array(i.length);let a=[];for(let e=0;eVy(t,n,e))):Vy(s,n,e)}return a.length?Promise.all(a).then(()=>n):n}});function Uy(e,t,n,r,i,a){let o=n in r;if(e.issues.length){if(i&&a&&!o)return;t.issues.push(...z_(n,e.issues))}if(!o&&!i){e.issues.length||t.issues.push({code:`invalid_type`,expected:`nonoptional`,input:void 0,path:[n]});return}e.value===void 0?o&&(t.value[n]=void 0):t.value[n]=e.value}function Wy(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(`$ZodType`))throw Error(`Invalid element at key "${n}": expected a Zod schema`);let n=O_(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function Gy(e,t,n,r,i,a){let o=[],s=i.keySet,c=i.catchall._zod,l=c.def.type,u=c.optin===`optional`,d=c.optout===`optional`;for(let i in t){if(i===`__proto__`||s.has(i))continue;if(l===`never`){o.push(i);continue}let a=c.run({value:t[i],issues:[]},r);a instanceof Promise?e.push(a.then(e=>Uy(e,n,i,t,u,d))):Uy(a,n,i,t,u,d)}return o.length&&n.issues.push({code:`unrecognized_keys`,keys:o,input:t,inst:a}),e.length?Promise.all(e).then(()=>n):n}var Zee=V(`$ZodObject`,(e,t)=>{if(sy.init(e,t),!Object.getOwnPropertyDescriptor(t,`shape`)?.get){let e=t.shape;Object.defineProperty(t,`shape`,{get:()=>{let n={...e};return Object.defineProperty(t,`shape`,{value:n}),n}})}let n=l_(()=>Wy(t));m_(e._zod,`propValues`,()=>{let e=t.shape,n={};for(let t in e){let r=e[t]._zod;if(r.values){n[t]??(n[t]=new Set);for(let e of r.values)n[t].add(e)}}return n});let r=b_,i=t.catchall,a;e._zod.parse=(t,o)=>{a??=n.value;let s=t.value;if(!r(s))return t.issues.push({expected:`object`,code:`invalid_type`,input:s,inst:e}),t;t.value={};let c=[],l=a.shape;for(let e of a.keys){let n=l[e],r=n._zod.optin===`optional`,i=n._zod.optout===`optional`,a=n._zod.run({value:s[e],issues:[]},o);a instanceof Promise?c.push(a.then(n=>Uy(n,t,e,s,r,i))):Uy(a,t,e,s,r,i)}return i?Gy(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}}),Qee=V(`$ZodObjectJIT`,(e,t)=>{Zee.init(e,t);let n=e._zod.parse,r=l_(()=>Wy(t)),i=e=>{let t=new ay([`shape`,`payload`,`ctx`]),n=r.value,i=e=>{let t=__(e);return`shape[${t}]._zod.run({ value: input[${t}], issues: [] }, ctx)`};t.write(`const input = payload.value;`);let a=Object.create(null),o=0;for(let e of n.keys)a[e]=`key_${o++}`;t.write(`const newResult = {};`);for(let r of n.keys){let n=a[r],o=__(r),s=e[r],c=s?._zod?.optin===`optional`,l=s?._zod?.optout===`optional`;t.write(`const ${n} = ${i(r)};`),c&&l?t.write(` - if (${n}.issues.length) { - if (${o} in input) { - payload.issues = payload.issues.concat(${n}.issues.map(iss => ({ - ...iss, - path: iss.path ? [${o}, ...iss.path] : [${o}] - }))); - } - } - - if (${n}.value === undefined) { - if (${o} in input) { - newResult[${o}] = undefined; - } - } else { - newResult[${o}] = ${n}.value; - } - - `):c?t.write(` - if (${n}.issues.length) { - payload.issues = payload.issues.concat(${n}.issues.map(iss => ({ - ...iss, - path: iss.path ? [${o}, ...iss.path] : [${o}] - }))); - } - - if (${n}.value === undefined) { - if (${o} in input) { - newResult[${o}] = undefined; - } - } else { - newResult[${o}] = ${n}.value; - } - - `):t.write(` - const ${n}_present = ${o} in input; - if (${n}.issues.length) { - payload.issues = payload.issues.concat(${n}.issues.map(iss => ({ - ...iss, - path: iss.path ? [${o}, ...iss.path] : [${o}] - }))); - } - if (!${n}_present && !${n}.issues.length) { - payload.issues.push({ - code: "invalid_type", - expected: "nonoptional", - input: undefined, - path: [${o}] - }); - } - - if (${n}_present) { - if (${n}.value === undefined) { - newResult[${o}] = undefined; - } else { - newResult[${o}] = ${n}.value; - } - } - - `)}t.write(`payload.value = newResult;`),t.write(`return payload;`);let s=t.compile();return(t,n)=>s(e,t,n)},a,o=b_,s=!a_.jitless,c=s&&x_.value,l=t.catchall,u;e._zod.parse=(d,f)=>{u??=r.value;let p=d.value;return o(p)?s&&c&&f?.async===!1&&f.jitless!==!0?(a||=i(t.shape),d=a(d,f),l?Gy([],p,d,f,u,e):d):n(d,f):(d.issues.push({expected:`object`,code:`invalid_type`,input:p,inst:e}),d)}});function Ky(e,t,n,r){for(let n of e)if(n.issues.length===0)return t.value=n.value,t;let i=e.filter(e=>!L_(e));return i.length===1?(t.value=i[0].value,i[0]):(t.issues.push({code:`invalid_union`,input:t.value,inst:n,errors:e.map(e=>e.issues.map(e=>V_(e,r,o_())))}),t)}var $ee=V(`$ZodUnion`,(e,t)=>{sy.init(e,t),m_(e._zod,`optin`,()=>t.options.some(e=>e._zod.optin===`optional`)?`optional`:void 0),m_(e._zod,`optout`,()=>t.options.some(e=>e._zod.optout===`optional`)?`optional`:void 0),m_(e._zod,`values`,()=>{if(t.options.every(e=>e._zod.values))return new Set(t.options.flatMap(e=>Array.from(e._zod.values)))}),m_(e._zod,`pattern`,()=>{if(t.options.every(e=>e._zod.pattern)){let e=t.options.map(e=>e._zod.pattern);return RegExp(`^(${e.map(e=>d_(e.source)).join(`|`)})$`)}});let n=t.options.length===1?t.options[0]._zod.run:null;e._zod.parse=(r,i)=>{if(n)return n(r,i);let a=!1,o=[];for(let e of t.options){let t=e._zod.run({value:r.value,issues:[]},i);if(t instanceof Promise)o.push(t),a=!0;else{if(t.issues.length===0)return t;o.push(t)}}return a?Promise.all(o).then(t=>Ky(t,r,e,i)):Ky(o,r,e,i)}}),ete=V(`$ZodIntersection`,(e,t)=>{sy.init(e,t),e._zod.parse=(e,n)=>{let r=e.value,i=t.left._zod.run({value:r,issues:[]},n),a=t.right._zod.run({value:r,issues:[]},n);return i instanceof Promise||a instanceof Promise?Promise.all([i,a]).then(([t,n])=>Jy(e,t,n)):Jy(e,i,a)}});function qy(e,t){if(e===t||e instanceof Date&&t instanceof Date&&+e==+t)return{valid:!0,data:e};if(S_(e)&&S_(t)){let n=Object.keys(t),r=Object.keys(e).filter(e=>n.indexOf(e)!==-1),i={...e,...t};for(let n of r){let r=qy(e[n],t[n]);if(!r.valid)return{valid:!1,mergeErrorPath:[n,...r.mergeErrorPath]};i[n]=r.data}return{valid:!0,data:i}}if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return{valid:!1,mergeErrorPath:[]};let n=[];for(let r=0;re.l&&e.r).map(([e])=>e);if(a.length&&i&&e.issues.push({...i,keys:a}),L_(e))return e;let o=qy(t.value,n.value);if(!o.valid)throw Error(`Unmergable intersection. Error path: ${JSON.stringify(o.mergeErrorPath)}`);return e.value=o.data,e}var tte=V(`$ZodRecord`,(e,t)=>{sy.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!S_(i))return n.issues.push({expected:`record`,code:`invalid_type`,input:i,inst:e}),n;let a=[],o=t.keyType._zod.values;if(o){n.value={};let s=new Set;for(let c of o)if(typeof c==`string`||typeof c==`number`||typeof c==`symbol`){s.add(typeof c==`number`?c.toString():c);let o=t.keyType._zod.run({value:c,issues:[]},r);if(o instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(o.issues.length){n.issues.push({code:`invalid_key`,origin:`record`,issues:o.issues.map(e=>V_(e,r,o_())),input:c,path:[c],inst:e});continue}let l=o.value,u=t.valueType._zod.run({value:i[c],issues:[]},r);u instanceof Promise?a.push(u.then(e=>{e.issues.length&&n.issues.push(...z_(c,e.issues)),n.value[l]=e.value})):(u.issues.length&&n.issues.push(...z_(c,u.issues)),n.value[l]=u.value)}let c;for(let e in i)s.has(e)||(c??=[],c.push(e));c&&c.length>0&&n.issues.push({code:`unrecognized_keys`,input:i,inst:e,keys:c})}else{n.value={};for(let o of Reflect.ownKeys(i)){if(o===`__proto__`||!Object.prototype.propertyIsEnumerable.call(i,o))continue;let s=t.keyType._zod.run({value:o,issues:[]},r);if(s instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(typeof o==`string`&&Rv.test(o)&&s.issues.length){let e=t.keyType._zod.run({value:Number(o),issues:[]},r);if(e instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);e.issues.length===0&&(s=e)}if(s.issues.length){t.mode===`loose`?n.value[o]=i[o]:n.issues.push({code:`invalid_key`,origin:`record`,issues:s.issues.map(e=>V_(e,r,o_())),input:o,path:[o],inst:e});continue}let c=t.valueType._zod.run({value:i[o],issues:[]},r);c instanceof Promise?a.push(c.then(e=>{e.issues.length&&n.issues.push(...z_(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...z_(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}}),nte=V(`$ZodEnum`,(e,t)=>{sy.init(e,t);let n=s_(t.entries),r=new Set(n);e._zod.values=r,e._zod.pattern=RegExp(`^(${n.filter(e=>w_.has(typeof e)).map(e=>typeof e==`string`?T_(e):e.toString()).join(`|`)})$`),e._zod.parse=(t,i)=>{let a=t.value;return r.has(a)||t.issues.push({code:`invalid_value`,values:n,input:a,inst:e}),t}}),rte=V(`$ZodTransform`,(e,t)=>{sy.init(e,t),e._zod.optin=`optional`,e._zod.parse=(n,r)=>{if(r.direction===`backward`)throw new i_(e.constructor.name);let i=t.transform(n.value,n);if(r.async)return(i instanceof Promise?i:Promise.resolve(i)).then(e=>(n.value=e,n.fallback=!0,n));if(i instanceof Promise)throw new r_;return n.value=i,n.fallback=!0,n}});function Yy(e,t){return t===void 0&&(e.issues.length||e.fallback)?{issues:[],value:void 0}:e}var Xy=V(`$ZodOptional`,(e,t)=>{sy.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,m_(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),m_(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${d_(e.source)})?$`):void 0}),e._zod.parse=(e,n)=>{if(t.innerType._zod.optin===`optional`){let r=e.value,i=t.innerType._zod.run(e,n);return i instanceof Promise?i.then(e=>Yy(e,r)):Yy(i,r)}return e.value===void 0?e:t.innerType._zod.run(e,n)}}),ite=V(`$ZodExactOptional`,(e,t)=>{Xy.init(e,t),m_(e._zod,`values`,()=>t.innerType._zod.values),m_(e._zod,`pattern`,()=>t.innerType._zod.pattern),e._zod.parse=(e,n)=>t.innerType._zod.run(e,n)}),ate=V(`$ZodNullable`,(e,t)=>{sy.init(e,t),m_(e._zod,`optin`,()=>t.innerType._zod.optin),m_(e._zod,`optout`,()=>t.innerType._zod.optout),m_(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${d_(e.source)}|null)$`):void 0}),m_(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,null]):void 0),e._zod.parse=(e,n)=>e.value===null?e:t.innerType._zod.run(e,n)}),ote=V(`$ZodDefault`,(e,t)=>{sy.init(e,t),e._zod.optin=`optional`,m_(e._zod,`values`,()=>t.innerType._zod.values),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);if(e.value===void 0)return e.value=t.defaultValue,e;let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(e=>Zy(e,t)):Zy(r,t)}});function Zy(e,t){return e.value===void 0&&(e.value=t.defaultValue),e}var ste=V(`$ZodPrefault`,(e,t)=>{sy.init(e,t),e._zod.optin=`optional`,m_(e._zod,`values`,()=>t.innerType._zod.values),e._zod.parse=(e,n)=>(n.direction===`backward`||e.value===void 0&&(e.value=t.defaultValue),t.innerType._zod.run(e,n))}),cte=V(`$ZodNonOptional`,(e,t)=>{sy.init(e,t),m_(e._zod,`values`,()=>{let e=t.innerType._zod.values;return e?new Set([...e].filter(e=>e!==void 0)):void 0}),e._zod.parse=(n,r)=>{let i=t.innerType._zod.run(n,r);return i instanceof Promise?i.then(t=>Qy(t,e)):Qy(i,e)}});function Qy(e,t){return!e.issues.length&&e.value===void 0&&e.issues.push({code:`invalid_type`,expected:`nonoptional`,input:e.value,inst:t}),e}var lte=V(`$ZodCatch`,(e,t)=>{sy.init(e,t),e._zod.optin=`optional`,m_(e._zod,`optout`,()=>t.innerType._zod.optout),m_(e._zod,`values`,()=>t.innerType._zod.values),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(r=>(e.value=r.value,r.issues.length&&(e.value=t.catchValue({...e,error:{issues:r.issues.map(e=>V_(e,n,o_()))},input:e.value}),e.issues=[],e.fallback=!0),e)):(e.value=r.value,r.issues.length&&(e.value=t.catchValue({...e,error:{issues:r.issues.map(e=>V_(e,n,o_()))},input:e.value}),e.issues=[],e.fallback=!0),e)}}),ute=V(`$ZodPipe`,(e,t)=>{sy.init(e,t),m_(e._zod,`values`,()=>t.in._zod.values),m_(e._zod,`optin`,()=>t.in._zod.optin),m_(e._zod,`optout`,()=>t.out._zod.optout),m_(e._zod,`propValues`,()=>t.in._zod.propValues),e._zod.parse=(e,n)=>{if(n.direction===`backward`){let r=t.out._zod.run(e,n);return r instanceof Promise?r.then(e=>$y(e,t.in,n)):$y(r,t.in,n)}let r=t.in._zod.run(e,n);return r instanceof Promise?r.then(e=>$y(e,t.out,n)):$y(r,t.out,n)}});function $y(e,t,n){return e.issues.length?(e.aborted=!0,e):t._zod.run({value:e.value,issues:e.issues,fallback:e.fallback},n)}var dte=V(`$ZodReadonly`,(e,t)=>{sy.init(e,t),m_(e._zod,`propValues`,()=>t.innerType._zod.propValues),m_(e._zod,`values`,()=>t.innerType._zod.values),m_(e._zod,`optin`,()=>t.innerType?._zod?.optin),m_(e._zod,`optout`,()=>t.innerType?._zod?.optout),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(eb):eb(r)}});function eb(e){return e.value=Object.freeze(e.value),e}var fte=V(`$ZodCustom`,(e,t)=>{Hv.init(e,t),sy.init(e,t),e._zod.parse=(e,t)=>e,e._zod.check=n=>{let r=n.value,i=t.fn(r);if(i instanceof Promise)return i.then(t=>tb(t,n,r,e));tb(i,n,r,e)}});function tb(e,t,n,r){if(!e){let e={code:`custom`,input:n,inst:r,path:[...r._zod.def.path??[]],continue:!r._zod.def.abort};r._zod.def.params&&(e.params=r._zod.def.params),t.issues.push(U_(e))}}var nb,pte=class{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){let n=t[0];return this._map.set(e,n),n&&typeof n==`object`&&`id`in n&&this._idmap.set(n.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){let t=this._map.get(e);return t&&typeof t==`object`&&`id`in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){let t=e._zod.parent;if(t){let n={...this.get(t)??{}};delete n.id;let r={...n,...this._map.get(e)};return Object.keys(r).length?r:void 0}return this._map.get(e)}has(e){return this._map.has(e)}};function mte(){return new pte}(nb=globalThis).__zod_globalRegistry??(nb.__zod_globalRegistry=mte());var rb=globalThis.__zod_globalRegistry;function hte(e,t){return new e({type:`string`,...D_(t)})}function gte(e,t){return new e({type:`string`,format:`email`,check:`string_format`,abort:!1,...D_(t)})}function ib(e,t){return new e({type:`string`,format:`guid`,check:`string_format`,abort:!1,...D_(t)})}function _te(e,t){return new e({type:`string`,format:`uuid`,check:`string_format`,abort:!1,...D_(t)})}function vte(e,t){return new e({type:`string`,format:`uuid`,check:`string_format`,abort:!1,version:`v4`,...D_(t)})}function yte(e,t){return new e({type:`string`,format:`uuid`,check:`string_format`,abort:!1,version:`v6`,...D_(t)})}function bte(e,t){return new e({type:`string`,format:`uuid`,check:`string_format`,abort:!1,version:`v7`,...D_(t)})}function xte(e,t){return new e({type:`string`,format:`url`,check:`string_format`,abort:!1,...D_(t)})}function Ste(e,t){return new e({type:`string`,format:`emoji`,check:`string_format`,abort:!1,...D_(t)})}function Cte(e,t){return new e({type:`string`,format:`nanoid`,check:`string_format`,abort:!1,...D_(t)})}function wte(e,t){return new e({type:`string`,format:`cuid`,check:`string_format`,abort:!1,...D_(t)})}function Tte(e,t){return new e({type:`string`,format:`cuid2`,check:`string_format`,abort:!1,...D_(t)})}function Ete(e,t){return new e({type:`string`,format:`ulid`,check:`string_format`,abort:!1,...D_(t)})}function Dte(e,t){return new e({type:`string`,format:`xid`,check:`string_format`,abort:!1,...D_(t)})}function Ote(e,t){return new e({type:`string`,format:`ksuid`,check:`string_format`,abort:!1,...D_(t)})}function kte(e,t){return new e({type:`string`,format:`ipv4`,check:`string_format`,abort:!1,...D_(t)})}function Ate(e,t){return new e({type:`string`,format:`ipv6`,check:`string_format`,abort:!1,...D_(t)})}function jte(e,t){return new e({type:`string`,format:`cidrv4`,check:`string_format`,abort:!1,...D_(t)})}function Mte(e,t){return new e({type:`string`,format:`cidrv6`,check:`string_format`,abort:!1,...D_(t)})}function Nte(e,t){return new e({type:`string`,format:`base64`,check:`string_format`,abort:!1,...D_(t)})}function Pte(e,t){return new e({type:`string`,format:`base64url`,check:`string_format`,abort:!1,...D_(t)})}function Fte(e,t){return new e({type:`string`,format:`e164`,check:`string_format`,abort:!1,...D_(t)})}function Ite(e,t){return new e({type:`string`,format:`jwt`,check:`string_format`,abort:!1,...D_(t)})}function Lte(e,t){return new e({type:`string`,format:`datetime`,check:`string_format`,offset:!1,local:!1,precision:null,...D_(t)})}function Rte(e,t){return new e({type:`string`,format:`date`,check:`string_format`,...D_(t)})}function zte(e,t){return new e({type:`string`,format:`time`,check:`string_format`,precision:null,...D_(t)})}function Bte(e,t){return new e({type:`string`,format:`duration`,check:`string_format`,...D_(t)})}function Vte(e,t){return new e({type:`number`,checks:[],...D_(t)})}function Hte(e,t){return new e({type:`number`,check:`number_format`,abort:!1,format:`safeint`,...D_(t)})}function Ute(e,t){return new e({type:`boolean`,...D_(t)})}function Wte(e){return new e({type:`unknown`})}function Gte(e,t){return new e({type:`never`,...D_(t)})}function ab(e,t){return new Wv({check:`less_than`,...D_(t),value:e,inclusive:!1})}function ob(e,t){return new Wv({check:`less_than`,...D_(t),value:e,inclusive:!0})}function sb(e,t){return new Gv({check:`greater_than`,...D_(t),value:e,inclusive:!1})}function cb(e,t){return new Gv({check:`greater_than`,...D_(t),value:e,inclusive:!0})}function lb(e,t){return new Kv({check:`multiple_of`,...D_(t),value:e})}function ub(e,t){return new Jv({check:`max_length`,...D_(t),maximum:e})}function db(e,t){return new Yv({check:`min_length`,...D_(t),minimum:e})}function fb(e,t){return new Xv({check:`length_equals`,...D_(t),length:e})}function Kte(e,t){return new Qv({check:`string_format`,format:`regex`,...D_(t),pattern:e})}function qte(e){return new $v({check:`string_format`,format:`lowercase`,...D_(e)})}function Jte(e){return new ey({check:`string_format`,format:`uppercase`,...D_(e)})}function Yte(e,t){return new ty({check:`string_format`,format:`includes`,...D_(t),includes:e})}function Xte(e,t){return new ny({check:`string_format`,format:`starts_with`,...D_(t),prefix:e})}function Zte(e,t){return new ry({check:`string_format`,format:`ends_with`,...D_(t),suffix:e})}function pb(e){return new iy({check:`overwrite`,tx:e})}function Qte(e){return pb(t=>t.normalize(e))}function $te(){return pb(e=>e.trim())}function ene(){return pb(e=>e.toLowerCase())}function tne(){return pb(e=>e.toUpperCase())}function nne(){return pb(e=>v_(e))}function rne(e,t,n){return new e({type:`array`,element:t,...D_(n)})}function ine(e,t,n){return new e({type:`custom`,check:`custom`,fn:t,...D_(n)})}function ane(e,t){let n=one(t=>(t.addIssue=e=>{if(typeof e==`string`)t.issues.push(U_(e,t.value,n._zod.def));else{let r=e;r.fatal&&(r.continue=!1),r.code??=`custom`,r.input??=t.value,r.inst??=n,r.continue??=!n._zod.def.abort,t.issues.push(U_(r))}},e(t.value,t)),t);return n}function one(e,t){let n=new Hv({check:`custom`,...D_(t)});return n._zod.check=e,n}function mb(e){let t=e?.target??`draft-2020-12`;return t===`draft-4`&&(t=`draft-04`),t===`draft-7`&&(t=`draft-07`),{processors:e.processors??{},metadataRegistry:e?.metadata??rb,target:t,unrepresentable:e?.unrepresentable??`throw`,override:e?.override??(()=>{}),io:e?.io??`output`,counter:0,seen:new Map,cycles:e?.cycles??`ref`,reused:e?.reused??`inline`,external:e?.external??void 0}}function hb(e,t,n={path:[],schemaPath:[]}){var r;let i=e._zod.def,a=t.seen.get(e);if(a)return a.count++,n.schemaPath.includes(e)&&(a.cycle=n.path),a.schema;let o={schema:{},count:1,cycle:void 0,path:n.path};t.seen.set(e,o);let s=e._zod.toJSONSchema?.();if(s)o.schema=s;else{let r={...n,schemaPath:[...n.schemaPath,e],path:n.path};if(e._zod.processJSONSchema)e._zod.processJSONSchema(t,o.schema,r);else{let n=o.schema,a=t.processors[i.type];if(!a)throw Error(`[toJSONSchema]: Non-representable type encountered: ${i.type}`);a(e,t,n,r)}let a=e._zod.parent;a&&(o.ref||=a,hb(a,t,r),t.seen.get(a).isParent=!0)}let c=t.metadataRegistry.get(e);return c&&Object.assign(o.schema,c),t.io===`input`&&vb(e)&&(delete o.schema.examples,delete o.schema.default),t.io===`input`&&`_prefault`in o.schema&&((r=o.schema).default??(r.default=o.schema._prefault)),delete o.schema._prefault,t.seen.get(e).schema}function gb(e,t){let n=e.seen.get(t);if(!n)throw Error(`Unprocessed schema. This is a bug in Zod.`);let r=new Map;for(let t of e.seen.entries()){let n=e.metadataRegistry.get(t[0])?.id;if(n){let e=r.get(n);if(e&&e!==t[0])throw Error(`Duplicate schema id "${n}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);r.set(n,t[0])}}let i=t=>{let r=e.target===`draft-2020-12`?`$defs`:`definitions`;if(e.external){let n=e.external.registry.get(t[0])?.id,i=e.external.uri??(e=>e);if(n)return{ref:i(n)};let a=t[1].defId??t[1].schema.id??`schema${e.counter++}`;return t[1].defId=a,{defId:a,ref:`${i(`__shared`)}#/${r}/${a}`}}if(t[1]===n)return{ref:`#`};let i=`#/${r}/`,a=t[1].schema.id??`__schema${e.counter++}`;return{defId:a,ref:i+a}},a=e=>{if(e[1].schema.$ref)return;let t=e[1],{ref:n,defId:r}=i(e);t.def={...t.schema},r&&(t.defId=r);let a=t.schema;for(let e in a)delete a[e];a.$ref=n};if(e.cycles===`throw`)for(let t of e.seen.entries()){let e=t[1];if(e.cycle)throw Error(`Cycle detected: #/${e.cycle?.join(`/`)}/ - -Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`)}for(let n of e.seen.entries()){let r=n[1];if(t===n[0]){a(n);continue}if(e.external){let r=e.external.registry.get(n[0])?.id;if(t!==n[0]&&r){a(n);continue}}if(e.metadataRegistry.get(n[0])?.id){a(n);continue}if(r.cycle){a(n);continue}if(r.count>1&&e.reused===`ref`){a(n);continue}}}function _b(e,t){let n=e.seen.get(t);if(!n)throw Error(`Unprocessed schema. This is a bug in Zod.`);let r=t=>{let n=e.seen.get(t);if(n.ref===null)return;let i=n.def??n.schema,a={...i},o=n.ref;if(n.ref=null,o){r(o);let n=e.seen.get(o),s=n.schema;if(s.$ref&&(e.target===`draft-07`||e.target===`draft-04`||e.target===`openapi-3.0`)?(i.allOf=i.allOf??[],i.allOf.push(s)):Object.assign(i,s),Object.assign(i,a),t._zod.parent===o)for(let e in i)e===`$ref`||e===`allOf`||e in a||delete i[e];if(s.$ref&&n.def)for(let e in i)e===`$ref`||e===`allOf`||e in n.def&&JSON.stringify(i[e])===JSON.stringify(n.def[e])&&delete i[e]}let s=t._zod.parent;if(s&&s!==o){r(s);let t=e.seen.get(s);if(t?.schema.$ref&&(i.$ref=t.schema.$ref,t.def))for(let e in i)e===`$ref`||e===`allOf`||e in t.def&&JSON.stringify(i[e])===JSON.stringify(t.def[e])&&delete i[e]}e.override({zodSchema:t,jsonSchema:i,path:n.path??[]})};for(let t of[...e.seen.entries()].reverse())r(t[0]);let i={};if(e.target===`draft-2020-12`?i.$schema=`https://json-schema.org/draft/2020-12/schema`:e.target===`draft-07`?i.$schema=`http://json-schema.org/draft-07/schema#`:e.target===`draft-04`?i.$schema=`http://json-schema.org/draft-04/schema#`:e.target,e.external?.uri){let n=e.external.registry.get(t)?.id;if(!n)throw Error("Schema is missing an `id` property");i.$id=e.external.uri(n)}Object.assign(i,n.def??n.schema);let a=e.metadataRegistry.get(t)?.id;a!==void 0&&i.id===a&&delete i.id;let o=e.external?.defs??{};for(let t of e.seen.entries()){let e=t[1];e.def&&e.defId&&(e.def.id===e.defId&&delete e.def.id,o[e.defId]=e.def)}e.external||Object.keys(o).length>0&&(e.target===`draft-2020-12`?i.$defs=o:i.definitions=o);try{let n=JSON.parse(JSON.stringify(i));return Object.defineProperty(n,`~standard`,{value:{...t[`~standard`],jsonSchema:{input:yb(t,`input`,e.processors),output:yb(t,`output`,e.processors)}},enumerable:!1,writable:!1}),n}catch{throw Error(`Error converting schema to JSON.`)}}function vb(e,t){let n=t??{seen:new Set};if(n.seen.has(e))return!1;n.seen.add(e);let r=e._zod.def;if(r.type===`transform`)return!0;if(r.type===`array`)return vb(r.element,n);if(r.type===`set`)return vb(r.valueType,n);if(r.type===`lazy`)return vb(r.getter(),n);if(r.type===`promise`||r.type===`optional`||r.type===`nonoptional`||r.type===`nullable`||r.type===`readonly`||r.type===`default`||r.type===`prefault`)return vb(r.innerType,n);if(r.type===`intersection`)return vb(r.left,n)||vb(r.right,n);if(r.type===`record`||r.type===`map`)return vb(r.keyType,n)||vb(r.valueType,n);if(r.type===`pipe`)return e._zod.traits.has(`$ZodCodec`)?!0:vb(r.in,n)||vb(r.out,n);if(r.type===`object`){for(let e in r.shape)if(vb(r.shape[e],n))return!0;return!1}if(r.type===`union`){for(let e of r.options)if(vb(e,n))return!0;return!1}if(r.type===`tuple`){for(let e of r.items)if(vb(e,n))return!0;return!!(r.rest&&vb(r.rest,n))}return!1}var sne=(e,t={})=>n=>{let r=mb({...n,processors:t});return hb(e,r),gb(r,e),_b(r,e)},yb=(e,t,n={})=>r=>{let{libraryOptions:i,target:a}=r??{},o=mb({...i??{},target:a,io:t,processors:n});return hb(e,o),gb(o,e),_b(o,e)},cne={guid:`uuid`,url:`uri`,datetime:`date-time`,json_string:`json-string`,regex:``},lne=(e,t,n,r)=>{let i=n;i.type=`string`;let{minimum:a,maximum:o,format:s,patterns:c,contentEncoding:l}=e._zod.bag;if(typeof a==`number`&&(i.minLength=a),typeof o==`number`&&(i.maxLength=o),s&&(i.format=cne[s]??s,i.format===``&&delete i.format,s===`time`&&delete i.format),l&&(i.contentEncoding=l),c&&c.size>0){let e=[...c];e.length===1?i.pattern=e[0].source:e.length>1&&(i.allOf=[...e.map(e=>({...t.target===`draft-07`||t.target===`draft-04`||t.target===`openapi-3.0`?{type:`string`}:{},pattern:e.source}))])}},une=(e,t,n,r)=>{let i=n,{minimum:a,maximum:o,format:s,multipleOf:c,exclusiveMaximum:l,exclusiveMinimum:u}=e._zod.bag;typeof s==`string`&&s.includes(`int`)?i.type=`integer`:i.type=`number`;let d=typeof u==`number`&&u>=(a??-1/0),f=typeof l==`number`&&l<=(o??1/0),p=t.target===`draft-04`||t.target===`openapi-3.0`;d?p?(i.minimum=u,i.exclusiveMinimum=!0):i.exclusiveMinimum=u:typeof a==`number`&&(i.minimum=a),f?p?(i.maximum=l,i.exclusiveMaximum=!0):i.exclusiveMaximum=l:typeof o==`number`&&(i.maximum=o),typeof c==`number`&&(i.multipleOf=c)},dne=(e,t,n,r)=>{n.type=`boolean`},fne=(e,t,n,r)=>{n.not={}},pne=(e,t,n,r)=>{let i=e._zod.def,a=s_(i.entries);a.every(e=>typeof e==`number`)&&(n.type=`number`),a.every(e=>typeof e==`string`)&&(n.type=`string`),n.enum=a},mne=(e,t,n,r)=>{if(t.unrepresentable===`throw`)throw Error(`Custom types cannot be represented in JSON Schema`)},hne=(e,t,n,r)=>{if(t.unrepresentable===`throw`)throw Error(`Transforms cannot be represented in JSON Schema`)},gne=(e,t,n,r)=>{let i=n,a=e._zod.def,{minimum:o,maximum:s}=e._zod.bag;typeof o==`number`&&(i.minItems=o),typeof s==`number`&&(i.maxItems=s),i.type=`array`,i.items=hb(a.element,t,{...r,path:[...r.path,`items`]})},_ne=(e,t,n,r)=>{let i=n,a=e._zod.def;i.type=`object`,i.properties={};let o=a.shape;for(let e in o)i.properties[e]=hb(o[e],t,{...r,path:[...r.path,`properties`,e]});let s=new Set(Object.keys(o)),c=new Set([...s].filter(e=>{let n=a.shape[e]._zod;return t.io===`input`?n.optin===void 0:n.optout===void 0}));c.size>0&&(i.required=Array.from(c)),a.catchall?._zod.def.type===`never`?i.additionalProperties=!1:a.catchall?a.catchall&&(i.additionalProperties=hb(a.catchall,t,{...r,path:[...r.path,`additionalProperties`]})):t.io===`output`&&(i.additionalProperties=!1)},vne=(e,t,n,r)=>{let i=e._zod.def,a=i.inclusive===!1,o=i.options.map((e,n)=>hb(e,t,{...r,path:[...r.path,a?`oneOf`:`anyOf`,n]}));a?n.oneOf=o:n.anyOf=o},yne=(e,t,n,r)=>{let i=e._zod.def,a=hb(i.left,t,{...r,path:[...r.path,`allOf`,0]}),o=hb(i.right,t,{...r,path:[...r.path,`allOf`,1]}),s=e=>`allOf`in e&&Object.keys(e).length===1;n.allOf=[...s(a)?a.allOf:[a],...s(o)?o.allOf:[o]]},bne=(e,t,n,r)=>{let i=n,a=e._zod.def;i.type=`object`;let o=a.keyType,s=o._zod.bag?.patterns;if(a.mode===`loose`&&s&&s.size>0){let e=hb(a.valueType,t,{...r,path:[...r.path,`patternProperties`,`*`]});i.patternProperties={};for(let t of s)i.patternProperties[t.source]=e}else (t.target===`draft-07`||t.target===`draft-2020-12`)&&(i.propertyNames=hb(a.keyType,t,{...r,path:[...r.path,`propertyNames`]})),i.additionalProperties=hb(a.valueType,t,{...r,path:[...r.path,`additionalProperties`]});let c=o._zod.values;if(c){let e=[...c].filter(e=>typeof e==`string`||typeof e==`number`);e.length>0&&(i.required=e)}},xne=(e,t,n,r)=>{let i=e._zod.def,a=hb(i.innerType,t,r),o=t.seen.get(e);t.target===`openapi-3.0`?(o.ref=i.innerType,n.nullable=!0):n.anyOf=[a,{type:`null`}]},Sne=(e,t,n,r)=>{let i=e._zod.def;hb(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType},Cne=(e,t,n,r)=>{let i=e._zod.def;hb(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType,n.default=JSON.parse(JSON.stringify(i.defaultValue))},wne=(e,t,n,r)=>{let i=e._zod.def;hb(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType,t.io===`input`&&(n._prefault=JSON.parse(JSON.stringify(i.defaultValue)))},Tne=(e,t,n,r)=>{let i=e._zod.def;hb(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType;let o;try{o=i.catchValue(void 0)}catch{throw Error(`Dynamic catch values are not supported in JSON Schema`)}n.default=o},Ene=(e,t,n,r)=>{let i=e._zod.def,a=i.in._zod.traits.has(`$ZodTransform`),o=t.io===`input`?a?i.out:i.in:i.out;hb(o,t,r);let s=t.seen.get(e);s.ref=o},Dne=(e,t,n,r)=>{let i=e._zod.def;hb(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType,n.readOnly=!0},bb=(e,t,n,r)=>{let i=e._zod.def;hb(i.innerType,t,r);let a=t.seen.get(e);a.ref=i.innerType},One=V(`ZodISODateTime`,(e,t)=>{xy.init(e,t),Db.init(e,t)});function kne(e){return Lte(One,e)}var Ane=V(`ZodISODate`,(e,t)=>{Sy.init(e,t),Db.init(e,t)});function jne(e){return Rte(Ane,e)}var Mne=V(`ZodISOTime`,(e,t)=>{Cy.init(e,t),Db.init(e,t)});function Nne(e){return zte(Mne,e)}var Pne=V(`ZodISODuration`,(e,t)=>{wy.init(e,t),Db.init(e,t)});function Fne(e){return Bte(Pne,e)}var xb=V(`ZodError`,(e,t)=>{G_.init(e,t),e.name=`ZodError`,Object.defineProperties(e,{format:{value:t=>J_(e,t)},flatten:{value:t=>q_(e,t)},addIssue:{value:t=>{e.issues.push(t),e.message=JSON.stringify(e.issues,c_,2)}},addIssues:{value:t=>{e.issues.push(...t),e.message=JSON.stringify(e.issues,c_,2)}},isEmpty:{get(){return e.issues.length===0}}})},{Parent:Error}),Ine=Y_(xb),Lne=Z_(xb),Rne=$_(xb),zne=tv(xb),Bne=rv(xb),Vne=iv(xb),Hne=av(xb),Une=ov(xb),Wne=sv(xb),Gne=cv(xb),Kne=lv(xb),qne=uv(xb),Sb=new WeakMap;function Cb(e,t,n){let r=Object.getPrototypeOf(e),i=Sb.get(r);if(i||(i=new Set,Sb.set(r,i)),!i.has(t)){i.add(t);for(let e in n){let t=n[e];Object.defineProperty(r,e,{configurable:!0,enumerable:!1,get(){let n=t.bind(this);return Object.defineProperty(this,e,{configurable:!0,writable:!0,enumerable:!0,value:n}),n},set(t){Object.defineProperty(this,e,{configurable:!0,writable:!0,enumerable:!0,value:t})}})}}}var wb=V(`ZodType`,(e,t)=>(sy.init(e,t),Object.assign(e[`~standard`],{jsonSchema:{input:yb(e,`input`),output:yb(e,`output`)}}),e.toJSONSchema=sne(e,{}),e.def=t,e.type=t.type,Object.defineProperty(e,`_def`,{value:t}),e.parse=(t,n)=>Ine(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>Rne(e,t,n),e.parseAsync=async(t,n)=>Lne(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>zne(e,t,n),e.spa=e.safeParseAsync,e.encode=(t,n)=>Bne(e,t,n),e.decode=(t,n)=>Vne(e,t,n),e.encodeAsync=async(t,n)=>Hne(e,t,n),e.decodeAsync=async(t,n)=>Une(e,t,n),e.safeEncode=(t,n)=>Wne(e,t,n),e.safeDecode=(t,n)=>Gne(e,t,n),e.safeEncodeAsync=async(t,n)=>Kne(e,t,n),e.safeDecodeAsync=async(t,n)=>qne(e,t,n),Cb(e,`ZodType`,{check(...e){let t=this.def;return this.clone(g_(t,{checks:[...t.checks??[],...e.map(e=>typeof e==`function`?{_zod:{check:e,def:{check:`custom`},onattach:[]}}:e)]}),{parent:!0})},with(...e){return this.check(...e)},clone(e,t){return E_(this,e,t)},brand(){return this},register(e,t){return e.add(this,t),this},refine(e,t){return this.check(Zb(e,t))},superRefine(e,t){return this.check(Qb(e,t))},overwrite(e){return this.check(pb(e))},optional(){return zb(this)},exactOptional(){return kre(this)},nullable(){return Bb(this)},nullish(){return zb(Bb(this))},nonoptional(e){return Ub(this,e)},array(){return Nb(this)},or(e){return Sre([this,e])},and(e){return wre(this,e)},transform(e){return qb(this,Dre(e))},default(e){return Mre(this,e)},prefault(e){return Vb(this,e)},catch(e){return Gb(this,e)},pipe(e){return qb(this,e)},readonly(){return Yb(this)},describe(e){let t=this.clone();return rb.add(t,{description:e}),t},meta(...e){if(e.length===0)return rb.get(this);let t=this.clone();return rb.add(t,e[0]),t},isOptional(){return this.safeParse(void 0).success},isNullable(){return this.safeParse(null).success},apply(e){return e(this)}}),Object.defineProperty(e,`description`,{get(){return rb.get(e)?.description},configurable:!0}),e)),Tb=V(`_ZodString`,(e,t)=>{cy.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>lne(e,t,n,r);let n=e._zod.bag;e.format=n.format??null,e.minLength=n.minimum??null,e.maxLength=n.maximum??null,Cb(e,`_ZodString`,{regex(...e){return this.check(Kte(...e))},includes(...e){return this.check(Yte(...e))},startsWith(...e){return this.check(Xte(...e))},endsWith(...e){return this.check(Zte(...e))},min(...e){return this.check(db(...e))},max(...e){return this.check(ub(...e))},length(...e){return this.check(fb(...e))},nonempty(...e){return this.check(db(1,...e))},lowercase(e){return this.check(qte(e))},uppercase(e){return this.check(Jte(e))},trim(){return this.check($te())},normalize(...e){return this.check(Qte(...e))},toLowerCase(){return this.check(ene())},toUpperCase(){return this.check(tne())},slugify(){return this.check(nne())}})}),Jne=V(`ZodString`,(e,t)=>{cy.init(e,t),Tb.init(e,t),e.email=t=>e.check(gte(Yne,t)),e.url=t=>e.check(xte(Xne,t)),e.jwt=t=>e.check(Ite(dre,t)),e.emoji=t=>e.check(Ste(Zne,t)),e.guid=t=>e.check(ib(Ob,t)),e.uuid=t=>e.check(_te(kb,t)),e.uuidv4=t=>e.check(vte(kb,t)),e.uuidv6=t=>e.check(yte(kb,t)),e.uuidv7=t=>e.check(bte(kb,t)),e.nanoid=t=>e.check(Cte(Qne,t)),e.guid=t=>e.check(ib(Ob,t)),e.cuid=t=>e.check(wte($ne,t)),e.cuid2=t=>e.check(Tte(ere,t)),e.ulid=t=>e.check(Ete(tre,t)),e.base64=t=>e.check(Nte(cre,t)),e.base64url=t=>e.check(Pte(lre,t)),e.xid=t=>e.check(Dte(nre,t)),e.ksuid=t=>e.check(Ote(rre,t)),e.ipv4=t=>e.check(kte(ire,t)),e.ipv6=t=>e.check(Ate(are,t)),e.cidrv4=t=>e.check(jte(ore,t)),e.cidrv6=t=>e.check(Mte(sre,t)),e.e164=t=>e.check(Fte(ure,t)),e.datetime=t=>e.check(kne(t)),e.date=t=>e.check(jne(t)),e.time=t=>e.check(Nne(t)),e.duration=t=>e.check(Fne(t))});function Eb(e){return hte(Jne,e)}var Db=V(`ZodStringFormat`,(e,t)=>{ly.init(e,t),Tb.init(e,t)}),Yne=V(`ZodEmail`,(e,t)=>{fy.init(e,t),Db.init(e,t)}),Ob=V(`ZodGUID`,(e,t)=>{uy.init(e,t),Db.init(e,t)}),kb=V(`ZodUUID`,(e,t)=>{dy.init(e,t),Db.init(e,t)}),Xne=V(`ZodURL`,(e,t)=>{py.init(e,t),Db.init(e,t)}),Zne=V(`ZodEmoji`,(e,t)=>{my.init(e,t),Db.init(e,t)}),Qne=V(`ZodNanoID`,(e,t)=>{hy.init(e,t),Db.init(e,t)}),$ne=V(`ZodCUID`,(e,t)=>{gy.init(e,t),Db.init(e,t)}),ere=V(`ZodCUID2`,(e,t)=>{_y.init(e,t),Db.init(e,t)}),tre=V(`ZodULID`,(e,t)=>{vy.init(e,t),Db.init(e,t)}),nre=V(`ZodXID`,(e,t)=>{yy.init(e,t),Db.init(e,t)}),rre=V(`ZodKSUID`,(e,t)=>{by.init(e,t),Db.init(e,t)}),ire=V(`ZodIPv4`,(e,t)=>{Ty.init(e,t),Db.init(e,t)}),are=V(`ZodIPv6`,(e,t)=>{Ey.init(e,t),Db.init(e,t)}),ore=V(`ZodCIDRv4`,(e,t)=>{Dy.init(e,t),Db.init(e,t)}),sre=V(`ZodCIDRv6`,(e,t)=>{Oy.init(e,t),Db.init(e,t)}),cre=V(`ZodBase64`,(e,t)=>{Ay.init(e,t),Db.init(e,t)}),lre=V(`ZodBase64URL`,(e,t)=>{My.init(e,t),Db.init(e,t)}),ure=V(`ZodE164`,(e,t)=>{Ny.init(e,t),Db.init(e,t)}),dre=V(`ZodJWT`,(e,t)=>{Fy.init(e,t),Db.init(e,t)}),Ab=V(`ZodNumber`,(e,t)=>{Iy.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>une(e,t,n,r),Cb(e,`ZodNumber`,{gt(e,t){return this.check(sb(e,t))},gte(e,t){return this.check(cb(e,t))},min(e,t){return this.check(cb(e,t))},lt(e,t){return this.check(ab(e,t))},lte(e,t){return this.check(ob(e,t))},max(e,t){return this.check(ob(e,t))},int(e){return this.check(jb(e))},safe(e){return this.check(jb(e))},positive(e){return this.check(sb(0,e))},nonnegative(e){return this.check(cb(0,e))},negative(e){return this.check(ab(0,e))},nonpositive(e){return this.check(ob(0,e))},multipleOf(e,t){return this.check(lb(e,t))},step(e,t){return this.check(lb(e,t))},finite(){return this}});let n=e._zod.bag;e.minValue=Math.max(n.minimum??-1/0,n.exclusiveMinimum??-1/0)??null,e.maxValue=Math.min(n.maximum??1/0,n.exclusiveMaximum??1/0)??null,e.isInt=(n.format??``).includes(`int`)||Number.isSafeInteger(n.multipleOf??.5),e.isFinite=!0,e.format=n.format??null});function fre(e){return Vte(Ab,e)}var pre=V(`ZodNumberFormat`,(e,t)=>{Ly.init(e,t),Ab.init(e,t)});function jb(e){return Hte(pre,e)}var mre=V(`ZodBoolean`,(e,t)=>{Ry.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>dne(e,t,n,r)});function hre(e){return Ute(mre,e)}var gre=V(`ZodUnknown`,(e,t)=>{zy.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(e,t,n)=>void 0});function Mb(){return Wte(gre)}var _re=V(`ZodNever`,(e,t)=>{By.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>fne(e,t,n,r)});function vre(e){return Gte(_re,e)}var yre=V(`ZodArray`,(e,t)=>{Hy.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>gne(e,t,n,r),e.element=t.element,Cb(e,`ZodArray`,{min(e,t){return this.check(db(e,t))},nonempty(e){return this.check(db(1,e))},max(e,t){return this.check(ub(e,t))},length(e,t){return this.check(fb(e,t))},unwrap(){return this.element}})});function Nb(e,t){return rne(yre,e,t)}var bre=V(`ZodObject`,(e,t)=>{Qee.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>_ne(e,t,n,r),m_(e,`shape`,()=>t.shape),Cb(e,`ZodObject`,{keyof(){return Lb(Object.keys(this._zod.def.shape))},catchall(e){return this.clone({...this._zod.def,catchall:e})},passthrough(){return this.clone({...this._zod.def,catchall:Mb()})},loose(){return this.clone({...this._zod.def,catchall:Mb()})},strict(){return this.clone({...this._zod.def,catchall:vre()})},strip(){return this.clone({...this._zod.def,catchall:void 0})},extend(e){return M_(this,e)},safeExtend(e){return N_(this,e)},merge(e){return P_(this,e)},pick(e){return A_(this,e)},omit(e){return j_(this,e)},partial(...e){return F_(Rb,this,e[0])},required(...e){return I_(Hb,this,e[0])}})});function Pb(e,t){return new bre({type:`object`,shape:e??{},...D_(t)})}var xre=V(`ZodUnion`,(e,t)=>{$ee.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>vne(e,t,n,r),e.options=t.options});function Sre(e,t){return new xre({type:`union`,options:e,...D_(t)})}var Cre=V(`ZodIntersection`,(e,t)=>{ete.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>yne(e,t,n,r)});function wre(e,t){return new Cre({type:`intersection`,left:e,right:t})}var Fb=V(`ZodRecord`,(e,t)=>{tte.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>bne(e,t,n,r),e.keyType=t.keyType,e.valueType=t.valueType});function Tre(e,t,n){return!t||!t._zod?new Fb({type:`record`,keyType:Eb(),valueType:e,...D_(t)}):new Fb({type:`record`,keyType:e,valueType:t,...D_(n)})}var Ib=V(`ZodEnum`,(e,t)=>{nte.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>pne(e,t,n,r),e.enum=t.entries,e.options=Object.values(t.entries);let n=new Set(Object.keys(t.entries));e.extract=(e,r)=>{let i={};for(let r of e)if(n.has(r))i[r]=t.entries[r];else throw Error(`Key ${r} not found in enum`);return new Ib({...t,checks:[],...D_(r),entries:i})},e.exclude=(e,r)=>{let i={...t.entries};for(let t of e)if(n.has(t))delete i[t];else throw Error(`Key ${t} not found in enum`);return new Ib({...t,checks:[],...D_(r),entries:i})}});function Lb(e,t){return new Ib({type:`enum`,entries:Array.isArray(e)?Object.fromEntries(e.map(e=>[e,e])):e,...D_(t)})}var Ere=V(`ZodTransform`,(e,t)=>{rte.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>hne(e,t,n,r),e._zod.parse=(n,r)=>{if(r.direction===`backward`)throw new i_(e.constructor.name);n.addIssue=r=>{if(typeof r==`string`)n.issues.push(U_(r,n.value,t));else{let t=r;t.fatal&&(t.continue=!1),t.code??=`custom`,t.input??=n.value,t.inst??=e,n.issues.push(U_(t))}};let i=t.transform(n.value,n);return i instanceof Promise?i.then(e=>(n.value=e,n.fallback=!0,n)):(n.value=i,n.fallback=!0,n)}});function Dre(e){return new Ere({type:`transform`,transform:e})}var Rb=V(`ZodOptional`,(e,t)=>{Xy.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>bb(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function zb(e){return new Rb({type:`optional`,innerType:e})}var Ore=V(`ZodExactOptional`,(e,t)=>{ite.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>bb(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function kre(e){return new Ore({type:`optional`,innerType:e})}var Are=V(`ZodNullable`,(e,t)=>{ate.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>xne(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function Bb(e){return new Are({type:`nullable`,innerType:e})}var jre=V(`ZodDefault`,(e,t)=>{ote.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Cne(e,t,n,r),e.unwrap=()=>e._zod.def.innerType,e.removeDefault=e.unwrap});function Mre(e,t){return new jre({type:`default`,innerType:e,get defaultValue(){return typeof t==`function`?t():C_(t)}})}var Nre=V(`ZodPrefault`,(e,t)=>{ste.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>wne(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function Vb(e,t){return new Nre({type:`prefault`,innerType:e,get defaultValue(){return typeof t==`function`?t():C_(t)}})}var Hb=V(`ZodNonOptional`,(e,t)=>{cte.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Sne(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function Ub(e,t){return new Hb({type:`nonoptional`,innerType:e,...D_(t)})}var Wb=V(`ZodCatch`,(e,t)=>{lte.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Tne(e,t,n,r),e.unwrap=()=>e._zod.def.innerType,e.removeCatch=e.unwrap});function Gb(e,t){return new Wb({type:`catch`,innerType:e,catchValue:typeof t==`function`?t:()=>t})}var Kb=V(`ZodPipe`,(e,t)=>{ute.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Ene(e,t,n,r),e.in=t.in,e.out=t.out});function qb(e,t){return new Kb({type:`pipe`,in:e,out:t})}var Jb=V(`ZodReadonly`,(e,t)=>{dte.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>Dne(e,t,n,r),e.unwrap=()=>e._zod.def.innerType});function Yb(e){return new Jb({type:`readonly`,innerType:e})}var Xb=V(`ZodCustom`,(e,t)=>{fte.init(e,t),wb.init(e,t),e._zod.processJSONSchema=(t,n,r)=>mne(e,t,n,r)});function Zb(e,t={}){return ine(Xb,e,t)}function Qb(e,t){return ane(e,t)}var $b=`spinifex:v1:aws-credentials`,ex=Pb({accessKeyId:Eb().min(16,`Access Key ID must be at least 16 characters`),secretAccessKey:Eb().min(1,`Secret Access Key is required`)}),tx;function nx(){if(tx!==void 0)return tx;try{let e=localStorage.getItem($b);if(!e)return tx=null,null;let t=JSON.parse(e),n=ex.safeParse(t);return tx=n.success?n.data:null,tx}catch{return tx=null,null}}function rx(e){try{localStorage.setItem($b,JSON.stringify(e)),tx=e}catch{tx=e}}function ix(){try{localStorage.removeItem($b)}catch{}tx=null}var ax=e=>new TextEncoder().encode(e),ox=typeof Buffer<`u`&&Buffer.from?function(e){return Buffer.from(e,`utf8`)}:ax;function sx(e){return e instanceof Uint8Array?e:typeof e==`string`?ox(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}function cx(e){return typeof e==`string`?e.length===0:e.byteLength===0}function lx(e){return new Uint8Array([(e&4278190080)>>24,(e&16711680)>>16,(e&65280)>>8,e&255])}function ux(e){if(!Uint32Array.from){for(var t=new Uint32Array(e.length),n=0;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw TypeError(t?`Object is not iterable.`:`Symbol.iterator is not defined.`)}var bx=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),xx=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],Sx=2**53-1,Cx=function(){function e(){this.state=Int32Array.from(xx),this.temp=new Int32Array(64),this.buffer=new Uint8Array(64),this.bufferLength=0,this.bytesHashed=0,this.finished=!1}return e.prototype.update=function(e){if(this.finished)throw Error(`Attempted to update an already finished hash.`);var t=0,n=e.byteLength;if(this.bytesHashed+=n,this.bytesHashed*8>Sx)throw Error(`Cannot hash more than 2^53 - 1 bits`);for(;n>0;)this.buffer[this.bufferLength++]=e[t++],n--,this.bufferLength===64&&(this.hashBuffer(),this.bufferLength=0)},e.prototype.digest=function(){if(!this.finished){var e=this.bytesHashed*8,t=new DataView(this.buffer.buffer,this.buffer.byteOffset,this.buffer.byteLength),n=this.bufferLength;if(t.setUint8(this.bufferLength++,128),n%64>=56){for(var r=this.bufferLength;r<64;r++)t.setUint8(r,0);this.hashBuffer(),this.bufferLength=0}for(var r=this.bufferLength;r<56;r++)t.setUint8(r,0);t.setUint32(56,Math.floor(e/4294967296),!0),t.setUint32(60,e),this.hashBuffer(),this.finished=!0}for(var i=new Uint8Array(32),r=0;r<8;r++)i[r*4]=this.state[r]>>>24&255,i[r*4+1]=this.state[r]>>>16&255,i[r*4+2]=this.state[r]>>>8&255,i[r*4+3]=this.state[r]>>>0&255;return i},e.prototype.hashBuffer=function(){for(var e=this,t=e.buffer,n=e.state,r=n[0],i=n[1],a=n[2],o=n[3],s=n[4],c=n[5],l=n[6],u=n[7],d=0;d<64;d++){if(d<16)this.temp[d]=(t[d*4]&255)<<24|(t[d*4+1]&255)<<16|(t[d*4+2]&255)<<8|t[d*4+3]&255;else{var f=this.temp[d-2],p=(f>>>17|f<<15)^(f>>>19|f<<13)^f>>>10;f=this.temp[d-15];var m=(f>>>7|f<<25)^(f>>>18|f<<14)^f>>>3;this.temp[d]=(p+this.temp[d-7]|0)+(m+this.temp[d-16]|0)}var h=(((s>>>6|s<<26)^(s>>>11|s<<21)^(s>>>25|s<<7))+(s&c^~s&l)|0)+(u+(bx[d]+this.temp[d]|0)|0)|0,g=((r>>>2|r<<30)^(r>>>13|r<<19)^(r>>>22|r<<10))+(r&i^r&a^i&a)|0;u=l,l=c,c=s,s=o+h|0,o=a,a=i,i=r,r=h+g|0}n[0]+=r,n[1]+=i,n[2]+=a,n[3]+=o,n[4]+=s,n[5]+=c,n[6]+=l,n[7]+=u},e}(),wx=function(){function e(e){this.secret=e,this.hash=new Cx,this.reset()}return e.prototype.update=function(e){if(!(cx(e)||this.error))try{this.hash.update(sx(e))}catch(e){this.error=e}},e.prototype.digestSync=function(){if(this.error)throw this.error;return this.outer?(this.outer.finished||this.outer.update(this.hash.digest()),this.outer.digest()):this.hash.digest()},e.prototype.digest=function(){return _x(this,void 0,void 0,function(){return vx(this,function(e){return[2,this.digestSync()]})})},e.prototype.reset=function(){if(this.hash=new Cx,this.secret){this.outer=new Cx;var e=Tx(this.secret),t=new Uint8Array(64);t.set(e);for(var n=0;n<64;n++)e[n]^=54,t[n]^=92;this.hash.update(e),this.outer.update(t);for(var n=0;n64){var n=new Cx;n.update(t),t=n.digest()}var r=new Uint8Array(64);return r.set(t),r}var Ex=[`decrypt`,`digest`,`encrypt`,`exportKey`,`generateKey`,`importKey`,`sign`,`verify`];function Dx(e){if(Ox(e)&&typeof e.crypto.subtle==`object`){var t=e.crypto.subtle;return kx(t)}return!1}function Ox(e){return typeof e==`object`&&typeof e.crypto==`object`?typeof e.crypto.getRandomValues==`function`:!1}function kx(e){return e&&Ex.every(function(t){return typeof e[t]==`function`})}var Ax=function(){function e(e){Dx(hx())?this.hash=new gx(e):this.hash=new wx(e)}return e.prototype.update=function(e,t){this.hash.update(sx(e))},e.prototype.digest=function(){return this.hash.digest()},e.prototype.reset=function(){this.hash.reset()},e}(),jx=e=>({setHttpHandler(t){e.httpHandler=t},httpHandler(){return e.httpHandler},updateHttpClientConfig(t,n){e.httpHandler?.updateHttpClientConfig(t,n)},httpHandlerConfigs(){return e.httpHandler.httpHandlerConfigs()}}),Mx=e=>({httpHandler:e.httpHandler()}),Nx;(function(e){e.HTTP=`http`,e.HTTPS=`https`})(Nx||={});var Px;(function(e){e.MD5=`md5`,e.CRC32=`crc32`,e.CRC32C=`crc32c`,e.SHA1=`sha1`,e.SHA256=`sha256`})(Px||={});var Fx=`__smithy_context`,Ix=class e{method;protocol;hostname;port;path;query;headers;username;password;fragment;body;constructor(e){this.method=e.method||`GET`,this.hostname=e.hostname||`localhost`,this.port=e.port,this.query=e.query||{},this.headers=e.headers||{},this.body=e.body,this.protocol=e.protocol?e.protocol.slice(-1)===`:`?e.protocol:`${e.protocol}:`:`https:`,this.path=e.path?e.path.charAt(0)===`/`?e.path:`/${e.path}`:`/`,this.username=e.username,this.password=e.password,this.fragment=e.fragment}static clone(t){let n=new e({...t,headers:{...t.headers}});return n.query&&=Lx(n.query),n}static isInstance(e){if(!e)return!1;let t=e;return`method`in t&&`protocol`in t&&`hostname`in t&&`path`in t&&typeof t.query==`object`&&typeof t.headers==`object`}clone(){return e.clone(this)}};function Lx(e){return Object.keys(e).reduce((t,n)=>{let r=e[n];return{...t,[n]:Array.isArray(r)?[...r]:r}},{})}var Rx=class{statusCode;reason;headers;body;constructor(e){this.statusCode=e.statusCode,this.reason=e.reason,this.headers=e.headers||{},this.body=e.body}static isInstance(e){if(!e)return!1;let t=e;return typeof t.statusCode==`number`&&typeof t.headers==`object`}},zx={},Bx={};for(let e=0;e<256;e++){let t=e.toString(16).toLowerCase();t.length===1&&(t=`0${t}`),zx[e]=t,Bx[t]=e}function Vx(e){if(e.length%2!=0)throw Error(`Hex encoded strings must have an even number length`);let t=new Uint8Array(e.length/2);for(let n=0;nnew TextEncoder().encode(e),Wx=e=>typeof e==`string`?Ux(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e),Gx=e=>{if(typeof e==`string`)return e;if(typeof e!=`object`||typeof e.byteOffset!=`number`||typeof e.byteLength!=`number`)throw Error(`@smithy/util-utf8: toUtf8 encoder function only accepts string | Uint8Array.`);return new TextDecoder(`utf-8`).decode(e)},Kx=`X-Amz-Algorithm`,qx=`X-Amz-Credential`,Jx=`X-Amz-Date`,Yx=`X-Amz-SignedHeaders`,Xx=`X-Amz-Expires`,Zx=`X-Amz-Signature`,Qx=`X-Amz-Security-Token`,$x=`authorization`,eS=Jx.toLowerCase(),tS=[$x,eS,`date`];Zx.toLowerCase();var nS=`x-amz-content-sha256`,rS=Qx.toLowerCase(),iS={authorization:!0,"cache-control":!0,connection:!0,expect:!0,from:!0,"keep-alive":!0,"max-forwards":!0,pragma:!0,referer:!0,te:!0,trailer:!0,"transfer-encoding":!0,upgrade:!0,"user-agent":!0,"x-amzn-trace-id":!0},aS=/^proxy-/,oS=/^sec-/,sS=`AWS4-HMAC-SHA256`,cS=`AWS4-HMAC-SHA256-PAYLOAD`,lS=`UNSIGNED-PAYLOAD`,uS=`aws4_request`,dS={},fS=[],pS=(e,t,n)=>`${e}/${t}/${n}/${uS}`,mS=async(e,t,n,r,i)=>{let a=`${n}:${r}:${i}:${Hx(await hS(e,t.secretAccessKey,t.accessKeyId))}:${t.sessionToken}`;if(a in dS)return dS[a];for(fS.push(a);fS.length>50;)delete dS[fS.shift()];let o=`AWS4${t.secretAccessKey}`;for(let t of[n,r,i,uS])o=await hS(e,o,t);return dS[a]=o},hS=(e,t,n)=>{let r=new e(t);return r.update(Wx(n)),r.digest()},gS=({headers:e},t,n)=>{let r={};for(let i of Object.keys(e).sort()){if(e[i]==null)continue;let a=i.toLowerCase();(a in iS||t?.has(a)||aS.test(a)||oS.test(a))&&(!n||n&&!n.has(a))||(r[a]=e[i].trim().replace(/\s+/g,` `))}return r},_S=e=>typeof ArrayBuffer==`function`&&e instanceof ArrayBuffer||Object.prototype.toString.call(e)===`[object ArrayBuffer]`,vS=async({headers:e,body:t},n)=>{for(let t of Object.keys(e))if(t.toLowerCase()===`x-amz-content-sha256`)return e[t];if(t==null)return`e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`;if(typeof t==`string`||ArrayBuffer.isView(t)||_S(t)){let e=new n;return e.update(Wx(t)),Hx(await e.digest())}return lS},yS=class{format(e){let t=[];for(let n of Object.keys(e)){let r=Ux(n);t.push(Uint8Array.from([r.byteLength]),r,this.formatHeaderValue(e[n]))}let n=new Uint8Array(t.reduce((e,t)=>e+t.byteLength,0)),r=0;for(let e of t)n.set(e,r),r+=e.byteLength;return n}formatHeaderValue(e){switch(e.type){case`boolean`:return Uint8Array.from([+!e.value]);case`byte`:return Uint8Array.from([2,e.value]);case`short`:let t=new DataView(new ArrayBuffer(3));return t.setUint8(0,3),t.setInt16(1,e.value,!1),new Uint8Array(t.buffer);case`integer`:let n=new DataView(new ArrayBuffer(5));return n.setUint8(0,4),n.setInt32(1,e.value,!1),new Uint8Array(n.buffer);case`long`:let r=new Uint8Array(9);return r[0]=5,r.set(e.value.bytes,1),r;case`binary`:let i=new DataView(new ArrayBuffer(3+e.value.byteLength));i.setUint8(0,6),i.setUint16(1,e.value.byteLength,!1);let a=new Uint8Array(i.buffer);return a.set(e.value,3),a;case`string`:let o=Ux(e.value),s=new DataView(new ArrayBuffer(3+o.byteLength));s.setUint8(0,7),s.setUint16(1,o.byteLength,!1);let c=new Uint8Array(s.buffer);return c.set(o,3),c;case`timestamp`:let l=new Uint8Array(9);return l[0]=8,l.set(SS.fromNumber(e.value.valueOf()).bytes,1),l;case`uuid`:if(!xS.test(e.value))throw Error(`Invalid UUID received: ${e.value}`);let u=new Uint8Array(17);return u[0]=9,u.set(Vx(e.value.replace(/\-/g,``)),1),u}}},bS;(function(e){e[e.boolTrue=0]=`boolTrue`,e[e.boolFalse=1]=`boolFalse`,e[e.byte=2]=`byte`,e[e.short=3]=`short`,e[e.integer=4]=`integer`,e[e.long=5]=`long`,e[e.byteArray=6]=`byteArray`,e[e.string=7]=`string`,e[e.timestamp=8]=`timestamp`,e[e.uuid=9]=`uuid`})(bS||={});var xS=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,SS=class e{bytes;constructor(e){if(this.bytes=e,e.byteLength!==8)throw Error(`Int64 buffers must be exactly 8 bytes`)}static fromNumber(t){if(t>0x8000000000000000||t<-0x8000000000000000)throw Error(`${t} is too large (or, if negative, too small) to represent as an Int64`);let n=new Uint8Array(8);for(let e=7,r=Math.abs(Math.round(t));e>-1&&r>0;e--,r/=256)n[e]=r;return t<0&&CS(n),new e(n)}valueOf(){let e=this.bytes.slice(0),t=e[0]&128;return t&&CS(e),parseInt(Hx(e),16)*(t?-1:1)}toString(){return String(this.valueOf())}};function CS(e){for(let t=0;t<8;t++)e[t]^=255;for(let t=7;t>-1&&(e[t]++,e[t]===0);t--);}var wS=(e,t)=>{e=e.toLowerCase();for(let n of Object.keys(t))if(e===n.toLowerCase())return!0;return!1},TS=(e,t={})=>{let{headers:n,query:r={}}=Ix.clone(e);for(let e of Object.keys(n)){let i=e.toLowerCase();(i.slice(0,6)===`x-amz-`&&!t.unhoistableHeaders?.has(i)||t.hoistableHeaders?.has(i))&&(r[e]=n[e],delete n[e])}return{...e,headers:n,query:r}},ES=e=>{e=Ix.clone(e);for(let t of Object.keys(e.headers))tS.indexOf(t.toLowerCase())>-1&&delete e.headers[t];return e},DS=e=>e.__smithy_context||={},OS=e=>{if(typeof e==`function`)return e;let t=Promise.resolve(e);return()=>t},kS=e=>encodeURIComponent(e).replace(/[!'()*]/g,AS),AS=e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`,jS=({query:e={}})=>{let t=[],n={};for(let r of Object.keys(e)){if(r.toLowerCase()===`x-amz-signature`)continue;let i=kS(r);t.push(i);let a=e[r];typeof a==`string`?n[i]=`${i}=${kS(a)}`:Array.isArray(a)&&(n[i]=a.slice(0).reduce((e,t)=>e.concat([`${i}=${kS(t)}`]),[]).sort().join(`&`))}return t.sort().map(e=>n[e]).filter(e=>e).join(`&`)},MS=e=>NS(e).toISOString().replace(/\.\d{3}Z$/,`Z`),NS=e=>typeof e==`number`?new Date(e*1e3):typeof e==`string`?Number(e)?new Date(Number(e)*1e3):new Date(e):e,PS=class{service;regionProvider;credentialProvider;sha256;uriEscapePath;applyChecksum;constructor({applyChecksum:e,credentials:t,region:n,service:r,sha256:i,uriEscapePath:a=!0}){this.service=r,this.sha256=i,this.uriEscapePath=a,this.applyChecksum=typeof e==`boolean`?e:!0,this.regionProvider=OS(n),this.credentialProvider=OS(t)}createCanonicalRequest(e,t,n){let r=Object.keys(t).sort();return`${e.method} -${this.getCanonicalPath(e)} -${jS(e)} -${r.map(e=>`${e}:${t[e]}`).join(` -`)} - -${r.join(`;`)} -${n}`}async createStringToSign(e,t,n,r){let i=new this.sha256;return i.update(Wx(n)),`${r} -${e} -${t} -${Hx(await i.digest())}`}getCanonicalPath({path:e}){if(this.uriEscapePath){let t=[];for(let n of e.split(`/`))n?.length!==0&&n!==`.`&&(n===`..`?t.pop():t.push(n));return kS(`${e?.startsWith(`/`)?`/`:``}${t.join(`/`)}${t.length>0&&e?.endsWith(`/`)?`/`:``}`).replace(/%2F/g,`/`)}return e}validateResolvedCredentials(e){if(typeof e!=`object`||typeof e.accessKeyId!=`string`||typeof e.secretAccessKey!=`string`)throw Error(`Resolved credential object is not valid`)}formatDate(e){let t=MS(e).replace(/[\-:]/g,``);return{longDate:t,shortDate:t.slice(0,8)}}getCanonicalHeaderList(e){return Object.keys(e).sort().join(`;`)}},FS=class extends PS{headerFormatter=new yS;constructor({applyChecksum:e,credentials:t,region:n,service:r,sha256:i,uriEscapePath:a=!0}){super({applyChecksum:e,credentials:t,region:n,service:r,sha256:i,uriEscapePath:a})}async presign(e,t={}){let{signingDate:n=new Date,expiresIn:r=3600,unsignableHeaders:i,unhoistableHeaders:a,signableHeaders:o,hoistableHeaders:s,signingRegion:c,signingService:l}=t,u=await this.credentialProvider();this.validateResolvedCredentials(u);let d=c??await this.regionProvider(),{longDate:f,shortDate:p}=this.formatDate(n);if(r>604800)return Promise.reject(`Signature version 4 presigned URLs must have an expiration date less than one week in the future`);let m=pS(p,d,l??this.service),h=TS(ES(e),{unhoistableHeaders:a,hoistableHeaders:s});u.sessionToken&&(h.query[Qx]=u.sessionToken),h.query[Kx]=sS,h.query[qx]=`${u.accessKeyId}/${m}`,h.query[Jx]=f,h.query[Xx]=r.toString(10);let g=gS(h,i,o);return h.query[Yx]=this.getCanonicalHeaderList(g),h.query[Zx]=await this.getSignature(f,m,this.getSigningKey(u,d,p,l),this.createCanonicalRequest(h,g,await vS(e,this.sha256))),h}async sign(e,t){return typeof e==`string`?this.signString(e,t):e.headers&&e.payload?this.signEvent(e,t):e.message?this.signMessage(e,t):this.signRequest(e,t)}async signEvent({headers:e,payload:t},{signingDate:n=new Date,priorSignature:r,signingRegion:i,signingService:a,eventStreamCredentials:o}){let s=i??await this.regionProvider(),{shortDate:c,longDate:l}=this.formatDate(n),u=pS(c,s,a??this.service),d=await vS({headers:{},body:t},this.sha256),f=new this.sha256;f.update(e);let p=[cS,l,u,r,Hx(await f.digest()),d].join(` -`);return this.signString(p,{signingDate:n,signingRegion:s,signingService:a,eventStreamCredentials:o})}async signMessage(e,{signingDate:t=new Date,signingRegion:n,signingService:r,eventStreamCredentials:i}){return this.signEvent({headers:this.headerFormatter.format(e.message.headers),payload:e.message.body},{signingDate:t,signingRegion:n,signingService:r,priorSignature:e.priorSignature,eventStreamCredentials:i}).then(t=>({message:e.message,signature:t}))}async signString(e,{signingDate:t=new Date,signingRegion:n,signingService:r,eventStreamCredentials:i}={}){let a=i??await this.credentialProvider();this.validateResolvedCredentials(a);let o=n??await this.regionProvider(),{shortDate:s}=this.formatDate(t),c=new this.sha256(await this.getSigningKey(a,o,s,r));return c.update(Wx(e)),Hx(await c.digest())}async signRequest(e,{signingDate:t=new Date,signableHeaders:n,unsignableHeaders:r,signingRegion:i,signingService:a}={}){let o=await this.credentialProvider();this.validateResolvedCredentials(o);let s=i??await this.regionProvider(),c=ES(e),{longDate:l,shortDate:u}=this.formatDate(t),d=pS(u,s,a??this.service);c.headers[eS]=l,o.sessionToken&&(c.headers[rS]=o.sessionToken);let f=await vS(c,this.sha256);!wS(`x-amz-content-sha256`,c.headers)&&this.applyChecksum&&(c.headers[nS]=f);let p=gS(c,r,n),m=await this.getSignature(l,d,this.getSigningKey(o,s,u,a),this.createCanonicalRequest(c,p,f));return c.headers[$x]=`${sS} Credential=${o.accessKeyId}/${d}, SignedHeaders=${this.getCanonicalHeaderList(p)}, Signature=${m}`,c}async getSignature(e,t,n,r){let i=await this.createStringToSign(e,t,r,sS),a=new this.sha256(await n);return a.update(Wx(i)),Hx(await a.digest())}getSigningKey(e,t,n,r){return mS(this.sha256,e,n,t,r||this.service)}},IS={SignatureV4a:null},LS=`ap-southeast-2`,RS=9999,zS=class extends Error{status;constructor(e,t,n){super(e),this.name=t,this.status=n}};function BS(e,t){let n=`<${t}>`,r=``,i=e.indexOf(n);if(i===-1)return null;let a=e.indexOf(r,i+n.length);return a===-1?null:e.slice(i+n.length,a)}async function VS({action:e,credentials:t,service:n=`spinifex`}){let r=window.location.protocol.replace(`:`,``),i=`Action=${e}`,a=new Ix({method:`POST`,protocol:r,hostname:`localhost`,port:RS,path:`/`,headers:{host:`localhost:${RS}`,"content-type":`application/x-www-form-urlencoded`},body:i}),o=await new FS({credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey},region:LS,service:n,sha256:Ax}).sign(a),s={};for(let[e,t]of Object.entries(o.headers))typeof t==`string`&&(s[e]=t);let c=`${window.location.protocol}//${window.location.host}/proxy/awsgw/`,l=await fetch(c,{method:`POST`,headers:s,body:i});if(!l.ok){let t=await l.text().catch(()=>``),n=BS(t,`Code`),r=BS(t,`Message`)??t;throw new zS(`${e} failed: ${l.status}${r?` - ${r}`:``}`,n??`SignedFetchError`,l.status)}return await l.json()}var HS=`000000000001`,US=(0,P.createContext)(void 0);function WS({children:e}){let[t,n]=(0,P.useState)({isAdmin:!1,accountId:null,userName:null,version:null,license:null,loading:!0});return(0,P.useEffect)(()=>{async function e(){let e=nx();if(!e){n(e=>({...e,loading:!1}));return}try{let t=await VS({action:`GetCallerIdentity`,credentials:e}),r=t.account_id===HS,i=null;if(r)try{i=await VS({action:`GetVersion`,credentials:e})}catch{}n({isAdmin:r,accountId:t.account_id,userName:t.user_name,version:i,license:i?.license??null,loading:!1})}catch{n(e=>({...e,loading:!1}))}}e()},[]),(0,F.jsx)(US,{value:t,children:e})}function GS(){let e=(0,P.useContext)(US);if(e===void 0)throw Error(`useAdmin must be used within an AdminProvider`);return e}var KS=new Set([`InvalidClientTokenId`,`SignatureDoesNotMatch`,`AuthFailure`,`MissingAuthenticationToken`,`IncompleteSignature`,`UnrecognizedClientException`,`ExpiredToken`,`ExpiredTokenException`,`InvalidAccessKeyId`]);function qS(e){if(typeof e!=`object`||!e||!(`name`in e))return!1;let{name:t}=e;return typeof t==`string`&&KS.has(t)}function JS(e){return e}var YS=e=>t=>async n=>{if(!Ix.isInstance(n.request))return t(n);let{request:r}=n,{handlerProtocol:i=``}=e.requestHandler.metadata||{};if(i.indexOf(`h2`)>=0&&!r.headers[`:authority`])delete r.headers.host,r.headers[`:authority`]=r.hostname+(r.port?`:`+r.port:``);else if(!r.headers.host){let e=r.hostname;r.port!=null&&(e+=`:${r.port}`),r.headers.host=e}return t(n)},XS={name:`hostHeaderMiddleware`,step:`build`,priority:`low`,tags:[`HOST`],override:!0},ZS=e=>({applyToStack:t=>{t.add(YS(e),XS)}}),QS=()=>(e,t)=>async n=>{try{let r=await e(n),{clientName:i,commandName:a,logger:o,dynamoDbDocumentClientOptions:s={}}=t,{overrideInputFilterSensitiveLog:c,overrideOutputFilterSensitiveLog:l}=s,u=c??t.inputFilterSensitiveLog,d=l??t.outputFilterSensitiveLog,{$metadata:f,...p}=r.output;return o?.info?.({clientName:i,commandName:a,input:u(n.input),output:d(p),metadata:f}),r}catch(e){let{clientName:r,commandName:i,logger:a,dynamoDbDocumentClientOptions:o={}}=t,{overrideInputFilterSensitiveLog:s}=o,c=s??t.inputFilterSensitiveLog;throw a?.error?.({clientName:r,commandName:i,input:c(n.input),error:e,metadata:e.$metadata}),e}},$S={name:`loggerMiddleware`,tags:[`LOGGER`],step:`initialize`,override:!0},eC=e=>({applyToStack:e=>{e.add(QS(),$S)}}),tC={step:`build`,tags:[`RECURSION_DETECTION`],name:`recursionDetectionMiddleware`,override:!0,priority:`low`},nC=()=>e=>async t=>e(t),rC=e=>({applyToStack:e=>{e.add(nC(),tC)}}),iC=(e,t)=>{if(!t||t.length===0)return e;let n=[];for(let r of t)for(let t of e)t.schemeId.split(`#`)[1]===r&&n.push(t);for(let t of e)n.find(({schemeId:e})=>e===t.schemeId)||n.push(t);return n};function aC(e){let t=new Map;for(let n of e)t.set(n.schemeId,n);return t}var oC=(e,t)=>(n,r)=>async i=>{let a=iC(e.httpAuthSchemeProvider(await t.httpAuthSchemeParametersProvider(e,r,i.input)),e.authSchemePreference?await e.authSchemePreference():[]),o=aC(e.httpAuthSchemes),s=DS(r),c=[];for(let n of a){let i=o.get(n.schemeId);if(!i){c.push(`HttpAuthScheme \`${n.schemeId}\` was not enabled for this service.`);continue}let a=i.identityProvider(await t.identityProviderConfigProvider(e));if(!a){c.push(`HttpAuthScheme \`${n.schemeId}\` did not have an IdentityProvider configured.`);continue}let{identityProperties:l={},signingProperties:u={}}=n.propertiesExtractor?.(e,r)||{};n.identityProperties=Object.assign(n.identityProperties||{},l),n.signingProperties=Object.assign(n.signingProperties||{},u),s.selectedHttpAuthScheme={httpAuthOption:n,identity:await a(n.identityProperties),signer:i.signer};break}if(!s.selectedHttpAuthScheme)throw Error(c.join(` -`));return n(i)},sC={step:`serialize`,tags:[`HTTP_AUTH_SCHEME`],name:`httpAuthSchemeMiddleware`,override:!0,relation:`before`,toMiddleware:`endpointV2Middleware`},cC=(e,{httpAuthSchemeParametersProvider:t,identityProviderConfigProvider:n})=>({applyToStack:r=>{r.addRelativeTo(oC(e,{httpAuthSchemeParametersProvider:t,identityProviderConfigProvider:n}),sC)}}),lC=e=>e=>{throw e},uC=(e,t)=>{},dC=e=>(e,t)=>async n=>{if(!Ix.isInstance(n.request))return e(n);let r=DS(t).selectedHttpAuthScheme;if(!r)throw Error(`No HttpAuthScheme was selected: unable to sign request`);let{httpAuthOption:{signingProperties:i={}},identity:a,signer:o}=r,s=await e({...n,request:await o.sign(n.request,a,i)}).catch((o.errorHandler||lC)(i));return(o.successHandler||uC)(s.response,i),s},fC={step:`finalizeRequest`,tags:[`HTTP_SIGNING`],name:`httpSigningMiddleware`,aliases:[`apiKeyMiddleware`,`tokenMiddleware`,`awsAuthMiddleware`],override:!0,relation:`after`,toMiddleware:`retryMiddleware`},pC=e=>({applyToStack:t=>{t.addRelativeTo(dC(e),fC)}}),mC=e=>{if(typeof e==`function`)return e;let t=Promise.resolve(e);return()=>t},hC=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`,gC=Object.entries(hC).reduce((e,[t,n])=>(e[n]=Number(t),e),{}),_C=hC.split(``),vC=e=>{let t=e.length/4*3;e.slice(-2)===`==`?t-=2:e.slice(-1)===`=`&&t--;let n=new ArrayBuffer(t),r=new DataView(n);for(let t=0;t>=6;let a=t/4*3;n>>=i%8;let o=Math.floor(i/8);for(let e=0;e>t)}}return new Uint8Array(n)};function yC(e){let t;t=typeof e==`string`?Ux(e):e;let n=typeof t==`object`&&typeof t.length==`number`,r=typeof t==`object`&&typeof t.byteOffset==`number`&&typeof t.byteLength==`number`;if(!n&&!r)throw Error(`@smithy/util-base64: toBase64 encoder function only accepts string | Uint8Array.`);let i=``;for(let e=0;e>t]}i+=`==`.slice(0,4-a)}return i}var bC=class e extends Uint8Array{static fromString(t,n=`utf-8`){if(typeof t==`string`)return n===`base64`?e.mutate(vC(t)):e.mutate(Ux(t));throw Error(`Unsupported conversion from ${typeof t} to Uint8ArrayBlobAdapter.`)}static mutate(t){return Object.setPrototypeOf(t,e.prototype),t}transformToString(e=`utf-8`){return e===`base64`?yC(this):Gx(this)}},xC=e=>typeof ReadableStream==`function`&&(e?.constructor?.name===ReadableStream.name||e instanceof ReadableStream),SC=(e,t)=>{let{base64Encoder:n,bodyLengthChecker:r,checksumAlgorithmFn:i,checksumLocationName:a,streamHasher:o}=t,s=n!==void 0&&r!==void 0&&i!==void 0&&a!==void 0&&o!==void 0,c=s?o(i,e):void 0,l=e.getReader();return new ReadableStream({async pull(e){let{value:t,done:i}=await l.read();if(i){if(e.enqueue(`0\r -`),s){let t=n(await c);e.enqueue(`${a}:${t}\r\n`),e.enqueue(`\r -`)}e.close()}else e.enqueue(`${(r(t)||0).toString(16)}\r\n${t}\r\n`)}})};function CC(e){let t=[];for(let n of Object.keys(e).sort()){let r=e[n];if(n=kS(n),Array.isArray(r))for(let e=0,i=r.length;e{e&&setTimeout(()=>{let t=Error(`Request did not complete within ${e} ms`);t.name=`TimeoutError`,n(t)},e)})}var EC={supported:void 0},DC=class e{config;configProvider;static create(t){return typeof t?.handle==`function`?t:new e(t)}constructor(e){typeof e==`function`?this.configProvider=e().then(e=>e||{}):(this.config=e??{},this.configProvider=Promise.resolve(this.config)),EC.supported===void 0&&(EC.supported=typeof Request<`u`&&`keepalive`in wC(`https://[::1]`))}destroy(){}async handle(e,{abortSignal:t,requestTimeout:n}={}){this.config||=await this.configProvider;let r=n??this.config.requestTimeout,i=this.config.keepAlive===!0,a=this.config.credentials;if(t?.aborted){let e=OC(t);return Promise.reject(e)}let o=e.path,s=CC(e.query||{});s&&(o+=`?${s}`),e.fragment&&(o+=`#${e.fragment}`);let c=``;(e.username!=null||e.password!=null)&&(c=`${e.username??``}:${e.password??``}@`);let{port:l,method:u}=e,d=`${e.protocol}//${c}${e.hostname}${l?`:${l}`:``}${o}`,f=u===`GET`||u===`HEAD`?void 0:e.body,p={body:f,headers:new Headers(e.headers),method:u,credentials:a};this.config?.cache&&(p.cache=this.config.cache),f&&(p.duplex=`half`),typeof AbortController<`u`&&(p.signal=t),EC.supported&&(p.keepalive=i),typeof this.config.requestInit==`function`&&Object.assign(p,this.config.requestInit(e));let m=()=>{},h=wC(d,p),g=[fetch(h).then(e=>{let t=e.headers,n={};for(let e of t.entries())n[e[0]]=e[1];return e.body==null?e.blob().then(t=>({response:new Rx({headers:n,reason:e.statusText,statusCode:e.status,body:t})})):{response:new Rx({headers:n,reason:e.statusText,statusCode:e.status,body:e.body})}}),TC(r)];return t&&g.push(new Promise((e,n)=>{let r=()=>{n(OC(t))};if(typeof t.addEventListener==`function`){let e=t;e.addEventListener(`abort`,r,{once:!0}),m=()=>e.removeEventListener(`abort`,r)}else t.onabort=r})),Promise.race(g).finally(m)}updateHttpClientConfig(e,t){this.config=void 0,this.configProvider=this.configProvider.then(n=>(n[e]=t,n))}httpHandlerConfigs(){return this.config??{}}};function OC(e){let t=e&&typeof e==`object`&&`reason`in e?e.reason:void 0;if(t){if(t instanceof Error){let e=Error(`Request aborted`);return e.name=`AbortError`,e.cause=t,e}let e=Error(String(t));return e.name=`AbortError`,e}let n=Error(`Request aborted`);return n.name=`AbortError`,n}var kC=async e=>typeof Blob==`function`&&e instanceof Blob||e.constructor?.name===`Blob`?Blob.prototype.arrayBuffer===void 0?AC(e):new Uint8Array(await e.arrayBuffer()):jC(e);async function AC(e){let t=vC(await MC(e));return new Uint8Array(t)}async function jC(e){let t=[],n=e.getReader(),r=!1,i=0;for(;!r;){let{done:e,value:a}=await n.read();a&&(t.push(a),i+=a.length),r=e}let a=new Uint8Array(i),o=0;for(let e of t)a.set(e,o),o+=e.length;return a}function MC(e){return new Promise((t,n)=>{let r=new FileReader;r.onloadend=()=>{if(r.readyState!==2)return n(Error(`Reader aborted too early`));let e=r.result??``,i=e.indexOf(`,`),a=i>-1?i+1:e.length;t(e.substring(a))},r.onabort=()=>n(Error(`Read aborted`)),r.onerror=()=>n(r.error),r.readAsDataURL(e)})}var NC=`The stream has already been transformed.`,PC=e=>{if(!FC(e)&&!xC(e)){let t=e?.__proto__?.constructor?.name||e;throw Error(`Unexpected stream implementation, expect Blob or ReadableStream, got ${t}`)}let t=!1,n=async()=>{if(t)throw Error(NC);return t=!0,await kC(e)},r=e=>{if(typeof e.stream!=`function`)throw Error(`Cannot transform payload Blob to web stream. Please make sure the Blob.stream() is polyfilled. -If you are using React Native, this API is not yet supported, see: https://react-native.canny.io/feature-requests/p/fetch-streaming-body`);return e.stream()};return Object.assign(e,{transformToByteArray:n,transformToString:async e=>{let t=await n();if(e===`base64`)return yC(t);if(e===`hex`)return Hx(t);if(e===void 0||e===`utf8`||e===`utf-8`)return Gx(t);if(typeof TextDecoder==`function`)return new TextDecoder(e).decode(t);throw Error(`TextDecoder is not available, please make sure polyfill is provided.`)},transformToWebStream:()=>{if(t)throw Error(NC);if(t=!0,FC(e))return r(e);if(xC(e))return e;throw Error(`Cannot transform payload to web stream, got ${e}`)}})},FC=e=>typeof Blob==`function`&&e instanceof Blob,IC=async(e=new Uint8Array,t)=>{if(e instanceof Uint8Array)return bC.mutate(e);if(!e)return bC.mutate(new Uint8Array);let n=t.streamCollector(e);return bC.mutate(await n)};function LC(e){return encodeURIComponent(e).replace(/[!'()*]/g,function(e){return`%`+e.charCodeAt(0).toString(16).toUpperCase()})}var RC=e=>typeof e==`function`?e():e,zC=(e,t,n,r,i)=>({name:t,namespace:e,traits:n,input:r,output:i}),BC=e=>(t,n)=>async r=>{let{response:i}=await t(r),{operationSchema:a}=DS(n),[,o,s,c,l,u]=a??[];try{return{response:i,output:await e.protocol.deserializeResponse(zC(o,s,c,l,u),{...e,...n},i)}}catch(e){if(Object.defineProperty(e,`$response`,{value:i,enumerable:!1,writable:!1,configurable:!1}),!(`$metadata`in e)){let t=`Deserialization error: to see the raw response, inspect the hidden field {error}.$response on this object.`;try{e.message+=` - `+t}catch{!n.logger||n.logger?.constructor?.name===`NoOpLogger`?console.warn(t):n.logger?.warn?.(t)}e.$responseBodyText!==void 0&&e.$response&&(e.$response.body=e.$responseBodyText);try{if(Rx.isInstance(i)){let{headers:t={}}=i,n=Object.entries(t);e.$metadata={httpStatusCode:i.statusCode,requestId:VC(/^x-[\w-]+-request-?id$/,n),extendedRequestId:VC(/^x-[\w-]+-id-2$/,n),cfId:VC(/^x-[\w-]+-cf-id$/,n)}}}catch{}}throw e}},VC=(e,t)=>(t.find(([t])=>t.match(e))||[void 0,void 0])[1];function HC(e){let t={};if(e=e.replace(/^\?/,``),e)for(let n of e.split(`&`)){let[e,r=null]=n.split(`=`);e=decodeURIComponent(e),r&&=decodeURIComponent(r),e in t?Array.isArray(t[e])?t[e].push(r):t[e]=[t[e],r]:t[e]=r}return t}var UC=e=>{if(typeof e==`string`)return UC(new URL(e));let{hostname:t,pathname:n,port:r,protocol:i,search:a}=e,o;return a&&(o=HC(a)),{hostname:t,port:r?parseInt(r):void 0,protocol:i,path:n,query:o}},WC=e=>{if(typeof e==`object`){if(`url`in e){let t=UC(e.url);if(e.headers){t.headers={};for(let n in e.headers)t.headers[n.toLowerCase()]=e.headers[n].join(`, `)}return t}return e}return UC(e)},GC=e=>(t,n)=>async r=>{let{operationSchema:i}=DS(n),[,a,o,s,c,l]=i??[],u=n.endpointV2?async()=>WC(n.endpointV2):e.endpoint,d=await e.protocol.serializeRequest(zC(a,o,s,c,l),r.input,{...e,...n,endpoint:u});return t({...r,request:d})},KC={name:`deserializerMiddleware`,step:`deserialize`,tags:[`DESERIALIZER`],override:!0},qC={name:`serializerMiddleware`,step:`serialize`,tags:[`SERIALIZER`],override:!0};function JC(e){return{applyToStack:t=>{t.add(GC(e),qC),t.add(BC(e),KC),e.protocol.setSerdeContext(e)}}}var YC=[];function XC(e){if(typeof e==`object`)return e;if(e|=0,YC[e])return YC[e];let t={},n=0;for(let r of[`httpLabel`,`idempotent`,`idempotencyToken`,`sensitive`,`httpPayload`,`httpResponseCode`,`httpQueryParams`])(e>>n++&1)==1&&(t[r]=1);return YC[e]=t}var ZC={it:Symbol.for(`@smithy/nor-struct-it`),ns:Symbol.for(`@smithy/ns`)},QC=[],$C={},ew=class e{ref;memberName;static symbol=Symbol.for(`@smithy/nor`);symbol=e.symbol;name;schema;_isMemberSchema;traits;memberTraits;normalizedTraits;constructor(t,n){this.ref=t,this.memberName=n;let r=[],i=t,a=t;for(this._isMemberSchema=!1;nw(i);)r.push(i[1]),i=i[0],a=RC(i),this._isMemberSchema=!0;if(r.length>0){this.memberTraits={};for(let e=r.length-1;e>=0;--e){let t=r[e];Object.assign(this.memberTraits,XC(t))}}else this.memberTraits=0;if(a instanceof e){let e=this.memberTraits;Object.assign(this,a),this.memberTraits=Object.assign({},e,a.getMemberTraits(),this.getMemberTraits()),this.normalizedTraits=void 0,this.memberName=n??a.memberName;return}if(this.schema=RC(a),rw(this.schema)?(this.name=`${this.schema[1]}#${this.schema[2]}`,this.traits=this.schema[3]):(this.name=this.memberName??String(a),this.traits=0),this._isMemberSchema&&!n)throw Error(`@smithy/core/schema - NormalizedSchema member init ${this.getName(!0)} missing member name.`)}static[Symbol.hasInstance](e){let t=this.prototype.isPrototypeOf(e);return!t&&typeof e==`object`&&e?e.symbol===this.symbol:t}static of(t){let n=typeof t==`function`||typeof t==`object`&&!!t;if(typeof t==`number`){if(QC[t])return QC[t]}else if(typeof t==`string`){if($C[t])return $C[t]}else if(n&&t[ZC.ns])return t[ZC.ns];let r=RC(t);if(r instanceof e)return r;if(nw(r)){let[n,i]=r;if(n instanceof e)return Object.assign(n.getMergedTraits(),XC(i)),n;throw Error(`@smithy/core/schema - may not init unwrapped member schema=${JSON.stringify(t,null,2)}.`)}let i=new e(r);return n?t[ZC.ns]=i:typeof r==`string`?$C[r]=i:typeof r==`number`?QC[r]=i:i}getSchema(){let e=this.schema;return Array.isArray(e)&&e[0]===0?e[4]:e}getName(e=!1){let{name:t}=this;return!e&&t&&t.includes(`#`)?t.split(`#`)[1]:t||void 0}getMemberName(){return this.memberName}isMemberSchema(){return this._isMemberSchema}isListSchema(){let e=this.getSchema();return typeof e==`number`?e>=64&&e<128:e[0]===1}isMapSchema(){let e=this.getSchema();return typeof e==`number`?e>=128&&e<=255:e[0]===2}isStructSchema(){let e=this.getSchema();if(typeof e!=`object`)return!1;let t=e[0];return t===3||t===-3||t===4}isUnionSchema(){let e=this.getSchema();return typeof e==`object`?e[0]===4:!1}isBlobSchema(){let e=this.getSchema();return e===21||e===42}isTimestampSchema(){let e=this.getSchema();return typeof e==`number`&&e>=4&&e<=7}isUnitSchema(){return this.getSchema()===`unit`}isDocumentSchema(){return this.getSchema()===15}isStringSchema(){return this.getSchema()===0}isBooleanSchema(){return this.getSchema()===2}isNumericSchema(){return this.getSchema()===1}isBigIntegerSchema(){return this.getSchema()===17}isBigDecimalSchema(){return this.getSchema()===19}isStreaming(){let{streaming:e}=this.getMergedTraits();return!!e||this.getSchema()===42}isIdempotencyToken(){return!!this.getMergedTraits().idempotencyToken}getMergedTraits(){return this.normalizedTraits??={...this.getOwnTraits(),...this.getMemberTraits()}}getMemberTraits(){return XC(this.memberTraits)}getOwnTraits(){return XC(this.traits)}getKeySchema(){let[e,t]=[this.isDocumentSchema(),this.isMapSchema()];if(!e&&!t)throw Error(`@smithy/core/schema - cannot get key for non-map: ${this.getName(!0)}`);let n=this.getSchema();return tw([e?15:n[4]??0,0],`key`)}getValueSchema(){let e=this.getSchema(),[t,n,r]=[this.isDocumentSchema(),this.isMapSchema(),this.isListSchema()],i=typeof e==`number`?63&e:e&&typeof e==`object`&&(n||r)?e[3+e[0]]:t?15:void 0;if(i!=null)return tw([i,0],n?`value`:`member`);throw Error(`@smithy/core/schema - ${this.getName(!0)} has no value member.`)}getMemberSchema(e){let t=this.getSchema();if(this.isStructSchema()&&t[4].includes(e)){let n=t[4].indexOf(e),r=t[5][n];return tw(nw(r)?r:[r,0],e)}if(this.isDocumentSchema())return tw([15,0],e);throw Error(`@smithy/core/schema - ${this.getName(!0)} has no member=${e}.`)}getMemberSchemas(){let e={};try{for(let[t,n]of this.structIterator())e[t]=n}catch{}return e}getEventStreamMember(){if(this.isStructSchema()){for(let[e,t]of this.structIterator())if(t.isStreaming()&&t.isStructSchema())return e}return``}*structIterator(){if(this.isUnitSchema())return;if(!this.isStructSchema())throw Error(`@smithy/core/schema - cannot iterate non-struct schema.`);let e=this.getSchema(),t=e[4].length,n=e[ZC.it];if(n&&t===n.length){yield*n;return}n=Array(t);for(let r=0;rArray.isArray(e)&&e.length===2,rw=e=>Array.isArray(e)&&e.length>=5,iw=class e{namespace;schemas;exceptions;static registries=new Map;constructor(e,t=new Map,n=new Map){this.namespace=e,this.schemas=t,this.exceptions=n}static for(t){return e.registries.has(t)||e.registries.set(t,new e(t)),e.registries.get(t)}copyFrom(e){let{schemas:t,exceptions:n}=this;for(let[n,r]of e.schemas)t.has(n)||t.set(n,r);for(let[t,r]of e.exceptions)n.has(t)||n.set(t,r)}register(t,n){let r=this.normalizeShapeId(t);for(let t of[this,e.for(r.split(`#`)[0])])t.schemas.set(r,n)}getSchema(e){let t=this.normalizeShapeId(e);if(!this.schemas.has(t))throw Error(`@smithy/core/schema - schema not found for ${t}`);return this.schemas.get(t)}registerError(t,n){let r=t,i=r[1];for(let t of[this,e.for(i)])t.schemas.set(i+`#`+r[2],r),t.exceptions.set(r,n)}getErrorCtor(t){let n=t;return this.exceptions.has(n)?this.exceptions.get(n):e.for(n[1]).exceptions.get(n)}getBaseException(){for(let e of this.exceptions.keys())if(Array.isArray(e)){let[,t,n]=e,r=t+`#`+n;if(r.startsWith(`smithy.ts.sdk.synthetic.`)&&r.endsWith(`ServiceException`))return e}}find(e){for(let t of this.schemas.values())if(e(t))return t}clear(){this.schemas.clear(),this.exceptions.clear()}normalizeShapeId(e){return e.includes(`#`)?e:this.namespace+`#`+e}},aw=e=>{if(e!=null){if(typeof e==`string`){let t=parseFloat(e);if(!Number.isNaN(t))return String(t)!==String(e)&&yw.warn(vw(`Expected number but observed string: ${e}`)),t}if(typeof e==`number`)return e;throw TypeError(`Expected number, got ${typeof e}: ${e}`)}},ow=Math.ceil(2**127*(2-2**-23)),sw=e=>{let t=aw(e);if(t!==void 0&&!Number.isNaN(t)&&t!==1/0&&t!==-1/0&&Math.abs(t)>ow)throw TypeError(`Expected 32-bit float, got ${e}`);return t},cw=e=>{if(e!=null){if(Number.isInteger(e)&&!Number.isNaN(e))return e;throw TypeError(`Expected integer, got ${typeof e}: ${e}`)}},lw=e=>dw(e,16),uw=e=>dw(e,8),dw=(e,t)=>{let n=cw(e);if(n!==void 0&&fw(n,t)!==n)throw TypeError(`Expected ${t}-bit integer, got ${e}`);return n},fw=(e,t)=>{switch(t){case 32:return Int32Array.of(e)[0];case 16:return Int16Array.of(e)[0];case 8:return Int8Array.of(e)[0]}},pw=e=>sw(typeof e==`string`?hw(e):e),mw=/(-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)|(-?Infinity)|(NaN)/g,hw=e=>{let t=e.match(mw);if(t===null||t[0].length!==e.length)throw TypeError(`Expected real number, got implicit NaN`);return parseFloat(e)},gw=e=>lw(typeof e==`string`?hw(e):e),_w=e=>uw(typeof e==`string`?hw(e):e),vw=e=>String(TypeError(e).stack||e).split(` -`).slice(0,5).filter(e=>!e.includes(`stackTraceWarning`)).join(` -`),yw={warn:console.warn},bw=[`Sun`,`Mon`,`Tue`,`Wed`,`Thu`,`Fri`,`Sat`],xw=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`];function Sw(e){let t=e.getUTCFullYear(),n=e.getUTCMonth(),r=e.getUTCDay(),i=e.getUTCDate(),a=e.getUTCHours(),o=e.getUTCMinutes(),s=e.getUTCSeconds(),c=i<10?`0${i}`:`${i}`,l=a<10?`0${a}`:`${a}`,u=o<10?`0${o}`:`${o}`,d=s<10?`0${s}`:`${s}`;return`${bw[r]}, ${c} ${xw[n]} ${t} ${l}:${u}:${d} GMT`}var Cw=new RegExp(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d{2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? GMT$/),ww=new RegExp(/^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d{2})-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d{2}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? GMT$/),Tw=new RegExp(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( [1-9]|\d{2}) (\d{1,2}):(\d{2}):(\d{2})(?:\.(\d+))? (\d{4})$/),Ew=e=>{if(e==null)return;if(typeof e!=`string`)throw TypeError(`RFC-7231 date-times must be expressed as strings`);let t=Cw.exec(e);if(t){let[e,n,r,i,a,o,s,c]=t;return Dw(gw(Lw(i)),jw(r),Fw(n,`day`,1,31),{hours:a,minutes:o,seconds:s,fractionalMilliseconds:c})}if(t=ww.exec(e),t){let[e,n,r,i,a,o,s,c]=t;return Aw(Dw(Ow(i),jw(r),Fw(n,`day`,1,31),{hours:a,minutes:o,seconds:s,fractionalMilliseconds:c}))}if(t=Tw.exec(e),t){let[e,n,r,i,a,o,s,c]=t;return Dw(gw(Lw(c)),jw(n),Fw(r.trimLeft(),`day`,1,31),{hours:i,minutes:a,seconds:o,fractionalMilliseconds:s})}throw TypeError(`Invalid RFC-7231 date-time value`)},Dw=(e,t,n,r)=>{let i=t-1;return Nw(e,i,n),new Date(Date.UTC(e,i,n,Fw(r.hours,`hour`,0,23),Fw(r.minutes,`minute`,0,59),Fw(r.seconds,`seconds`,0,60),Iw(r.fractionalMilliseconds)))},Ow=e=>{let t=new Date().getUTCFullYear(),n=Math.floor(t/100)*100+gw(Lw(e));return ne.getTime()-new Date().getTime()>kw?new Date(Date.UTC(e.getUTCFullYear()-100,e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds())):e,jw=e=>{let t=xw.indexOf(e);if(t<0)throw TypeError(`Invalid month: ${e}`);return t+1},Mw=[31,28,31,30,31,30,31,31,30,31,30,31],Nw=(e,t,n)=>{let r=Mw[t];if(t===1&&Pw(e)&&(r=29),n>r)throw TypeError(`Invalid day for ${xw[t]} in ${e}: ${n}`)},Pw=e=>e%4==0&&(e%100!=0||e%400==0),Fw=(e,t,n,r)=>{let i=_w(Lw(e));if(ir)throw TypeError(`${t} must be between ${n} and ${r}, inclusive`);return i},Iw=e=>e==null?0:pw(`0.`+e)*1e3,Lw=e=>{let t=0;for(;tt.toString(16).padStart(2,`0`)),Bw=()=>{if(Rw)return Rw();let e=new Uint8Array(16);return crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128,zw[e[0]]+zw[e[1]]+zw[e[2]]+zw[e[3]]+`-`+zw[e[4]]+zw[e[5]]+`-`+zw[e[6]]+zw[e[7]]+`-`+zw[e[8]]+zw[e[9]]+`-`+zw[e[10]]+zw[e[11]]+zw[e[12]]+zw[e[13]]+zw[e[14]]+zw[e[15]]},Vw=function(e){return Object.assign(new String(e),{deserializeJSON(){return JSON.parse(String(e))},toString(){return String(e)},toJSON(){return String(e)}})};Vw.from=e=>e&&typeof e==`object`&&(e instanceof Vw||`deserializeJSON`in e)?e:typeof e==`string`||Object.getPrototypeOf(e)===String.prototype?Vw(String(e)):Vw(JSON.stringify(e)),Vw.fromObject=Vw.from;function Hw(e){return(e.includes(`,`)||e.includes(`"`))&&(e=`"${e.replace(/"/g,`\\"`)}"`),e}var Uw=`(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)(?:[ne|u?r]?s?day)?`,Ww=`(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)`,Gw=`(\\d?\\d):(\\d{2}):(\\d{2})(?:\\.(\\d+))?`,Kw=`(\\d?\\d)`,qw=`(\\d{4})`,Jw=new RegExp(/^(\d{4})-(\d\d)-(\d\d)[tT](\d\d):(\d\d):(\d\d)(\.(\d+))?(([-+]\d\d:\d\d)|[zZ])$/),Yw=RegExp(`^${Uw}, ${Kw} ${Ww} ${qw} ${Gw} GMT$`),Xw=RegExp(`^${Uw}, ${Kw}-${Ww}-(\\d\\d) ${Gw} GMT$`),Zw=RegExp(`^${Uw} ${Ww} ( [1-9]|\\d\\d) ${Gw} ${qw}$`),Qw=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],$w=e=>{if(e==null)return;let t=NaN;if(typeof e==`number`)t=e;else if(typeof e==`string`){if(!/^-?\d*\.?\d+$/.test(e))throw TypeError(`parseEpochTimestamp - numeric string invalid.`);t=Number.parseFloat(e)}else typeof e==`object`&&e.tag===1&&(t=e.value);if(isNaN(t)||Math.abs(t)===1/0)throw TypeError(`Epoch timestamps must be valid finite numbers.`);return new Date(Math.round(t*1e3))},eT=e=>{if(e==null)return;if(typeof e!=`string`)throw TypeError(`RFC3339 timestamps must be strings`);let t=Jw.exec(e);if(!t)throw TypeError(`Invalid RFC3339 timestamp format ${e}`);let[,n,r,i,a,o,s,,c,l]=t;nT(r,1,12),nT(i,1,31),nT(a,0,23),nT(o,0,59),nT(s,0,60);let u=new Date(Date.UTC(Number(n),Number(r)-1,Number(i),Number(a),Number(o),Number(s),Number(c)?Math.round(parseFloat(`0.${c}`)*1e3):0));if(u.setUTCFullYear(Number(n)),l.toUpperCase()!=`Z`){let[,e,t,n]=/([+-])(\d\d):(\d\d)/.exec(l)||[void 0,`+`,0,0],r=e===`-`?1:-1;u.setTime(u.getTime()+r*(Number(t)*60*60*1e3+Number(n)*60*1e3))}return u},tT=e=>{if(e==null)return;if(typeof e!=`string`)throw TypeError(`RFC7231 timestamps must be strings.`);let t,n,r,i,a,o,s,c;if((c=Yw.exec(e))?[,t,n,r,i,a,o,s]=c:(c=Xw.exec(e))?([,t,n,r,i,a,o,s]=c,r=(Number(r)+1900).toString()):(c=Zw.exec(e))&&([,n,t,i,a,o,s,r]=c),r&&o){let e=Date.UTC(Number(r),Qw.indexOf(n),Number(t),Number(i),Number(a),Number(o),s?Math.round(parseFloat(`0.${s}`)*1e3):0);nT(t,1,31),nT(i,0,23),nT(a,0,59),nT(o,0,60);let c=new Date(e);return c.setUTCFullYear(Number(r)),c}throw TypeError(`Invalid RFC7231 date-time value ${e}.`)};function nT(e,t,n){let r=Number(e);if(rn)throw Error(`Value ${r} out of range [${t}, ${n}]`)}function rT(e,t,n){if(n<=0||!Number.isInteger(n))throw Error(`Invalid number of delimiters (`+n+`) for splitEvery.`);let r=e.split(t);if(n===1)return r;let i=[],a=``;for(let e=0;e{let t=e.length,n=[],r=!1,i,a=0;for(let o=0;o{e=e.trim();let t=e.length;return t<2?e:(e[0]===`"`&&e[t-1]===`"`&&(e=e.slice(1,t-1)),e.replace(/\\"/g,`"`))})},aT=/^-?\d*(\.\d+)?$/,oT=class e{string;type;constructor(e,t){if(this.string=e,this.type=t,!aT.test(e))throw Error(`@smithy/core/serde - NumericValue must only contain [0-9], at most one decimal point ".", and an optional negation prefix "-".`)}toString(){return this.string}static[Symbol.hasInstance](t){if(!t||typeof t!=`object`)return!1;let n=t;return e.prototype.isPrototypeOf(t)||n.type===`bigDecimal`&&aT.test(n.string)}},sT=class{serdeContext;setSerdeContext(e){this.serdeContext=e}},cT=`modulepreload`,lT=function(e){return`/`+e},uT={},H=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=lT(t,n),t in uT)return;uT[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:cT,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},dT=class extends sT{options;compositeErrorRegistry;constructor(e){super(),this.options=e,this.compositeErrorRegistry=iw.for(e.defaultNamespace);for(let t of e.errorTypeRegistries??[])this.compositeErrorRegistry.copyFrom(t)}getRequestType(){return Ix}getResponseType(){return Rx}setSerdeContext(e){this.serdeContext=e,this.serializer.setSerdeContext(e),this.deserializer.setSerdeContext(e),this.getPayloadCodec()&&this.getPayloadCodec().setSerdeContext(e)}updateServiceEndpoint(e,t){if(`url`in t){e.protocol=t.url.protocol,e.hostname=t.url.hostname,e.port=t.url.port?Number(t.url.port):void 0,e.path=t.url.pathname,e.fragment=t.url.hash||void 0,e.username=t.url.username||void 0,e.password=t.url.password||void 0,e.query||={};for(let[n,r]of t.url.searchParams.entries())e.query[n]=r;if(t.headers)for(let n in t.headers)e.headers[n]=t.headers[n].join(`, `);return e}else{if(e.protocol=t.protocol,e.hostname=t.hostname,e.port=t.port?Number(t.port):void 0,e.path=t.path,e.query={...t.query},t.headers)for(let n in t.headers)e.headers[n]=t.headers[n];return e}}setHostPrefix(e,t,n){if(this.serdeContext?.disableHostPrefix)return;let r=ew.of(t.input),i=XC(t.traits??{});if(i.endpoint){let t=i.endpoint?.[0];if(typeof t==`string`){for(let[e,i]of r.structIterator()){if(!i.getMergedTraits().hostLabel)continue;let r=n[e];if(typeof r!=`string`)throw Error(`@smithy/core/schema - ${e} in input must be a string as hostLabel.`);t=t.replace(`{${e}}`,r)}e.hostname=t+e.hostname}}}deserializeMetadata(e){return{httpStatusCode:e.statusCode,requestId:e.headers[`x-amzn-requestid`]??e.headers[`x-amzn-request-id`]??e.headers[`x-amz-request-id`],extendedRequestId:e.headers[`x-amz-id-2`],cfId:e.headers[`x-amz-cf-id`]}}async serializeEventStream({eventStream:e,requestSchema:t,initialRequest:n}){return(await this.loadEventStreamCapability()).serializeEventStream({eventStream:e,requestSchema:t,initialRequest:n})}async deserializeEventStream({response:e,responseSchema:t,initialResponseContainer:n}){return(await this.loadEventStreamCapability()).deserializeEventStream({response:e,responseSchema:t,initialResponseContainer:n})}async loadEventStreamCapability(){let{EventStreamSerde:e}=await H(async()=>{let{EventStreamSerde:e}=await import(`./event-streams.js`);return{EventStreamSerde:e}},[]);return new e({marshaller:this.getEventStreamMarshaller(),serializer:this.serializer,deserializer:this.deserializer,serdeContext:this.serdeContext,defaultContentType:this.getDefaultContentType()})}getDefaultContentType(){throw Error(`@smithy/core/protocols - ${this.constructor.name} getDefaultContentType() implementation missing.`)}async deserializeHttpMessage(e,t,n,r,i){return[]}getEventStreamMarshaller(){let e=this.serdeContext;if(!e.eventStreamMarshaller)throw Error(`@smithy/core - HttpProtocol: eventStreamMarshaller missing in serdeContext.`);return e.eventStreamMarshaller}},fT=class extends dT{async serializeRequest(e,t,n){let r=t&&typeof t==`object`?t:{},i=this.serializer,a={},o={},s=await n.endpoint(),c=ew.of(e?.input),l=[],u=[],d=!1,f,p=new Ix({protocol:``,hostname:``,port:void 0,path:``,fragment:void 0,query:a,headers:o,body:void 0});if(s){this.updateServiceEndpoint(p,s),this.setHostPrefix(p,e,r);let t=XC(e.traits);if(t.http){p.method=t.http[0];let[e,n]=t.http[1].split(`?`);p.path==`/`?p.path=e:p.path+=e;let r=new URLSearchParams(n??``);for(let[e,t]of r)a[e]=t}}for(let[e,t]of c.structIterator()){let n=t.getMergedTraits()??{},s=r[e];if(s==null&&!t.isIdempotencyToken()){if(n.httpLabel&&(p.path.includes(`{${e}+}`)||p.path.includes(`{${e}}`)))throw Error(`No value provided for input HTTP label: ${e}.`);continue}if(n.httpPayload)t.isStreaming()?t.isStructSchema()?r[e]&&(f=await this.serializeEventStream({eventStream:r[e],requestSchema:c})):f=s:(i.write(t,s),f=i.flush());else if(n.httpLabel){i.write(t,s);let n=i.flush();p.path.includes(`{${e}+}`)?p.path=p.path.replace(`{${e}+}`,n.split(`/`).map(LC).join(`/`)):p.path.includes(`{${e}}`)&&(p.path=p.path.replace(`{${e}}`,LC(n)))}else if(n.httpHeader)i.write(t,s),o[n.httpHeader.toLowerCase()]=String(i.flush());else if(typeof n.httpPrefixHeaders==`string`)for(let e in s){let r=s[e],a=n.httpPrefixHeaders+e;i.write([t.getValueSchema(),{httpHeader:a}],r),o[a.toLowerCase()]=i.flush()}else n.httpQuery||n.httpQueryParams?this.serializeQuery(t,s,a):(d=!0,l.push(e),u.push(t))}if(d&&r){let[e,t]=(c.getName(!0)??`#Unknown`).split(`#`),n=c.getSchema()[6],a=[3,e,t,c.getMergedTraits(),l,u,void 0];n?a[6]=n:a.pop(),i.write(a,r),f=i.flush()}return p.headers=o,p.query=a,p.body=f,p}serializeQuery(e,t,n){let r=this.serializer,i=e.getMergedTraits();if(i.httpQueryParams){for(let r in t)if(!(r in n)){let a=t[r],o=e.getValueSchema();Object.assign(o.getMergedTraits(),{...i,httpQuery:r,httpQueryParams:void 0}),this.serializeQuery(o,a,n)}return}if(e.isListSchema()){let a=!!e.getMergedTraits().sparse,o=[];for(let n of t){r.write([e.getValueSchema(),i],n);let t=r.flush();(a||t!==void 0)&&o.push(t)}n[i.httpQuery]=o}else r.write([e,i],t),n[i.httpQuery]=r.flush()}async deserializeResponse(e,t,n){let r=this.deserializer,i=ew.of(e.output),a={};if(n.statusCode>=300){let i=await IC(n.body,t);throw i.byteLength>0&&Object.assign(a,await r.read(15,i)),await this.handleError(e,t,n,a,this.deserializeMetadata(n)),Error(`@smithy/core/protocols - HTTP Protocol error handler failed to throw.`)}for(let e in n.headers){let t=n.headers[e];delete n.headers[e],n.headers[e.toLowerCase()]=t}let o=await this.deserializeHttpMessage(i,t,n,a);if(o.length){let e=await IC(n.body,t);if(e.byteLength>0){let t=await r.read(i,e);for(let e of o)t[e]!=null&&(a[e]=t[e])}}else o.discardResponseBody&&await IC(n.body,t);return a.$metadata=this.deserializeMetadata(n),a}async deserializeHttpMessage(e,t,n,r,i){let a;a=r instanceof Set?i:r;let o=!0,s=this.deserializer,c=ew.of(e),l=[];for(let[e,r]of c.structIterator()){let i=r.getMemberTraits();if(i.httpPayload){if(o=!1,r.isStreaming())r.isStructSchema()?a[e]=await this.deserializeEventStream({response:n,responseSchema:c}):a[e]=PC(n.body);else if(n.body){let i=await IC(n.body,t);i.byteLength>0&&(a[e]=await s.read(r,i))}}else if(i.httpHeader){let t=String(i.httpHeader).toLowerCase(),o=n.headers[t];if(o!=null)if(r.isListSchema()){let n=r.getValueSchema();n.getMergedTraits().httpHeader=t;let i;i=n.isTimestampSchema()&&n.getSchema()===4?rT(o,`,`,2):iT(o);let c=[];for(let e of i)c.push(await s.read(n,e.trim()));a[e]=c}else a[e]=await s.read(r,o)}else if(i.httpPrefixHeaders!==void 0){a[e]={};for(let t in n.headers)if(t.startsWith(i.httpPrefixHeaders)){let o=n.headers[t],c=r.getValueSchema();c.getMergedTraits().httpHeader=t,a[e][t.slice(i.httpPrefixHeaders.length)]=await s.read(c,o)}}else i.httpResponseCode?a[e]=n.statusCode:l.push(e)}return l.discardResponseBody=o,l}},pT=class extends dT{async serializeRequest(e,t,n){let r=this.serializer,i={},a={},o=await n.endpoint(),s=ew.of(e?.input),c=s.getSchema(),l,u=t&&typeof t==`object`?t:{},d=new Ix({protocol:``,hostname:``,port:void 0,path:`/`,fragment:void 0,query:i,headers:a,body:void 0});if(o&&(this.updateServiceEndpoint(d,o),this.setHostPrefix(d,e,u)),u){let e=s.getEventStreamMember();if(e){if(u[e]){let t={};for(let[n,i]of s.structIterator())n!==e&&u[n]&&(r.write(i,u[n]),t[n]=r.flush());l=await this.serializeEventStream({eventStream:u[e],requestSchema:s,initialRequest:t})}}else r.write(c,u),l=r.flush()}return d.headers=Object.assign(d.headers,a),d.query=i,d.body=l,d.method=`POST`,d}async deserializeResponse(e,t,n){let r=this.deserializer,i=ew.of(e.output),a={};if(n.statusCode>=300){let i=await IC(n.body,t);throw i.byteLength>0&&Object.assign(a,await r.read(15,i)),await this.handleError(e,t,n,a,this.deserializeMetadata(n)),Error(`@smithy/core/protocols - RPC Protocol error handler failed to throw.`)}for(let e in n.headers){let t=n.headers[e];delete n.headers[e],n.headers[e.toLowerCase()]=t}let o=i.getEventStreamMember();if(o)a[o]=await this.deserializeEventStream({response:n,responseSchema:i,initialResponseContainer:a});else{let e=await IC(n.body,t);e.byteLength>0&&Object.assign(a,await r.read(i,e))}return a.$metadata=this.deserializeMetadata(n),a}};function mT(e,t){if(t.timestampFormat.useTrait&&e.isTimestampSchema()&&(e.getSchema()===5||e.getSchema()===6||e.getSchema()===7))return e.getSchema();let{httpLabel:n,httpPrefixHeaders:r,httpHeader:i,httpQuery:a}=e.getMergedTraits();return(t.httpBindings?typeof r==`string`||i?6:a||n?5:void 0:void 0)??t.timestampFormat.default}var hT=class extends sT{settings;constructor(e){super(),this.settings=e}read(e,t){let n=ew.of(e);if(n.isListSchema())return iT(t).map(e=>this.read(n.getValueSchema(),e));if(n.isBlobSchema())return(this.serdeContext?.base64Decoder??vC)(t);if(n.isTimestampSchema())switch(mT(n,this.settings)){case 5:return eT(t);case 6:return tT(t);case 7:return $w(t);default:return console.warn(`Missing timestamp format, parsing value with Date constructor:`,t),new Date(t)}if(n.isStringSchema()){let e=n.getMergedTraits().mediaType,r=t;if(e)return n.getMergedTraits().httpHeader&&(r=this.base64ToUtf8(r)),(e===`application/json`||e.endsWith(`+json`))&&(r=Vw.from(r)),r}return n.isNumericSchema()?Number(t):n.isBigIntegerSchema()?BigInt(t):n.isBigDecimalSchema()?new oT(t,`bigDecimal`):n.isBooleanSchema()?String(t).toLowerCase()===`true`:t}base64ToUtf8(e){return(this.serdeContext?.utf8Encoder??Gx)((this.serdeContext?.base64Decoder??vC)(e))}},gT=class extends sT{codecDeserializer;stringDeserializer;constructor(e,t){super(),this.codecDeserializer=e,this.stringDeserializer=new hT(t)}setSerdeContext(e){this.stringDeserializer.setSerdeContext(e),this.codecDeserializer.setSerdeContext(e),this.serdeContext=e}read(e,t){let n=ew.of(e),r=n.getMergedTraits(),i=this.serdeContext?.utf8Encoder??Gx;if(r.httpHeader||r.httpResponseCode)return this.stringDeserializer.read(n,i(t));if(r.httpPayload){if(n.isBlobSchema()){let e=this.serdeContext?.utf8Decoder??Ux;return typeof t==`string`?e(t):t}else if(n.isStringSchema())return`byteLength`in t?i(t):t}return this.codecDeserializer.read(n,t)}},_T=class extends sT{settings;stringBuffer=``;constructor(e){super(),this.settings=e}write(e,t){let n=ew.of(e);switch(typeof t){case`object`:if(t===null){this.stringBuffer=`null`;return}if(n.isTimestampSchema()){if(!(t instanceof Date))throw Error(`@smithy/core/protocols - received non-Date value ${t} when schema expected Date in ${n.getName(!0)}`);switch(mT(n,this.settings)){case 5:this.stringBuffer=t.toISOString().replace(`.000Z`,`Z`);break;case 6:this.stringBuffer=Sw(t);break;case 7:this.stringBuffer=String(t.getTime()/1e3);break;default:console.warn(`Missing timestamp format, using epoch seconds`,t),this.stringBuffer=String(t.getTime()/1e3)}return}if(n.isBlobSchema()&&`byteLength`in t){this.stringBuffer=(this.serdeContext?.base64Encoder??yC)(t);return}if(n.isListSchema()&&Array.isArray(t)){let e=``;for(let r of t){this.write([n.getValueSchema(),n.getMergedTraits()],r);let t=this.flush(),i=n.getValueSchema().isTimestampSchema()?t:Hw(t);e!==``&&(e+=`, `),e+=i}this.stringBuffer=e;return}this.stringBuffer=JSON.stringify(t,null,2);break;case`string`:let e=n.getMergedTraits().mediaType,r=t;if(e&&((e===`application/json`||e.endsWith(`+json`))&&(r=Vw.from(r)),n.getMergedTraits().httpHeader)){this.stringBuffer=(this.serdeContext?.base64Encoder??yC)(r.toString());return}this.stringBuffer=t;break;default:n.isIdempotencyToken()?this.stringBuffer=Bw():this.stringBuffer=String(t)}}flush(){let e=this.stringBuffer;return this.stringBuffer=``,e}},vT=class{codecSerializer;stringSerializer;buffer;constructor(e,t,n=new _T(t)){this.codecSerializer=e,this.stringSerializer=n}setSerdeContext(e){this.codecSerializer.setSerdeContext(e),this.stringSerializer.setSerdeContext(e)}write(e,t){let n=ew.of(e),r=n.getMergedTraits();if(r.httpHeader||r.httpLabel||r.httpQuery){this.stringSerializer.write(n,t),this.buffer=this.stringSerializer.flush();return}return this.codecSerializer.write(n,t)}flush(){if(this.buffer!==void 0){let e=this.buffer;return this.buffer=void 0,e}return this.codecSerializer.flush()}};function yT(e,t,n){e.__smithy_context?e.__smithy_context.features||(e.__smithy_context.features={}):e.__smithy_context={features:{}},e.__smithy_context.features[t]=n}var bT=class{authSchemes=new Map;constructor(e){for(let t in e){let n=e[t];n!==void 0&&this.authSchemes.set(t,n)}}getIdentityProvider(e){return this.authSchemes.get(e)}},xT=(e=>function(t){return ST(t)&&t.expiration.getTime()-Date.now()e.expiration!==void 0,CT=(e,t,n)=>{if(e===void 0)return;let r=typeof e==`function`?e:async()=>Promise.resolve(e),i,a,o,s=!1,c=async e=>{a||=r(e);try{i=await a,o=!0,s=!1}finally{a=void 0}return i};return t===void 0?async e=>((!o||e?.forceRefresh)&&(i=await c(e)),i):async e=>((!o||e?.forceRefresh)&&(i=await c(e)),s?i:n(i)?(t(i)&&await c(e),i):(s=!0,i))};function wT(e){return e===void 0?!0:typeof e==`string`&&e.length<=50}function TT(e){let t=mC(e.userAgentAppId??void 0),{customUserAgent:n}=e;return Object.assign(e,{customUserAgent:typeof n==`string`?[[n]]:n,userAgentAppId:async()=>{let n=await t();if(!wT(n)){let t=e.logger?.constructor?.name===`NoOpLogger`||!e.logger?console:e.logger;typeof n==`string`?n.length>50&&t?.warn(`The provided userAgentAppId exceeds the maximum length of 50 characters.`):t?.warn(`userAgentAppId must be a string or undefined.`)}return n}})}var ET=class e{nodes;root;conditions;results;constructor(e,t,n,r){this.nodes=e,this.root=t,this.conditions=n,this.results=r}static from(t,n,r,i){return new e(t,n,r,i)}},DT=class{capacity;data=new Map;parameters=[];constructor({size:e,params:t}){this.capacity=e??50,t&&(this.parameters=t)}get(e,t){let n=this.hash(e);if(n===!1)return t();if(!this.data.has(n)){if(this.data.size>this.capacity+10){let e=this.data.keys(),t=0;for(;;){let{value:n,done:r}=e.next();if(this.data.delete(n),r||++t>10)break}}this.data.set(n,t())}return this.data.get(n)}size(){return this.data.size}hash(e){let t=``,{parameters:n}=this;if(n.length===0)return!1;for(let r of n){let n=String(e[r]??``);if(n.includes(`|;`))return!1;t+=n+`|;`}return t}},OT=class extends Error{constructor(e){super(e),this.name=`EndpointError`}},kT=`endpoints`;function AT(e){return typeof e!=`object`||!e?e:`ref`in e?`$${AT(e.ref)}`:`fn`in e?`${e.fn}(${(e.argv||[]).map(AT).join(`, `)})`:JSON.stringify(e,null,2)}var jT={},MT=(e,t)=>e===t;function NT(...e){for(let t of e)if(t!=null)return t}var PT=e=>{let t=e.split(`.`),n=[];for(let r of t){let t=r.indexOf(`[`);if(t!==-1){if(r.indexOf(`]`)!==r.length-1)throw new OT(`Path: '${e}' does not end with ']'`);let i=r.slice(t+1,-1);if(Number.isNaN(parseInt(i)))throw new OT(`Invalid array index: '${i}' in path: '${e}'`);t!==0&&n.push(r.slice(0,t)),n.push(i)}else n.push(r)}return n},FT=(e,t)=>PT(t).reduce((n,r)=>{if(typeof n!=`object`)throw new OT(`Index '${r}' in '${t}' not found in '${JSON.stringify(e)}'`);if(Array.isArray(n)){let e=parseInt(r);return n[e<0?n.length+e:e]}return n[r]},e),IT=e=>e!=null,LT=RegExp(`^(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}$`),RT=(e,t=!1)=>{if(!t)return LT.test(e);let n=e.split(`.`);for(let e of n)if(!RT(e))return!1;return!0};function zT(e,t,n){return e?t:n}var BT=e=>!e,VT=RegExp(`^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$`),HT=e=>VT.test(e)||e.startsWith(`[`)&&e.endsWith(`]`),UT={[Nx.HTTP]:80,[Nx.HTTPS]:443},WT=e=>{let t=(()=>{try{if(e instanceof URL)return e;if(typeof e==`object`&&`hostname`in e){let{hostname:t,port:n,protocol:r=``,path:i=``,query:a={}}=e,o=new URL(`${r}//${t}${n?`:${n}`:``}${i}`);return o.search=Object.entries(a).map(([e,t])=>`${e}=${t}`).join(`&`),o}return new URL(e)}catch{return null}})();if(!t)return console.error(`Unable to parse ${JSON.stringify(e)} as a whatwg URL.`),null;let n=t.href,{host:r,hostname:i,pathname:a,protocol:o,search:s}=t;if(s)return null;let c=o.slice(0,-1);if(!Object.values(Nx).includes(c))return null;let l=HT(i);return{scheme:c,authority:`${r}${n.includes(`${r}:${UT[c]}`)||typeof e==`string`&&e.includes(`${r}:${UT[c]}`)?`:${UT[c]}`:``}`,path:a,normalizedPath:a.endsWith(`/`)?a:`${a}/`,isIp:l}};function GT(e,t,n){if(n===1)return[e];if(e===``)return[``];let r=e.split(t);return n===0?r:r.slice(0,n-1).concat(r.slice(1).join(t))}var KT={booleanEquals:MT,coalesce:NT,getAttr:FT,isSet:IT,isValidHostLabel:RT,ite:zT,not:BT,parseURL:WT,split:GT,stringEquals:(e,t)=>e===t,substring:(e,t,n,r)=>e==null||t>=n||e.lengthencodeURIComponent(e).replace(/[!*'()]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`)},qT=(e,t)=>{let n=[],{referenceRecord:r,endpointParams:i}=t,a=0;for(;at.referenceRecord[e]??t.endpointParams[e],YT=(e,t,n)=>{if(typeof e==`string`)return qT(e,n);if(e.fn)return ZT.callFunction(e,n);if(e.ref)return JT(e,n);throw new OT(`'${t}': ${String(e)} is not a string, function or reference.`)},XT=({fn:e,argv:t},n)=>{let r=Array(t.length);for(let e=0;e{let{assign:n}=e;if(n&&n in t.referenceRecord)throw new OT(`'${n}' is already defined in Reference Record.`);let r=XT(e,t);t.logger?.debug?.(`${kT} evaluateCondition: ${AT(e)} = ${AT(r)}`);let i=r===``?!0:!!r;return n==null?{result:i}:{result:i,toAssign:{name:n,value:r}}},$T=(e,t)=>Object.entries(e??{}).reduce((e,[n,r])=>(e[n]=r.map(e=>{let r=YT(e,`Header value entry`,t);if(typeof r!=`string`)throw new OT(`Header '${n}' value '${r}' is not a string`);return r}),e),{}),eE=(e,t)=>Object.entries(e).reduce((e,[n,r])=>(e[n]=nE.getEndpointProperty(r,t),e),{}),tE=(e,t)=>{if(Array.isArray(e))return e.map(e=>tE(e,t));switch(typeof e){case`string`:return qT(e,t);case`object`:if(e===null)throw new OT(`Unexpected endpoint property: ${e}`);return nE.getEndpointProperties(e,t);case`boolean`:return e;default:throw new OT(`Unexpected endpoint property type: ${typeof e}`)}},nE={getEndpointProperty:tE,getEndpointProperties:eE},rE=(e,t)=>{let n=YT(e,`Endpoint URL`,t);if(typeof n==`string`)try{return new URL(n)}catch(e){throw console.error(`Failed to construct URL with ${n}`,e),e}throw new OT(`Endpoint URL must be a string, got ${typeof n}`)},iE=1e8,aE=(e,t)=>{let{nodes:n,root:r,results:i,conditions:a}=e,o=r,s={},c={referenceRecord:s,endpointParams:t.endpointParams,logger:t.logger};for(;o!==1&&o!==-1&&o=0===f.result?r:i}if(o>=iE){let e=i[o-iE];if(e[0]===-1){let[,t]=e;throw new OT(YT(t,`Error`,c))}let[t,n,r]=e;return{url:rE(t,c),properties:eE(n,c),headers:$T(r??{},c)}}throw new OT(`No matching endpoint.`)},oE=(e,t=!1)=>{if(t){for(let t of e.split(`.`))if(!oE(t))return!1;return!0}return!(!RT(e)||e.length<3||e.length>63||e!==e.toLowerCase()||HT(e))},sE=`:`,cE=`/`,lE=e=>{let t=e.split(sE);if(t.length<6)return null;let[n,r,i,a,o,...s]=t;return n!==`arn`||r===``||i===``||s.join(sE)===``?null:{partition:r,service:i,region:a,accountId:o,resourceId:s.map(e=>e.split(cE)).flat()}},uE={partitions:[{id:`aws`,outputs:{dnsSuffix:`amazonaws.com`,dualStackDnsSuffix:`api.aws`,implicitGlobalRegion:`us-east-1`,name:`aws`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^(us|eu|ap|sa|ca|me|af|il|mx)\\-\\w+\\-\\d+$`,regions:{"af-south-1":{description:`Africa (Cape Town)`},"ap-east-1":{description:`Asia Pacific (Hong Kong)`},"ap-east-2":{description:`Asia Pacific (Taipei)`},"ap-northeast-1":{description:`Asia Pacific (Tokyo)`},"ap-northeast-2":{description:`Asia Pacific (Seoul)`},"ap-northeast-3":{description:`Asia Pacific (Osaka)`},"ap-south-1":{description:`Asia Pacific (Mumbai)`},"ap-south-2":{description:`Asia Pacific (Hyderabad)`},"ap-southeast-1":{description:`Asia Pacific (Singapore)`},"ap-southeast-2":{description:`Asia Pacific (Sydney)`},"ap-southeast-3":{description:`Asia Pacific (Jakarta)`},"ap-southeast-4":{description:`Asia Pacific (Melbourne)`},"ap-southeast-5":{description:`Asia Pacific (Malaysia)`},"ap-southeast-6":{description:`Asia Pacific (New Zealand)`},"ap-southeast-7":{description:`Asia Pacific (Thailand)`},"aws-global":{description:`aws global region`},"ca-central-1":{description:`Canada (Central)`},"ca-west-1":{description:`Canada West (Calgary)`},"eu-central-1":{description:`Europe (Frankfurt)`},"eu-central-2":{description:`Europe (Zurich)`},"eu-north-1":{description:`Europe (Stockholm)`},"eu-south-1":{description:`Europe (Milan)`},"eu-south-2":{description:`Europe (Spain)`},"eu-west-1":{description:`Europe (Ireland)`},"eu-west-2":{description:`Europe (London)`},"eu-west-3":{description:`Europe (Paris)`},"il-central-1":{description:`Israel (Tel Aviv)`},"me-central-1":{description:`Middle East (UAE)`},"me-south-1":{description:`Middle East (Bahrain)`},"mx-central-1":{description:`Mexico (Central)`},"sa-east-1":{description:`South America (Sao Paulo)`},"us-east-1":{description:`US East (N. Virginia)`},"us-east-2":{description:`US East (Ohio)`},"us-west-1":{description:`US West (N. California)`},"us-west-2":{description:`US West (Oregon)`}}},{id:`aws-cn`,outputs:{dnsSuffix:`amazonaws.com.cn`,dualStackDnsSuffix:`api.amazonwebservices.com.cn`,implicitGlobalRegion:`cn-northwest-1`,name:`aws-cn`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^cn\\-\\w+\\-\\d+$`,regions:{"aws-cn-global":{description:`aws-cn global region`},"cn-north-1":{description:`China (Beijing)`},"cn-northwest-1":{description:`China (Ningxia)`}}},{id:`aws-eusc`,outputs:{dnsSuffix:`amazonaws.eu`,dualStackDnsSuffix:`api.amazonwebservices.eu`,implicitGlobalRegion:`eusc-de-east-1`,name:`aws-eusc`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^eusc\\-(de)\\-\\w+\\-\\d+$`,regions:{"eusc-de-east-1":{description:`AWS European Sovereign Cloud (Germany)`}}},{id:`aws-iso`,outputs:{dnsSuffix:`c2s.ic.gov`,dualStackDnsSuffix:`api.aws.ic.gov`,implicitGlobalRegion:`us-iso-east-1`,name:`aws-iso`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^us\\-iso\\-\\w+\\-\\d+$`,regions:{"aws-iso-global":{description:`aws-iso global region`},"us-iso-east-1":{description:`US ISO East`},"us-iso-west-1":{description:`US ISO WEST`}}},{id:`aws-iso-b`,outputs:{dnsSuffix:`sc2s.sgov.gov`,dualStackDnsSuffix:`api.aws.scloud`,implicitGlobalRegion:`us-isob-east-1`,name:`aws-iso-b`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^us\\-isob\\-\\w+\\-\\d+$`,regions:{"aws-iso-b-global":{description:`aws-iso-b global region`},"us-isob-east-1":{description:`US ISOB East (Ohio)`},"us-isob-west-1":{description:`US ISOB West`}}},{id:`aws-iso-e`,outputs:{dnsSuffix:`cloud.adc-e.uk`,dualStackDnsSuffix:`api.cloud-aws.adc-e.uk`,implicitGlobalRegion:`eu-isoe-west-1`,name:`aws-iso-e`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^eu\\-isoe\\-\\w+\\-\\d+$`,regions:{"aws-iso-e-global":{description:`aws-iso-e global region`},"eu-isoe-west-1":{description:`EU ISOE West`}}},{id:`aws-iso-f`,outputs:{dnsSuffix:`csp.hci.ic.gov`,dualStackDnsSuffix:`api.aws.hci.ic.gov`,implicitGlobalRegion:`us-isof-south-1`,name:`aws-iso-f`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^us\\-isof\\-\\w+\\-\\d+$`,regions:{"aws-iso-f-global":{description:`aws-iso-f global region`},"us-isof-east-1":{description:`US ISOF EAST`},"us-isof-south-1":{description:`US ISOF SOUTH`}}},{id:`aws-us-gov`,outputs:{dnsSuffix:`amazonaws.com`,dualStackDnsSuffix:`api.aws`,implicitGlobalRegion:`us-gov-west-1`,name:`aws-us-gov`,supportsDualStack:!0,supportsFIPS:!0},regionRegex:`^us\\-gov\\-\\w+\\-\\d+$`,regions:{"aws-us-gov-global":{description:`aws-us-gov global region`},"us-gov-east-1":{description:`AWS GovCloud (US-East)`},"us-gov-west-1":{description:`AWS GovCloud (US-West)`}}}],version:`1.1`},dE=``,fE=e=>{let{partitions:t}=uE;for(let n of t){let{regions:t,outputs:r}=n;for(let[n,i]of Object.entries(t))if(n===e)return{...r,...i}}for(let n of t){let{regionRegex:t,outputs:r}=n;if(new RegExp(t).test(e))return{...r}}let n=t.find(e=>e.id===`aws`);if(!n)throw Error(`Provided region was not found in the partition array or regex, and default partition with id 'aws' doesn't exist.`);return{...n.outputs}},pE=()=>dE,mE={isVirtualHostableS3Bucket:oE,parseArn:lE,partition:fE};jT.aws=mE;function hE(e,t,n){return e.$source||={},e.$source[t]=n,e}var gE;(function(e){e.STANDARD=`standard`,e.ADAPTIVE=`adaptive`})(gE||={});var _E=gE.STANDARD,vE=[`BandwidthLimitExceeded`,`EC2ThrottledException`,`LimitExceededException`,`PriorRequestNotComplete`,`ProvisionedThroughputExceededException`,`RequestLimitExceeded`,`RequestThrottled`,`RequestThrottledException`,`SlowDown`,`ThrottledException`,`Throttling`,`ThrottlingException`,`TooManyRequestsException`,`TransactionInProgressException`],yE=[`TimeoutError`,`RequestTimeout`,`RequestTimeoutException`],bE=[500,502,503,504],xE=[`ECONNRESET`,`ECONNREFUSED`,`EPIPE`,`ETIMEDOUT`],SE=[`EHOSTUNREACH`,`ENETUNREACH`,`ENOTFOUND`],CE=e=>e?.$retryable!==void 0,wE=e=>e.$metadata?.clockSkewCorrected,TE=e=>{let t=new Set([`Failed to fetch`,`NetworkError when attempting to fetch resource`,`The Internet connection appears to be offline`,`Load failed`,`Network request failed`]);return e&&e instanceof TypeError?t.has(e.message):!1},EE=e=>e.$metadata?.httpStatusCode===429||vE.includes(e.name)||e.$retryable?.throttling==1,DE=(e,t=0)=>CE(e)||wE(e)||e.name===`InvalidSignatureException`&&e.message?.includes(`Signature expired`)||yE.includes(e.name)||xE.includes(e?.code||``)||SE.includes(e?.code||``)||bE.includes(e.$metadata?.httpStatusCode||0)||TE(e)||kE(e)||e.cause!==void 0&&t<=10&&DE(e.cause,t+1),OE=e=>{if(e.$metadata?.httpStatusCode!==void 0){let t=e.$metadata.httpStatusCode;return 500<=t&&t<=599&&!DE(e)}return!1};function kE(e){return e.code===`ERR_HTTP2_STREAM_ERROR`&&e.message.includes(`NGHTTP2_REFUSED_STREAM`)}var AE=class e{static setTimeoutFn=setTimeout;beta;minCapacity;minFillRate;scaleConstant;smooth;enabled=!1;availableTokens=0;lastMaxRate=0;measuredTxRate=0;requestCount=0;fillRate;lastThrottleTime;lastTimestamp=0;lastTxRateBucket;maxCapacity;timeWindow=0;constructor(e){this.beta=e?.beta??.7,this.minCapacity=e?.minCapacity??1,this.minFillRate=e?.minFillRate??.5,this.scaleConstant=e?.scaleConstant??.4,this.smooth=e?.smooth??.8,this.lastThrottleTime=this.getCurrentTimeInSeconds(),this.lastTxRateBucket=Math.floor(this.getCurrentTimeInSeconds()),this.fillRate=this.minFillRate,this.maxCapacity=this.minCapacity}async getSendToken(){return this.acquireTokenBucket(1)}updateClientSendingRate(e){let t;this.updateMeasuredRate();let n=e;if(n?.errorType===`THROTTLING`||EE(n?.error??e)){let e=this.enabled?Math.min(this.measuredTxRate,this.fillRate):this.measuredTxRate;this.lastMaxRate=e,this.calculateTimeWindow(),this.lastThrottleTime=this.getCurrentTimeInSeconds(),t=this.cubicThrottle(e),this.enableTokenBucket()}else this.calculateTimeWindow(),t=this.cubicSuccess(this.getCurrentTimeInSeconds());let r=Math.min(t,2*this.measuredTxRate);this.updateTokenBucketRate(r)}getCurrentTimeInSeconds(){return Date.now()/1e3}async acquireTokenBucket(t){if(this.enabled){for(this.refillTokenBucket();t>this.availableTokens;){let n=(t-this.availableTokens)/this.fillRate*1e3;await new Promise(t=>e.setTimeoutFn(t,n)),this.refillTokenBucket()}this.availableTokens-=t}}refillTokenBucket(){let e=this.getCurrentTimeInSeconds();if(!this.lastTimestamp){this.lastTimestamp=e;return}let t=(e-this.lastTimestamp)*this.fillRate;this.availableTokens=Math.min(this.maxCapacity,this.availableTokens+t),this.lastTimestamp=e}calculateTimeWindow(){this.timeWindow=this.getPrecise((this.lastMaxRate*(1-this.beta)/this.scaleConstant)**(1/3))}cubicThrottle(e){return this.getPrecise(e*this.beta)}cubicSuccess(e){return this.getPrecise(this.scaleConstant*(e-this.lastThrottleTime-this.timeWindow)**3+this.lastMaxRate)}enableTokenBucket(){this.enabled=!0}updateTokenBucketRate(e){this.refillTokenBucket(),this.fillRate=Math.max(e,this.minFillRate),this.maxCapacity=Math.max(e,this.minCapacity),this.availableTokens=Math.min(this.availableTokens,this.maxCapacity)}updateMeasuredRate(){let e=this.getCurrentTimeInSeconds(),t=Math.floor(e*2)/2;if(this.requestCount++,t>this.lastTxRateBucket){let e=this.requestCount/(t-this.lastTxRateBucket);this.measuredTxRate=this.getPrecise(e*this.smooth+this.measuredTxRate*(1-this.smooth)),this.requestCount=0,this.lastTxRateBucket=t}}getPrecise(e){return parseFloat(e.toFixed(8))}},jE=20*1e3,ME=`amz-sdk-invocation-id`,NE=`amz-sdk-request`,PE=class e{static v2026=typeof process<`u`&&{}?.SMITHY_NEW_RETRIES_2026===`true`;static delay(){return e.v2026?50:100}static throttlingDelay(){return e.v2026?1e3:500}static cost(){return e.v2026?14:5}static throttlingCost(){return e.v2026?5:10}static modifiedCostType(){return e.v2026?`THROTTLING`:`TRANSIENT`}},FE=class{x=PE.delay();computeNextBackoffDelay(e){let t=Math.random()*Math.min(this.x*2**e,jE);return Math.floor(t)}setDelayBase(e){this.x=e}},IE=class{delay;count;cost;longPoll;constructor(e,t,n,r){this.delay=e,this.count=t,this.cost=n,this.longPoll=r}getRetryCount(){return this.count}getRetryDelay(){return Math.min(jE,this.delay)}getRetryCost(){return this.cost}isLongPoll(){return this.longPoll}},LE={incompatible:1,attempts:2,capacity:3},RE=class{mode=gE.STANDARD;capacity=500;retryBackoffStrategy;maxAttemptsProvider;baseDelay;constructor(e){typeof e==`number`?this.maxAttemptsProvider=async()=>e:typeof e==`function`?this.maxAttemptsProvider=e:e&&typeof e==`object`&&(this.maxAttemptsProvider=async()=>e.maxAttempts,this.baseDelay=e.baseDelay,this.retryBackoffStrategy=e.backoff),this.maxAttemptsProvider??=async()=>3,this.baseDelay??=PE.delay(),this.retryBackoffStrategy??=new FE}async acquireInitialRetryToken(e){return new IE(PE.delay(),0,void 0,PE.v2026&&e.includes(`:longpoll`))}async refreshRetryTokenForRetry(e,t){let n=await this.getMaxAttempts(),r=this.retryCode(e,t,n),i=r===0,a=e.isLongPoll?.();if(i||a){let n=t.errorType;this.retryBackoffStrategy.setDelayBase(n===`THROTTLING`?PE.throttlingDelay():this.baseDelay);let o=this.retryBackoffStrategy.computeNextBackoffDelay(e.getRetryCount()),s=o;if(t.retryAfterHint instanceof Date&&(s=Math.max(o,Math.min(t.retryAfterHint.getTime()-Date.now(),o+5e3))),i){let t=this.getCapacityCost(n);return this.capacity-=t,new IE(s,e.getRetryCount()+1,t,e.isLongPoll?.()??!1)}else throw Object.assign(Error(`No retry token available`),{$backoff:PE.v2026&&r===LE.capacity&&a?s:0})}throw Error(`No retry token available`)}recordSuccess(e){this.capacity=Math.min(500,this.capacity+(e.getRetryCost()??1))}getCapacity(){return this.capacity}async maxAttempts(){return this.maxAttemptsProvider()}async getMaxAttempts(){try{return await this.maxAttemptsProvider()}catch{return console.warn(`Max attempts provider could not resolve. Using default of 3`),3}}retryCode(e,t,n){let r=e.getRetryCount()+1,i=this.isRetryableError(t.errorType)?0:LE.incompatible,a=r=this.getCapacityCost(t.errorType)?0:LE.capacity;return i||a||o}getCapacityCost(e){return e===PE.modifiedCostType()?PE.throttlingCost():PE.cost()}isRetryableError(e){return e===`THROTTLING`||e===`TRANSIENT`}},zE=class{mode=gE.ADAPTIVE;rateLimiter;standardRetryStrategy;constructor(e,t){let{rateLimiter:n}=t??{};this.rateLimiter=n??new AE,this.standardRetryStrategy=t?new RE({maxAttempts:typeof e==`number`?e:3,...t}):new RE(e)}async acquireInitialRetryToken(e){let t=await this.standardRetryStrategy.acquireInitialRetryToken(e);return await this.rateLimiter.getSendToken(),t}async refreshRetryTokenForRetry(e,t){this.rateLimiter.updateClientSendingRate(t);let n=await this.standardRetryStrategy.refreshRetryTokenForRetry(e,t);return await this.rateLimiter.getSendToken(),n}recordSuccess(e){this.rateLimiter.updateClientSendingRate({}),this.standardRetryStrategy.recordSuccess(e)}async maxAttemptsProvider(){return this.standardRetryStrategy.maxAttempts()}};PE.v2026||=typeof process==`object`&&{}?.AWS_NEW_RETRIES_2026===`true`;function BE(e,t,n){e.__aws_sdk_context?e.__aws_sdk_context.features||(e.__aws_sdk_context.features={}):e.__aws_sdk_context={features:{}},e.__aws_sdk_context.features[t]=n}var VE=/\d{12}\.ddb/;async function HE(e,t,n){if(n.request?.headers?.[`smithy-protocol`]===`rpc-v2-cbor`&&BE(e,`PROTOCOL_RPC_V2_CBOR`,`M`),typeof t.retryStrategy==`function`){let n=await t.retryStrategy();if(typeof n.mode==`string`)switch(n.mode){case gE.ADAPTIVE:BE(e,`RETRY_MODE_ADAPTIVE`,`F`);break;case gE.STANDARD:BE(e,`RETRY_MODE_STANDARD`,`E`);break}}if(typeof t.accountIdEndpointMode==`function`){let n=e.endpointV2;switch(String(n?.url?.hostname).match(VE)&&BE(e,`ACCOUNT_ID_ENDPOINT`,`O`),await t.accountIdEndpointMode?.()){case`disabled`:BE(e,`ACCOUNT_ID_MODE_DISABLED`,`Q`);break;case`preferred`:BE(e,`ACCOUNT_ID_MODE_PREFERRED`,`P`);break;case`required`:BE(e,`ACCOUNT_ID_MODE_REQUIRED`,`R`);break}}let r=e.__smithy_context?.selectedHttpAuthScheme?.identity;if(r?.$source){let t=r;t.accountId&&BE(e,`RESOLVED_ACCOUNT_ID`,`T`);for(let[n,r]of Object.entries(t.$source??{}))BE(e,n,r)}}var UE=`user-agent`,WE=`x-amz-user-agent`,GE=/[^!$%&'*+\-.^_`|~\w]/g,KE=/[^!$%&'*+\-.^_`|~\w#]/g,qE=1024;function JE(e){let t=``;for(let n in e){let r=e[n];if(t.length+r.length+1<=qE){t.length?t+=`,`+r:t+=r;continue}break}return t}var YE=e=>(t,n)=>async r=>{let{request:i}=r;if(!Ix.isInstance(i))return t(r);let{headers:a}=i,o=n?.userAgent?.map(XE)||[],s=(await e.defaultUserAgentProvider()).map(XE);await HE(n,e,r);let c=n;s.push(`m/${JE(Object.assign({},n.__smithy_context?.features,c.__aws_sdk_context?.features))}`);let l=e?.customUserAgent?.map(XE)||[],u=await e.userAgentAppId();u&&s.push(XE([`app`,`${u}`]));let d=pE(),f=(d?[d]:[]).concat([...s,...o,...l]).join(` `),p=[...s.filter(e=>e.startsWith(`aws-sdk-`)),...l].join(` `);return e.runtime===`browser`?a[WE]=f:(p&&(a[WE]=a[`x-amz-user-agent`]?`${a[UE]} ${p}`:p),a[UE]=f),t({...r,request:i})},XE=e=>{let t=e[0].split(`/`).map(e=>e.replace(GE,`-`)).join(`/`),n=e[1]?.replace(KE,`-`),r=t.indexOf(`/`),i=t.substring(0,r),a=t.substring(r+1);return i===`api`&&(a=a.toLowerCase()),[i,a,n].filter(e=>e&&e.length>0).reduce((e,t,n)=>{switch(n){case 0:return t;case 1:return`${e}/${t}`;default:return`${e}#${t}`}},``)},ZE={name:`getUserAgentMiddleware`,step:`build`,priority:`low`,tags:[`SET_USER_AGENT`,`USER_AGENT`],override:!0},QE=e=>({applyToStack:t=>{t.add(YE(e),ZE)}}),$E=new Set,eD=(e,t=RT)=>{if(!$E.has(e)&&!t(e))if(e===`*`)console.warn(`@smithy/config-resolver WARN - Please use the caller region instead of "*". See "sigv4a" in https://github.com/aws/aws-sdk-js-v3/blob/main/supplemental-docs/CLIENTS.md.`);else throw Error(`Region not accepted: region="${e}" is not a valid hostname component.`);else $E.add(e)},tD=e=>typeof e==`string`&&(e.startsWith(`fips-`)||e.endsWith(`-fips`)),Pre=e=>tD(e)?[`fips-aws-global`,`aws-fips`].includes(e)?`us-east-1`:e.replace(/fips-(dkr-|prod-)?|-fips/,``):e,nD=e=>{let{region:t,useFipsEndpoint:n}=e;if(!t)throw Error(`Region is missing`);return Object.assign(e,{region:async()=>{let e=Pre(typeof t==`function`?await t():t);return eD(e),e},useFipsEndpoint:async()=>tD(typeof t==`string`?t:await t())?!0:typeof n==`function`?n():Promise.resolve(!!n)})},rD=`content-length`;function Fre(e){return t=>async n=>{let r=n.request;if(Ix.isInstance(r)){let{body:t,headers:n}=r;if(t&&Object.keys(n).map(e=>e.toLowerCase()).indexOf(rD)===-1)try{let n=e(t);r.headers={...r.headers,[rD]:String(n)}}catch{}}return t({...n,request:r})}}var Ire={step:`build`,tags:[`SET_CONTENT_LENGTH`,`CONTENT_LENGTH`],name:`contentLengthMiddleware`,override:!0},iD=e=>({applyToStack:t=>{t.add(Fre(e.bodyLengthChecker),Ire)}}),Lre=async e=>{let t=e?.Bucket||``;if(typeof e.Bucket==`string`&&(e.Bucket=t.replace(/#/g,`%23`).replace(/\?/g,`%3F`)),Hre(t)){if(e.ForcePathStyle===!0)throw Error(`Path-style addressing cannot be used with ARN buckets`)}else (!Vre(t)||t.indexOf(`.`)!==-1&&!String(e.Endpoint).startsWith(`http:`)||t.toLowerCase()!==t||t.length<3)&&(e.ForcePathStyle=!0);return e.DisableMultiRegionAccessPoints&&(e.disableMultiRegionAccessPoints=!0,e.DisableMRAP=!0),e},Rre=/^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$/,zre=/(\d+\.){3}\d+/,Bre=/\.\./,Vre=e=>Rre.test(e)&&!zre.test(e)&&!Bre.test(e),Hre=e=>{let[t,n,r,,,i]=e.split(`:`),a=t===`arn`&&e.split(`:`).length>=6,o=!!(a&&n&&r&&i);if(a&&!o)throw Error(`Invalid ARN: ${e} was an invalid ARN.`);return o},Ure=(e,t,n,r=!1)=>{let i=async()=>{let i;return i=r?n.clientContextParams?.[e]??n[e]??n[t]:n[e]??n[t],typeof i==`function`?i():i};return e===`credentialScope`||t===`CredentialScope`?async()=>{let e=typeof n.credentials==`function`?await n.credentials():n.credentials;return e?.credentialScope??e?.CredentialScope}:e===`accountId`||t===`AccountId`?async()=>{let e=typeof n.credentials==`function`?await n.credentials():n.credentials;return e?.accountId??e?.AccountId}:e===`endpoint`||t===`endpoint`?async()=>{if(n.isCustomEndpoint===!1)return;let e=await i();if(e&&typeof e==`object`){if(`url`in e)return e.url.href;if(`hostname`in e){let{protocol:t,hostname:n,port:r,path:i}=e;return`${t}//${n}${r?`:`+r:``}${i}`}}return e}:i},aD=async e=>void 0,oD=e=>{if(typeof e==`object`){if(`url`in e){let t=UC(e.url);if(e.headers){t.headers={};for(let[n,r]of Object.entries(e.headers))t.headers[n.toLowerCase()]=r.join(`, `)}return t}return e}return UC(e)},sD=async(e,t,n,r)=>{if(!n.isCustomEndpoint){let e;e=n.serviceConfiguredEndpoint?await n.serviceConfiguredEndpoint():await aD(n.serviceId),e&&(n.endpoint=()=>Promise.resolve(oD(e)),n.isCustomEndpoint=!0)}let i=await cD(e,t,n);if(typeof n.endpointProvider!=`function`)throw Error(`config.endpointProvider is not set.`);let a=n.endpointProvider(i,r);if(n.isCustomEndpoint&&n.endpoint){let e=await n.endpoint();if(e?.headers){a.headers??={};for(let[t,n]of Object.entries(e.headers))a.headers[t]=Array.isArray(n)?n:[n]}}return a},cD=async(e,t,n)=>{let r={},i=t?.getEndpointParameterInstructions?.()||{};for(let[t,a]of Object.entries(i))switch(a.type){case`staticContextParams`:r[t]=a.value;break;case`contextParams`:r[t]=e[a.name];break;case`clientContextParams`:case`builtInParams`:r[t]=await Ure(a.name,t,n,a.type!==`builtInParams`)();break;case`operationContextParams`:r[t]=a.get(e);break;default:throw Error(`Unrecognized endpoint parameter instruction: `+JSON.stringify(a))}return Object.keys(i).length===0&&Object.assign(r,n),String(n.serviceId).toLowerCase()===`s3`&&await Lre(r),r},Wre=({config:e,instructions:t})=>(n,r)=>async i=>{e.isCustomEndpoint&&yT(r,`ENDPOINT_OVERRIDE`,`N`);let a=await sD(i.input,{getEndpointParameterInstructions(){return t}},{...e},r);r.endpointV2=a,r.authSchemes=a.properties?.authSchemes;let o=r.authSchemes?.[0];if(o){r.signing_region=o.signingRegion,r.signing_service=o.signingName;let e=DS(r)?.selectedHttpAuthScheme?.httpAuthOption;e&&(e.signingProperties=Object.assign(e.signingProperties||{},{signing_region:o.signingRegion,signingRegion:o.signingRegion,signing_service:o.signingName,signingName:o.signingName,signingRegionSet:o.signingRegionSet},o.properties))}return n({...i})},Gre={step:`serialize`,tags:[`ENDPOINT_PARAMETERS`,`ENDPOINT_V2`,`ENDPOINT`],name:`endpointV2Middleware`,override:!0,relation:`before`,toMiddleware:{name:`serializerMiddleware`,step:`serialize`,tags:[`SERIALIZER`],override:!0}.name},lD=(e,t)=>({applyToStack:n=>{n.addRelativeTo(Wre({config:e,instructions:t}),Gre)}}),uD=e=>{let t=e.tls??!0,{endpoint:n,useDualstackEndpoint:r,useFipsEndpoint:i}=e,a=Object.assign(e,{endpoint:n==null?void 0:async()=>oD(await OS(n)()),tls:t,isCustomEndpoint:!!n,useDualstackEndpoint:OS(r??!1),useFipsEndpoint:OS(i??!1)}),o;return a.serviceConfiguredEndpoint=async()=>(e.serviceId&&!o&&(o=aD(e.serviceId)),o),a},Kre=e=>e instanceof Error?e:e instanceof Object?Object.assign(Error(),e):Error(typeof e==`string`?e:`AWS SDK error wrapper for ${e}`),dD=e=>{let{retryStrategy:t,retryMode:n}=e,r=OS(e.maxAttempts??3),i=t?Promise.resolve(t):void 0,a=async()=>await OS(n)()===gE.ADAPTIVE?new zE(r):new RE(r);return Object.assign(e,{maxAttempts:r,retryStrategy:()=>i??=a()})},fD=(e,t)=>{let n=[];if(e&&n.push(e),t)for(let e of t)n.push(e);return n},pD=(e,t)=>`${e||`anonymous`}${t&&t.length>0?` (a.k.a. ${t.join(`,`)})`:``}`,mD=()=>{let e=[],t=[],n=!1,r=new Set,i=e=>e.sort((e,t)=>hD[t.step]-hD[e.step]||gD[t.priority||`normal`]-gD[e.priority||`normal`]),a=n=>{let i=!1,a=e=>{let t=fD(e.name,e.aliases);if(t.includes(n)){i=!0;for(let e of t)r.delete(e);return!1}return!0};return e=e.filter(a),t=t.filter(a),i},o=n=>{let i=!1,a=e=>{if(e.middleware===n){i=!0;for(let t of fD(e.name,e.aliases))r.delete(t);return!1}return!0};return e=e.filter(a),t=t.filter(a),i},s=n=>(e.forEach(e=>{n.add(e.middleware,{...e})}),t.forEach(e=>{n.addRelativeTo(e.middleware,{...e})}),n.identifyOnResolve?.(u.identifyOnResolve()),n),c=e=>{let t=[];return e.before.forEach(e=>{e.before.length===0&&e.after.length===0?t.push(e):t.push(...c(e))}),t.push(e),e.after.reverse().forEach(e=>{e.before.length===0&&e.after.length===0?t.push(e):t.push(...c(e))}),t},l=(n=!1)=>{let r=[],a=[],o={};return e.forEach(e=>{let t={...e,before:[],after:[]};for(let e of fD(t.name,t.aliases))o[e]=t;r.push(t)}),t.forEach(e=>{let t={...e,before:[],after:[]};for(let e of fD(t.name,t.aliases))o[e]=t;a.push(t)}),a.forEach(e=>{if(e.toMiddleware){let t=o[e.toMiddleware];if(t===void 0){if(n)return;throw Error(`${e.toMiddleware} is not found when adding ${pD(e.name,e.aliases)} middleware ${e.relation} ${e.toMiddleware}`)}e.relation===`after`&&t.after.push(e),e.relation===`before`&&t.before.push(e)}}),i(r).map(c).reduce((e,t)=>(e.push(...t),e),[])},u={add:(t,n={})=>{let{name:i,override:a,aliases:o}=n,s={step:`initialize`,priority:`normal`,middleware:t,...n},c=fD(i,o);if(c.length>0){if(c.some(e=>r.has(e))){if(!a)throw Error(`Duplicate middleware name '${pD(i,o)}'`);for(let t of c){let n=e.findIndex(e=>e.name===t||e.aliases?.some(e=>e===t));if(n===-1)continue;let r=e[n];if(r.step!==s.step||s.priority!==r.priority)throw Error(`"${pD(r.name,r.aliases)}" middleware with ${r.priority} priority in ${r.step} step cannot be overridden by "${pD(i,o)}" middleware with ${s.priority} priority in ${s.step} step.`);e.splice(n,1)}}for(let e of c)r.add(e)}e.push(s)},addRelativeTo:(e,n)=>{let{name:i,override:a,aliases:o}=n,s={middleware:e,...n},c=fD(i,o);if(c.length>0){if(c.some(e=>r.has(e))){if(!a)throw Error(`Duplicate middleware name '${pD(i,o)}'`);for(let e of c){let n=t.findIndex(t=>t.name===e||t.aliases?.some(t=>t===e));if(n===-1)continue;let r=t[n];if(r.toMiddleware!==s.toMiddleware||r.relation!==s.relation)throw Error(`"${pD(r.name,r.aliases)}" middleware ${r.relation} "${r.toMiddleware}" middleware cannot be overridden by "${pD(i,o)}" middleware ${s.relation} "${s.toMiddleware}" middleware.`);t.splice(n,1)}}for(let e of c)r.add(e)}t.push(s)},clone:()=>s(mD()),use:e=>{e.applyToStack(u)},remove:e=>typeof e==`string`?a(e):o(e),removeByTag:n=>{let i=!1,a=e=>{let{tags:t,name:a,aliases:o}=e;if(t&&t.includes(n)){let e=fD(a,o);for(let t of e)r.delete(t);return i=!0,!1}return!0};return e=e.filter(a),t=t.filter(a),i},concat:e=>{let t=s(mD());return t.use(e),t.identifyOnResolve(n||t.identifyOnResolve()||(e.identifyOnResolve?.()??!1)),t},applyToStack:s,identify:()=>l(!0).map(e=>{let t=e.step??e.relation+` `+e.toMiddleware;return pD(e.name,e.aliases)+` - `+t}),identifyOnResolve(e){return typeof e==`boolean`&&(n=e),n},resolve:(e,t)=>{for(let n of l().map(e=>e.middleware).reverse())e=n(e,t);return n&&console.log(u.identify()),e}};return u},hD={initialize:5,serialize:4,build:3,finalizeRequest:2,deserialize:1},gD={high:3,normal:2,low:1},_D=class{config;middlewareStack=mD();initConfig;handlers;constructor(e){this.config=e;let{protocol:t,protocolSettings:n}=e;n&&typeof t==`function`&&(e.protocol=new t(n))}send(e,t,n){let r=typeof t==`function`?void 0:t,i=typeof t==`function`?t:n,a=r===void 0&&this.config.cacheMiddleware===!0,o;if(a){this.handlers||=new WeakMap;let t=this.handlers;t.has(e.constructor)?o=t.get(e.constructor):(o=e.resolveMiddleware(this.middlewareStack,this.config,r),t.set(e.constructor,o))}else delete this.handlers,o=e.resolveMiddleware(this.middlewareStack,this.config,r);if(i)o(e).then(e=>i(null,e.output),e=>i(e)).catch(()=>{});else return o(e).then(e=>e.output)}destroy(){this.config?.requestHandler?.destroy?.(),delete this.handlers}},vD=`***SensitiveInformation***`;function yD(e,t){if(t==null)return t;let n=ew.of(e);if(n.getMergedTraits().sensitive)return vD;if(n.isListSchema()){if(n.getValueSchema().getMergedTraits().sensitive)return vD}else if(n.isMapSchema()){if(n.getKeySchema().getMergedTraits().sensitive||n.getValueSchema().getMergedTraits().sensitive)return vD}else if(n.isStructSchema()&&typeof t==`object`){let e=t,r={};for(let[t,i]of n.structIterator())e[t]!=null&&(r[t]=yD(i,e[t]));return r}return t}var bD=class{middlewareStack=mD();schema;static classBuilder(){return new qre}resolveMiddlewareWithContext(e,t,n,{middlewareFn:r,clientName:i,commandName:a,inputFilterSensitiveLog:o,outputFilterSensitiveLog:s,smithyContext:c,additionalContext:l,CommandCtor:u}){for(let i of r.bind(this)(u,e,t,n))this.middlewareStack.use(i);let d=e.concat(this.middlewareStack),{logger:f}=t,p={logger:f,clientName:i,commandName:a,inputFilterSensitiveLog:o,outputFilterSensitiveLog:s,[Fx]:{commandInstance:this,...c},...l},{requestHandler:m}=t,h=n??{};return c.eventStream&&(h={isEventStream:!0,...h}),d.resolve(e=>m.handle(e.request,h),p)}},qre=class{_init=()=>{};_ep={};_middlewareFn=()=>[];_commandName=``;_clientName=``;_additionalContext={};_smithyContext={};_inputFilterSensitiveLog=void 0;_outputFilterSensitiveLog=void 0;_serializer=null;_deserializer=null;_operationSchema;init(e){this._init=e}ep(e){return this._ep=e,this}m(e){return this._middlewareFn=e,this}s(e,t,n={}){return this._smithyContext={service:e,operation:t,...n},this}c(e={}){return this._additionalContext=e,this}n(e,t){return this._clientName=e,this._commandName=t,this}f(e=e=>e,t=e=>e){return this._inputFilterSensitiveLog=e,this._outputFilterSensitiveLog=t,this}ser(e){return this._serializer=e,this}de(e){return this._deserializer=e,this}sc(e){return this._operationSchema=e,this._smithyContext.operationSchema=e,this}build(){let e=this,t;return t=class extends bD{input;static getEndpointParameterInstructions(){return e._ep}constructor(...[t]){super(),this.input=t??{},e._init(this),this.schema=e._operationSchema}resolveMiddleware(n,r,i){let a=e._operationSchema,o=a?.[4]??a?.input,s=a?.[5]??a?.output;return this.resolveMiddlewareWithContext(n,r,i,{CommandCtor:t,middlewareFn:e._middlewareFn,clientName:e._clientName,commandName:e._commandName,inputFilterSensitiveLog:e._inputFilterSensitiveLog??(a?yD.bind(null,o):e=>e),outputFilterSensitiveLog:e._outputFilterSensitiveLog??(a?yD.bind(null,s):e=>e),smithyContext:e._smithyContext,additionalContext:e._additionalContext})}serialize=e._serializer;deserialize=e._deserializer}}},xD=class e extends Error{$fault;$response;$retryable;$metadata;constructor(e){super(e.message),Object.setPrototypeOf(this,Object.getPrototypeOf(this).constructor.prototype),this.name=e.name,this.$fault=e.$fault,this.$metadata=e.$metadata}static isInstance(t){if(!t)return!1;let n=t;return e.prototype.isPrototypeOf(n)||!!n.$fault&&!!n.$metadata&&(n.$fault===`client`||n.$fault===`server`)}static[Symbol.hasInstance](t){if(!t)return!1;let n=t;return this===e?e.isInstance(t):e.isInstance(t)?n.name&&this.name?this.prototype.isPrototypeOf(t)||n.name===this.name:this.prototype.isPrototypeOf(t):!1}},SD=(e,t={})=>(Object.entries(t).filter(([,e])=>e!==void 0).forEach(([t,n])=>{(e[t]==null||e[t]===``)&&(e[t]=n)}),e.message=e.message||e.Message||`UnknownError`,delete e.Message,e),CD=e=>{switch(e){case`standard`:return{retryMode:`standard`,connectionTimeout:3100};case`in-region`:return{retryMode:`standard`,connectionTimeout:1100};case`cross-region`:return{retryMode:`standard`,connectionTimeout:3100};case`mobile`:return{retryMode:`standard`,connectionTimeout:3e4};default:return{}}},wD=Object.values(Px),Jre=e=>{let t=[];for(let n in Px){let r=Px[n];e[r]!==void 0&&t.push({algorithmId:()=>r,checksumConstructor:()=>e[r]})}for(let[n,r]of Object.entries(e.checksumAlgorithms??{}))t.push({algorithmId:()=>n,checksumConstructor:()=>r});return{addChecksumAlgorithm(n){e.checksumAlgorithms=e.checksumAlgorithms??{};let r=n.algorithmId(),i=n.checksumConstructor();wD.includes(r)?e.checksumAlgorithms[r.toUpperCase()]=i:e.checksumAlgorithms[r]=i,t.push(n)},checksumAlgorithms(){return t}}},Yre=e=>{let t={};return e.checksumAlgorithms().forEach(e=>{let n=e.algorithmId();wD.includes(n)&&(t[n]=e.checksumConstructor())}),t},Xre=e=>({setRetryStrategy(t){e.retryStrategy=t},retryStrategy(){return e.retryStrategy}}),Zre=e=>{let t={};return t.retryStrategy=e.retryStrategy(),t},TD=e=>Object.assign(Jre(e),Xre(e)),ED=e=>Object.assign(Yre(e),Zre(e)),DD=e=>{let t=`#text`;for(let n in e)e.hasOwnProperty(n)&&e[n][t]!==void 0?e[n]=e[n][t]:typeof e[n]==`object`&&e[n]!==null&&(e[n]=DD(e[n]));return e},OD=class{trace(){}debug(){}info(){}warn(){}error(){}},Qre=e=>e?.body instanceof ReadableStream;function $re(e,t){if(Rx.isInstance(e))for(let n of Object.keys(e.headers)){let r=n.toLowerCase();if(r===`retry-after`){let r=e.headers[n],i=NaN;if(r.endsWith(`GMT`))try{i=(Ew(r).getTime()-Date.now())/1e3}catch(e){t?.trace?.(`Failed to parse retry-after header`),t?.trace?.(e)}else r.match(/ GMT, ((\d+)|(\d+\.\d+))$/)?i=Number(r.match(/ GMT, ([\d.]+)$/)?.[1]):r.match(/^((\d+)|(\d+\.\d+))$/)?i=Number(r):Date.parse(r)>=Date.now()&&(i=(Date.parse(r)-Date.now())/1e3);return isNaN(i)?void 0:new Date(Date.now()+i*1e3)}else if(r===`x-amz-retry-after`){let r=e.headers[n],i=Number(r);if(isNaN(i)){t?.trace?.(`Failed to parse x-amz-retry-after=${r}`);return}return new Date(Date.now()+i)}}}var eie=e=>(t,n)=>async r=>{let i=await e.retryStrategy(),a=await e.maxAttempts();if(tie(i)){i=i;let o=await i.acquireInitialRetryToken((n.partition_id??``)+(n.__retryLongPoll?`:longpoll`:``)),s=Error(),c=0,l=0,{request:u}=r,d=Ix.isInstance(u);for(d&&(u.headers[ME]=Bw());;)try{d&&(u.headers[NE]=`attempt=${c+1}; max=${a}`);let{response:e,output:n}=await t(r);return i.recordSuccess(o),n.$metadata.attempts=c+1,n.$metadata.totalRetryDelay=l,{response:e,output:n}}catch(t){let r=nie(t,e.logger);if(s=Kre(t),d&&Qre(u))throw(n.logger instanceof OD?console:n.logger)?.warn(`An error was encountered in a non-retryable streaming request.`),s;try{o=await i.refreshRetryTokenForRetry(o,r)}catch(e){throw typeof e.$backoff==`number`&&await kD(e.$backoff),s.$metadata||={},s.$metadata.attempts=c+1,s.$metadata.totalRetryDelay=l,s}c=o.getRetryCount();let a=o.getRetryDelay();l+=a,await kD(a)}}else return i=i,i?.mode&&(n.userAgent=[...n.userAgent||[],[`cfg/retry-mode`,i.mode]]),i.retry(t,r)},kD=e=>new Promise(t=>setTimeout(t,e)),tie=e=>e.acquireInitialRetryToken!==void 0&&e.refreshRetryTokenForRetry!==void 0&&e.recordSuccess!==void 0,nie=(e,t)=>{let n={error:e,errorType:rie(e)},r=$re(e.$response,t);return r&&(n.retryAfterHint=r),n},rie=e=>EE(e)?`THROTTLING`:DE(e)?`TRANSIENT`:OE(e)?`SERVER_ERROR`:`CLIENT_ERROR`,iie={name:`retryMiddleware`,tags:[`RETRY`],step:`finalizeRequest`,priority:`high`,override:!0},AD=e=>({applyToStack:t=>{t.add(eie(e),iie)}}),jD=e=>Rx.isInstance(e)?e.headers?.date??e.headers?.Date:void 0,MD=e=>new Date(Date.now()+e),aie=(e,t)=>Math.abs(MD(t).getTime()-e)>=3e5,ND=(e,t)=>{let n=Date.parse(e);return aie(n,t)?n-Date.now():t},PD=(e,t)=>{if(!t)throw Error(`Property \`${e}\` is not resolved for AWS SDK SigV4Auth`);return t},FD=async e=>{let t=PD(`context`,e.context),n=PD(`config`,e.config),r=t.endpointV2?.properties?.authSchemes?.[0];return{config:n,signer:await PD(`signer`,n.signer)(r),signingRegion:e?.signingRegion,signingRegionSet:e?.signingRegionSet,signingName:e?.signingName}},ID=class{async sign(e,t,n){if(!Ix.isInstance(e))throw Error("The request is not an instance of `HttpRequest` and cannot be signed");let r=await FD(n),{config:i,signer:a}=r,{signingRegion:o,signingName:s}=r,c=n.context;if(c?.authSchemes?.length??!1){let[e,t]=c.authSchemes;e?.name===`sigv4a`&&t?.name===`sigv4`&&(o=t?.signingRegion??o,s=t?.signingName??s)}return await a.sign(e,{signingDate:MD(i.systemClockOffset),signingRegion:o,signingService:s})}errorHandler(e){return t=>{let n=t.ServerTime??jD(t.$response);if(n){let r=PD(`config`,e.config),i=r.systemClockOffset;r.systemClockOffset=ND(n,r.systemClockOffset),r.systemClockOffset!==i&&t.$metadata&&(t.$metadata.clockSkewCorrected=!0)}throw t}}successHandler(e,t){let n=jD(e);if(n){let e=PD(`config`,t.config);e.systemClockOffset=ND(n,e.systemClockOffset)}}},oie=class extends ID{async sign(e,t,n){if(!Ix.isInstance(e))throw Error("The request is not an instance of `HttpRequest` and cannot be signed");let{config:r,signer:i,signingRegion:a,signingRegionSet:o,signingName:s}=await FD(n),c=(await r.sigv4aSigningRegionSet?.()??o??[a]).join(`,`);return await i.sign(e,{signingDate:MD(r.systemClockOffset),signingRegion:c,signingService:s})}},sie=(e,t,n)=>{let r,i,a,o=!1,s=async()=>{i||=e();try{r=await i,a=!0,o=!1}finally{i=void 0}return r};return t===void 0?async e=>((!a||e?.forceRefresh)&&(r=await s()),r):async e=>((!a||e?.forceRefresh)&&(r=await s()),o?r:n&&!n(r)?(o=!0,r):(t(r)&&await s(),r))},cie=e=>(e.sigv4aSigningRegionSet=mC(e.sigv4aSigningRegionSet),e),LD=e=>{let t=e.credentials,n=!!e.credentials,r;Object.defineProperty(e,`credentials`,{set(i){i&&i!==t&&i!==r&&(n=!0),t=i;let a=uie(e,lie(e,{credentials:t,credentialDefaultProvider:e.credentialDefaultProvider}));if(n&&!a.attributed){let e=typeof t==`object`&&!!t;r=async t=>{let n=await a(t);return e&&(!n.$source||Object.keys(n.$source).length===0)?hE(n,`CREDENTIALS_CODE`,`e`):n},r.memoized=a.memoized,r.configBound=a.configBound,r.attributed=!0}else r=a},get(){return r},enumerable:!0,configurable:!0}),e.credentials=t;let{signingEscapePath:i=!0,systemClockOffset:a=e.systemClockOffset||0,sha256:o}=e,s;return s=e.signer?mC(e.signer):e.regionInfoProvider?()=>mC(e.region)().then(async t=>[await e.regionInfoProvider(t,{useFipsEndpoint:await e.useFipsEndpoint(),useDualstackEndpoint:await e.useDualstackEndpoint()})||{},t]).then(([t,n])=>{let{signingRegion:r,signingService:a}=t;e.signingRegion=e.signingRegion||r||n,e.signingName=e.signingName||a||e.serviceId;let s={...e,credentials:e.credentials,region:e.signingRegion,service:e.signingName,sha256:o,uriEscapePath:i};return new(e.signerConstructor||FS)(s)}):async t=>{t=Object.assign({},{name:`sigv4`,signingName:e.signingName||e.defaultSigningName,signingRegion:await mC(e.region)(),properties:{}},t);let n=t.signingRegion,r=t.signingName;e.signingRegion=e.signingRegion||n,e.signingName=e.signingName||r||e.serviceId;let a={...e,credentials:e.credentials,region:e.signingRegion,service:e.signingName,sha256:o,uriEscapePath:i};return new(e.signerConstructor||FS)(a)},Object.assign(e,{systemClockOffset:a,signingEscapePath:i,signer:s})};function lie(e,{credentials:t,credentialDefaultProvider:n}){let r;return r=t?t?.memoized?t:CT(t,xT,ST):n?mC(n(Object.assign({},e,{parentClientConfig:e}))):async()=>{throw Error("@aws-sdk/core::resolveAwsSdkSigV4Config - `credentials` not provided and no credentialDefaultProvider was configured.")},r.memoized=!0,r}function uie(e,t){if(t.configBound)return t;let n=async n=>t({...n,callerClientConfig:e});return n.memoized=t.memoized,n.configBound=!0,n}var die=async(e,t,n)=>({operation:DS(t).operation,region:await OS(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()});function fie(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`ec2`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var pie=e=>{let t=[];switch(e.operation){default:t.push(fie(e))}return t},mie=e=>{let t=LD(e);return Object.assign(t,{authSchemePreference:OS(e.authSchemePreference??[])})},hie=e=>Object.assign(e,{useDualstackEndpoint:e.useDualstackEndpoint??!1,useFipsEndpoint:e.useFipsEndpoint??!1,defaultSigningName:`ec2`}),RD={UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},gie={name:`@aws-sdk/client-ec2`,description:`AWS SDK for JavaScript Ec2 Client for Node.js, Browser and React Native`,version:`3.1045.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-ec2`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo ec2`,test:`yarn g:vitest run --passWithNoTests`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`,"test:integration":`yarn g:vitest run --passWithNoTests -c vitest.config.integ.mts`,"test:integration:watch":`yarn g:vitest run --passWithNoTests -c vitest.config.integ.mts`,"test:watch":`yarn g:vitest watch --passWithNoTests`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.8`,"@aws-sdk/credential-provider-node":`^3.972.39`,"@aws-sdk/middleware-host-header":`^3.972.10`,"@aws-sdk/middleware-logger":`^3.972.10`,"@aws-sdk/middleware-recursion-detection":`^3.972.11`,"@aws-sdk/middleware-sdk-ec2":`^3.972.22`,"@aws-sdk/middleware-user-agent":`^3.972.38`,"@aws-sdk/region-config-resolver":`^3.972.13`,"@aws-sdk/types":`^3.973.8`,"@aws-sdk/util-endpoints":`^3.996.8`,"@aws-sdk/util-user-agent-browser":`^3.972.10`,"@aws-sdk/util-user-agent-node":`^3.973.24`,"@smithy/config-resolver":`^4.4.17`,"@smithy/core":`^3.23.17`,"@smithy/fetch-http-handler":`^5.3.17`,"@smithy/hash-node":`^4.2.14`,"@smithy/invalid-dependency":`^4.2.14`,"@smithy/middleware-content-length":`^4.2.14`,"@smithy/middleware-endpoint":`^4.4.32`,"@smithy/middleware-retry":`^4.5.7`,"@smithy/middleware-serde":`^4.2.20`,"@smithy/middleware-stack":`^4.2.14`,"@smithy/node-config-provider":`^4.3.14`,"@smithy/node-http-handler":`^4.6.1`,"@smithy/protocol-http":`^5.3.14`,"@smithy/smithy-client":`^4.12.13`,"@smithy/types":`^4.14.1`,"@smithy/url-parser":`^4.2.14`,"@smithy/util-base64":`^4.3.2`,"@smithy/util-body-length-browser":`^4.2.2`,"@smithy/util-body-length-node":`^4.2.3`,"@smithy/util-defaults-mode-browser":`^4.3.49`,"@smithy/util-defaults-mode-node":`^4.2.54`,"@smithy/util-endpoints":`^3.4.2`,"@smithy/util-middleware":`^4.2.14`,"@smithy/util-retry":`^4.3.6`,"@smithy/util-utf8":`^4.2.2`,"@smithy/util-waiter":`^4.3.0`,tslib:`^2.6.2`},devDependencies:{"@smithy/snapshot-testing":`^2.0.8`,"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`,vitest:`^4.0.17`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-ec2`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-ec2`}},zD=({serviceId:e,clientVersion:t})=>async n=>{let r=typeof window<`u`?window.navigator:void 0,i=r?.userAgent??``,a=r?.userAgentData?.platform??BD.os(i)??`other`,o=r?.userAgentData?.brands??[],s=o[o.length-1],c=s?.brand??BD.browser(i)??`unknown`,l=s?.version??`unknown`,u=[[`aws-sdk-js`,t],[`ua`,`2.1`],[`os/${a}`,void 0],[`lang/js`],[`md/browser`,`${c}_${l}`]];e&&u.push([`api/${e}`,t]);let d=await n?.userAgentAppId?.();return d&&u.push([`app/${d}`]),u},BD={os(e){if(/iPhone|iPad|iPod/.test(e))return`iOS`;if(/Macintosh|Mac OS X/.test(e))return`macOS`;if(/Windows NT/.test(e))return`Windows`;if(/Android/.test(e))return`Android`;if(/Linux/.test(e))return`Linux`},browser(e){if(/EdgiOS|EdgA|Edg\//.test(e))return`Microsoft Edge`;if(/Firefox\//.test(e))return`Firefox`;if(/Chrome\//.test(e))return`Chrome`;if(/Safari\//.test(e))return`Safari`}},VD=e=>()=>Promise.reject(e),HD=typeof TextEncoder==`function`?new TextEncoder:null,UD=e=>{if(typeof e==`string`){if(HD)return HD.encode(e).byteLength;let t=e.length;for(let n=t-1;n>=0;n--){let r=e.charCodeAt(n);r>127&&r<=2047?t++:r>2047&&r<=65535&&(t+=2),r>=56320&&r<=57343&&n--}return t}else if(typeof e.byteLength==`number`)return e.byteLength;else if(typeof e.size==`number`)return e.size;throw Error(`Body Length computation failed for ${e}`)},_ie=[`in-region`,`cross-region`,`mobile`,`standard`,`legacy`],WD=({defaultsMode:e}={})=>sie(async()=>{let t=typeof e==`function`?await e():e;switch(t?.toLowerCase()){case`auto`:return Promise.resolve(vie()?`mobile`:`standard`);case`mobile`:case`in-region`:case`cross-region`:case`standard`:case`legacy`:return Promise.resolve(t?.toLocaleLowerCase());case void 0:return Promise.resolve(`legacy`);default:throw Error(`Invalid parameter for "defaultsMode", expect ${_ie.join(`, `)}, got ${t}`)}}),vie=()=>{let e=window?.navigator;if(e?.connection){let{effectiveType:t,rtt:n,downlink:r}=e?.connection;if(typeof t==`string`&&t!==`4g`||Number(n)>100||Number(r)<10)return!0}return e?.userAgentData?.mobile||typeof e?.maxTouchPoints==`number`&&e?.maxTouchPoints>1},GD=class{queryCompat;errorRegistry;constructor(e=!1){this.queryCompat=e}resolveRestContentType(e,t){let n=t.getMemberSchemas(),r=Object.values(n).find(e=>!!e.getMergedTraits().httpPayload);if(r)return r.getMergedTraits().mediaType||(r.isStringSchema()?`text/plain`:r.isBlobSchema()?`application/octet-stream`:e);if(!t.isUnitSchema()&&Object.values(n).find(e=>{let{httpQuery:t,httpQueryParams:n,httpHeader:r,httpLabel:i,httpPrefixHeaders:a}=e.getMergedTraits();return!t&&!n&&!r&&!i&&a===void 0}))return e}async getErrorSchemaOrThrowBaseException(e,t,n,r,i,a){let o=e;e.includes(`#`)&&([,o]=e.split(`#`));let s={$metadata:i,$fault:n.statusCode<500?`client`:`server`};if(!this.errorRegistry)throw Error(`@aws-sdk/core/protocols - error handler not initialized.`);try{return{errorSchema:a?.(this.errorRegistry,o)??this.errorRegistry.getSchema(e),errorMetadata:s}}catch{r.message=r.message??r.Message??`UnknownError`;let e=this.errorRegistry,t=e.getBaseException();if(t){let n=e.getErrorCtor(t)??Error;throw this.decorateServiceException(Object.assign(new n({name:o}),s),r)}let n=r,i=n?.message??n?.Message??n?.Error?.Message??n?.Error?.message;throw this.decorateServiceException(Object.assign(Error(i),{name:o},s),r)}}compose(e,t,n){let r=n;t.includes(`#`)&&([r]=t.split(`#`));let i=iw.for(r),a=iw.for(`smithy.ts.sdk.synthetic.`+n);e.copyFrom(i),e.copyFrom(a),this.errorRegistry=e}decorateServiceException(e,t={}){if(this.queryCompat){let n=e.Message??t.Message,r=SD(e,t);n&&(r.message=n);let i=r.Error??{};i.Type=r.Error?.Type,i.Code=r.Error?.Code,i.Message=r.Error?.message??r.Error?.Message??n,r.Error=i;let a=r.$metadata.requestId;return a&&(r.RequestId=a),r}return SD(e,t)}setQueryCompatError(e,t){let n=t.headers?.[`x-amzn-query-error`];if(e!==void 0&&n!=null){let[t,r]=n.split(`;`),i=Object.keys(e),a={Code:t,Type:r};e.Code=t,e.Type=r;for(let t=0;tew.of(e).getMergedTraits().awsQueryError?.[0]===t)}}},KD=class{serdeContext;setSerdeContext(e){this.serdeContext=e}},yie=class{from;to;keys;constructor(e,t){this.from=e,this.to=t;let n=Object.keys(this.from),r=new Set(n);r.delete(`__type`),this.keys=r}mark(e){this.keys.delete(e)}hasUnknown(){return this.keys.size===1&&Object.keys(this.to).length===0}writeUnknown(){if(this.hasUnknown()){let e=this.keys.values().next().value,t=this.from[e];this.to.$unknown=[e,t]}}},bie=/[&<>"]/g,xie={"&":`&`,"<":`<`,">":`>`,'"':`"`};function Sie(e){return e.replace(bie,e=>xie[e])}var Cie=/[&"'<>\r\n\u0085\u2028]/g,wie={"&":`&`,'"':`"`,"'":`'`,"<":`<`,">":`>`,"\r":` `,"\n":` `,"…":`…`,"\u2028":`
`};function Tie(e){return e.replace(Cie,e=>wie[e])}var qD=class{value;constructor(e){this.value=e}toString(){return Tie(``+this.value)}},JD=class e{name;children;attributes={};static of(t,n,r){let i=new e(t);return n!==void 0&&i.addChildNode(new qD(n)),r!==void 0&&i.withName(r),i}constructor(e,t=[]){this.name=e,this.children=t}withName(e){return this.name=e,this}addAttribute(e,t){return this.attributes[e]=t,this}addChildNode(e){return this.children.push(e),this}removeAttribute(e){return delete this.attributes[e],this}n(e){return this.name=e,this}c(e){return this.children.push(e),this}a(e,t){return t!=null&&(this.attributes[e]=t),this}cc(t,n,r=n){if(t[n]!=null){let i=e.of(n,t[n]).withName(r);this.c(i)}}l(e,t,n,r){e[t]!=null&&r().map(e=>{e.withName(n),this.c(e)})}lc(t,n,r,i){if(t[n]!=null){let t=i(),n=new e(r);t.map(e=>{n.c(e)}),this.c(n)}}toString(){let e=!!this.children.length,t=`<${this.name}`,n=this.attributes;for(let e of Object.keys(n)){let r=n[e];r!=null&&(t+=` ${e}="${Sie(``+r)}"`)}return t+=e?`>${this.children.map(e=>e.toString()).join(``)}`:`/>`}},YD;function Eie(e){YD||=new DOMParser;let t=YD.parseFromString(e,`application/xml`);if(t.getElementsByTagName(`parsererror`).length>0)throw Error(`DOMParser XML parsing error.`);let n=e=>{if(e.nodeType===Node.TEXT_NODE&&e.textContent?.trim())return e.textContent;if(e.nodeType===Node.ELEMENT_NODE){let t=e;if(t.attributes.length===0&&t.childNodes.length===0)return``;let r={},i=Array.from(t.attributes);for(let e of i)r[`${e.name}`]=e.value;let a=Array.from(t.childNodes);for(let e of a){let o=n(e);if(o!=null){let t=e.nodeName;if(a.length===1&&i.length===0&&t===`#text`)return o;r[t]?Array.isArray(r[t])?r[t].push(o):r[t]=[r[t],o]:r[t]=o}else if(a.length===1&&i.length===0)return t.textContent}return r}return null};return{[t.documentElement.nodeName]:n(t.documentElement)}}var XD=class extends KD{settings;stringDeserializer;constructor(e){super(),this.settings=e,this.stringDeserializer=new hT(e)}setSerdeContext(e){this.serdeContext=e,this.stringDeserializer.setSerdeContext(e)}read(e,t,n){let r=ew.of(e),i=r.getMemberSchemas();if(r.isStructSchema()&&r.isMemberSchema()&&Object.values(i).find(e=>!!e.getMemberTraits().eventPayload)){let e={},n=Object.keys(i)[0];return i[n].isBlobSchema()?e[n]=t:e[n]=this.read(i[n],t),e}let a=(this.serdeContext?.utf8Encoder??Gx)(t),o=this.parseXml(a);return this.readSchema(e,n?o[n]:o)}readSchema(e,t){let n=ew.of(e);if(n.isUnitSchema())return;let r=n.getMergedTraits();if(n.isListSchema()&&!Array.isArray(t))return this.readSchema(n,[t]);if(t==null)return t;if(typeof t==`object`){let e=!!r.xmlFlattened;if(n.isListSchema()){let r=n.getValueSchema(),i=[],a=r.getMergedTraits().xmlName??`member`,o=e?t:(t[0]??t)[a];if(o==null)return i;let s=Array.isArray(o)?o:[o];for(let e of s)i.push(this.readSchema(r,e));return i}let i={};if(n.isMapSchema()){let r=n.getKeySchema(),a=n.getValueSchema(),o;o=e?Array.isArray(t)?t:[t]:Array.isArray(t.entry)?t.entry:[t.entry];let s=r.getMergedTraits().xmlName??`key`,c=a.getMergedTraits().xmlName??`value`;for(let e of o){let t=e[s],n=e[c];i[t]=this.readSchema(a,n)}return i}if(n.isStructSchema()){let e=n.isUnionSchema(),r;e&&(r=new yie(t,i));for(let[a,o]of n.structIterator()){let n=o.getMergedTraits(),s=n.httpPayload?n.xmlName??o.getName():o.getMemberTraits().xmlName??a;e&&r.mark(s),t[s]!=null&&(i[a]=this.readSchema(o,t[s]))}return e&&r.writeUnknown(),i}if(n.isDocumentSchema())return t;throw Error(`@aws-sdk/core/protocols - xml deserializer unhandled schema type for ${n.getName(!0)}`)}return n.isListSchema()?[]:n.isMapSchema()||n.isStructSchema()?{}:this.stringDeserializer.read(n,t)}parseXml(e){if(e.length){let t;try{t=Eie(e)}catch(t){throw t&&typeof t==`object`&&Object.defineProperty(t,`$responseBodyText`,{value:e}),t}let n=`#text`,r=Object.keys(t)[0],i=t[r];return i[n]&&(i[r]=i[n],delete i[n]),DD(i)}return{}}},Die=class extends KD{settings;buffer;constructor(e){super(),this.settings=e}write(e,t,n=``){this.buffer===void 0&&(this.buffer=``);let r=ew.of(e);if(n&&!n.endsWith(`.`)&&(n+=`.`),r.isBlobSchema())(typeof t==`string`||t instanceof Uint8Array)&&(this.writeKey(n),this.writeValue((this.serdeContext?.base64Encoder??yC)(t)));else if(r.isBooleanSchema()||r.isNumericSchema()||r.isStringSchema())t==null?r.isIdempotencyToken()&&(this.writeKey(n),this.writeValue(Bw())):(this.writeKey(n),this.writeValue(String(t)));else if(r.isBigIntegerSchema())t!=null&&(this.writeKey(n),this.writeValue(String(t)));else if(r.isBigDecimalSchema())t!=null&&(this.writeKey(n),this.writeValue(t instanceof oT?t.string:String(t)));else if(r.isTimestampSchema()){if(t instanceof Date)switch(this.writeKey(n),mT(r,this.settings)){case 5:this.writeValue(t.toISOString().replace(`.000Z`,`Z`));break;case 6:this.writeValue(Sw(t));break;case 7:this.writeValue(String(t.getTime()/1e3));break}}else if(r.isDocumentSchema())Array.isArray(t)?this.write(79,t,n):t instanceof Date?this.write(4,t,n):t instanceof Uint8Array?this.write(21,t,n):t&&typeof t==`object`?this.write(143,t,n):(this.writeKey(n),this.writeValue(String(t)));else if(r.isListSchema()){if(Array.isArray(t))if(t.length===0)this.settings.serializeEmptyLists&&(this.writeKey(n),this.writeValue(``));else{let e=r.getValueSchema(),i=this.settings.flattenLists||r.getMergedTraits().xmlFlattened,a=1;for(let r of t){if(r==null)continue;let t=e.getMergedTraits(),o=this.getKey(`member`,t.xmlName,t.ec2QueryName),s=i?`${n}${a}`:`${n}${o}.${a}`;this.write(e,r,s),++a}}}else if(r.isMapSchema()){if(t&&typeof t==`object`){let e=r.getKeySchema(),i=r.getValueSchema(),a=r.getMergedTraits().xmlFlattened,o=1;for(let r in t){let s=t[r];if(s==null)continue;let c=e.getMergedTraits(),l=this.getKey(`key`,c.xmlName,c.ec2QueryName),u=a?`${n}${o}.${l}`:`${n}entry.${o}.${l}`,d=i.getMergedTraits(),f=this.getKey(`value`,d.xmlName,d.ec2QueryName),p=a?`${n}${o}.${f}`:`${n}entry.${o}.${f}`;this.write(e,r,u),this.write(i,s,p),++o}}}else if(r.isStructSchema()){if(t&&typeof t==`object`){let e=!1;for(let[i,a]of r.structIterator()){if(t[i]==null&&!a.isIdempotencyToken())continue;let r=a.getMergedTraits(),o=this.getKey(i,r.xmlName,r.ec2QueryName,`struct`),s=`${n}${o}`;this.write(a,t[i],s),e=!0}if(!e&&r.isUnionSchema()){let{$unknown:e}=t;if(Array.isArray(e)){let[t,r]=e,i=`${n}${t}`;this.write(15,r,i)}}}}else if(!r.isUnitSchema())throw Error(`@aws-sdk/core/protocols - QuerySerializer unrecognized schema type ${r.getName(!0)}`)}flush(){if(this.buffer===void 0)throw Error(`@aws-sdk/core/protocols - QuerySerializer cannot flush with nothing written to buffer.`);let e=this.buffer;return delete this.buffer,e}getKey(e,t,n,r){let{ec2:i,capitalizeKeys:a}=this.settings;if(i&&n)return n;let o=t??e;return a&&r===`struct`?o[0].toUpperCase()+o.slice(1):o}writeKey(e){e.endsWith(`.`)&&(e=e.slice(0,e.length-1)),this.buffer+=`&${LC(e)}=`}writeValue(e){this.buffer+=LC(e)}},ZD=class extends pT{options;serializer;deserializer;mixin=new GD;constructor(e){super({defaultNamespace:e.defaultNamespace,errorTypeRegistries:e.errorTypeRegistries}),this.options=e;let t={timestampFormat:{useTrait:!0,default:5},httpBindings:!1,xmlNamespace:e.xmlNamespace,serviceNamespace:e.defaultNamespace,serializeEmptyLists:!0};this.serializer=new Die(t),this.deserializer=new XD(t)}getShapeId(){return`aws.protocols#awsQuery`}setSerdeContext(e){this.serializer.setSerdeContext(e),this.deserializer.setSerdeContext(e)}getPayloadCodec(){throw Error(`AWSQuery protocol has no payload codec.`)}async serializeRequest(e,t,n){let r=await super.serializeRequest(e,t,n);return r.path.endsWith(`/`)||(r.path+=`/`),r.headers[`content-type`]=`application/x-www-form-urlencoded`,(RC(e.input)===`unit`||!r.body)&&(r.body=``),r.body=`Action=${e.name.split(`#`)[1]??e.name}&Version=${this.options.version}`+r.body,r.body.endsWith(`&`)&&(r.body=r.body.slice(-1)),r}async deserializeResponse(e,t,n){let r=this.deserializer,i=ew.of(e.output),a={};if(n.statusCode>=300){let i=await IC(n.body,t);i.byteLength>0&&Object.assign(a,await r.read(15,i)),await this.handleError(e,t,n,a,this.deserializeMetadata(n))}for(let e in n.headers){let t=n.headers[e];delete n.headers[e],n.headers[e.toLowerCase()]=t}let o=e.name.split(`#`)[1]??e.name,s=i.isStructSchema()&&this.useNestedResult()?o+`Result`:void 0,c=await IC(n.body,t);return c.byteLength>0&&Object.assign(a,await r.read(i,c,s)),a.$metadata=this.deserializeMetadata(n),a}useNestedResult(){return!0}async handleError(e,t,n,r,i){let a=this.loadQueryErrorCode(n,r)??`Unknown`;this.mixin.compose(this.compositeErrorRegistry,a,this.options.defaultNamespace);let o=this.loadQueryError(r)??{},s=this.loadQueryErrorMessage(r);o.message=s,o.Error={Type:o.Type,Code:o.Code,Message:s};let{errorSchema:c,errorMetadata:l}=await this.mixin.getErrorSchemaOrThrowBaseException(a,this.options.defaultNamespace,n,o,i,this.mixin.findQueryCompatibleError),u=ew.of(c),d=new((this.compositeErrorRegistry.getErrorCtor(c))??Error)(s),f={Type:o.Error.Type,Code:o.Error.Code,Error:o.Error};for(let[e,t]of u.structIterator()){let n=t.getMergedTraits().xmlName??e,i=o[n]??r[n];f[e]=this.deserializer.readSchema(t,i)}throw this.mixin.decorateServiceException(Object.assign(d,l,{$fault:u.getMergedTraits().error,message:s},f),r)}loadQueryErrorCode(e,t){let n=(t.Errors?.[0]?.Error??t.Errors?.Error??t.Error)?.Code;if(n!==void 0)return n;if(e.statusCode==404)return`NotFound`}loadQueryError(e){return e.Errors?.[0]?.Error??e.Errors?.Error??e.Error}loadQueryErrorMessage(e){let t=this.loadQueryError(e);return t?.message??t?.Message??e.message??e.Message??`Unknown`}getDefaultContentType(){return`application/x-www-form-urlencoded`}},Oie=class extends ZD{options;constructor(e){super(e),this.options=e,Object.assign(this.serializer.settings,{capitalizeKeys:!0,flattenLists:!0,serializeEmptyLists:!1,ec2:!0})}getShapeId(){return`aws.protocols#ec2Query`}useNestedResult(){return!1}},kie=(e,t)=>{if(t?.Error?.Code!==void 0)return t.Error.Code;if(t?.Code!==void 0)return t.Code;if(e.statusCode==404)return`NotFound`},Aie=class extends KD{settings;stringBuffer;byteBuffer;buffer;constructor(e){super(),this.settings=e}write(e,t){let n=ew.of(e);if(n.isStringSchema()&&typeof t==`string`)this.stringBuffer=t;else if(n.isBlobSchema())this.byteBuffer=`byteLength`in t?t:(this.serdeContext?.base64Decoder??vC)(t);else{this.buffer=this.writeStruct(n,t,void 0);let e=n.getMergedTraits();e.httpPayload&&!e.xmlName&&this.buffer.withName(n.getName())}}flush(){if(this.byteBuffer!==void 0){let e=this.byteBuffer;return delete this.byteBuffer,e}if(this.stringBuffer!==void 0){let e=this.stringBuffer;return delete this.stringBuffer,e}let e=this.buffer;return this.settings.xmlNamespace&&(e?.attributes?.xmlns||e.addAttribute(`xmlns`,this.settings.xmlNamespace)),delete this.buffer,e.toString()}writeStruct(e,t,n){let r=e.getMergedTraits(),i=e.isMemberSchema()&&!r.httpPayload?e.getMemberTraits().xmlName??e.getMemberName():r.xmlName??e.getName();if(!i||!e.isStructSchema())throw Error(`@aws-sdk/core/protocols - xml serializer, cannot write struct with empty name or non-struct, schema=${e.getName(!0)}.`);let a=JD.of(i),[o,s]=this.getXmlnsAttribute(e,n);for(let[n,r]of e.structIterator()){let e=t[n];if(e!=null||r.isIdempotencyToken()){if(r.getMergedTraits().xmlAttribute){a.addAttribute(r.getMergedTraits().xmlName??n,this.writeSimple(r,e));continue}if(r.isListSchema())this.writeList(r,e,a,s);else if(r.isMapSchema())this.writeMap(r,e,a,s);else if(r.isStructSchema())a.addChildNode(this.writeStruct(r,e,s));else{let t=JD.of(r.getMergedTraits().xmlName??r.getMemberName());this.writeSimpleInto(r,e,t,s),a.addChildNode(t)}}}let{$unknown:c}=t;if(c&&e.isUnionSchema()&&Array.isArray(c)&&Object.keys(t).length===1){let[e,n]=c,r=JD.of(e);if(typeof n!=`string`)if(t instanceof JD||t instanceof qD)a.addChildNode(t);else throw Error(`@aws-sdk - $unknown union member in XML requires value of type string, @aws-sdk/xml-builder::XmlNode or XmlText.`);this.writeSimpleInto(0,n,r,s),a.addChildNode(r)}return s&&a.addAttribute(o,s),a}writeList(e,t,n,r){if(!e.isMemberSchema())throw Error(`@aws-sdk/core/protocols - xml serializer, cannot write non-member list: ${e.getName(!0)}`);let i=e.getMergedTraits(),a=e.getValueSchema(),o=a.getMergedTraits(),s=!!o.sparse,c=!!i.xmlFlattened,[l,u]=this.getXmlnsAttribute(e,r),d=(t,n)=>{if(a.isListSchema())this.writeList(a,Array.isArray(n)?n:[n],t,u);else if(a.isMapSchema())this.writeMap(a,n,t,u);else if(a.isStructSchema()){let r=this.writeStruct(a,n,u);t.addChildNode(r.withName(c?i.xmlName??e.getMemberName():o.xmlName??`member`))}else{let r=JD.of(c?i.xmlName??e.getMemberName():o.xmlName??`member`);this.writeSimpleInto(a,n,r,u),t.addChildNode(r)}};if(c)for(let e of t)(s||e!=null)&&d(n,e);else{let r=JD.of(i.xmlName??e.getMemberName());u&&r.addAttribute(l,u);for(let e of t)(s||e!=null)&&d(r,e);n.addChildNode(r)}}writeMap(e,t,n,r,i=!1){if(!e.isMemberSchema())throw Error(`@aws-sdk/core/protocols - xml serializer, cannot write non-member map: ${e.getName(!0)}`);let a=e.getMergedTraits(),o=e.getKeySchema(),s=o.getMergedTraits().xmlName??`key`,c=e.getValueSchema(),l=c.getMergedTraits(),u=l.xmlName??`value`,d=!!l.sparse,f=!!a.xmlFlattened,[p,m]=this.getXmlnsAttribute(e,r),h=(e,t,n)=>{let r=JD.of(s,t),[i,a]=this.getXmlnsAttribute(o,m);a&&r.addAttribute(i,a),e.addChildNode(r);let l=JD.of(u);c.isListSchema()?this.writeList(c,n,l,m):c.isMapSchema()?this.writeMap(c,n,l,m,!0):c.isStructSchema()?l=this.writeStruct(c,n,m):this.writeSimpleInto(c,n,l,m),e.addChildNode(l)};if(f)for(let r in t){let i=t[r];if(d||i!=null){let t=JD.of(a.xmlName??e.getMemberName());h(t,r,i),n.addChildNode(t)}}else{let r;i||(r=JD.of(a.xmlName??e.getMemberName()),m&&r.addAttribute(p,m),n.addChildNode(r));for(let e in t){let a=t[e];if(d||a!=null){let t=JD.of(`entry`);h(t,e,a),(i?n:r).addChildNode(t)}}}}writeSimple(e,t){if(t===null)throw Error(`@aws-sdk/core/protocols - (XML serializer) cannot write null value.`);let n=ew.of(e),r=null;if(t&&typeof t==`object`)if(n.isBlobSchema())r=(this.serdeContext?.base64Encoder??yC)(t);else if(n.isTimestampSchema()&&t instanceof Date)switch(mT(n,this.settings)){case 5:r=t.toISOString().replace(`.000Z`,`Z`);break;case 6:r=Sw(t);break;case 7:r=String(t.getTime()/1e3);break;default:console.warn(`Missing timestamp format, using http date`,t),r=Sw(t);break}else if(n.isBigDecimalSchema()&&t)return t instanceof oT?t.string:String(t);else if(n.isMapSchema()||n.isListSchema())throw Error(`@aws-sdk/core/protocols - xml serializer, cannot call _write() on List/Map schema, call writeList or writeMap() instead.`);else throw Error(`@aws-sdk/core/protocols - xml serializer, unhandled schema type for object value and schema: ${n.getName(!0)}`);if((n.isBooleanSchema()||n.isNumericSchema()||n.isBigIntegerSchema()||n.isBigDecimalSchema())&&(r=String(t)),n.isStringSchema()&&(r=t===void 0&&n.isIdempotencyToken()?Bw():String(t)),r===null)throw Error(`Unhandled schema-value pair ${n.getName(!0)}=${t}`);return r}writeSimpleInto(e,t,n,r){let i=this.writeSimple(e,t),a=ew.of(e),o=new qD(i),[s,c]=this.getXmlnsAttribute(a,r);c&&n.addAttribute(s,c),n.addChildNode(o)}getXmlnsAttribute(e,t){let[n,r]=e.getMergedTraits().xmlNamespace??[];return r&&r!==t?[n?`xmlns:${n}`:`xmlns`,r]:[void 0,void 0]}},jie=class extends KD{settings;constructor(e){super(),this.settings=e}createSerializer(){let e=new Aie(this.settings);return e.setSerdeContext(this.serdeContext),e}createDeserializer(){let e=new XD(this.settings);return e.setSerdeContext(this.serdeContext),e}},Mie=class extends fT{codec;serializer;deserializer;mixin=new GD;constructor(e){super(e);let t={timestampFormat:{useTrait:!0,default:5},httpBindings:!0,xmlNamespace:e.xmlNamespace,serviceNamespace:e.defaultNamespace};this.codec=new jie(t),this.serializer=new vT(this.codec.createSerializer(),t),this.deserializer=new gT(this.codec.createDeserializer(),t)}getPayloadCodec(){return this.codec}getShapeId(){return`aws.protocols#restXml`}async serializeRequest(e,t,n){let r=await super.serializeRequest(e,t,n),i=ew.of(e.input);if(!r.headers[`content-type`]){let e=this.mixin.resolveRestContentType(this.getDefaultContentType(),i);e&&(r.headers[`content-type`]=e)}return typeof r.body==`string`&&r.headers[`content-type`]===this.getDefaultContentType()&&!r.body.startsWith(``+r.body),r}async deserializeResponse(e,t,n){return super.deserializeResponse(e,t,n)}async handleError(e,t,n,r,i){let a=kie(n,r)??`Unknown`;if(this.mixin.compose(this.compositeErrorRegistry,a,this.options.defaultNamespace),r.Error&&typeof r.Error==`object`)for(let e of Object.keys(r.Error))r[e]=r.Error[e],e.toLowerCase()===`message`&&(r.message=r.Error[e]);r.RequestId&&!i.requestId&&(i.requestId=r.RequestId);let{errorSchema:o,errorMetadata:s}=await this.mixin.getErrorSchemaOrThrowBaseException(a,this.options.defaultNamespace,n,r,i),c=ew.of(o),l=r.Error?.message??r.Error?.Message??r.message??r.Message??`UnknownError`,u=new((this.compositeErrorRegistry.getErrorCtor(o))??Error)(l);await this.deserializeHttpMessage(o,t,n,r);let d={},f=this.codec.createDeserializer();for(let[e,t]of c.structIterator()){let n=t.getMergedTraits().xmlName??e,i=r.Error?.[n]??r[n];d[e]=f.readSchema(t,i)}throw this.mixin.decorateServiceException(Object.assign(u,s,{$fault:c.getMergedTraits().error,message:l},d),r)}getDefaultContentType(){return`application/xml`}hasUnstructuredPayloadBinding(e){for(let[,t]of e.structIterator())if(t.getMergedTraits().httpPayload)return!(t.isStructSchema()||t.isMapSchema()||t.isListSchema());return!1}},QD=`ref`,$D=-1,eO=!0,tO=`isSet`,nO=`PartitionResult`,rO=`booleanEquals`,iO=`getAttr`,aO={[QD]:`Endpoint`},oO={[QD]:nO},sO={},cO=[{[QD]:`Region`}],lO={conditions:[[tO,[aO]],[tO,cO],[`aws.partition`,cO,nO],[rO,[{[QD]:`UseFIPS`},eO]],[rO,[{[QD]:`UseDualStack`},eO]],[rO,[{fn:iO,argv:[oO,`supportsDualStack`]},eO]],[rO,[{fn:iO,argv:[oO,`supportsFIPS`]},eO]],[`stringEquals`,[{fn:iO,argv:[oO,`name`]},`aws-us-gov`]]],results:[[$D],[$D,`Invalid Configuration: FIPS and custom endpoint are not supported`],[$D,`Invalid Configuration: Dualstack and custom endpoint are not supported`],[aO,sO],[`https://ec2-fips.{Region}.{PartitionResult#dualStackDnsSuffix}`,sO],[$D,`FIPS and DualStack are enabled, but this partition does not support one or both`],[`https://ec2.{Region}.amazonaws.com`,sO],[`https://ec2-fips.{Region}.{PartitionResult#dnsSuffix}`,sO],[$D,`FIPS is enabled but this partition does not support FIPS`],[`https://ec2.{Region}.{PartitionResult#dualStackDnsSuffix}`,sO],[$D,`DualStack is enabled but this partition does not support DualStack`],[`https://ec2.{Region}.{PartitionResult#dnsSuffix}`,sO],[$D,`Invalid Configuration: Missing Region`]]},Nie=2,uO=1e8,Pie=new Int32Array([-1,1,-1,0,13,3,1,4,uO+12,2,5,uO+12,3,8,6,4,7,uO+11,5,uO+9,uO+10,4,11,9,6,10,uO+8,7,uO+6,uO+7,5,12,uO+5,6,uO+4,uO+5,3,uO+1,14,4,uO+2,uO+3]),Fie=ET.from(Pie,Nie,lO.conditions,lO.results),Iie=new DT({size:50,params:[`Endpoint`,`Region`,`UseDualStack`,`UseFIPS`]}),Lie=(e,t={})=>Iie.get(e,()=>aE(Fie,{endpointParams:e,logger:t.logger}));jT.aws=mE;var Rie=class e extends xD{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},dO=`Address`,zie=`AllocateAddressRequest`,Bie=`AllocateAddressResult`,Vie=`AssociateAddressRequest`,Hie=`AssociateAddressResult`,Uie=`AllAvailabilityZones`,Wie=`AllocateAddress`,Gie=`ApplianceArn`,Kie=`AssociateAddress`,qie=`AttachedAppliances`,Jie=`AttributeBooleanValue`,Yie=`AssociateCarrierIpAddress`,Xie=`AdditionalFlexibleNetworkInterfaces`,Zie=`AvailableIpAddressCount`,fO=`AssignIpv6AddressOnCreation`,Qie=`AttachInternetGateway`,$ie=`AttachInternetGatewayRequest`,eae=`AllocationIdList`,pO=`AdditionalInfo`,mO=`AllocationId`,hO=`AllocationIds`,gO=`AssociationId`,_O=`AttachmentId`,tae=`AmiLaunchIndex`,nae=`AttachmentLimitType`,rae=`AddressList`,vO=`AvailabilityMode`,iae=`AssociatePublicIpAddress`,aae=`AmazonProvidedIpv6CidrBlock`,oae=`AutoProvisionZones`,sae=`AutoRecoverySupported`,cae=`AssociateRouteTableRequest`,lae=`AssociateRouteTableResult`,uae=`AssociateRouteTable`,dae=`AllowReassociation`,fae=`AllRegions`,yO=`AssociatedResource`,bO=`AutoRecovery`,xO=`AssociationState`,pae=`AuthorizeSecurityGroupEgress`,mae=`AuthorizeSecurityGroupEgressRequest`,hae=`AuthorizeSecurityGroupEgressResult`,gae=`AuthorizeSecurityGroupIngressRequest`,_ae=`AuthorizeSecurityGroupIngressResult`,vae=`AuthorizeSecurityGroupIngress`,yae=`AutoScalingIps`,SO=`AmdSevSnp`,bae=`AttachmentState`,xae=`ArchitectureTypeList`,CO=`AttachTime`,Sae=`AttachVolumeRequest`,Cae=`AttributeValue`,wae=`AttachVolume`,wO=`AvailabilityZone`,TO=`AvailabilityZoneAddress`,EO=`AvailabilityZoneAddresses`,Tae=`AvailabilityZoneGeography`,Eae=`AvailabilityZoneGeographyList`,DO=`AvailabilityZoneId`,Dae=`AvailabilityZoneList`,Oae=`AvailabilityZoneMessage`,kae=`AvailabilityZoneMessageList`,Aae=`AvailabilityZoneSubGeography`,jae=`AvailabilityZoneSubGeographyList`,Mae=`AvailabilityZones`,OO=`Accelerators`,Nae=`Addresses`,Pae=`Affinity`,kO=`Arn`,AO=`Architecture`,Fae=`Associations`,jO=`Association`,Iae=`Attribute`,MO=`Attachment`,NO=`Attachments`,Lae=`BlobAttributeValue`,Rae=`BaselineBandwidthInGbps`,PO=`BaselineBandwidthInMbps`,FO=`BlockDeviceMapping`,zae=`BlockDeviceMappingList`,Bae=`BlockDeviceMappingRequestList`,IO=`BlockDeviceMappings`,Vae=`BlockDurationMinutes`,LO=`BaselineIops`,RO=`BootMode`,Hae=`BootModeTypeList`,Uae=`BareMetal`,zO=`BlockPublicAccessStates`,Wae=`BurstablePerformanceSupported`,BO=`BaselineThroughputInMBps`,VO=`BandwidthWeighting`,Gae=`BandwidthWeightingTypeList`,Kae=`BandwidthWeightings`,HO=`CidrBlock`,qae=`CidrBlockAssociationSet`,Jae=`CapacityBlockId`,Yae=`CidrBlockState`,Xae=`CoreCountList`,UO=`CoreCount`,Zae=`CpuCredits`,WO=`CompletionDurationMinutes`,Qae=`CreationDate`,GO=`CarrierGatewayId`,$ae=`CurrentGeneration`,KO=`CarrierIp`,eoe=`CurrentInstanceBootMode`,toe=`CreateInternetGateway`,noe=`CreateInternetGatewayRequest`,roe=`CreateInternetGatewayResult`,ioe=`CreateImageRequest`,aoe=`CreateImageResult`,qO=`CidrIp`,JO=`CidrIpv6`,YO=`CidrIpv4`,ooe=`CoreInfo`,soe=`CreateImage`,coe=`CreateKeyPair`,loe=`CreateKeyPairRequest`,XO=`CoreNetworkArn`,uoe=`CreateNatGateway`,doe=`CreateNatGatewayRequest`,foe=`CreateNatGatewayResult`,ZO=`CpuOptions`,QO=`CustomerOwnedIp`,$O=`CustomerOwnedIpv4Pool`,poe=`CpuOptionsRequest`,moe=`CreatePlacementGroup`,hoe=`CreatePlacementGroupRequest`,goe=`CreatePlacementGroupResult`,ek=`CapacityReservationId`,tk=`CapacityReservationPreference`,_oe=`CreateRouteRequest`,nk=`CapacityReservationResourceGroupArn`,voe=`CreateRouteResult`,yoe=`CapacityReservationSpecificationResponse`,rk=`CapacityReservationSpecification`,ik=`CapacityReservationTarget`,boe=`CapacityReservationTargetResponse`,xoe=`CreateRouteTableRequest`,Soe=`CreateRouteTableResult`,Coe=`CreateRouteTable`,woe=`CreateRoute`,Toe=`CreateSecurityGroup`,Eoe=`CreateSecurityGroupRequest`,Doe=`CreateSecurityGroupResult`,Ooe=`CopySnapshotRequest`,koe=`CopySnapshotRequestPSU`,Aoe=`CopySnapshotResult`,joe=`CreateSnapshotRequest`,Moe=`CreateSubnetRequest`,Noe=`CreateSubnetResult`,Poe=`CreditSpecificationRequest`,Foe=`CopySnapshot`,Ioe=`CreditSpecification`,Loe=`CreateSnapshot`,Roe=`CreateSubnet`,zoe=`CurrentState`,ak=`ClientToken`,ok=`ConnectionTrackingConfiguration`,Boe=`ConnectionTrackingSpecification`,Voe=`ConnectionTrackingSpecificationRequest`,Hoe=`ConnectionTrackingSpecificationResponse`,Uoe=`CompletionTime`,sk=`ConnectivityType`,ck=`CreateTime`,Woe=`CreateVolumeRequest`,Goe=`CreateVpcRequest`,Koe=`CreateVpcResult`,qoe=`CreateVolume`,Joe=`CreateVpc`,lk=`Code`,uk=`Configured`,dk=`Count`,Yoe=`DescribeAddressesRequest`,Xoe=`DescribeAddressesResult`,Zoe=`DisassociateAddressRequest`,fk=`DisableApiStop`,pk=`DisableApiTermination`,Qoe=`DestinationAvailabilityZone`,$oe=`DescribeAvailabilityZonesRequest`,ese=`DescribeAvailabilityZonesResult`,tse=`DescribeAvailabilityZones`,nse=`DescribeAddresses`,rse=`DisassociateAddress`,mk=`DestinationCidrBlock`,ise=`DefaultConnectionTrackingConfiguration`,ase=`DefaultCores`,ose=`DataEncryptionKeyId`,sse=`DefaultEnaQueueCountPerInterface`,cse=`DefaultForAz`,lse=`DedicatedHostsSupported`,hk=`DeviceIndex`,gk=`DestinationIpv6CidrBlock`,use=`DeleteInternetGateway`,dse=`DeleteInternetGatewayRequest`,fse=`DescribeInternetGatewaysRequest`,pse=`DescribeInternetGatewaysResult`,mse=`DetachInternetGatewayRequest`,hse=`DescribeInternetGateways`,gse=`DetachInternetGateway`,_se=`DiskInfoList`,vse=`DescribeImagesRequest`,yse=`DescribeImagesResult`,bse=`DescribeInstancesRequest`,xse=`DescribeInstancesResult`,Sse=`DescribeInstanceTypesRequest`,Cse=`DescribeInstanceTypesResult`,wse=`DescribeInstanceTypes`,Tse=`DescribeImages`,Ese=`DescribeInstances`,Dse=`DiskInfo`,Ose=`DeleteKeyPair`,kse=`DeleteKeyPairRequest`,Ase=`DeleteKeyPairResult`,jse=`DescribeKeyPairsRequest`,Mse=`DescribeKeyPairsResult`,Nse=`DescribeKeyPairs`,Pse=`DisableLniAtDeviceIndex`,_k=`DeviceName`,Fse=`DefaultNetworkCardIndex`,Ise=`DeleteNatGateway`,Lse=`DeleteNatGatewayRequest`,Rse=`DeleteNatGatewayResult`,zse=`DescribeNatGatewaysRequest`,Bse=`DescribeNatGatewaysResult`,Vse=`DescribeNatGateways`,Hse=`DestinationOutpostArn`,Use=`DhcpOptionsId`,vk=`DeleteOnTermination`,Wse=`DeletePlacementGroup`,Gse=`DeletePlacementGroupRequest`,Kse=`DescribePlacementGroupsRequest`,qse=`DescribePlacementGroupsResult`,Jse=`DescribePlacementGroups`,yk=`DestinationPrefixListId`,Yse=`DeregistrationProtection`,U=`DryRun`,Xse=`DeleteRouteRequest`,Zse=`DescribeRegionsRequest`,Qse=`DescribeRegionsResult`,$se=`DeleteRouteTable`,ece=`DeleteRouteTableRequest`,tce=`DescribeRouteTablesRequest`,nce=`DescribeRouteTablesResult`,rce=`DisassociateRouteTableRequest`,ice=`DescribeRouteTables`,ace=`DisassociateRouteTable`,oce=`DestinationRegion`,sce=`DeleteRoute`,cce=`DescribeRegions`,lce=`DeleteSecurityGroup`,uce=`DeleteSecurityGroupRequest`,dce=`DeleteSecurityGroupResult`,fce=`DescribeSecurityGroupsRequest`,pce=`DescribeSecurityGroupsResult`,mce=`DescribeSecurityGroups`,hce=`DeleteSnapshotRequest`,gce=`DeleteSubnetRequest`,_ce=`DescribeSnapshotsRequest`,vce=`DescribeSnapshotsResult`,yce=`DescribeSubnetsRequest`,bce=`DescribeSubnetsResult`,xce=`DeleteSnapshot`,Sce=`DeleteSubnet`,Cce=`DescribeSnapshots`,wce=`DescribeSubnets`,Tce=`DefaultTcpEstablishedTimeout`,Ece=`DefaultThreadsPerCore`,Dce=`DeprecationTime`,Oce=`DeleteTime`,kce=`DefaultUdpStreamTimeout`,Ace=`DefaultUdpTimeout`,jce=`DefaultVCpus`,Mce=`DeleteVolumeRequest`,Nce=`DeleteVpcRequest`,Pce=`DescribeVolumesRequest`,Fce=`DescribeVolumesResult`,Ice=`DescribeVpcsRequest`,Lce=`DescribeVpcsResult`,Rce=`DetachVolumeRequest`,zce=`DeleteVolume`,Bce=`DeleteVpc`,Vce=`DescribeVolumes`,Hce=`DescribeVpcs`,Uce=`DetachVolume`,bk=`Description`,xk=`Device`,Wce=`Disks`,Sk=`Domain`,Gce=`EbsBlockDevice`,Kce=`ExecutableByStringList`,Ck=`ExecutableBy`,wk=`EbsCardIndex`,qce=`EbsCardInfoList`,Jce=`EbsCardInfo`,Yce=`EbsCards`,Xce=`EncryptionControl`,Tk=`EnableDns64`,Zce=`ElasticFileSystem`,Qce=`ElasticFileSystemExclusion`,$ce=`ElasticGpuAssociation`,ele=`ElasticGpuAssociationId`,tle=`ElasticGpuAssociationList`,nle=`ElasticGpuAssociationState`,rle=`ElasticGpuAssociationTime`,ile=`ElasticGpuAssociations`,ale=`ElasticGpuId`,Ek=`ElasticGpuSpecification`,ole=`ElasticGpuSpecifications`,Dk=`ElasticInferenceAccelerator`,sle=`ElasticInferenceAcceleratorAssociation`,cle=`ElasticInferenceAcceleratorAssociationId`,lle=`ElasticInferenceAcceleratorAssociationList`,ule=`ElasticInferenceAcceleratorAssociationState`,dle=`ElasticInferenceAcceleratorAssociationTime`,fle=`ElasticInferenceAcceleratorArn`,ple=`ElasticInferenceAcceleratorAssociations`,Ok=`ElasticInferenceAccelerators`,mle=`EbsInstanceBlockDevice`,hle=`EbsInstanceBlockDeviceSpecification`,gle=`EncryptionInTransitSupported`,kk=`EbsInfo`,Ak=`EfaInfo`,jk=`EnableLniAtDeviceIndex`,Mk=`EbsOptimized`,Nk=`EbsOptimizedInfo`,_le=`EgressOnlyInternetGateway`,vle=`EgressOnlyInternetGatewayExclusion`,Pk=`EgressOnlyInternetGatewayId`,yle=`EnclaveOptionsRequest`,ble=`EbsOptimizedSupport`,Fk=`EnclaveOptions`,xle=`EnablePrimaryIpv6`,Ik=`EnaQueueCount`,Lk=`EnableResourceNameDnsAAAARecord`,Sle=`EnableResourceNameDnsAAAARecordOnLaunch`,Rk=`EnableResourceNameDnsARecord`,Cle=`EnableResourceNameDnsARecordOnLaunch`,zk=`EnaSrdEnabled`,Bk=`EnaSrdSpecification`,wle=`EnaSrdSpecificationRequest`,Tle=`EnaSrdSupported`,Vk=`EnaSrdUdpEnabled`,Hk=`EnaSrdUdpSpecification`,Ele=`EnaSrdUdpSpecificationRequest`,Dle=`EfaSupported`,Uk=`EncryptionSupport`,Wk=`EnaSupport`,Ole=`EndTime`,kle=`ExecutableUsers`,Gk=`Ebs`,Kk=`Enabled`,qk=`Encrypted`,Ale=`Endpoint`,Jk=`Force`,Yk=`FailureCode`,jle=`FpgaDeviceInfo`,Mle=`FpgaDeviceInfoList`,Nle=`FpgaDeviceMemoryInfo`,Ple=`FlexibleEnaQueuesSupport`,Xk=`FpgaInfo`,Fle=`FilterList`,Zk=`FailureMessage`,Qk=`FromPort`,Ile=`FastRestored`,Lle=`FullSnapshotSizeInBytes`,$k=`FreeTierEligible`,eA=`Filters`,tA=`Filter`,Rle=`Fpgas`,nA=`Groups`,zle=`GroupArn`,Ble=`GetConsoleOutput`,Vle=`GetConsoleOutputRequest`,Hle=`GetConsoleOutputResult`,Ule=`GroupDescription`,Wle=`GpuDeviceInfo`,Gle=`GpuDeviceInfoList`,Kle=`GpuDeviceMemoryInfo`,rA=`GatewayId`,qle=`GroupIdentifierList`,Jle=`GroupIdStringList`,iA=`GpuInfo`,aA=`GroupId`,oA=`GroupIds`,Yle=`GroupIdentifier`,Xle=`GroupLongName`,sA=`GroupName`,Zle=`GroupNameStringList`,cA=`GroupNames`,Qle=`GroupOwnerId`,$le=`GpuPartitionSize`,lA=`Geography`,eue=`Gpus`,tue=`HiddenByDefault`,uA=`HttpEndpoint`,nue=`HostId`,dA=`HibernationOptions`,rue=`HibernationOptionsRequest`,fA=`HttpProtocolIpv6`,pA=`HttpPutResponseHopLimit`,iue=`HostResourceGroupArn`,aue=`HibernationSupported`,mA=`HttpTokens`,hA=`HostnameType`,oue=`Hibernate`,gA=`Hypervisor`,sue=`Id`,_A=`Ipv6Addresses`,vA=`Ipv6AddressAttribute`,yA=`Ipv6AddressCount`,cue=`InstanceAttachmentEnaSrdSpecification`,lue=`InstanceAttachmentEnaSrdUdpSpecification`,bA=`InferenceAcceleratorInfo`,uue=`Ipv4AddressesPerInterface`,due=`Ipv6AddressesPerInterface`,fue=`Ipv4AddressesPerSecondaryInterface`,pue=`ImageAllowed`,mue=`IpAddress`,xA=`Ipv6Address`,hue=`InstanceBlockDeviceMapping`,gue=`InstanceBlockDeviceMappingList`,_ue=`InstanceBlockDeviceMappingSpecification`,vue=`InstanceBlockDeviceMappingSpecificationList`,SA=`Ipv6CidrBlock`,CA=`Ipv6CidrBlockAssociationSet`,yue=`Ipv6CidrBlockNetworkBorderGroup`,wA=`Ipv6CidrBlockState`,bue=`IncludeDeprecated`,xue=`InferenceDeviceInfo`,Sue=`InferenceDeviceInfoList`,Cue=`InferenceDeviceMemoryInfo`,wue=`IncludeDisabled`,Tue=`IsDefault`,TA=`IsEgress`,EA=`InternetGateway`,Eue=`InternetGatewayAttachment`,Due=`InternetGatewayAttachmentList`,Oue=`InternetGatewayBlockMode`,kue=`InternetGatewayExclusion`,DA=`InternetGatewayId`,Aue=`InternetGatewayIdList`,jue=`InternetGatewayIds`,Mue=`InternetGatewayList`,Nue=`InternetGateways`,OA=`InstanceId`,Pue=`InstanceIpv6Address`,Fue=`InstanceIpv6AddressList`,Iue=`InstanceInterruptionBehavior`,kA=`IamInstanceProfile`,AA=`Ipv6IpamPoolId`,jA=`Ipv4IpamPoolId`,Lue=`InstanceIpv4PrefixList`,Rue=`InstanceIpv6PrefixList`,zue=`IamInstanceProfileSpecification`,Bue=`InstanceIpv4Prefix`,Vue=`InstanceIpv6Prefix`,MA=`InstanceInitiatedShutdownBehavior`,Hue=`ImageIdStringList`,Uue=`InstanceIdStringList`,NA=`ImageId`,Wue=`ImageIds`,PA=`InstanceIds`,Gue=`ImportKeyPair`,Kue=`ImportKeyPairRequest`,que=`ImportKeyPairResult`,Jue=`ImageLocation`,Yue=`ImageList`,Xue=`InstanceLifecycle`,Zue=`InstanceList`,Que=`IpList`,FA=`InstanceMaintenanceOptions`,IA=`InstanceMaintenanceOptionsRequest`,LA=`InstanceMarketOptionsRequest`,RA=`InstanceMetadataOptionsRequest`,zA=`InstanceMetadataOptionsResponse`,BA=`InstanceMarketOptions`,VA=`IncludeManagedResources`,HA=`InstanceMetadataTags`,UA=`Ipv6Native`,WA=`InstanceNetworkInterface`,GA=`InstanceNetworkInterfaceAssociation`,KA=`InstanceNetworkInterfaceAttachment`,qA=`InstanceNetworkInterfaceList`,JA=`InstanceNetworkInterfaceSpecification`,YA=`InstanceNetworkInterfaceSpecificationList`,XA=`Ipv6NetmaskLength`,ZA=`Ipv4NetmaskLength`,QA=`InstanceNetworkPerformanceOptions`,$A=`InstanceNetworkPerformanceOptionsRequest`,ej=`ImageOwnerAlias`,tj=`IpOwnerId`,nj=`InstanceOwnerId`,rj=`InstanceOwningService`,ij=`Ipv6PrefixCount`,aj=`Ipv4PrefixCount`,oj=`IpPermissionsEgress`,sj=`IpamPoolId`,cj=`InstancePrivateIpAddress`,lj=`InstancePrivateIpAddressList`,uj=`IsPrimaryIpv6`,dj=`IncludePublicKey`,fj=`IpPermissionList`,pj=`Ipv4PrefixList`,mj=`Ipv6PrefixList`,hj=`Ipv4PrefixSpecificationRequest`,gj=`Ipv6PrefixSpecificationRequest`,_j=`Ipv6Prefixes`,vj=`IpPermissions`,yj=`IpPermission`,bj=`IpProtocol`,xj=`Ipv6Prefix`,Sj=`Ipv6Pool`,Cj=`Ipv4Prefixes`,wj=`Ipv4Prefix`,Tj=`IsPrimary`,Ej=`IpRangeList`,Dj=`Ipv6RangeList`,Oj=`IpRanges`,kj=`IpRange`,Aj=`Ipv6Ranges`,jj=`Ipv6Range`,Mj=`InstanceStateChange`,Nj=`InstanceStateChangeList`,Pj=`InstanceSecondaryInterfaceAttachment`,Fj=`InstanceSecondaryInterfaceList`,Ij=`InstanceSecondaryInterfacePrivateIpAddress`,Lj=`InstanceSecondaryInterfacePrivateIpAddressList`,Rj=`InstanceSecondaryInterfacePrivateIpAddressListRequest`,zj=`InstanceSecondaryInterfacePrivateIpAddressRequest`,Bj=`InstanceSecondaryInterfaceSpecificationListRequest`,Vj=`InstanceSecondaryInterfaceSpecificationRequest`,Hj=`InstanceSecondaryInterface`,Uj=`InstanceStorageInfo`,Wj=`InstanceStorageSupported`,Gj=`ImdsSupport`,Kj=`InstanceState`,qj=`IpSource`,Jj=`Ipv6Supported`,Yj=`InstanceType`,Xj=`InstanceTypeInfoList`,Zj=`InstanceTypeInfo`,Qj=`ImageType`,$j=`InstanceTenancy`,eM=`InstanceTypes`,tM=`InterfaceType`,nM=`IncludeUnsupportedInRegion`,rM=`Images`,iM=`Image`,aM=`Instance`,oM=`Instances`,sM=`Iops`,cM=`Key`,lM=`KeyFormat`,uM=`KeyFingerprint`,dM=`KernelId`,fM=`KmsKeyId`,pM=`KeyMaterial`,mM=`KeyName`,hM=`KeyNameStringList`,gM=`KeyNames`,_M=`KeyPairs`,vM=`KeyPairId`,yM=`KeyPairIdStringList`,bM=`KeyPairIds`,xM=`KeyPairInfo`,SM=`KeyPairList`,CM=`KeyPair`,wM=`KeyType`,TM=`Kernel`,EM=`LicenseConfigurationArn`,DM=`LicenseConfigurationRequest`,OM=`LicenseConfiguration`,kM=`LambdaExclusion`,AM=`LogicalGpuCount`,jM=`LocalGatewayId`,MM=`LinkedGroupId`,NM=`LicenseList`,PM=`LastLaunchedTime`,FM=`LicenseSpecifications`,IM=`LicenseSpecificationListRequest`,LM=`LicenseSpecification`,RM=`LaunchTemplate`,zM=`LaunchTemplateId`,BM=`LaunchTemplateName`,VM=`LaunchTemplateSpecification`,HM=`LaunchTime`,UM=`Latest`,WM=`Lambda`,GM=`Licenses`,KM=`Location`,qM=`MultiAttachEnabled`,JM=`MediaAcceleratorInfo`,YM=`MacAddress`,XM=`MaximumBandwidthInMbps`,ZM=`MapCustomerOwnedIpOnLaunch`,QM=`MaxCount`,$M=`MinCount`,eN=`MediaDeviceInfo`,tN=`MediaDeviceInfoList`,nN=`MediaDeviceMemoryInfo`,rN=`MaximumEbsAttachments`,iN=`MaximumEbsCards`,aN=`MaximumEfaInterfaces`,oN=`MaximumEnaQueueCount`,sN=`MaximumEnaQueueCountPerInterface`,cN=`MaximumIops`,lN=`ModifyInstanceAttributeRequest`,uN=`ModifyInstanceAttributeValue`,dN=`ModifyInstanceAttribute`,fN=`MemoryInfo`,pN=`MaximumNetworkCards`,mN=`MaximumNetworkInterfaces`,hN=`MetadataOptions`,gN=`MaintenanceOptions`,_N=`MapPublicIpOnLaunch`,vN=`MaxPrice`,yN=`MaxResults`,bN=`ModifySubnetAttributeRequest`,xN=`ModifySubnetAttribute`,SN=`MaximumSecondaryNetworkInterfaces`,CN=`ModificationState`,wN=`MarketType`,TN=`MaximumThroughputInMBps`,EN=`ModifyVolumeRequest`,DN=`ModifyVolumeResult`,ON=`ModifyVolume`,kN=`Main`,AN=`Manufacturer`,jN=`Managed`,MN=`Message`,NN=`Messages`,PN=`Mode`,FN=`Monitoring`,IN=`Name`,LN=`NetworkBorderGroup`,RN=`NetworkCards`,zN=`NetworkCardIndex`,BN=`NetworkCardInfoList`,VN=`NetworkCardInfo`,HN=`NoDevice`,UN=`NeuronDeviceCoreInfo`,WN=`NeuronDeviceInfo`,GN=`NeuronDeviceInfoList`,KN=`NeuronDeviceMemoryInfo`,qN=`NeuronDevices`,JN=`NitroEnclavesSupport`,YN=`NatGateway`,XN=`NatGatewayAddresses`,ZN=`NatGatewayAttachedAppliance`,QN=`NatGatewayAttachedApplianceList`,$N=`NatGatewayAddressList`,eP=`NatGatewayAddress`,tP=`NatGatewayExclusion`,nP=`NatGatewayId`,rP=`NatGatewayIdStringList`,iP=`NatGatewayIds`,aP=`NatGatewayList`,oP=`NatGateways`,sP=`NetworkInterfaceId`,cP=`NetworkInterfaceOwnerId`,lP=`NetworkInterfaces`,uP=`NetworkInfo`,dP=`NeuronInfo`,fP=`NetworkPerformanceOptions`,pP=`NetworkPerformance`,mP=`NoReboot`,hP=`NvmeSupport`,gP=`NitroTpmInfo`,_P=`NitroTpmSupport`,vP=`NitroTpmSupportedVersionsList`,yP=`NextToken`,bP=`NestedVirtualization`,xP=`Origin`,SP=`OutpostArn`,CP=`OwnerAlias`,wP=`OwnerId`,TP=`OptInStatus`,EP=`OriginalIops`,DP=`OwnerIds`,OP=`OriginalMultiAttachEnabled`,kP=`OdbNetworkArn`,AP=`OperatorRequest`,jP=`OperatorResponse`,MP=`OwnerStringList`,NP=`OriginalSize`,PP=`OriginalThroughput`,FP=`OriginalVolumeType`,IP=`Operator`,LP=`Output`,RP=`Owners`,zP=`Owner`,BP=`Principal`,VP=`ProvisionedBandwidth`,HP=`PeakBandwidthInGbps`,UP=`ProductCodeId`,WP=`ProductCodeList`,GP=`ProductCodeType`,KP=`PartitionCount`,qP=`ProductCode`,JP=`ProductCodes`,YP=`PrivateDnsHostnameTypeOnLaunch`,XP=`PrivateDnsName`,ZP=`PrivateDnsNameOptions`,QP=`PrivateDnsNameOptionsOnLaunch`,$P=`PrivateDnsNameOptionsRequest`,eF=`PrivateDnsNameOptionsResponse`,tF=`PublicDnsName`,nF=`PlatformDetails`,rF=`PlacementGroup`,iF=`PlacementGroupInfo`,aF=`PlacementGroupIdStringList`,oF=`PlacementGroupList`,sF=`PlacementGroupStrategyList`,cF=`PlacementGroups`,lF=`PublicIp`,uF=`PrivateIpAddressCount`,dF=`PrivateIpAddressSpecification`,fF=`PrivateIpAddressSpecificationList`,pF=`PrivateIpAddress`,mF=`PrivateIpAddresses`,hF=`PublicIpAddress`,gF=`PublicIpv4Pool`,_F=`PublicIpStringList`,vF=`PrimaryIpv6`,yF=`PrivateIp`,bF=`ProcessorInfo`,xF=`PublicIps`,SF=`PublicKey`,CF=`PublicKeyMaterial`,wF=`PrefixListId`,TF=`PrefixListIdList`,EF=`PrefixListIds`,DF=`PartitionNumber`,OF=`PeeringStatus`,kF=`PhcSupport`,AF=`PreviousState`,jF=`ProvisionTime`,MF=`PresignedUrl`,NF=`PropagatingVgw`,PF=`PropagatingVgwList`,FF=`PropagatingVgws`,IF=`ParentZoneId`,LF=`ParentZoneName`,RF=`Platform`,zF=`Placement`,BF=`Primary`,VF=`Progress`,HF=`Provisioned`,UF=`Public`,WF=`Return`,GF=`ReleaseAddressRequest`,KF=`ReleaseAddress`,qF=`RestorableByUserIds`,JF=`RestorableBy`,YF=`RootDeviceName`,XF=`RootDeviceType`,ZF=`RootDeviceTypeList`,QF=`RestoreExpiryTime`,$F=`ResourceExclusions`,eI=`RegionGeography`,tI=`ReferencedGroupId`,nI=`ReferencedGroupInfo`,rI=`RegionGeographyList`,iI=`RunInstancesMonitoringEnabled`,aI=`RebootInstancesRequest`,oI=`RunInstancesRequest`,sI=`RunInstancesUserData`,cI=`RamdiskId`,lI=`ReservationId`,uI=`RebootInstances`,dI=`RequesterId`,fI=`RunInstances`,pI=`RegionList`,mI=`ReservationList`,hI=`RouteList`,gI=`RebootMigrationSupport`,_I=`RebootMigration`,vI=`RegionName`,yI=`RegionNameStringList`,bI=`RegionNames`,xI=`ReferencedSecurityGroup`,SI=`RevokeSecurityGroupEgress`,CI=`RevokeSecurityGroupEgressRequest`,wI=`RevokeSecurityGroupEgressResult`,TI=`RevokeSecurityGroupIngressRequest`,EI=`RevokeSecurityGroupIngressResult`,DI=`RevokeSecurityGroupIngress`,OI=`RevokedSecurityGroupRule`,kI=`RevokedSecurityGroupRuleList`,AI=`RevokedSecurityGroupRules`,jI=`RouteTableAssociation`,MI=`RouteTableAssociationId`,NI=`RouteTableAssociationList`,PI=`RouteTableAssociationState`,FI=`RouteTableId`,II=`RouteTableIdStringList`,LI=`RouteTableIds`,RI=`RouteTableList`,zI=`ResourceType`,BI=`RequestTime`,VI=`RouteTable`,HI=`RouteTables`,UI=`Ramdisk`,WI=`Regions`,GI=`Region`,KI=`Requested`,qI=`Reservations`,JI=`Reservation`,YI=`Route`,XI=`Routes`,ZI=`SecondaryAllocationIds`,QI=`SecondaryAllocationId`,$I=`SupportedAdditionalProcessorFeatureList`,eL=`SubnetArn`,tL=`SupportedArchitectures`,nL=`SupportedBootModes`,rL=`SubnetCidrBlockState`,iL=`SustainedClockSpeedInGhz`,aL=`SourceDestCheck`,oL=`SpreadDomain`,sL=`SupportedFeatures`,cL=`SubGeography`,lL=`SecurityGroupArn`,uL=`SecurityGroupId`,dL=`SecurityGroupIdStringList`,fL=`SecurityGroupIds`,pL=`SecurityGroupList`,mL=`SecurityGroupRules`,hL=`SecurityGroupRuleArn`,gL=`SecurityGroupRuleIds`,_L=`SecurityGroupRuleIdList`,vL=`SecurityGroupRuleId`,yL=`SecurityGroupRuleList`,bL=`SecurityGroupRule`,xL=`SecurityGroupStringList`,SL=`SecurityGroups`,CL=`SecurityGroup`,wL=`SubnetIds`,TL=`SubnetIpv6CidrBlockAssociation`,EL=`SubnetIpv6CidrBlockAssociationSet`,DL=`SizeInGB`,OL=`SourceImageId`,kL=`SecondaryInterfaceId`,AL=`SourceInstanceId`,jL=`SizeInMiB`,ML=`SpotInstanceRequestId`,NL=`SourceImageRegion`,PL=`StartInstancesRequest`,FL=`StartInstancesResult`,IL=`StopInstancesRequest`,LL=`StopInstancesResult`,RL=`SupportedInRegion`,zL=`SnapshotIdStringList`,BL=`SubnetIdStringList`,VL=`SpotInstanceType`,HL=`SecondaryInterfaces`,UL=`SecondaryInterface`,WL=`SnapshotId`,GL=`SnapshotIds`,KL=`StartingInstances`,qL=`StartInstances`,JL=`StoppingInstances`,YL=`StopInstances`,XL=`SubnetId`,ZL=`SnapshotLocation`,QL=`SnapshotList`,$L=`SpreadLevel`,eR=`SubnetList`,tR=`ServiceManaged`,nR=`SpotMarketOptions`,rR=`StatusMessage`,iR=`StateMessage`,aR=`SecondaryNetworkId`,oR=`SriovNetSupport`,sR=`SecondaryNetworkSupported`,cR=`SpotOptions`,lR=`SkipOsShutdown`,uR=`SecondaryPrivateIpAddresses`,dR=`SecondaryPrivateIpAddressCount`,fR=`SecondaryPrivateIpAddress`,pR=`SupportedRootDeviceTypes`,mR=`SourceRegion`,hR=`StateReason`,gR=`SourceSecurityGroupName`,_R=`SourceSecurityGroupOwnerId`,vR=`SourceSnapshotId`,yR=`SecondarySubnetId`,bR=`SupportedStrategies`,xR=`StartTime`,SR=`StateTransitionReason`,CR=`SseType`,wR=`StorageTier`,TR=`SupportedUsageClasses`,ER=`SensitiveUserData`,DR=`SourceVolumeId`,OR=`SupportedVirtualizationTypes`,kR=`SupportedVersions`,AR=`Size`,jR=`Snapshots`,MR=`Snapshot`,NR=`State`,PR=`Status`,FR=`Strategy`,IR=`Subnet`,LR=`Subnets`,RR=`Tags`,zR=`TcpEstablishedTimeout`,BR=`TotalFpgaMemoryInMiB`,VR=`TransitGatewayId`,HR=`TotalGpuMemoryInMiB`,UR=`TerminateInstances`,WR=`TotalInferenceMemoryInMiB`,GR=`TerminateInstancesRequest`,KR=`TerminateInstancesResult`,qR=`TargetIops`,JR=`TerminatingInstances`,YR=`TagList`,XR=`TargetMultiAttachEnabled`,ZR=`TotalMediaMemoryInMiB`,QR=`TotalNeuronDeviceMemoryInMiB`,$R=`ToPort`,ez=`ThreadsPerCore`,tz=`ThreadsPerCoreList`,nz=`TagSpecifications`,rz=`TotalSizeInGB`,iz=`TagSpecificationList`,az=`TagSpecification`,oz=`TargetSize`,sz=`TpmSupport`,cz=`TargetThroughput`,lz=`TransferType`,uz=`TargetVolumeType`,dz=`Tag`,fz=`Tenancy`,pz=`Throughput`,mz=`Timestamp`,hz=`Type`,gz=`UsageClassTypeList`,_z=`UserData`,vz=`UserId`,yz=`UserIdGroupPairs`,bz=`UserIdGroupPairList`,xz=`UserIdGroupPair`,Sz=`UnknownIpPermissions`,Cz=`UsageOperation`,wz=`UsageOperationUpdateTime`,Tz=`UdpStreamTimeout`,Ez=`UdpTimeout`,Dz=`Value`,Oz=`VolumeAttachmentList`,kz=`VolumeAttachment`,Az=`VpcCidrBlockAssociation`,jz=`VpcCidrBlockAssociationSet`,Mz=`VpcCidrBlockState`,Nz=`VCpuInfo`,Pz=`ValidCores`,Fz=`VpcEncryptionControl`,Iz=`VpcEncryptionControlConfiguration`,Lz=`VpcEncryptionControlExclusion`,Rz=`VpcEncryptionControlExclusions`,zz=`VpcEncryptionControlId`,Bz=`VpcEndpointId`,Vz=`VpcId`,Hz=`VpcIpv6CidrBlockAssociation`,Uz=`VpcIpv6CidrBlockAssociationSet`,Wz=`VolumeInitializationRate`,Gz=`VolumeIdStringList`,Kz=`VpcIdStringList`,qz=`VolumeId`,Jz=`VolumeIds`,Yz=`VpcIds`,Xz=`VpcLattice`,Zz=`VpcLatticeExclusion`,Qz=`VolumeList`,$z=`VpcList`,eB=`VolumeModification`,tB=`VirtualName`,nB=`VolumeOwnerId`,rB=`VpcPeeringConnectionId`,iB=`VpcPeeringExclusion`,aB=`VirtualPrivateGateway`,oB=`VirtualPrivateGatewayExclusion`,sB=`VpcPeering`,cB=`ValueStringList`,lB=`VolumeSize`,uB=`VolumeType`,dB=`VirtualizationTypeList`,fB=`ValidThreadsPerCore`,pB=`VirtualizationType`,mB=`ValidUntil`,hB=`Values`,gB=`Version`,_B=`Volumes`,vB=`Volume`,yB=`Vpc`,bB=`Vpcs`,xB=`WorkloadsList`,SB=`Workloads`,CB=`ZoneId`,wB=`ZoneIdStringList`,TB=`ZoneIds`,EB=`ZoneName`,DB=`ZoneNameStringList`,OB=`ZoneNames`,kB=`ZoneType`,AB=`attachedApplianceSet`,jB=`applianceArn`,MB=`additionalFlexibleNetworkInterfaces`,NB=`allocationId`,PB=`availableIpAddressCount`,FB=`assignIpv6AddressOnCreation`,IB=`additionalInfo`,LB=`associationId`,RB=`attachmentId`,zB=`amiLaunchIndex`,BB=`attachmentLimitType`,VB=`availabilityMode`,HB=`associatePublicIpAddress`,UB=`amazonProvidedIpv6CidrBlock`,WB=`autoProvisionZones`,GB=`autoRecoverySupported`,KB=`allowReassociation`,qB=`associatedResource`,JB=`autoRecovery`,YB=`associationState`,XB=`autoScalingIps`,ZB=`amdSevSnp`,QB=`addressesSet`,$B=`associationSet`,eV=`attachmentSet`,tV=`attachmentState`,nV=`attachTime`,rV=`availabilityZone`,iV=`availabilityZoneId`,aV=`availabilityZoneInfo`,oV=`accelerators`,sV=`affinity`,cV=`arn`,lV=`architecture`,uV=`association`,dV=`attachment`,fV=`attribute`,pV=`baselineBandwidthInGbps`,mV=`baselineBandwidthInMbps`,hV=`blockDeviceMapping`,gV=`baselineIops`,_V=`bootMode`,vV=`bareMetal`,yV=`blockPublicAccessStates`,bV=`burstablePerformanceSupported`,xV=`baselineThroughputInMBps`,SV=`bandwidthWeighting`,CV=`bandwidthWeightings`,wV=`cidrBlock`,TV=`cidrBlockAssociationSet`,EV=`capacityBlockId`,DV=`cidrBlockState`,OV=`coreCount`,kV=`completionDurationMinutes`,AV=`creationDate`,jV=`carrierGatewayId`,MV=`currentGeneration`,NV=`carrierIp`,PV=`currentInstanceBootMode`,FV=`cidrIp`,IV=`cidrIpv6`,LV=`cidrIpv4`,RV=`coreInfo`,zV=`coreNetworkArn`,BV=`cpuOptions`,VV=`customerOwnedIp`,HV=`customerOwnedIpv4Pool`,UV=`capacityReservationId`,WV=`capacityReservationPreference`,GV=`capacityReservationResourceGroupArn`,KV=`capacityReservationSpecification`,qV=`capacityReservationTarget`,JV=`currentState`,YV=`clientToken`,XV=`connectionTrackingConfiguration`,ZV=`completionTime`,QV=`connectivityType`,$V=`createTime`,eH=`code`,tH=`configured`,nH=`count`,rH=`disableApiTermination`,iH=`destinationCidrBlock`,aH=`defaultCores`,oH=`dataEncryptionKeyId`,sH=`defaultEnaQueueCountPerInterface`,cH=`defaultForAz`,lH=`dedicatedHostsSupported`,uH=`deviceIndex`,dH=`destinationIpv6CidrBlock`,fH=`deviceName`,pH=`defaultNetworkCardIndex`,mH=`dnsName`,hH=`dhcpOptionsId`,gH=`deleteOnTermination`,_H=`destinationPrefixListId`,vH=`deregistrationProtection`,yH=`dryRun`,bH=`destinationRegion`,xH=`defaultTcpEstablishedTimeout`,SH=`defaultThreadsPerCore`,CH=`deprecationTime`,wH=`deleteTime`,TH=`defaultUdpStreamTimeout`,EH=`defaultUdpTimeout`,DH=`defaultVCpus`,OH=`description`,kH=`device`,AH=`disks`,jH=`domain`,MH=`ebsCardIndex`,NH=`ebsCardSet`,PH=`encryptionControl`,FH=`enableDns64`,IH=`elasticFileSystem`,LH=`elasticGpuAssociationId`,RH=`elasticGpuAssociationState`,zH=`elasticGpuAssociationSet`,BH=`elasticGpuAssociationTime`,VH=`elasticGpuId`,HH=`elasticInferenceAcceleratorArn`,UH=`elasticInferenceAcceleratorAssociationId`,WH=`elasticInferenceAcceleratorAssociationState`,GH=`elasticInferenceAcceleratorAssociationSet`,KH=`elasticInferenceAcceleratorAssociationTime`,qH=`encryptionInTransitSupported`,JH=`ebsInfo`,YH=`efaInfo`,XH=`enableLniAtDeviceIndex`,ZH=`ebsOptimized`,QH=`ebsOptimizedInfo`,$H=`egressOnlyInternetGateway`,eU=`egressOnlyInternetGatewayId`,tU=`ebsOptimizedSupport`,nU=`enclaveOptions`,rU=`enaQueueCount`,W=`ec2QueryName`,iU=`enableResourceNameDnsAAAARecord`,aU=`enableResourceNameDnsARecord`,oU=`enaSrdEnabled`,sU=`enaSrdSpecification`,cU=`enaSrdSupported`,lU=`enaSrdUdpEnabled`,uU=`enaSrdUdpSpecification`,dU=`efaSupported`,fU=`encryptionSupport`,pU=`enaSupport`,mU=`endTime`,hU=`ebs`,gU=`enabled`,_U=`encrypted`,vU=`failureCode`,yU=`flexibleEnaQueuesSupport`,bU=`fpgaInfo`,xU=`failureMessage`,SU=`fromPort`,CU=`fastRestored`,wU=`fullSnapshotSizeInBytes`,TU=`freeTierEligible`,EU=`force`,DU=`fpgas`,OU=`groupArn`,kU=`groupDescription`,AU=`gatewayId`,jU=`gpuInfo`,MU=`groupId`,NU=`groupLongName`,PU=`groupName`,FU=`groupOwnerId`,IU=`gpuPartitionSize`,LU=`geographySet`,RU=`groupSet`,zU=`gpus`,BU=`groups`,VU=`hypervisor`,HU=`hiddenByDefault`,UU=`httpEndpoint`,WU=`hostId`,GU=`hibernationOptions`,KU=`httpProtocolIpv6`,qU=`httpPutResponseHopLimit`,JU=`hostResourceGroupArn`,YU=`hibernationSupported`,XU=`httpTokens`,ZU=`hostnameType`,QU=`id`,$U=`ipv6AddressAttribute`,eW=`ipv6AddressCount`,tW=`inferenceAcceleratorInfo`,nW=`ipv4AddressesPerInterface`,rW=`ipv6AddressesPerInterface`,iW=`ipv4AddressesPerSecondaryInterface`,aW=`ipv6AddressesSet`,oW=`imageAllowed`,sW=`ipAddress`,cW=`ipv6Address`,lW=`ipv6CidrBlock`,uW=`ipv6CidrBlockAssociationSet`,dW=`ipv6CidrBlockState`,fW=`isDefault`,pW=`isEgress`,mW=`internetGateway`,hW=`internetGatewayBlockMode`,gW=`internetGatewayId`,_W=`internetGatewaySet`,vW=`instanceId`,yW=`iamInstanceProfile`,bW=`instanceInitiatedShutdownBehavior`,xW=`imageId`,SW=`imageLocation`,CW=`instanceLifecycle`,wW=`instanceMetadataTags`,TW=`ipv6Native`,EW=`imageOwnerAlias`,DW=`imageOwnerId`,OW=`instanceOwnerId`,kW=`ipOwnerId`,AW=`instanceOwningService`,jW=`ipPermissionsEgress`,MW=`isPrimaryIpv6`,NW=`ipv4PrefixSet`,PW=`ipv6PrefixSet`,FW=`ipProtocol`,IW=`ipPermissions`,LW=`ipv4Prefix`,RW=`ipv6Pool`,zW=`ipv6Prefix`,BW=`isPublic`,VW=`isPrimary`,HW=`ipRanges`,UW=`ipv6Ranges`,WW=`instanceStorageInfo`,GW=`instanceStorageSupported`,KW=`imagesSet`,qW=`imageState`,JW=`imdsSupport`,YW=`instancesSet`,XW=`instanceState`,ZW=`ipSource`,QW=`ipv6Supported`,$W=`instanceType`,eG=`instanceTypeSet`,tG=`idempotencyToken`,nG=`imageType`,rG=`instanceTenancy`,iG=`interfaceType`,aG=`iops`,G=`item`,oG=`key`,sG=`keyFingerprint`,cG=`kernelId`,lG=`kmsKeyId`,uG=`keyMaterial`,dG=`keyName`,fG=`keyPairId`,pG=`keySet`,mG=`keyType`,hG=`kernel`,gG=`licenseConfigurationArn`,_G=`logicalGpuCount`,vG=`localGatewayId`,yG=`linkedGroupId`,bG=`lastLaunchedTime`,xG=`licenseSet`,SG=`launchTime`,CG=`lambda`,wG=`multiAttachEnabled`,TG=`mediaAcceleratorInfo`,EG=`macAddress`,DG=`maximumBandwidthInMbps`,OG=`mapCustomerOwnedIpOnLaunch`,kG=`maximumEbsAttachments`,AG=`maximumEbsCards`,jG=`maximumEfaInterfaces`,MG=`maximumEnaQueueCount`,NG=`maximumEnaQueueCountPerInterface`,PG=`maximumIops`,FG=`memoryInfo`,IG=`maximumNetworkCards`,LG=`maximumNetworkInterfaces`,RG=`metadataOptions`,zG=`maintenanceOptions`,BG=`mapPublicIpOnLaunch`,VG=`maxResults`,HG=`messageSet`,UG=`maximumSecondaryNetworkInterfaces`,WG=`modificationState`,GG=`maximumThroughputInMBps`,KG=`main`,qG=`manufacturer`,JG=`managed`,YG=`message`,XG=`monitoring`,ZG=`mode`,QG=`name`,$G=`networkBorderGroup`,eK=`networkCards`,tK=`networkCardIndex`,nK=`noDevice`,rK=`neuronDevices`,iK=`nitroEnclavesSupport`,aK=`natGateway`,oK=`natGatewayAddressSet`,sK=`natGatewayId`,cK=`natGatewaySet`,lK=`networkInterfaceId`,uK=`networkInterfaceOwnerId`,dK=`networkInterfaceSet`,fK=`networkInterface`,pK=`networkInfo`,mK=`neuronInfo`,hK=`networkPerformanceOptions`,gK=`networkPerformance`,_K=`noReboot`,vK=`nvmeSupport`,yK=`nitroTpmInfo`,bK=`nitroTpmSupport`,xK=`nextToken`,SK=`nestedVirtualization`,CK=`origin`,wK=`outpostArn`,TK=`ownerAlias`,EK=`ownerId`,DK=`optInStatus`,OK=`originalIops`,kK=`originalMultiAttachEnabled`,AK=`odbNetworkArn`,jK=`originalSize`,MK=`originalThroughput`,NK=`originalVolumeType`,PK=`operator`,FK=`output`,IK=`principal`,LK=`provisionedBandwidth`,RK=`peakBandwidthInGbps`,zK=`productCodes`,BK=`partitionCount`,VK=`productCode`,HK=`privateDnsNameOptions`,UK=`privateDnsNameOptionsOnLaunch`,WK=`privateDnsName`,GK=`publicDnsName`,KK=`platformDetails`,qK=`placementGroup`,JK=`placementGroupInfo`,YK=`placementGroupSet`,XK=`publicIp`,ZK=`privateIpAddressesSet`,QK=`privateIpAddressSet`,$K=`privateIpAddress`,eq=`publicIpv4Pool`,tq=`processorInfo`,nq=`privateIp`,rq=`publicKey`,iq=`publicKeyMaterial`,aq=`prefixListId`,oq=`prefixListIds`,sq=`partitionNumber`,cq=`peeringStatus`,lq=`phcSupport`,uq=`previousState`,dq=`provisionTime`,fq=`presignedUrl`,pq=`propagatingVgwSet`,mq=`parentZoneId`,hq=`parentZoneName`,gq=`platform`,_q=`placement`,vq=`primary`,yq=`progress`,bq=`provisioned`,xq=`return`,Sq=`rootDeviceName`,Cq=`rootDeviceType`,wq=`restoreExpiryTime`,Tq=`regionEndpoint`,Eq=`resourceExclusions`,Dq=`referencedGroupId`,Oq=`referencedGroupInfo`,kq=`reservationId`,Aq=`ramdiskId`,jq=`regionInfo`,Mq=`requesterId`,Nq=`rebootMigrationSupport`,Pq=`rebootMigration`,Fq=`regionName`,Iq=`revokedSecurityGroupRuleSet`,Lq=`reservationSet`,Rq=`routeSet`,zq=`routeTableAssociationId`,Bq=`routeTableId`,Vq=`routeTableSet`,Hq=`resourceType`,Uq=`requestTime`,Wq=`routeTable`,Gq=`ramdisk`,Kq=`reason`,qq=`requested`,Jq=`smithy.ts.sdk.synthetic.com.amazonaws.ec2`,Yq=`supportedArchitectures`,Xq=`subnetArn`,Zq=`supportedBootModes`,Qq=`sustainedClockSpeedInGhz`,$q=`sourceDestCheck`,eJ=`spreadDomain`,tJ=`supportedFeatures`,nJ=`securityGroupArn`,rJ=`securityGroupInfo`,iJ=`securityGroupRuleArn`,aJ=`securityGroupRuleId`,oJ=`securityGroupRuleSet`,sJ=`subGeographySet`,cJ=`sizeInGB`,lJ=`sourceInstanceId`,uJ=`secondaryInterfaceId`,dJ=`sourceImageId`,fJ=`sizeInMiB`,pJ=`sourceImageRegion`,mJ=`spotInstanceRequestId`,hJ=`supportedInRegion`,gJ=`secondaryInterfaceSet`,_J=`snapshotId`,vJ=`subnetId`,yJ=`spreadLevel`,bJ=`serviceManaged`,xJ=`statusMessage`,SJ=`stateMessage`,CJ=`secondaryNetworkId`,wJ=`secondaryNetworkSupported`,TJ=`sriovNetSupport`,EJ=`secondaryPrivateIpAddressCount`,DJ=`supportedRootDeviceTypes`,OJ=`stateReason`,kJ=`sourceSecurityGroupName`,AJ=`sourceSecurityGroupOwnerId`,jJ=`secondarySubnetId`,MJ=`snapshotSet`,NJ=`subnetSet`,PJ=`supportedStrategies`,FJ=`startTime`,IJ=`sseType`,LJ=`storageTier`,RJ=`supportedUsageClasses`,zJ=`sourceVolumeId`,BJ=`supportedVirtualizationTypes`,VJ=`supportedVersions`,HJ=`size`,UJ=`state`,WJ=`status`,GJ=`strategy`,KJ=`subnet`,qJ=`tenancy`,JJ=`tcpEstablishedTimeout`,YJ=`totalFpgaMemoryInMiB`,XJ=`transitGatewayId`,ZJ=`totalGpuMemoryInMiB`,QJ=`totalInferenceMemoryInMiB`,$J=`targetIops`,eY=`targetMultiAttachEnabled`,tY=`totalMediaMemoryInMiB`,nY=`totalNeuronDeviceMemoryInMiB`,rY=`toPort`,iY=`threadsPerCore`,aY=`tagSet`,oY=`totalSizeInGB`,sY=`targetSize`,cY=`tpmSupport`,lY=`targetThroughput`,uY=`transferType`,dY=`targetVolumeType`,fY=`throughput`,pY=`timestamp`,mY=`type`,hY=`userData`,gY=`userId`,_Y=`unknownIpPermissionSet`,vY=`usageOperation`,yY=`usageOperationUpdateTime`,bY=`udpStreamTimeout`,xY=`udpTimeout`,SY=`value`,CY=`vCpuInfo`,wY=`validCores`,TY=`vpcEncryptionControlId`,EY=`vpcEndpointId`,DY=`vpcId`,OY=`volumeInitializationRate`,kY=`volumeId`,AY=`vpcLattice`,jY=`volumeModification`,MY=`virtualName`,NY=`volumeOwnerId`,PY=`vpcPeeringConnectionId`,FY=`virtualPrivateGateway`,IY=`vpcPeering`,LY=`volumeSet`,RY=`volumeSize`,zY=`vpcSet`,BY=`volumeType`,VY=`validThreadsPerCore`,HY=`virtualizationType`,UY=`version`,WY=`vpc`,GY=`workloadSet`,K=`xmlName`,KY=`zoneId`,qY=`zoneName`,JY=`zoneState`,YY=`zoneType`,q=`com.amazonaws.ec2`,XY=iw.for(Jq),ZY=[-3,Jq,`EC2ServiceException`,0,[],[]];XY.registerError(ZY,Rie);var QY=[XY],$Y=[0,q,koe,8,0],eX=[0,q,uN,8,0],tX=[0,q,sI,8,0],nX=[0,q,ER,8,0],rX=[3,q,dO,0,[mO,gO,Sk,sP,cP,pF,RR,gF,LN,QO,$O,KO,XL,tR,OA,lF],[[0,{[W]:`AllocationId`,[K]:NB}],[0,{[W]:`AssociationId`,[K]:LB}],[0,{[W]:`Domain`,[K]:jH}],[0,{[W]:`NetworkInterfaceId`,[K]:lK}],[0,{[W]:`NetworkInterfaceOwnerId`,[K]:uK}],[0,{[W]:`PrivateIpAddress`,[K]:$K}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`PublicIpv4Pool`,[K]:eq}],[0,{[W]:`NetworkBorderGroup`,[K]:$G}],[0,{[W]:`CustomerOwnedIp`,[K]:VV}],[0,{[W]:`CustomerOwnedIpv4Pool`,[K]:HV}],[0,{[W]:`CarrierIp`,[K]:NV}],[0,{[W]:`SubnetId`,[K]:vJ}],[0,{[W]:`ServiceManaged`,[K]:bJ}],[0,{[W]:`InstanceId`,[K]:vW}],[0,{[W]:`PublicIp`,[K]:XK}]]],iX=[3,q,zie,0,[Sk,dO,gF,LN,$O,nz,sj,U],[0,0,0,0,0,[()=>U$,{[K]:az}],0,[2,{[W]:`DryRun`,[K]:yH}]]],aX=[3,q,Bie,0,[mO,gF,LN,Sk,QO,$O,KO,lF],[[0,{[W]:`AllocationId`,[K]:NB}],[0,{[W]:`PublicIpv4Pool`,[K]:eq}],[0,{[W]:`NetworkBorderGroup`,[K]:$G}],[0,{[W]:`Domain`,[K]:jH}],[0,{[W]:`CustomerOwnedIp`,[K]:VV}],[0,{[W]:`CustomerOwnedIpv4Pool`,[K]:HV}],[0,{[W]:`CarrierIp`,[K]:NV}],[0,{[W]:`PublicIp`,[K]:XK}]]],oX=[3,q,Vie,0,[mO,OA,lF,U,sP,pF,dae],[0,0,0,[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`NetworkInterfaceId`,[K]:lK}],[0,{[W]:`PrivateIpAddress`,[K]:$K}],[2,{[W]:`AllowReassociation`,[K]:KB}]]],sX=[3,q,Hie,0,[gO],[[0,{[W]:`AssociationId`,[K]:LB}]]],cX=[3,q,cae,0,[FI,rA,gF,U,XL],[[0,{[W]:`RouteTableId`,[K]:Bq}],0,0,[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`SubnetId`,[K]:vJ}]],1],lX=[3,q,lae,0,[gO,xO],[[0,{[W]:`AssociationId`,[K]:LB}],[()=>_$,{[W]:`AssociationState`,[K]:YB}]]],uX=[3,q,$ie,0,[DA,Vz,U],[[0,{[W]:`InternetGatewayId`,[K]:gW}],[0,{[W]:`VpcId`,[K]:DY}],[2,{[W]:`DryRun`,[K]:yH}]],2],dX=[3,q,Sae,0,[xk,OA,qz,wk,U],[0,0,0,1,[2,{[W]:`DryRun`,[K]:yH}]],3],fX=[3,q,Jie,0,[Dz],[[2,{[W]:`Value`,[K]:SY}]]],pX=[3,q,Cae,0,[Dz],[[0,{[W]:`Value`,[K]:SY}]]],mX=[3,q,mae,0,[aA,nz,U,gR,_R,bj,Qk,$R,qO,vj],[[0,{[W]:`GroupId`,[K]:MU}],[()=>U$,{[K]:az}],[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`SourceSecurityGroupName`,[K]:kJ}],[0,{[W]:`SourceSecurityGroupOwnerId`,[K]:AJ}],[0,{[W]:`IpProtocol`,[K]:FW}],[1,{[W]:`FromPort`,[K]:SU}],[1,{[W]:`ToPort`,[K]:rY}],[0,{[W]:`CidrIp`,[K]:FV}],[()=>F$,{[W]:`IpPermissions`,[K]:IW}]],1],hX=[3,q,hae,0,[WF,mL],[[2,{[W]:`Return`,[K]:xq}],[()=>V$,{[W]:`SecurityGroupRuleSet`,[K]:oJ}]]],gX=[3,q,gae,0,[qO,Qk,aA,sA,vj,bj,gR,_R,$R,nz,U],[0,1,0,0,[()=>F$,0],0,0,0,1,[()=>U$,{[K]:az}],[2,{[W]:`DryRun`,[K]:yH}]]],_X=[3,q,_ae,0,[WF,mL],[[2,{[W]:`Return`,[K]:xq}],[()=>V$,{[W]:`SecurityGroupRuleSet`,[K]:oJ}]]],vX=[3,q,wO,0,[TP,NN,vI,EB,CB,sA,LN,kB,LF,IF,Xle,lA,cL,NR],[[0,{[W]:`OptInStatus`,[K]:DK}],[()=>Dfe,{[W]:`MessageSet`,[K]:HG}],[0,{[W]:`RegionName`,[K]:Fq}],[0,{[W]:`ZoneName`,[K]:qY}],[0,{[W]:`ZoneId`,[K]:KY}],[0,{[W]:`GroupName`,[K]:PU}],[0,{[W]:`NetworkBorderGroup`,[K]:$G}],[0,{[W]:`ZoneType`,[K]:YY}],[0,{[W]:`ParentZoneName`,[K]:hq}],[0,{[W]:`ParentZoneId`,[K]:mq}],[0,{[W]:`GroupLongName`,[K]:NU}],[()=>Tfe,{[W]:`GeographySet`,[K]:LU}],[()=>Ofe,{[W]:`SubGeographySet`,[K]:sJ}],[0,{[W]:`ZoneState`,[K]:JY}]]],yX=[3,q,TO,0,[wO,DO,hO],[0,0,[()=>D$,{[K]:mO}]]],bX=[3,q,Tae,0,[IN],[[0,{[W]:`Name`,[K]:QG}]]],xX=[3,q,Oae,0,[MN],[[0,{[W]:`Message`,[K]:YG}]]],SX=[3,q,Aae,0,[IN],[[0,{[W]:`Name`,[K]:QG}]]],CX=[3,q,Lae,0,[Dz],[[21,{[W]:`Value`,[K]:SY}]]],wX=[3,q,FO,0,[Gk,HN,_k,tB],[[()=>nQ,{[W]:`Ebs`,[K]:hU}],[0,{[W]:`NoDevice`,[K]:nK}],[0,{[W]:`DeviceName`,[K]:fH}],[0,{[W]:`VirtualName`,[K]:MY}]]],TX=[3,q,zO,0,[Oue],[[0,{[W]:`InternetGatewayBlockMode`,[K]:hW}]]],EX=[3,q,rk,0,[tk,ik],[0,()=>OX]],DX=[3,q,yoe,0,[tk,ik],[[0,{[W]:`CapacityReservationPreference`,[K]:WV}],[()=>kX,{[W]:`CapacityReservationTarget`,[K]:qV}]]],OX=[3,q,ik,0,[ek,nk],[0,0]],kX=[3,q,boe,0,[ek,nk],[[0,{[W]:`CapacityReservationId`,[K]:UV}],[0,{[W]:`CapacityReservationResourceGroupArn`,[K]:GV}]]],AX=[3,q,Voe,0,[zR,Tz,Ez],[1,1,1]],jX=[3,q,Hoe,0,[zR,Tz,Ez],[[1,{[W]:`TcpEstablishedTimeout`,[K]:JJ}],[1,{[W]:`UdpStreamTimeout`,[K]:bY}],[1,{[W]:`UdpTimeout`,[K]:xY}]]],MX=[3,q,Ooe,0,[mR,vR,bk,Hse,oce,qk,fM,MF,nz,WO,Qoe,U],[0,0,0,0,[0,{[W]:`DestinationRegion`,[K]:bH}],[2,{[W]:`Encrypted`,[K]:_U}],[0,{[W]:`KmsKeyId`,[K]:lG}],[()=>$Y,{[W]:`PresignedUrl`,[K]:fq}],[()=>U$,{[K]:az}],1,0,[2,{[W]:`DryRun`,[K]:yH}]],2],NX=[3,q,Aoe,0,[RR,WL],[[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`SnapshotId`,[K]:_J}]]],PX=[3,q,ZO,0,[UO,ez,SO,bP],[[1,{[W]:`CoreCount`,[K]:OV}],[1,{[W]:`ThreadsPerCore`,[K]:iY}],[0,{[W]:`AmdSevSnp`,[K]:ZB}],[0,{[W]:`NestedVirtualization`,[K]:SK}]]],FX=[3,q,poe,0,[UO,ez,SO,bP],[1,1,0,0]],IX=[3,q,ioe,0,[OA,IN,nz,ZL,U,bk,mP,IO],[[0,{[W]:`InstanceId`,[K]:vW}],[0,{[W]:`Name`,[K]:QG}],[()=>U$,{[K]:az}],0,[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`Description`,[K]:OH}],[2,{[W]:`NoReboot`,[K]:_K}],[()=>O$,{[W]:`BlockDeviceMapping`,[K]:hV}]],2],LX=[3,q,aoe,0,[NA],[[0,{[W]:`ImageId`,[K]:xW}]]],RX=[3,q,noe,0,[nz,U],[[()=>U$,{[K]:az}],[2,{[W]:`DryRun`,[K]:yH}]]],zX=[3,q,roe,0,[EA],[[()=>l$,{[W]:`InternetGateway`,[K]:mW}]]],BX=[3,q,loe,0,[mM,wM,nz,lM,U],[0,0,[()=>U$,{[K]:az}],0,[2,{[W]:`DryRun`,[K]:yH}]],1],VX=[3,q,doe,0,[vO,mO,ak,U,XL,Vz,EO,nz,sk,pF,ZI,uR,dR],[0,0,[0,4],2,0,0,[()=>wfe,{[K]:TO}],[()=>U$,{[K]:az}],0,0,[()=>D$,{[K]:QI}],[()=>cpe,{[K]:fR}],1]],HX=[3,q,foe,0,[ak,YN],[[0,{[W]:`ClientToken`,[K]:YV}],[()=>u$,{[W]:`NatGateway`,[K]:aK}]]],UX=[3,q,hoe,0,[KP,nz,$L,MM,IP,U,sA,FR],[1,[()=>U$,{[K]:az}],0,0,()=>d$,[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`GroupName`,[K]:PU}],[0,{[W]:`Strategy`,[K]:GJ}]]],WX=[3,q,goe,0,[rF],[[()=>m$,{[W]:`PlacementGroup`,[K]:qK}]]],GX=[3,q,_oe,0,[FI,yk,Bz,VR,jM,GO,XO,kP,U,mk,rA,gk,Pk,OA,sP,rB,nP],[[0,{[W]:`RouteTableId`,[K]:Bq}],0,0,0,0,0,0,0,[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`DestinationCidrBlock`,[K]:iH}],[0,{[W]:`GatewayId`,[K]:AU}],[0,{[W]:`DestinationIpv6CidrBlock`,[K]:dH}],[0,{[W]:`EgressOnlyInternetGatewayId`,[K]:eU}],[0,{[W]:`InstanceId`,[K]:vW}],[0,{[W]:`NetworkInterfaceId`,[K]:lK}],[0,{[W]:`VpcPeeringConnectionId`,[K]:PY}],[0,{[W]:`NatGatewayId`,[K]:sK}]],1],KX=[3,q,voe,0,[WF],[[2,{[W]:`Return`,[K]:xq}]]],qX=[3,q,xoe,0,[Vz,nz,ak,U],[[0,{[W]:`VpcId`,[K]:DY}],[()=>U$,{[K]:az}],[0,4],[2,{[W]:`DryRun`,[K]:yH}]],1],JX=[3,q,Soe,0,[VI,ak],[[()=>g$,{[W]:`RouteTable`,[K]:Wq}],[0,{[W]:`ClientToken`,[K]:YV}]]],YX=[3,q,Eoe,0,[bk,sA,Vz,nz,U],[[0,{[K]:Ule}],0,0,[()=>U$,{[K]:az}],[2,{[W]:`DryRun`,[K]:yH}]],2],XX=[3,q,Doe,0,[aA,RR,lL],[[0,{[W]:`GroupId`,[K]:MU}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`SecurityGroupArn`,[K]:nJ}]]],ZX=[3,q,joe,0,[qz,bk,SP,nz,KM,U],[0,0,0,[()=>U$,{[K]:az}],0,[2,{[W]:`DryRun`,[K]:yH}]],1],QX=[3,q,Moe,0,[Vz,nz,wO,DO,HO,SA,SP,UA,jA,ZA,AA,XA,U],[0,[()=>U$,{[K]:az}],0,0,0,0,0,2,0,1,0,1,[2,{[W]:`DryRun`,[K]:yH}]],1],$X=[3,q,Noe,0,[IR],[[()=>b$,{[W]:`Subnet`,[K]:KJ}]]],eZ=[3,q,Woe,0,[wO,DO,qk,sM,fM,SP,AR,WL,uB,nz,qM,pz,ak,Wz,IP,U],[0,0,[2,{[W]:`Encrypted`,[K]:_U}],1,0,0,1,0,0,[()=>U$,{[K]:az}],2,1,[0,4],1,()=>d$,[2,{[W]:`DryRun`,[K]:yH}]]],tZ=[3,q,Goe,0,[HO,Sj,SA,jA,ZA,AA,XA,yue,Fz,nz,U,$j,aae],[0,0,0,0,1,0,1,0,()=>yfe,[()=>U$,{[K]:az}],[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`InstanceTenancy`,[K]:rG}],[2,{[W]:`AmazonProvidedIpv6CidrBlock`,[K]:UB}]]],nZ=[3,q,Koe,0,[yB],[[()=>C$,{[W]:`Vpc`,[K]:WY}]]],rZ=[3,q,Poe,0,[Zae],[0],1],iZ=[3,q,ise,0,[Tce,Ace,kce],[[1,{[W]:`DefaultTcpEstablishedTimeout`,[K]:xH}],[1,{[W]:`DefaultUdpTimeout`,[K]:EH}],[1,{[W]:`DefaultUdpStreamTimeout`,[K]:TH}]]],aZ=[3,q,dse,0,[DA,U],[[0,{[W]:`InternetGatewayId`,[K]:gW}],[2,{[W]:`DryRun`,[K]:yH}]],1],oZ=[3,q,kse,0,[mM,vM,U],[0,0,[2,{[W]:`DryRun`,[K]:yH}]]],sZ=[3,q,Ase,0,[WF,vM],[[2,{[W]:`Return`,[K]:xq}],[0,{[W]:`KeyPairId`,[K]:fG}]]],cZ=[3,q,Lse,0,[nP,U],[[0,{[K]:nP}],2],1],lZ=[3,q,Rse,0,[nP],[[0,{[W]:`NatGatewayId`,[K]:sK}]]],uZ=[3,q,Gse,0,[sA,U],[[0,{[W]:`GroupName`,[K]:PU}],[2,{[W]:`DryRun`,[K]:yH}]],1],dZ=[3,q,Xse,0,[FI,yk,U,mk,gk],[[0,{[W]:`RouteTableId`,[K]:Bq}],0,[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`DestinationCidrBlock`,[K]:iH}],[0,{[W]:`DestinationIpv6CidrBlock`,[K]:dH}]],1],fZ=[3,q,ece,0,[FI,U],[[0,{[W]:`RouteTableId`,[K]:Bq}],[2,{[W]:`DryRun`,[K]:yH}]],1],pZ=[3,q,uce,0,[aA,sA,U],[0,0,[2,{[W]:`DryRun`,[K]:yH}]]],mZ=[3,q,dce,0,[WF,aA],[[2,{[W]:`Return`,[K]:xq}],[0,{[W]:`GroupId`,[K]:MU}]]],hZ=[3,q,hce,0,[WL,U],[0,[2,{[W]:`DryRun`,[K]:yH}]],1],gZ=[3,q,gce,0,[XL,U],[0,[2,{[W]:`DryRun`,[K]:yH}]],1],_Z=[3,q,Mce,0,[qz,U],[0,[2,{[W]:`DryRun`,[K]:yH}]],1],vZ=[3,q,Nce,0,[Vz,U],[0,[2,{[W]:`DryRun`,[K]:yH}]],1],yZ=[3,q,Yoe,0,[xF,U,eA,hO],[[()=>Mpe,{[K]:lF}],[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}],[()=>D$,{[K]:mO}]]],bZ=[3,q,Xoe,0,[Nae],[[()=>Sfe,{[W]:`AddressesSet`,[K]:QB}]]],xZ=[3,q,$oe,0,[OB,TB,Uie,U,eA],[[()=>ume,{[K]:EB}],[()=>lme,{[K]:CB}],2,[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}]]],SZ=[3,q,ese,0,[Mae],[[()=>Efe,{[W]:`AvailabilityZoneInfo`,[K]:aV}]]],CZ=[3,q,vse,0,[kle,Wue,RP,bue,wue,yN,yP,U,eA],[[()=>zfe,{[K]:Ck}],[()=>Ufe,{[K]:NA}],[()=>I$,{[K]:zP}],2,2,1,0,[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}]]],wZ=[3,q,yse,0,[yP,rM],[[0,{[W]:`NextToken`,[K]:xK}],[()=>Wfe,{[W]:`ImagesSet`,[K]:KW}]]],TZ=[3,q,bse,0,[PA,VA,U,eA,yP,yN],[[()=>M$,{[K]:OA}],2,[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}],[0,{[W]:`NextToken`,[K]:xK}],[1,{[W]:`MaxResults`,[K]:VG}]]],EZ=[3,q,xse,0,[yP,qI],[[0,{[W]:`NextToken`,[K]:xK}],[()=>Ipe,{[W]:`ReservationSet`,[K]:Lq}]]],DZ=[3,q,Sse,0,[U,eM,eA,yN,yP,nM],[2,[64,{[K]:Yj}],[()=>k$,{[K]:tA}],1,0,2]],OZ=[3,q,Cse,0,[eM,yP],[[()=>ipe,{[W]:`InstanceTypeSet`,[K]:eG}],[0,{[W]:`NextToken`,[K]:xK}]]],kZ=[3,q,fse,0,[yP,yN,U,jue,eA],[0,1,[2,{[W]:`DryRun`,[K]:yH}],[()=>ope,{[W]:`InternetGatewayId`,[K]:gW}],[()=>k$,{[K]:tA}]]],AZ=[3,q,pse,0,[Nue,yP],[[()=>spe,{[W]:`InternetGatewaySet`,[K]:_W}],[0,{[W]:`NextToken`,[K]:xK}]]],jZ=[3,q,jse,0,[gM,bM,dj,U,eA],[[()=>ppe,{[K]:mM}],[()=>mpe,{[K]:vM}],2,[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}]]],MZ=[3,q,Mse,0,[_M],[[()=>hpe,{[W]:`KeySet`,[K]:pG}]]],NZ=[3,q,zse,0,[U,tA,yN,iP,yP],[2,[()=>k$,0],1,[()=>xpe,{[K]:nP}],0]],PZ=[3,q,Bse,0,[oP,yP],[[()=>Spe,{[W]:`NatGatewaySet`,[K]:cK}],[0,{[W]:`NextToken`,[K]:xK}]]],FZ=[3,q,Kse,0,[oA,U,cA,eA],[[()=>Epe,{[K]:aA}],[2,{[W]:`DryRun`,[K]:yH}],[64,{[W]:`GroupName`,[K]:PU}],[()=>k$,{[K]:tA}]]],IZ=[3,q,qse,0,[cF],[[()=>Dpe,{[W]:`PlacementGroupSet`,[K]:YK}]]],LZ=[3,q,Zse,0,[bI,fae,U,eA],[[()=>Fpe,{[K]:vI}],2,[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}]]],RZ=[3,q,Qse,0,[WI],[[()=>Ppe,{[W]:`RegionInfo`,[K]:jq}]]],zZ=[3,q,tce,0,[yP,yN,U,LI,eA],[0,1,[2,{[W]:`DryRun`,[K]:yH}],[()=>Bpe,{[K]:FI}],[()=>k$,{[K]:tA}]]],BZ=[3,q,nce,0,[HI,yP],[[()=>Vpe,{[W]:`RouteTableSet`,[K]:Vq}],[0,{[W]:`NextToken`,[K]:xK}]]],VZ=[3,q,fce,0,[oA,cA,yP,yN,U,eA],[[()=>j$,{[K]:aA}],[()=>Hfe,{[K]:sA}],0,1,[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}]]],HZ=[3,q,pce,0,[yP,SL],[[0,{[W]:`NextToken`,[K]:xK}],[()=>Hpe,{[W]:`SecurityGroupInfo`,[K]:rJ}]]],UZ=[3,q,_ce,0,[yN,yP,DP,qF,GL,U,eA],[1,0,[()=>I$,{[K]:zP}],[64,{[K]:JF}],[()=>Wpe,{[K]:WL}],[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}]]],WZ=[3,q,vce,0,[yP,jR],[[0,{[W]:`NextToken`,[K]:xK}],[()=>Gpe,{[W]:`SnapshotSet`,[K]:MJ}]]],GZ=[3,q,yce,0,[eA,wL,yP,yN,U],[[()=>k$,{[K]:tA}],[()=>Kpe,{[K]:XL}],0,1,[2,{[W]:`DryRun`,[K]:yH}]]],KZ=[3,q,bce,0,[yP,LR],[[0,{[W]:`NextToken`,[K]:xK}],[()=>Jpe,{[W]:`SubnetSet`,[K]:NJ}]]],qZ=[3,q,Pce,0,[Jz,VA,U,eA,yP,yN],[[()=>nme,{[K]:qz}],2,[2,{[W]:`DryRun`,[K]:yH}],[()=>k$,{[K]:tA}],[0,{[W]:`NextToken`,[K]:xK}],[1,{[W]:`MaxResults`,[K]:VG}]]],JZ=[3,q,Fce,0,[yP,_B],[[0,{[W]:`NextToken`,[K]:xK}],[()=>rme,{[W]:`VolumeSet`,[K]:LY}]]],YZ=[3,q,Ice,0,[eA,Yz,yP,yN,U],[[()=>k$,{[K]:tA}],[()=>ame,{[K]:Vz}],0,1,[2,{[W]:`DryRun`,[K]:yH}]]],XZ=[3,q,Lce,0,[yP,bB],[[0,{[W]:`NextToken`,[K]:xK}],[()=>sme,{[W]:`VpcSet`,[K]:zY}]]],ZZ=[3,q,mse,0,[DA,Vz,U],[[0,{[W]:`InternetGatewayId`,[K]:gW}],[0,{[W]:`VpcId`,[K]:DY}],[2,{[W]:`DryRun`,[K]:yH}]],2],QZ=[3,q,Rce,0,[qz,xk,Jk,OA,U],[0,0,2,0,[2,{[W]:`DryRun`,[K]:yH}]],1],$Z=[3,q,Zoe,0,[gO,lF,U],[0,0,[2,{[W]:`DryRun`,[K]:yH}]]],eQ=[3,q,rce,0,[gO,U],[[0,{[W]:`AssociationId`,[K]:LB}],[2,{[W]:`DryRun`,[K]:yH}]],1],tQ=[3,q,Dse,0,[DL,dk,hz],[[1,{[W]:`SizeInGB`,[K]:cJ}],[1,{[W]:`Count`,[K]:nH}],[0,{[W]:`Type`,[K]:mY}]]],nQ=[3,q,Gce,0,[vk,sM,WL,lB,uB,fM,pz,SP,wO,qk,Wz,DO,wk],[[2,{[W]:`DeleteOnTermination`,[K]:gH}],[1,{[W]:`Iops`,[K]:aG}],[0,{[W]:`SnapshotId`,[K]:_J}],[1,{[W]:`VolumeSize`,[K]:RY}],[0,{[W]:`VolumeType`,[K]:BY}],[0,{[W]:`KmsKeyId`,[K]:lG}],[1,{[W]:`Throughput`,[K]:fY}],[0,{[W]:`OutpostArn`,[K]:wK}],[0,{[W]:`AvailabilityZone`,[K]:rV}],[2,{[W]:`Encrypted`,[K]:_U}],1,0,1]],rQ=[3,q,Jce,0,[wk,PO,BO,LO,XM,TN,cN],[[1,{[W]:`EbsCardIndex`,[K]:MH}],[1,{[W]:`BaselineBandwidthInMbps`,[K]:mV}],[1,{[W]:`BaselineThroughputInMBps`,[K]:xV}],[1,{[W]:`BaselineIops`,[K]:gV}],[1,{[W]:`MaximumBandwidthInMbps`,[K]:DG}],[1,{[W]:`MaximumThroughputInMBps`,[K]:GG}],[1,{[W]:`MaximumIops`,[K]:PG}]]],iQ=[3,q,kk,0,[ble,Uk,Nk,hP,rN,nae,iN,Yce],[[0,{[W]:`EbsOptimizedSupport`,[K]:tU}],[0,{[W]:`EncryptionSupport`,[K]:fU}],[()=>sQ,{[W]:`EbsOptimizedInfo`,[K]:QH}],[0,{[W]:`NvmeSupport`,[K]:vK}],[1,{[W]:`MaximumEbsAttachments`,[K]:kG}],[0,{[W]:`AttachmentLimitType`,[K]:BB}],[1,{[W]:`MaximumEbsCards`,[K]:AG}],[()=>Pfe,{[W]:`EbsCardSet`,[K]:NH}]]],aQ=[3,q,mle,0,[CO,vk,PR,qz,yO,nB,IP,wk],[[4,{[W]:`AttachTime`,[K]:nV}],[2,{[W]:`DeleteOnTermination`,[K]:gH}],[0,{[W]:`Status`,[K]:WJ}],[0,{[W]:`VolumeId`,[K]:kY}],[0,{[W]:`AssociatedResource`,[K]:qB}],[0,{[W]:`VolumeOwnerId`,[K]:NY}],[()=>f$,{[W]:`Operator`,[K]:PK}],[1,{[W]:`EbsCardIndex`,[K]:MH}]]],oQ=[3,q,hle,0,[qz,vk],[[0,{[W]:`VolumeId`,[K]:kY}],[2,{[W]:`DeleteOnTermination`,[K]:gH}]]],sQ=[3,q,Nk,0,[PO,BO,LO,XM,TN,cN],[[1,{[W]:`BaselineBandwidthInMbps`,[K]:mV}],[1,{[W]:`BaselineThroughputInMBps`,[K]:xV}],[1,{[W]:`BaselineIops`,[K]:gV}],[1,{[W]:`MaximumBandwidthInMbps`,[K]:DG}],[1,{[W]:`MaximumThroughputInMBps`,[K]:GG}],[1,{[W]:`MaximumIops`,[K]:PG}]]],cQ=[3,q,Ak,0,[aN],[[1,{[W]:`MaximumEfaInterfaces`,[K]:jG}]]],lQ=[3,q,$ce,0,[ale,ele,nle,rle],[[0,{[W]:`ElasticGpuId`,[K]:VH}],[0,{[W]:`ElasticGpuAssociationId`,[K]:LH}],[0,{[W]:`ElasticGpuAssociationState`,[K]:RH}],[0,{[W]:`ElasticGpuAssociationTime`,[K]:BH}]]],uQ=[3,q,Ek,0,[hz],[0],1],dQ=[3,q,Dk,0,[hz,dk],[0,1],1],fQ=[3,q,sle,0,[fle,cle,ule,dle],[[0,{[W]:`ElasticInferenceAcceleratorArn`,[K]:HH}],[0,{[W]:`ElasticInferenceAcceleratorAssociationId`,[K]:UH}],[0,{[W]:`ElasticInferenceAcceleratorAssociationState`,[K]:WH}],[4,{[W]:`ElasticInferenceAcceleratorAssociationTime`,[K]:KH}]]],pQ=[3,q,wle,0,[zk,Hk],[2,()=>mQ]],mQ=[3,q,Ele,0,[Vk],[2]],hQ=[3,q,Fk,0,[Kk],[[2,{[W]:`Enabled`,[K]:gU}]]],gQ=[3,q,yle,0,[Kk],[2]],_Q=[3,q,tA,0,[IN,hB],[0,[()=>$pe,{[K]:Dz}]]],vQ=[3,q,jle,0,[IN,AN,dk,fN],[[0,{[W]:`Name`,[K]:QG}],[0,{[W]:`Manufacturer`,[K]:qG}],[1,{[W]:`Count`,[K]:nH}],[()=>yQ,{[W]:`MemoryInfo`,[K]:FG}]]],yQ=[3,q,Nle,0,[jL],[[1,{[W]:`SizeInMiB`,[K]:fJ}]]],bQ=[3,q,Xk,0,[Rle,BR],[[()=>Bfe,{[W]:`Fpgas`,[K]:DU}],[1,{[W]:`TotalFpgaMemoryInMiB`,[K]:YJ}]]],xQ=[3,q,Vle,0,[OA,UM,U],[0,2,[2,{[W]:`DryRun`,[K]:yH}]],1],SQ=[3,q,Hle,0,[OA,mz,LP],[[0,{[W]:`InstanceId`,[K]:vW}],[4,{[W]:`Timestamp`,[K]:pY}],[0,{[W]:`Output`,[K]:FK}]]],CQ=[3,q,Wle,0,[IN,AN,dk,AM,$le,SB,fN],[[0,{[W]:`Name`,[K]:QG}],[0,{[W]:`Manufacturer`,[K]:qG}],[1,{[W]:`Count`,[K]:nH}],[1,{[W]:`LogicalGpuCount`,[K]:_G}],[1,{[W]:`GpuPartitionSize`,[K]:IU}],[()=>cme,{[W]:`WorkloadSet`,[K]:GY}],[()=>wQ,{[W]:`MemoryInfo`,[K]:FG}]]],wQ=[3,q,Kle,0,[jL],[[1,{[W]:`SizeInMiB`,[K]:fJ}]]],TQ=[3,q,iA,0,[eue,HR],[[()=>Vfe,{[W]:`Gpus`,[K]:zU}],[1,{[W]:`TotalGpuMemoryInMiB`,[K]:ZJ}]]],EQ=[3,q,Yle,0,[aA,sA],[[0,{[W]:`GroupId`,[K]:MU}],[0,{[W]:`GroupName`,[K]:PU}]]],DQ=[3,q,dA,0,[uk],[[2,{[W]:`Configured`,[K]:tH}]]],OQ=[3,q,rue,0,[uk],[2]],kQ=[3,q,kA,0,[kO,sue],[[0,{[W]:`Arn`,[K]:cV}],[0,{[W]:`Id`,[K]:QU}]]],AQ=[3,q,zue,0,[kO,IN],[[0,{[W]:`Arn`,[K]:cV}],[0,{[W]:`Name`,[K]:QG}]]],jQ=[3,q,iM,0,[nF,Cz,IO,bk,Wk,gA,ej,IN,YF,XF,oR,hR,RR,pB,RO,sz,Dce,Gj,AL,Yse,PM,pue,OL,NL,$k,NA,Jue,NR,wP,Qae,UF,JP,AO,Qj,dM,cI,RF],[[0,{[W]:`PlatformDetails`,[K]:KK}],[0,{[W]:`UsageOperation`,[K]:vY}],[()=>Afe,{[W]:`BlockDeviceMapping`,[K]:hV}],[0,{[W]:`Description`,[K]:OH}],[2,{[W]:`EnaSupport`,[K]:pU}],[0,{[W]:`Hypervisor`,[K]:VU}],[0,{[W]:`ImageOwnerAlias`,[K]:EW}],[0,{[W]:`Name`,[K]:QG}],[0,{[W]:`RootDeviceName`,[K]:Sq}],[0,{[W]:`RootDeviceType`,[K]:Cq}],[0,{[W]:`SriovNetSupport`,[K]:TJ}],[()=>y$,{[W]:`StateReason`,[K]:OJ}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`VirtualizationType`,[K]:HY}],[0,{[W]:`BootMode`,[K]:_V}],[0,{[W]:`TpmSupport`,[K]:cY}],[0,{[W]:`DeprecationTime`,[K]:CH}],[0,{[W]:`ImdsSupport`,[K]:JW}],[0,{[W]:`SourceInstanceId`,[K]:lJ}],[0,{[W]:`DeregistrationProtection`,[K]:vH}],[0,{[W]:`LastLaunchedTime`,[K]:bG}],[2,{[W]:`ImageAllowed`,[K]:oW}],[0,{[W]:`SourceImageId`,[K]:dJ}],[0,{[W]:`SourceImageRegion`,[K]:pJ}],[2,{[W]:`FreeTierEligible`,[K]:TU}],[0,{[W]:`ImageId`,[K]:xW}],[0,{[W]:`ImageLocation`,[K]:SW}],[0,{[W]:`ImageState`,[K]:qW}],[0,{[W]:`ImageOwnerId`,[K]:DW}],[0,{[W]:`CreationDate`,[K]:AV}],[2,{[W]:`IsPublic`,[K]:BW}],[()=>L$,{[W]:`ProductCodes`,[K]:zK}],[0,{[W]:`Architecture`,[K]:lV}],[0,{[W]:`ImageType`,[K]:nG}],[0,{[W]:`KernelId`,[K]:cG}],[0,{[W]:`RamdiskId`,[K]:Aq}],[0,{[W]:`Platform`,[K]:gq}]]],MQ=[3,q,Kue,0,[mM,CF,nz,U],[[0,{[W]:`KeyName`,[K]:dG}],[21,{[W]:`PublicKeyMaterial`,[K]:iq}],[()=>U$,{[K]:az}],[2,{[W]:`DryRun`,[K]:yH}]],2],NQ=[3,q,que,0,[uM,mM,vM,RR],[[0,{[W]:`KeyFingerprint`,[K]:sG}],[0,{[W]:`KeyName`,[K]:dG}],[0,{[W]:`KeyPairId`,[K]:fG}],[()=>H$,{[W]:`TagSet`,[K]:aY}]]],PQ=[3,q,bA,0,[OO,WR],[[()=>Gfe,{[W]:`Accelerators`,[K]:oV}],[1,{[W]:`TotalInferenceMemoryInMiB`,[K]:QJ}]]],FQ=[3,q,xue,0,[dk,IN,AN,fN],[[1,{[W]:`Count`,[K]:nH}],[0,{[W]:`Name`,[K]:QG}],[0,{[W]:`Manufacturer`,[K]:qG}],[()=>IQ,{[W]:`MemoryInfo`,[K]:FG}]]],IQ=[3,q,Cue,0,[jL],[[1,{[W]:`SizeInMiB`,[K]:fJ}]]],LQ=[3,q,aM,0,[AO,IO,ak,Mk,Wk,gA,kA,Xue,ile,ple,lP,SP,YF,XF,SL,aL,ML,oR,hR,RR,pB,ZO,Jae,ek,rk,dA,GM,hN,Fk,RO,nF,Cz,wz,ZP,xA,sz,gN,eoe,fP,IP,HL,OA,NA,NR,XP,tF,SR,mM,tae,JP,Yj,HM,zF,dM,cI,RF,FN,XL,Vz,pF,hF],[[0,{[W]:`Architecture`,[K]:lV}],[()=>Kfe,{[W]:`BlockDeviceMapping`,[K]:hV}],[0,{[W]:`ClientToken`,[K]:YV}],[2,{[W]:`EbsOptimized`,[K]:ZH}],[2,{[W]:`EnaSupport`,[K]:pU}],[0,{[W]:`Hypervisor`,[K]:VU}],[()=>kQ,{[W]:`IamInstanceProfile`,[K]:yW}],[0,{[W]:`InstanceLifecycle`,[K]:CW}],[()=>Ffe,{[W]:`ElasticGpuAssociationSet`,[K]:zH}],[()=>Lfe,{[W]:`ElasticInferenceAcceleratorAssociationSet`,[K]:GH}],[()=>Zfe,{[W]:`NetworkInterfaceSet`,[K]:dK}],[0,{[W]:`OutpostArn`,[K]:wK}],[0,{[W]:`RootDeviceName`,[K]:Sq}],[0,{[W]:`RootDeviceType`,[K]:Cq}],[()=>A$,{[W]:`GroupSet`,[K]:RU}],[2,{[W]:`SourceDestCheck`,[K]:$q}],[0,{[W]:`SpotInstanceRequestId`,[K]:mJ}],[0,{[W]:`SriovNetSupport`,[K]:TJ}],[()=>y$,{[W]:`StateReason`,[K]:OJ}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`VirtualizationType`,[K]:HY}],[()=>PX,{[W]:`CpuOptions`,[K]:BV}],[0,{[W]:`CapacityBlockId`,[K]:EV}],[0,{[W]:`CapacityReservationId`,[K]:UV}],[()=>DX,{[W]:`CapacityReservationSpecification`,[K]:KV}],[()=>DQ,{[W]:`HibernationOptions`,[K]:GU}],[()=>gpe,{[W]:`LicenseSet`,[K]:xG}],[()=>YQ,{[W]:`MetadataOptions`,[K]:RG}],[()=>hQ,{[W]:`EnclaveOptions`,[K]:nU}],[0,{[W]:`BootMode`,[K]:_V}],[0,{[W]:`PlatformDetails`,[K]:KK}],[0,{[W]:`UsageOperation`,[K]:vY}],[4,{[W]:`UsageOperationUpdateTime`,[K]:yY}],[()=>Fde,{[W]:`PrivateDnsNameOptions`,[K]:HK}],[0,{[W]:`Ipv6Address`,[K]:cW}],[0,{[W]:`TpmSupport`,[K]:cY}],[()=>GQ,{[W]:`MaintenanceOptions`,[K]:zG}],[0,{[W]:`CurrentInstanceBootMode`,[K]:PV}],[()=>e$,{[W]:`NetworkPerformanceOptions`,[K]:hK}],[()=>f$,{[W]:`Operator`,[K]:PK}],[()=>epe,{[W]:`SecondaryInterfaceSet`,[K]:gJ}],[0,{[W]:`InstanceId`,[K]:vW}],[0,{[W]:`ImageId`,[K]:xW}],[()=>c$,{[W]:`InstanceState`,[K]:XW}],[0,{[W]:`PrivateDnsName`,[K]:WK}],[0,{[W]:`DnsName`,[K]:mH}],[0,{[W]:`Reason`,[K]:Kq}],[0,{[W]:`KeyName`,[K]:dG}],[1,{[W]:`AmiLaunchIndex`,[K]:zB}],[()=>L$,{[W]:`ProductCodes`,[K]:zK}],[0,{[W]:`InstanceType`,[K]:$W}],[4,{[W]:`LaunchTime`,[K]:SG}],[()=>p$,{[W]:`Placement`,[K]:_q}],[0,{[W]:`KernelId`,[K]:cG}],[0,{[W]:`RamdiskId`,[K]:Aq}],[0,{[W]:`Platform`,[K]:gq}],[()=>xde,{[W]:`Monitoring`,[K]:XG}],[0,{[W]:`SubnetId`,[K]:vJ}],[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`PrivateIpAddress`,[K]:$K}],[0,{[W]:`IpAddress`,[K]:sW}]]],RQ=[3,q,cue,0,[zk,Hk],[[2,{[W]:`EnaSrdEnabled`,[K]:oU}],[()=>zQ,{[W]:`EnaSrdUdpSpecification`,[K]:uU}]]],zQ=[3,q,lue,0,[Vk],[[2,{[W]:`EnaSrdUdpEnabled`,[K]:lU}]]],BQ=[3,q,hue,0,[_k,Gk],[[0,{[W]:`DeviceName`,[K]:fH}],[()=>aQ,{[W]:`Ebs`,[K]:hU}]]],VQ=[3,q,_ue,0,[_k,Gk,tB,HN],[[0,{[W]:`DeviceName`,[K]:fH}],[()=>oQ,{[W]:`Ebs`,[K]:hU}],[0,{[W]:`VirtualName`,[K]:MY}],[0,{[W]:`NoDevice`,[K]:nK}]]],HQ=[3,q,Bue,0,[wj],[[0,{[W]:`Ipv4Prefix`,[K]:LW}]]],UQ=[3,q,Pue,0,[xA,uj],[[0,{[W]:`Ipv6Address`,[K]:cW}],[2,{[W]:`IsPrimaryIpv6`,[K]:MW}]]],WQ=[3,q,Vue,0,[xj],[[0,{[W]:`Ipv6Prefix`,[K]:zW}]]],GQ=[3,q,FA,0,[bO,_I],[[0,{[W]:`AutoRecovery`,[K]:JB}],[0,{[W]:`RebootMigration`,[K]:Pq}]]],KQ=[3,q,IA,0,[bO],[0]],qQ=[3,q,LA,0,[wN,cR],[0,()=>rfe]],JQ=[3,q,RA,0,[mA,pA,uA,fA,HA],[0,1,0,0,0]],YQ=[3,q,zA,0,[NR,mA,pA,uA,fA,HA],[[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`HttpTokens`,[K]:XU}],[1,{[W]:`HttpPutResponseHopLimit`,[K]:qU}],[0,{[W]:`HttpEndpoint`,[K]:UU}],[0,{[W]:`HttpProtocolIpv6`,[K]:KU}],[0,{[W]:`InstanceMetadataTags`,[K]:wW}]]],XQ=[3,q,WA,0,[jO,MO,bk,nA,_A,YM,sP,wP,XP,pF,mF,aL,PR,XL,Vz,tM,Cj,_j,ok,IP],[[()=>ZQ,{[W]:`Association`,[K]:uV}],[()=>QQ,{[W]:`Attachment`,[K]:dV}],[0,{[W]:`Description`,[K]:OH}],[()=>A$,{[W]:`GroupSet`,[K]:RU}],[()=>N$,{[W]:`Ipv6AddressesSet`,[K]:aW}],[0,{[W]:`MacAddress`,[K]:EG}],[0,{[W]:`NetworkInterfaceId`,[K]:lK}],[0,{[W]:`OwnerId`,[K]:EK}],[0,{[W]:`PrivateDnsName`,[K]:WK}],[0,{[W]:`PrivateIpAddress`,[K]:$K}],[()=>$fe,{[W]:`PrivateIpAddressesSet`,[K]:ZK}],[2,{[W]:`SourceDestCheck`,[K]:$q}],[0,{[W]:`Status`,[K]:WJ}],[0,{[W]:`SubnetId`,[K]:vJ}],[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`InterfaceType`,[K]:iG}],[()=>Jfe,{[W]:`Ipv4PrefixSet`,[K]:NW}],[()=>Yfe,{[W]:`Ipv6PrefixSet`,[K]:PW}],[()=>jX,{[W]:`ConnectionTrackingConfiguration`,[K]:XV}],[()=>f$,{[W]:`Operator`,[K]:PK}]]],ZQ=[3,q,GA,0,[KO,QO,tj,tF,lF],[[0,{[W]:`CarrierIp`,[K]:NV}],[0,{[W]:`CustomerOwnedIp`,[K]:VV}],[0,{[W]:`IpOwnerId`,[K]:kW}],[0,{[W]:`PublicDnsName`,[K]:GK}],[0,{[W]:`PublicIp`,[K]:XK}]]],QQ=[3,q,KA,0,[CO,_O,vk,hk,PR,zN,Bk,Ik],[[4,{[W]:`AttachTime`,[K]:nV}],[0,{[W]:`AttachmentId`,[K]:RB}],[2,{[W]:`DeleteOnTermination`,[K]:gH}],[1,{[W]:`DeviceIndex`,[K]:uH}],[0,{[W]:`Status`,[K]:WJ}],[1,{[W]:`NetworkCardIndex`,[K]:tK}],[()=>RQ,{[W]:`EnaSrdSpecification`,[K]:sU}],[1,{[W]:`EnaQueueCount`,[K]:rU}]]],$Q=[3,q,JA,0,[iae,vk,bk,hk,nA,yA,_A,sP,pF,mF,dR,XL,Yie,tM,zN,Cj,aj,_j,ij,vF,Bk,Boe,Ik],[[2,{[W]:`AssociatePublicIpAddress`,[K]:HB}],[2,{[W]:`DeleteOnTermination`,[K]:gH}],[0,{[W]:`Description`,[K]:OH}],[1,{[W]:`DeviceIndex`,[K]:uH}],[()=>z$,{[K]:uL}],[1,{[W]:`Ipv6AddressCount`,[K]:eW}],[()=>N$,{[W]:`Ipv6Addresses`,[K]:aW}],[0,{[W]:`NetworkInterfaceId`,[K]:lK}],[0,{[W]:`PrivateIpAddress`,[K]:$K}],[()=>Ape,{[W]:`PrivateIpAddresses`,[K]:ZK}],[1,{[W]:`SecondaryPrivateIpAddressCount`,[K]:EJ}],[0,{[W]:`SubnetId`,[K]:vJ}],2,0,1,[()=>upe,{[K]:wj}],1,[()=>dpe,{[K]:xj}],1,2,()=>pQ,()=>AX,1]],e$=[3,q,QA,0,[VO],[[0,{[W]:`BandwidthWeighting`,[K]:SV}]]],t$=[3,q,$A,0,[VO],[0]],n$=[3,q,cj,0,[jO,BF,XP,pF],[[()=>ZQ,{[W]:`Association`,[K]:uV}],[2,{[W]:`Primary`,[K]:vq}],[0,{[W]:`PrivateDnsName`,[K]:WK}],[0,{[W]:`PrivateIpAddress`,[K]:$K}]]],r$=[3,q,Hj,0,[MO,YM,kL,wP,mF,aL,PR,yR,aR,tM],[[()=>i$,{[W]:`Attachment`,[K]:dV}],[0,{[W]:`MacAddress`,[K]:EG}],[0,{[W]:`SecondaryInterfaceId`,[K]:uJ}],[0,{[W]:`OwnerId`,[K]:EK}],[()=>tpe,{[W]:`PrivateIpAddressSet`,[K]:QK}],[2,{[W]:`SourceDestCheck`,[K]:$q}],[0,{[W]:`Status`,[K]:WJ}],[0,{[W]:`SecondarySubnetId`,[K]:jJ}],[0,{[W]:`SecondaryNetworkId`,[K]:CJ}],[0,{[W]:`InterfaceType`,[K]:iG}]]],i$=[3,q,Pj,0,[CO,_O,vk,hk,PR,zN],[[4,{[W]:`AttachTime`,[K]:nV}],[0,{[W]:`AttachmentId`,[K]:RB}],[2,{[W]:`DeleteOnTermination`,[K]:gH}],[1,{[W]:`DeviceIndex`,[K]:uH}],[0,{[W]:`Status`,[K]:WJ}],[1,{[W]:`NetworkCardIndex`,[K]:tK}]]],a$=[3,q,Ij,0,[pF],[[0,{[W]:`PrivateIpAddress`,[K]:$K}]]],o$=[3,q,zj,0,[pF],[0],1],s$=[3,q,Vj,0,[vk,hk,mF,uF,yR,tM,zN],[2,1,[()=>npe,{[K]:pF}],1,0,0,1]],c$=[3,q,Kj,0,[lk,IN],[[1,{[W]:`Code`,[K]:eH}],[0,{[W]:`Name`,[K]:QG}]]],$ue=[3,q,Mj,0,[OA,zoe,AF],[[0,{[W]:`InstanceId`,[K]:vW}],[()=>c$,{[W]:`CurrentState`,[K]:JV}],[()=>c$,{[W]:`PreviousState`,[K]:uq}]]],ede=[3,q,Uj,0,[rz,Wce,hP,Uk],[[1,{[W]:`TotalSizeInGB`,[K]:oY}],[()=>Nfe,{[W]:`Disks`,[K]:AH}],[0,{[W]:`NvmeSupport`,[K]:vK}],[0,{[W]:`EncryptionSupport`,[K]:fU}]]],tde=[3,q,Zj,0,[Yj,$ae,$k,TR,pR,OR,Uae,gA,bF,Nz,fN,Wj,Uj,kk,uP,iA,Xk,iF,bA,aue,Wae,lse,sae,nL,JN,_P,gP,JM,dP,kF,gI,RL],[[0,{[W]:`InstanceType`,[K]:$W}],[2,{[W]:`CurrentGeneration`,[K]:MV}],[2,{[W]:`FreeTierEligible`,[K]:TU}],[()=>Zpe,{[W]:`SupportedUsageClasses`,[K]:RJ}],[()=>Lpe,{[W]:`SupportedRootDeviceTypes`,[K]:DJ}],[()=>eme,{[W]:`SupportedVirtualizationTypes`,[K]:BJ}],[2,{[W]:`BareMetal`,[K]:vV}],[0,{[W]:`Hypervisor`,[K]:VU}],[()=>Lde,{[W]:`ProcessorInfo`,[K]:tq}],[()=>hfe,{[W]:`VCpuInfo`,[K]:CY}],[()=>gde,{[W]:`MemoryInfo`,[K]:FG}],[2,{[W]:`InstanceStorageSupported`,[K]:GW}],[()=>ede,{[W]:`InstanceStorageInfo`,[K]:WW}],[()=>iQ,{[W]:`EbsInfo`,[K]:JH}],[()=>Tde,{[W]:`NetworkInfo`,[K]:pK}],[()=>TQ,{[W]:`GpuInfo`,[K]:jU}],[()=>bQ,{[W]:`FpgaInfo`,[K]:bU}],[()=>jde,{[W]:`PlacementGroupInfo`,[K]:JK}],[()=>PQ,{[W]:`InferenceAcceleratorInfo`,[K]:tW}],[2,{[W]:`HibernationSupported`,[K]:YU}],[2,{[W]:`BurstablePerformanceSupported`,[K]:bV}],[2,{[W]:`DedicatedHostsSupported`,[K]:lH}],[2,{[W]:`AutoRecoverySupported`,[K]:GB}],[()=>jfe,{[W]:`SupportedBootModes`,[K]:Zq}],[0,{[W]:`NitroEnclavesSupport`,[K]:iK}],[0,{[W]:`NitroTpmSupport`,[K]:bK}],[()=>Ade,{[W]:`NitroTpmInfo`,[K]:yK}],[()=>pde,{[W]:`MediaAcceleratorInfo`,[K]:TG}],[()=>kde,{[W]:`NeuronInfo`,[K]:mK}],[0,{[W]:`PhcSupport`,[K]:lq}],[0,{[W]:`RebootMigrationSupport`,[K]:Nq}],[2,{[W]:`SupportedInRegion`,[K]:hJ}]]],l$=[3,q,EA,0,[NO,DA,wP,RR],[[()=>ape,{[W]:`AttachmentSet`,[K]:eV}],[0,{[W]:`InternetGatewayId`,[K]:gW}],[0,{[W]:`OwnerId`,[K]:EK}],[()=>H$,{[W]:`TagSet`,[K]:aY}]]],nde=[3,q,Eue,0,[NR,Vz],[[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`VpcId`,[K]:DY}]]],rde=[3,q,yj,0,[bj,Qk,$R,yz,Oj,Aj,EF],[[0,{[W]:`IpProtocol`,[K]:FW}],[1,{[W]:`FromPort`,[K]:SU}],[1,{[W]:`ToPort`,[K]:rY}],[()=>Qpe,{[W]:`Groups`,[K]:BU}],[()=>lpe,{[W]:`IpRanges`,[K]:HW}],[()=>fpe,{[W]:`Ipv6Ranges`,[K]:UW}],[()=>kpe,{[W]:`PrefixListIds`,[K]:oq}]]],ide=[3,q,kj,0,[bk,qO],[[0,{[W]:`Description`,[K]:OH}],[0,{[W]:`CidrIp`,[K]:FV}]]],ade=[3,q,hj,0,[wj],[0]],ode=[3,q,gj,0,[xj],[0]],sde=[3,q,jj,0,[bk,JO],[[0,{[W]:`Description`,[K]:OH}],[0,{[W]:`CidrIpv6`,[K]:IV}]]],cde=[3,q,CM,0,[vM,RR,mM,uM,pM],[[0,{[W]:`KeyPairId`,[K]:fG}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`KeyName`,[K]:dG}],[0,{[W]:`KeyFingerprint`,[K]:sG}],[()=>nX,{[W]:`KeyMaterial`,[K]:uG}]]],lde=[3,q,xM,0,[vM,wM,RR,SF,ck,mM,uM],[[0,{[W]:`KeyPairId`,[K]:fG}],[0,{[W]:`KeyType`,[K]:mG}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`PublicKey`,[K]:rq}],[4,{[W]:`CreateTime`,[K]:$V}],[0,{[W]:`KeyName`,[K]:dG}],[0,{[W]:`KeyFingerprint`,[K]:sG}]]],ude=[3,q,VM,0,[zM,BM,gB],[0,0,0]],dde=[3,q,OM,0,[EM],[[0,{[W]:`LicenseConfigurationArn`,[K]:gG}]]],fde=[3,q,DM,0,[EM],[0]],pde=[3,q,JM,0,[OO,ZR],[[()=>vpe,{[W]:`Accelerators`,[K]:oV}],[1,{[W]:`TotalMediaMemoryInMiB`,[K]:tY}]]],mde=[3,q,eN,0,[dk,IN,AN,fN],[[1,{[W]:`Count`,[K]:nH}],[0,{[W]:`Name`,[K]:QG}],[0,{[W]:`Manufacturer`,[K]:qG}],[()=>hde,{[W]:`MemoryInfo`,[K]:FG}]]],hde=[3,q,nN,0,[jL],[[1,{[W]:`SizeInMiB`,[K]:fJ}]]],gde=[3,q,fN,0,[jL],[[1,{[W]:`SizeInMiB`,[K]:fJ}]]],_de=[3,q,lN,0,[OA,aL,fk,U,Iae,Dz,IO,pk,Yj,TM,UI,_z,MA,nA,Mk,oR,Wk],[[0,{[W]:`InstanceId`,[K]:vW}],[()=>fX,0],[()=>fX,0],[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`Attribute`,[K]:fV}],[()=>eX,{[W]:`Value`,[K]:SY}],[()=>qfe,{[W]:`BlockDeviceMapping`,[K]:hV}],[()=>fX,{[W]:`DisableApiTermination`,[K]:rH}],[()=>pX,{[W]:`InstanceType`,[K]:$W}],[()=>pX,{[W]:`Kernel`,[K]:hG}],[()=>pX,{[W]:`Ramdisk`,[K]:Gq}],[()=>CX,{[W]:`UserData`,[K]:hY}],[()=>pX,{[W]:`InstanceInitiatedShutdownBehavior`,[K]:bW}],[()=>j$,{[K]:aA}],[()=>fX,{[W]:`EbsOptimized`,[K]:ZH}],[()=>pX,{[W]:`SriovNetSupport`,[K]:TJ}],[()=>fX,{[W]:`EnaSupport`,[K]:pU}]],1],vde=[3,q,bN,0,[XL,fO,_N,ZM,$O,Tk,YP,Cle,Sle,jk,Pse],[[0,{[W]:`SubnetId`,[K]:vJ}],[()=>fX,0],[()=>fX,0],[()=>fX,0],0,[()=>fX,0],0,[()=>fX,0],[()=>fX,0],1,[()=>fX,0]],1],yde=[3,q,EN,0,[qz,U,AR,uB,sM,pz,qM],[0,2,1,0,1,1,2],1],bde=[3,q,DN,0,[eB],[[()=>gfe,{[W]:`VolumeModification`,[K]:jY}]]],xde=[3,q,FN,0,[NR],[[0,{[W]:`State`,[K]:UJ}]]],u$=[3,q,YN,0,[ck,Oce,Yk,Zk,XN,nP,VP,NR,XL,Vz,RR,sk,vO,yae,oae,qie,FI],[[4,{[W]:`CreateTime`,[K]:$V}],[4,{[W]:`DeleteTime`,[K]:wH}],[0,{[W]:`FailureCode`,[K]:vU}],[0,{[W]:`FailureMessage`,[K]:xU}],[()=>ype,{[W]:`NatGatewayAddressSet`,[K]:oK}],[0,{[W]:`NatGatewayId`,[K]:sK}],[()=>Bde,{[W]:`ProvisionedBandwidth`,[K]:LK}],[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`SubnetId`,[K]:vJ}],[0,{[W]:`VpcId`,[K]:DY}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`ConnectivityType`,[K]:QV}],[0,{[W]:`AvailabilityMode`,[K]:VB}],[0,{[W]:`AutoScalingIps`,[K]:XB}],[0,{[W]:`AutoProvisionZones`,[K]:WB}],[()=>bpe,{[W]:`AttachedApplianceSet`,[K]:AB}],[0,{[W]:`RouteTableId`,[K]:Bq}]]],Sde=[3,q,eP,0,[mO,sP,yF,lF,gO,Tj,Zk,PR,wO,DO],[[0,{[W]:`AllocationId`,[K]:NB}],[0,{[W]:`NetworkInterfaceId`,[K]:lK}],[0,{[W]:`PrivateIp`,[K]:nq}],[0,{[W]:`PublicIp`,[K]:XK}],[0,{[W]:`AssociationId`,[K]:LB}],[2,{[W]:`IsPrimary`,[K]:VW}],[0,{[W]:`FailureMessage`,[K]:xU}],[0,{[W]:`Status`,[K]:WJ}],[0,{[W]:`AvailabilityZone`,[K]:rV}],[0,{[W]:`AvailabilityZoneId`,[K]:iV}]]],Cde=[3,q,ZN,0,[hz,Gie,Bz,bae,CN,Yk,Zk],[[0,{[W]:`Type`,[K]:mY}],[0,{[W]:`ApplianceArn`,[K]:jB}],[0,{[W]:`VpcEndpointId`,[K]:EY}],[0,{[W]:`AttachmentState`,[K]:tV}],[0,{[W]:`ModificationState`,[K]:WG}],[0,{[W]:`FailureCode`,[K]:vU}],[0,{[W]:`FailureMessage`,[K]:xU}]]],wde=[3,q,VN,0,[zN,pP,mN,Xie,Rae,HP,sse,oN,sN],[[1,{[W]:`NetworkCardIndex`,[K]:tK}],[0,{[W]:`NetworkPerformance`,[K]:gK}],[1,{[W]:`MaximumNetworkInterfaces`,[K]:LG}],[1,{[W]:`AdditionalFlexibleNetworkInterfaces`,[K]:MB}],[1,{[W]:`BaselineBandwidthInGbps`,[K]:pV}],[1,{[W]:`PeakBandwidthInGbps`,[K]:RK}],[1,{[W]:`DefaultEnaQueueCountPerInterface`,[K]:sH}],[1,{[W]:`MaximumEnaQueueCount`,[K]:MG}],[1,{[W]:`MaximumEnaQueueCountPerInterface`,[K]:NG}]]],Tde=[3,q,uP,0,[pP,mN,pN,Fse,RN,uue,due,Jj,Wk,Dle,Ak,gle,Tle,Kae,Ple,ok,sR,SN,fue],[[0,{[W]:`NetworkPerformance`,[K]:gK}],[1,{[W]:`MaximumNetworkInterfaces`,[K]:LG}],[1,{[W]:`MaximumNetworkCards`,[K]:IG}],[1,{[W]:`DefaultNetworkCardIndex`,[K]:pH}],[()=>Cpe,{[W]:`NetworkCards`,[K]:eK}],[1,{[W]:`Ipv4AddressesPerInterface`,[K]:nW}],[1,{[W]:`Ipv6AddressesPerInterface`,[K]:rW}],[2,{[W]:`Ipv6Supported`,[K]:QW}],[0,{[W]:`EnaSupport`,[K]:pU}],[2,{[W]:`EfaSupported`,[K]:dU}],[()=>cQ,{[W]:`EfaInfo`,[K]:YH}],[2,{[W]:`EncryptionInTransitSupported`,[K]:qH}],[2,{[W]:`EnaSrdSupported`,[K]:cU}],[()=>kfe,{[W]:`BandwidthWeightings`,[K]:CV}],[0,{[W]:`FlexibleEnaQueuesSupport`,[K]:yU}],[()=>iZ,{[W]:`ConnectionTrackingConfiguration`,[K]:XV}],[2,{[W]:`SecondaryNetworkSupported`,[K]:wJ}],[1,{[W]:`MaximumSecondaryNetworkInterfaces`,[K]:UG}],[1,{[W]:`Ipv4AddressesPerSecondaryInterface`,[K]:iW}]]],Ede=[3,q,UN,0,[dk,gB],[[1,{[W]:`Count`,[K]:nH}],[1,{[W]:`Version`,[K]:UY}]]],Dde=[3,q,WN,0,[dk,IN,ooe,fN],[[1,{[W]:`Count`,[K]:nH}],[0,{[W]:`Name`,[K]:QG}],[()=>Ede,{[W]:`CoreInfo`,[K]:RV}],[()=>Ode,{[W]:`MemoryInfo`,[K]:FG}]]],Ode=[3,q,KN,0,[jL],[[1,{[W]:`SizeInMiB`,[K]:fJ}]]],kde=[3,q,dP,0,[qN,QR],[[()=>wpe,{[W]:`NeuronDevices`,[K]:rK}],[1,{[W]:`TotalNeuronDeviceMemoryInMiB`,[K]:nY}]]],Ade=[3,q,gP,0,[kR],[[()=>Tpe,{[W]:`SupportedVersions`,[K]:VJ}]]],d$=[3,q,AP,0,[BP],[0]],f$=[3,q,jP,0,[jN,BP,tue],[[2,{[W]:`Managed`,[K]:JG}],[0,{[W]:`Principal`,[K]:IK}],[2,{[W]:`HiddenByDefault`,[K]:HU}]]],p$=[3,q,zF,0,[DO,Pae,sA,DF,nue,fz,oL,iue,aA,wO],[[0,{[W]:`AvailabilityZoneId`,[K]:iV}],[0,{[W]:`Affinity`,[K]:sV}],[0,{[W]:`GroupName`,[K]:PU}],[1,{[W]:`PartitionNumber`,[K]:sq}],[0,{[W]:`HostId`,[K]:WU}],[0,{[W]:`Tenancy`,[K]:qJ}],[0,{[W]:`SpreadDomain`,[K]:eJ}],[0,{[W]:`HostResourceGroupArn`,[K]:JU}],[0,{[W]:`GroupId`,[K]:MU}],[0,{[W]:`AvailabilityZone`,[K]:rV}]]],m$=[3,q,rF,0,[sA,NR,FR,KP,aA,RR,zle,$L,MM,IP],[[0,{[W]:`GroupName`,[K]:PU}],[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`Strategy`,[K]:GJ}],[1,{[W]:`PartitionCount`,[K]:BK}],[0,{[W]:`GroupId`,[K]:MU}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`GroupArn`,[K]:OU}],[0,{[W]:`SpreadLevel`,[K]:yJ}],[0,{[W]:`LinkedGroupId`,[K]:yG}],[()=>f$,{[W]:`Operator`,[K]:PK}]]],jde=[3,q,iF,0,[bR],[[()=>Ope,{[W]:`SupportedStrategies`,[K]:PJ}]]],Mde=[3,q,wF,0,[bk,wF],[[0,{[W]:`Description`,[K]:OH}],[0,{[W]:`PrefixListId`,[K]:aq}]]],Nde=[3,q,QP,0,[hA,Rk,Lk],[[0,{[W]:`HostnameType`,[K]:ZU}],[2,{[W]:`EnableResourceNameDnsARecord`,[K]:aU}],[2,{[W]:`EnableResourceNameDnsAAAARecord`,[K]:iU}]]],Pde=[3,q,$P,0,[hA,Rk,Lk],[0,2,2]],Fde=[3,q,eF,0,[hA,Rk,Lk],[[0,{[W]:`HostnameType`,[K]:ZU}],[2,{[W]:`EnableResourceNameDnsARecord`,[K]:aU}],[2,{[W]:`EnableResourceNameDnsAAAARecord`,[K]:iU}]]],Ide=[3,q,dF,0,[BF,pF],[[2,{[W]:`Primary`,[K]:vq}],[0,{[W]:`PrivateIpAddress`,[K]:$K}]]],Lde=[3,q,bF,0,[tL,iL,sL,AN],[[()=>Cfe,{[W]:`SupportedArchitectures`,[K]:Yq}],[1,{[W]:`SustainedClockSpeedInGhz`,[K]:Qq}],[()=>Ype,{[W]:`SupportedFeatures`,[K]:tJ}],[0,{[W]:`Manufacturer`,[K]:qG}]]],Rde=[3,q,qP,0,[UP,GP],[[0,{[W]:`ProductCode`,[K]:VK}],[0,{[W]:`Type`,[K]:mY}]]],zde=[3,q,NF,0,[rA],[[0,{[W]:`GatewayId`,[K]:AU}]]],Bde=[3,q,VP,0,[jF,HF,BI,KI,PR],[[4,{[W]:`ProvisionTime`,[K]:dq}],[0,{[W]:`Provisioned`,[K]:bq}],[4,{[W]:`RequestTime`,[K]:Uq}],[0,{[W]:`Requested`,[K]:qq}],[0,{[W]:`Status`,[K]:WJ}]]],Vde=[3,q,aI,0,[PA,U],[[()=>M$,{[K]:OA}],[2,{[W]:`DryRun`,[K]:yH}]],1],Hde=[3,q,xI,0,[aA,OF,vz,Vz,rB],[[0,{[W]:`GroupId`,[K]:MU}],[0,{[W]:`PeeringStatus`,[K]:cq}],[0,{[W]:`UserId`,[K]:gY}],[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`VpcPeeringConnectionId`,[K]:PY}]]],Ude=[3,q,GI,0,[TP,lA,vI,Ale],[[0,{[W]:`OptInStatus`,[K]:DK}],[()=>Npe,{[W]:`GeographySet`,[K]:LU}],[0,{[W]:`RegionName`,[K]:Fq}],[0,{[W]:`RegionEndpoint`,[K]:Tq}]]],Wde=[3,q,eI,0,[IN],[[0,{[W]:`Name`,[K]:QG}]]],Gde=[3,q,GF,0,[mO,lF,LN,U],[0,0,0,[2,{[W]:`DryRun`,[K]:yH}]]],h$=[3,q,JI,0,[lI,wP,dI,nA,oM],[[0,{[W]:`ReservationId`,[K]:kq}],[0,{[W]:`OwnerId`,[K]:EK}],[0,{[W]:`RequesterId`,[K]:Mq}],[()=>A$,{[W]:`GroupSet`,[K]:RU}],[()=>Xfe,{[W]:`InstancesSet`,[K]:YW}]]],Kde=[3,q,OI,0,[vL,aA,TA,bj,Qk,$R,YO,JO,wF,tI,bk],[[0,{[W]:`SecurityGroupRuleId`,[K]:aJ}],[0,{[W]:`GroupId`,[K]:MU}],[2,{[W]:`IsEgress`,[K]:pW}],[0,{[W]:`IpProtocol`,[K]:FW}],[1,{[W]:`FromPort`,[K]:SU}],[1,{[W]:`ToPort`,[K]:rY}],[0,{[W]:`CidrIpv4`,[K]:LV}],[0,{[W]:`CidrIpv6`,[K]:IV}],[0,{[W]:`PrefixListId`,[K]:aq}],[0,{[W]:`ReferencedGroupId`,[K]:Dq}],[0,{[W]:`Description`,[K]:OH}]]],qde=[3,q,CI,0,[aA,gL,U,gR,_R,bj,Qk,$R,qO,vj],[[0,{[W]:`GroupId`,[K]:MU}],[()=>B$,{[K]:vL}],[2,{[W]:`DryRun`,[K]:yH}],[0,{[W]:`SourceSecurityGroupName`,[K]:kJ}],[0,{[W]:`SourceSecurityGroupOwnerId`,[K]:AJ}],[0,{[W]:`IpProtocol`,[K]:FW}],[1,{[W]:`FromPort`,[K]:SU}],[1,{[W]:`ToPort`,[K]:rY}],[0,{[W]:`CidrIp`,[K]:FV}],[()=>F$,{[W]:`IpPermissions`,[K]:IW}]],1],Jde=[3,q,wI,0,[WF,Sz,AI],[[2,{[W]:`Return`,[K]:xq}],[()=>F$,{[W]:`UnknownIpPermissionSet`,[K]:_Y}],[()=>R$,{[W]:`RevokedSecurityGroupRuleSet`,[K]:Iq}]]],Yde=[3,q,TI,0,[qO,Qk,aA,sA,vj,bj,gR,_R,$R,gL,U],[0,1,0,0,[()=>F$,0],0,0,0,1,[()=>B$,{[K]:vL}],[2,{[W]:`DryRun`,[K]:yH}]]],Xde=[3,q,EI,0,[WF,Sz,AI],[[2,{[W]:`Return`,[K]:xq}],[()=>F$,{[W]:`UnknownIpPermissionSet`,[K]:_Y}],[()=>R$,{[W]:`RevokedSecurityGroupRuleSet`,[K]:Iq}]]],Zde=[3,q,YI,0,[mk,gk,yk,Pk,rA,OA,nj,nP,VR,jM,GO,sP,xP,NR,rB,XO,kP,mue],[[0,{[W]:`DestinationCidrBlock`,[K]:iH}],[0,{[W]:`DestinationIpv6CidrBlock`,[K]:dH}],[0,{[W]:`DestinationPrefixListId`,[K]:_H}],[0,{[W]:`EgressOnlyInternetGatewayId`,[K]:eU}],[0,{[W]:`GatewayId`,[K]:AU}],[0,{[W]:`InstanceId`,[K]:vW}],[0,{[W]:`InstanceOwnerId`,[K]:OW}],[0,{[W]:`NatGatewayId`,[K]:sK}],[0,{[W]:`TransitGatewayId`,[K]:XJ}],[0,{[W]:`LocalGatewayId`,[K]:vG}],[0,{[W]:`CarrierGatewayId`,[K]:jV}],[0,{[W]:`NetworkInterfaceId`,[K]:lK}],[0,{[W]:`Origin`,[K]:CK}],[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`VpcPeeringConnectionId`,[K]:PY}],[0,{[W]:`CoreNetworkArn`,[K]:zV}],[0,{[W]:`OdbNetworkArn`,[K]:AK}],[0,{[W]:`IpAddress`,[K]:sW}]]],g$=[3,q,VI,0,[Fae,FF,FI,XI,RR,Vz,wP],[[()=>zpe,{[W]:`AssociationSet`,[K]:$B}],[()=>jpe,{[W]:`PropagatingVgwSet`,[K]:pq}],[0,{[W]:`RouteTableId`,[K]:Bq}],[()=>Rpe,{[W]:`RouteSet`,[K]:Rq}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`OwnerId`,[K]:EK}]]],Qde=[3,q,jI,0,[kN,MI,FI,XL,rA,gF,xO],[[2,{[W]:`Main`,[K]:KG}],[0,{[W]:`RouteTableAssociationId`,[K]:zq}],[0,{[W]:`RouteTableId`,[K]:Bq}],[0,{[W]:`SubnetId`,[K]:vJ}],[0,{[W]:`GatewayId`,[K]:AU}],[0,{[W]:`PublicIpv4Pool`,[K]:eq}],[()=>_$,{[W]:`AssociationState`,[K]:YB}]]],_$=[3,q,PI,0,[NR,rR],[[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`StatusMessage`,[K]:xJ}]]],$de=[3,q,iI,0,[Kk],[[2,{[W]:`Enabled`,[K]:gU}]],1],efe=[3,q,oI,0,[QM,$M,IO,NA,Yj,yA,_A,dM,mM,FN,zF,cI,fL,SL,XL,_z,Ek,Ok,nz,RM,BA,Ioe,ZO,rk,dA,FM,hN,Fk,ZP,gN,fk,xle,fP,IP,HL,U,pk,MA,pF,ak,pO,lP,kA,Mk],[1,1,[()=>O$,{[K]:FO}],0,0,1,[()=>N$,{[K]:xA}],0,0,[()=>$de,0],[()=>p$,0],0,[()=>z$,{[K]:uL}],[()=>Upe,{[K]:CL}],0,[()=>tX,0],[()=>Ife,0],[()=>Rfe,{[K]:Dk}],[()=>U$,{[K]:az}],()=>ude,()=>qQ,()=>rZ,()=>FX,()=>EX,()=>OQ,[()=>_pe,{[K]:LM}],()=>JQ,()=>gQ,()=>Pde,()=>KQ,2,2,()=>t$,()=>d$,[()=>rpe,{[K]:UL}],[2,{[W]:`DryRun`,[K]:yH}],[2,{[W]:`DisableApiTermination`,[K]:rH}],[0,{[W]:`InstanceInitiatedShutdownBehavior`,[K]:bW}],[0,{[W]:`PrivateIpAddress`,[K]:$K}],[0,{[W]:`ClientToken`,[tG]:1,[K]:YV}],[0,{[W]:`AdditionalInfo`,[K]:IB}],[()=>Qfe,{[W]:`NetworkInterface`,[K]:fK}],[()=>AQ,{[W]:`IamInstanceProfile`,[K]:yW}],[2,{[W]:`EbsOptimized`,[K]:ZH}]],2],tfe=[3,q,CL,0,[aA,oj,RR,Vz,lL,wP,sA,bk,vj],[[0,{[W]:`GroupId`,[K]:MU}],[()=>F$,{[W]:`IpPermissionsEgress`,[K]:jW}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`SecurityGroupArn`,[K]:nJ}],[0,{[W]:`OwnerId`,[K]:EK}],[0,{[W]:`GroupName`,[K]:PU}],[0,{[W]:`GroupDescription`,[K]:kU}],[()=>F$,{[W]:`IpPermissions`,[K]:IW}]]],nfe=[3,q,bL,0,[vL,aA,Qle,TA,bj,Qk,$R,YO,JO,wF,nI,bk,RR,hL],[[0,{[W]:`SecurityGroupRuleId`,[K]:aJ}],[0,{[W]:`GroupId`,[K]:MU}],[0,{[W]:`GroupOwnerId`,[K]:FU}],[2,{[W]:`IsEgress`,[K]:pW}],[0,{[W]:`IpProtocol`,[K]:FW}],[1,{[W]:`FromPort`,[K]:SU}],[1,{[W]:`ToPort`,[K]:rY}],[0,{[W]:`CidrIpv4`,[K]:LV}],[0,{[W]:`CidrIpv6`,[K]:IV}],[0,{[W]:`PrefixListId`,[K]:aq}],[()=>Hde,{[W]:`ReferencedGroupInfo`,[K]:Oq}],[0,{[W]:`Description`,[K]:OH}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`SecurityGroupRuleArn`,[K]:iJ}]]],v$=[3,q,MR,0,[CP,SP,RR,wR,QF,CR,wO,lz,WO,Uoe,Lle,WL,qz,NR,iR,xR,VF,wP,bk,lB,qk,fM,ose],[[0,{[W]:`OwnerAlias`,[K]:TK}],[0,{[W]:`OutpostArn`,[K]:wK}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`StorageTier`,[K]:LJ}],[4,{[W]:`RestoreExpiryTime`,[K]:wq}],[0,{[W]:`SseType`,[K]:IJ}],[0,{[W]:`AvailabilityZone`,[K]:rV}],[0,{[W]:`TransferType`,[K]:uY}],[1,{[W]:`CompletionDurationMinutes`,[K]:kV}],[4,{[W]:`CompletionTime`,[K]:ZV}],[1,{[W]:`FullSnapshotSizeInBytes`,[K]:wU}],[0,{[W]:`SnapshotId`,[K]:_J}],[0,{[W]:`VolumeId`,[K]:kY}],[0,{[W]:`Status`,[K]:WJ}],[0,{[W]:`StatusMessage`,[K]:xJ}],[4,{[W]:`StartTime`,[K]:FJ}],[0,{[W]:`Progress`,[K]:yq}],[0,{[W]:`OwnerId`,[K]:EK}],[0,{[W]:`Description`,[K]:OH}],[1,{[W]:`VolumeSize`,[K]:RY}],[2,{[W]:`Encrypted`,[K]:_U}],[0,{[W]:`KmsKeyId`,[K]:lG}],[0,{[W]:`DataEncryptionKeyId`,[K]:oH}]]],rfe=[3,q,nR,0,[vN,VL,Vae,mB,Iue],[0,0,1,4,0]],ife=[3,q,PL,0,[PA,pO,U],[[()=>M$,{[K]:OA}],[0,{[W]:`AdditionalInfo`,[K]:IB}],[2,{[W]:`DryRun`,[K]:yH}]],1],afe=[3,q,FL,0,[KL],[[()=>P$,{[W]:`InstancesSet`,[K]:YW}]]],y$=[3,q,hR,0,[lk,MN],[[0,{[W]:`Code`,[K]:eH}],[0,{[W]:`Message`,[K]:YG}]]],ofe=[3,q,IL,0,[PA,oue,lR,U,Jk],[[()=>M$,{[K]:OA}],2,2,[2,{[W]:`DryRun`,[K]:yH}],[2,{[W]:`Force`,[K]:EU}]],1],sfe=[3,q,LL,0,[JL],[[()=>P$,{[W]:`InstancesSet`,[K]:YW}]]],b$=[3,q,IR,0,[DO,jk,ZM,$O,wP,fO,CA,RR,eL,SP,Tk,UA,QP,zO,hz,XL,NR,Vz,HO,Zie,wO,cse,_N],[[0,{[W]:`AvailabilityZoneId`,[K]:iV}],[1,{[W]:`EnableLniAtDeviceIndex`,[K]:XH}],[2,{[W]:`MapCustomerOwnedIpOnLaunch`,[K]:OG}],[0,{[W]:`CustomerOwnedIpv4Pool`,[K]:HV}],[0,{[W]:`OwnerId`,[K]:EK}],[2,{[W]:`AssignIpv6AddressOnCreation`,[K]:FB}],[()=>qpe,{[W]:`Ipv6CidrBlockAssociationSet`,[K]:uW}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`SubnetArn`,[K]:Xq}],[0,{[W]:`OutpostArn`,[K]:wK}],[2,{[W]:`EnableDns64`,[K]:FH}],[2,{[W]:`Ipv6Native`,[K]:TW}],[()=>Nde,{[W]:`PrivateDnsNameOptionsOnLaunch`,[K]:UK}],[()=>TX,{[W]:`BlockPublicAccessStates`,[K]:yV}],[0,{[W]:`Type`,[K]:mY}],[0,{[W]:`SubnetId`,[K]:vJ}],[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`CidrBlock`,[K]:wV}],[1,{[W]:`AvailableIpAddressCount`,[K]:PB}],[0,{[W]:`AvailabilityZone`,[K]:rV}],[2,{[W]:`DefaultForAz`,[K]:cH}],[2,{[W]:`MapPublicIpOnLaunch`,[K]:BG}]]],cfe=[3,q,rL,0,[NR,rR],[[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`StatusMessage`,[K]:xJ}]]],lfe=[3,q,TL,0,[gO,SA,wA,vA,qj],[[0,{[W]:`AssociationId`,[K]:LB}],[0,{[W]:`Ipv6CidrBlock`,[K]:lW}],[()=>cfe,{[W]:`Ipv6CidrBlockState`,[K]:dW}],[0,{[W]:`Ipv6AddressAttribute`,[K]:$U}],[0,{[W]:`IpSource`,[K]:ZW}]]],ufe=[3,q,dz,0,[cM,Dz],[[0,{[W]:`Key`,[K]:oG}],[0,{[W]:`Value`,[K]:SY}]]],dfe=[3,q,az,0,[zI,RR],[[0,{[W]:`ResourceType`,[K]:Hq}],[()=>H$,{[K]:dz}]]],ffe=[3,q,GR,0,[PA,Jk,lR,U],[[()=>M$,{[K]:OA}],2,2,[2,{[W]:`DryRun`,[K]:yH}]],1],pfe=[3,q,KR,0,[JR],[[()=>P$,{[W]:`InstancesSet`,[K]:YW}]]],mfe=[3,q,xz,0,[bk,vz,sA,aA,Vz,rB,OF],[[0,{[W]:`Description`,[K]:OH}],[0,{[W]:`UserId`,[K]:gY}],[0,{[W]:`GroupName`,[K]:PU}],[0,{[W]:`GroupId`,[K]:MU}],[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`VpcPeeringConnectionId`,[K]:PY}],[0,{[W]:`PeeringStatus`,[K]:cq}]]],hfe=[3,q,Nz,0,[jce,ase,Ece,Pz,fB],[[1,{[W]:`DefaultVCpus`,[K]:DH}],[1,{[W]:`DefaultCores`,[K]:aH}],[1,{[W]:`DefaultThreadsPerCore`,[K]:SH}],[()=>Mfe,{[W]:`ValidCores`,[K]:wY}],[()=>Xpe,{[W]:`ValidThreadsPerCore`,[K]:VY}]]],x$=[3,q,vB,0,[DO,SP,DR,sM,RR,uB,Ile,qM,pz,CR,IP,Wz,qz,AR,WL,wO,NR,ck,NO,qk,fM],[[0,{[W]:`AvailabilityZoneId`,[K]:iV}],[0,{[W]:`OutpostArn`,[K]:wK}],[0,{[W]:`SourceVolumeId`,[K]:zJ}],[1,{[W]:`Iops`,[K]:aG}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[0,{[W]:`VolumeType`,[K]:BY}],[2,{[W]:`FastRestored`,[K]:CU}],[2,{[W]:`MultiAttachEnabled`,[K]:wG}],[1,{[W]:`Throughput`,[K]:fY}],[0,{[W]:`SseType`,[K]:IJ}],[()=>f$,{[W]:`Operator`,[K]:PK}],[1,{[W]:`VolumeInitializationRate`,[K]:OY}],[0,{[W]:`VolumeId`,[K]:kY}],[1,{[W]:`Size`,[K]:HJ}],[0,{[W]:`SnapshotId`,[K]:_J}],[0,{[W]:`AvailabilityZone`,[K]:rV}],[0,{[W]:`Status`,[K]:WJ}],[4,{[W]:`CreateTime`,[K]:$V}],[()=>tme,{[W]:`AttachmentSet`,[K]:eV}],[2,{[W]:`Encrypted`,[K]:_U}],[0,{[W]:`KmsKeyId`,[K]:lG}]]],S$=[3,q,kz,0,[vk,yO,rj,wk,qz,OA,xk,NR,CO],[[2,{[W]:`DeleteOnTermination`,[K]:gH}],[0,{[W]:`AssociatedResource`,[K]:qB}],[0,{[W]:`InstanceOwningService`,[K]:AW}],[1,{[W]:`EbsCardIndex`,[K]:MH}],[0,{[W]:`VolumeId`,[K]:kY}],[0,{[W]:`InstanceId`,[K]:vW}],[0,{[W]:`Device`,[K]:kH}],[0,{[W]:`Status`,[K]:WJ}],[4,{[W]:`AttachTime`,[K]:nV}]]],gfe=[3,q,eB,0,[qz,CN,rR,oz,qR,uz,cz,XR,NP,EP,FP,PP,OP,VF,xR,Ole,IP],[[0,{[W]:`VolumeId`,[K]:kY}],[0,{[W]:`ModificationState`,[K]:WG}],[0,{[W]:`StatusMessage`,[K]:xJ}],[1,{[W]:`TargetSize`,[K]:sY}],[1,{[W]:`TargetIops`,[K]:$J}],[0,{[W]:`TargetVolumeType`,[K]:dY}],[1,{[W]:`TargetThroughput`,[K]:lY}],[2,{[W]:`TargetMultiAttachEnabled`,[K]:eY}],[1,{[W]:`OriginalSize`,[K]:jK}],[1,{[W]:`OriginalIops`,[K]:OK}],[0,{[W]:`OriginalVolumeType`,[K]:NK}],[1,{[W]:`OriginalThroughput`,[K]:MK}],[2,{[W]:`OriginalMultiAttachEnabled`,[K]:kK}],[1,{[W]:`Progress`,[K]:yq}],[4,{[W]:`StartTime`,[K]:FJ}],[4,{[W]:`EndTime`,[K]:mU}],[()=>f$,{[W]:`Operator`,[K]:PK}]]],C$=[3,q,yB,0,[wP,$j,CA,qae,Tue,Xce,RR,zO,Vz,NR,HO,Use],[[0,{[W]:`OwnerId`,[K]:EK}],[0,{[W]:`InstanceTenancy`,[K]:rG}],[()=>ome,{[W]:`Ipv6CidrBlockAssociationSet`,[K]:uW}],[()=>ime,{[W]:`CidrBlockAssociationSet`,[K]:TV}],[2,{[W]:`IsDefault`,[K]:fW}],[()=>vfe,{[W]:`EncryptionControl`,[K]:PH}],[()=>H$,{[W]:`TagSet`,[K]:aY}],[()=>TX,{[W]:`BlockPublicAccessStates`,[K]:yV}],[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`CidrBlock`,[K]:wV}],[0,{[W]:`DhcpOptionsId`,[K]:hH}]]],_fe=[3,q,Az,0,[gO,HO,Yae],[[0,{[W]:`AssociationId`,[K]:LB}],[0,{[W]:`CidrBlock`,[K]:wV}],[()=>w$,{[W]:`CidrBlockState`,[K]:DV}]]],w$=[3,q,Mz,0,[NR,rR],[[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`StatusMessage`,[K]:xJ}]]],vfe=[3,q,Fz,0,[Vz,zz,PN,NR,iR,$F,RR],[[0,{[W]:`VpcId`,[K]:DY}],[0,{[W]:`VpcEncryptionControlId`,[K]:TY}],[0,{[W]:`Mode`,[K]:ZG}],[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`StateMessage`,[K]:SJ}],[()=>bfe,{[W]:`ResourceExclusions`,[K]:Eq}],[()=>H$,{[W]:`TagSet`,[K]:aY}]]],yfe=[3,q,Iz,0,[PN,kue,vle,tP,oB,iB,kM,Zz,Qce],[0,0,0,0,0,0,0,0,0],1],T$=[3,q,Lz,0,[NR,iR],[[0,{[W]:`State`,[K]:UJ}],[0,{[W]:`StateMessage`,[K]:SJ}]]],bfe=[3,q,Rz,0,[EA,_le,YN,aB,sB,WM,Xz,Zce],[[()=>T$,{[W]:`InternetGateway`,[K]:mW}],[()=>T$,{[W]:`EgressOnlyInternetGateway`,[K]:$H}],[()=>T$,{[W]:`NatGateway`,[K]:aK}],[()=>T$,{[W]:`VirtualPrivateGateway`,[K]:FY}],[()=>T$,{[W]:`VpcPeering`,[K]:IY}],[()=>T$,{[W]:`Lambda`,[K]:CG}],[()=>T$,{[W]:`VpcLattice`,[K]:AY}],[()=>T$,{[W]:`ElasticFileSystem`,[K]:IH}]]],xfe=[3,q,Hz,0,[gO,SA,wA,LN,Sj,vA,qj],[[0,{[W]:`AssociationId`,[K]:LB}],[0,{[W]:`Ipv6CidrBlock`,[K]:lW}],[()=>w$,{[W]:`Ipv6CidrBlockState`,[K]:dW}],[0,{[W]:`NetworkBorderGroup`,[K]:$G}],[0,{[W]:`Ipv6Pool`,[K]:RW}],[0,{[W]:`Ipv6AddressAttribute`,[K]:$U}],[0,{[W]:`IpSource`,[K]:ZW}]]],E$=`unit`,Sfe=[1,q,rae,0,[()=>rX,{[K]:G}]],D$=[1,q,eae,0,[0,{[K]:mO}]],Cfe=[1,q,xae,0,[0,{[K]:G}]],wfe=[1,q,EO,0,[()=>yX,{[K]:TO}]],Tfe=[1,q,Eae,0,[()=>bX,{[K]:G}]],Efe=[1,q,Dae,0,[()=>vX,{[K]:G}]],Dfe=[1,q,kae,0,[()=>xX,{[K]:G}]],Ofe=[1,q,jae,0,[()=>SX,{[K]:G}]],kfe=[1,q,Gae,0,[0,{[K]:G}]],Afe=[1,q,zae,0,[()=>wX,{[K]:G}]],O$=[1,q,Bae,0,[()=>wX,{[K]:FO}]],jfe=[1,q,Hae,0,[0,{[K]:G}]],Mfe=[1,q,Xae,0,[1,{[K]:G}]],Nfe=[1,q,_se,0,[()=>tQ,{[K]:G}]],Pfe=[1,q,qce,0,[()=>rQ,{[K]:G}]],Ffe=[1,q,tle,0,[()=>lQ,{[K]:G}]],Ife=[1,q,ole,0,[()=>uQ,{[K]:G}]],Lfe=[1,q,lle,0,[()=>fQ,{[K]:G}]],Rfe=[1,q,Ok,0,[()=>dQ,{[K]:G}]],zfe=[1,q,Kce,0,[0,{[K]:Ck}]],k$=[1,q,Fle,0,[()=>_Q,{[K]:tA}]],Bfe=[1,q,Mle,0,[()=>vQ,{[K]:G}]],Vfe=[1,q,Gle,0,[()=>CQ,{[K]:G}]],A$=[1,q,qle,0,[()=>EQ,{[K]:G}]],j$=[1,q,Jle,0,[0,{[K]:MU}]],Hfe=[1,q,Zle,0,[0,{[K]:sA}]],Ufe=[1,q,Hue,0,[0,{[K]:NA}]],Wfe=[1,q,Yue,0,[()=>jQ,{[K]:G}]],Gfe=[1,q,Sue,0,[()=>FQ,0]],Kfe=[1,q,gue,0,[()=>BQ,{[K]:G}]],qfe=[1,q,vue,0,[()=>VQ,{[K]:G}]],M$=[1,q,Uue,0,[0,{[K]:OA}]],Jfe=[1,q,Lue,0,[()=>HQ,{[K]:G}]],N$=[1,q,Fue,0,[()=>UQ,{[K]:G}]],Yfe=[1,q,Rue,0,[()=>WQ,{[K]:G}]],Xfe=[1,q,Zue,0,[()=>LQ,{[K]:G}]],Zfe=[1,q,qA,0,[()=>XQ,{[K]:G}]],Qfe=[1,q,YA,0,[()=>$Q,{[K]:G}]],$fe=[1,q,lj,0,[()=>n$,{[K]:G}]],epe=[1,q,Fj,0,[()=>r$,{[K]:G}]],tpe=[1,q,Lj,0,[()=>a$,{[K]:G}]],npe=[1,q,Rj,0,[()=>o$,{[K]:G}]],rpe=[1,q,Bj,0,[()=>s$,{[K]:G}]],P$=[1,q,Nj,0,[()=>$ue,{[K]:G}]],ipe=[1,q,Xj,0,[()=>tde,{[K]:G}]],ape=[1,q,Due,0,[()=>nde,{[K]:G}]],ope=[1,q,Aue,0,[0,{[K]:G}]],spe=[1,q,Mue,0,[()=>l$,{[K]:G}]],cpe=[1,q,Que,0,[0,{[K]:G}]],F$=[1,q,fj,0,[()=>rde,{[K]:G}]],lpe=[1,q,Ej,0,[()=>ide,{[K]:G}]],upe=[1,q,pj,0,[()=>ade,{[K]:G}]],dpe=[1,q,mj,0,[()=>ode,{[K]:G}]],fpe=[1,q,Dj,0,[()=>sde,{[K]:G}]],ppe=[1,q,hM,0,[0,{[K]:mM}]],mpe=[1,q,yM,0,[0,{[K]:vM}]],hpe=[1,q,SM,0,[()=>lde,{[K]:G}]],gpe=[1,q,NM,0,[()=>dde,{[K]:G}]],_pe=[1,q,IM,0,[()=>fde,{[K]:G}]],vpe=[1,q,tN,0,[()=>mde,{[K]:G}]],ype=[1,q,$N,0,[()=>Sde,{[K]:G}]],bpe=[1,q,QN,0,[()=>Cde,{[K]:G}]],xpe=[1,q,rP,0,[0,{[K]:G}]],Spe=[1,q,aP,0,[()=>u$,{[K]:G}]],Cpe=[1,q,BN,0,[()=>wde,{[K]:G}]],wpe=[1,q,GN,0,[()=>Dde,{[K]:G}]],Tpe=[1,q,vP,0,[0,{[K]:G}]],I$=[1,q,MP,0,[0,{[K]:zP}]],Epe=[1,q,aF,0,[0,{[K]:aA}]],Dpe=[1,q,oF,0,[()=>m$,{[K]:G}]],Ope=[1,q,sF,0,[0,{[K]:G}]],kpe=[1,q,TF,0,[()=>Mde,{[K]:G}]],Ape=[1,q,fF,0,[()=>Ide,{[K]:G}]],L$=[1,q,WP,0,[()=>Rde,{[K]:G}]],jpe=[1,q,PF,0,[()=>zde,{[K]:G}]],Mpe=[1,q,_F,0,[0,{[K]:lF}]],Npe=[1,q,rI,0,[()=>Wde,{[K]:G}]],Ppe=[1,q,pI,0,[()=>Ude,{[K]:G}]],Fpe=[1,q,yI,0,[0,{[K]:vI}]],Ipe=[1,q,mI,0,[()=>h$,{[K]:G}]],R$=[1,q,kI,0,[()=>Kde,{[K]:G}]],Lpe=[1,q,ZF,0,[0,{[K]:G}]],Rpe=[1,q,hI,0,[()=>Zde,{[K]:G}]],zpe=[1,q,NI,0,[()=>Qde,{[K]:G}]],Bpe=[1,q,II,0,[0,{[K]:G}]],Vpe=[1,q,RI,0,[()=>g$,{[K]:G}]],z$=[1,q,dL,0,[0,{[K]:uL}]],Hpe=[1,q,pL,0,[()=>tfe,{[K]:G}]],B$=[1,q,_L,0,[0,{[K]:G}]],V$=[1,q,yL,0,[()=>nfe,{[K]:G}]],Upe=[1,q,xL,0,[0,{[K]:CL}]],Wpe=[1,q,zL,0,[0,{[K]:WL}]],Gpe=[1,q,QL,0,[()=>v$,{[K]:G}]],Kpe=[1,q,BL,0,[0,{[K]:XL}]],qpe=[1,q,EL,0,[()=>lfe,{[K]:G}]],Jpe=[1,q,eR,0,[()=>b$,{[K]:G}]],Ype=[1,q,$I,0,[0,{[K]:G}]],H$=[1,q,YR,0,[()=>ufe,{[K]:G}]],U$=[1,q,iz,0,[()=>dfe,{[K]:G}]],Xpe=[1,q,tz,0,[1,{[K]:G}]],Zpe=[1,q,gz,0,[0,{[K]:G}]],Qpe=[1,q,bz,0,[()=>mfe,{[K]:G}]],$pe=[1,q,cB,0,[0,{[K]:G}]],eme=[1,q,dB,0,[0,{[K]:G}]],tme=[1,q,Oz,0,[()=>S$,{[K]:G}]],nme=[1,q,Gz,0,[0,{[K]:qz}]],rme=[1,q,Qz,0,[()=>x$,{[K]:G}]],ime=[1,q,jz,0,[()=>_fe,{[K]:G}]],ame=[1,q,Kz,0,[0,{[K]:Vz}]],ome=[1,q,Uz,0,[()=>xfe,{[K]:G}]],sme=[1,q,$z,0,[()=>C$,{[K]:G}]],cme=[1,q,xB,0,[0,{[K]:G}]],lme=[1,q,wB,0,[0,{[K]:CB}]],ume=[1,q,DB,0,[0,{[K]:EB}]],dme=[9,q,Wie,0,()=>iX,()=>aX],fme=[9,q,Kie,0,()=>oX,()=>sX],pme=[9,q,uae,0,()=>cX,()=>lX],mme=[9,q,Qie,0,()=>uX,()=>E$],hme=[9,q,wae,0,()=>dX,()=>S$],gme=[9,q,pae,0,()=>mX,()=>hX],_me=[9,q,vae,0,()=>gX,()=>_X],vme=[9,q,Foe,0,()=>MX,()=>NX],yme=[9,q,soe,0,()=>IX,()=>LX],bme=[9,q,toe,0,()=>RX,()=>zX],xme=[9,q,coe,0,()=>BX,()=>cde],Sme=[9,q,uoe,0,()=>VX,()=>HX],Cme=[9,q,moe,0,()=>UX,()=>WX],wme=[9,q,woe,0,()=>GX,()=>KX],Tme=[9,q,Coe,0,()=>qX,()=>JX],Eme=[9,q,Toe,0,()=>YX,()=>XX],Dme=[9,q,Loe,0,()=>ZX,()=>v$],Ome=[9,q,Roe,0,()=>QX,()=>$X],kme=[9,q,qoe,0,()=>eZ,()=>x$],Ame=[9,q,Joe,0,()=>tZ,()=>nZ],jme=[9,q,use,0,()=>aZ,()=>E$],Mme=[9,q,Ose,0,()=>oZ,()=>sZ],Nme=[9,q,Ise,0,()=>cZ,()=>lZ],Pme=[9,q,Wse,0,()=>uZ,()=>E$],Fme=[9,q,sce,0,()=>dZ,()=>E$],Ime=[9,q,$se,0,()=>fZ,()=>E$],Lme=[9,q,lce,0,()=>pZ,()=>mZ],Rme=[9,q,xce,0,()=>hZ,()=>E$],zme=[9,q,Sce,0,()=>gZ,()=>E$],Bme=[9,q,zce,0,()=>_Z,()=>E$],Vme=[9,q,Bce,0,()=>vZ,()=>E$],Hme=[9,q,nse,0,()=>yZ,()=>bZ],Ume=[9,q,tse,0,()=>xZ,()=>SZ],Wme=[9,q,Tse,0,()=>CZ,()=>wZ],Gme=[9,q,Ese,0,()=>TZ,()=>EZ],Kme=[9,q,wse,0,()=>DZ,()=>OZ],qme=[9,q,hse,0,()=>kZ,()=>AZ],Jme=[9,q,Nse,0,()=>jZ,()=>MZ],Yme=[9,q,Vse,0,()=>NZ,()=>PZ],Xme=[9,q,Jse,0,()=>FZ,()=>IZ],Zme=[9,q,cce,0,()=>LZ,()=>RZ],Qme=[9,q,ice,0,()=>zZ,()=>BZ],$me=[9,q,mce,0,()=>VZ,()=>HZ],ehe=[9,q,Cce,0,()=>UZ,()=>WZ],the=[9,q,wce,0,()=>GZ,()=>KZ],nhe=[9,q,Vce,0,()=>qZ,()=>JZ],rhe=[9,q,Hce,0,()=>YZ,()=>XZ],ihe=[9,q,gse,0,()=>ZZ,()=>E$],ahe=[9,q,Uce,0,()=>QZ,()=>S$],ohe=[9,q,rse,0,()=>$Z,()=>E$],she=[9,q,ace,0,()=>eQ,()=>E$],che=[9,q,Ble,0,()=>xQ,()=>SQ],lhe=[9,q,Gue,0,()=>MQ,()=>NQ],uhe=[9,q,dN,0,()=>_de,()=>E$],dhe=[9,q,xN,0,()=>vde,()=>E$],fhe=[9,q,ON,0,()=>yde,()=>bde],phe=[9,q,uI,0,()=>Vde,()=>E$],mhe=[9,q,KF,0,()=>Gde,()=>E$],hhe=[9,q,SI,0,()=>qde,()=>Jde],ghe=[9,q,DI,0,()=>Yde,()=>Xde],_he=[9,q,fI,0,()=>efe,()=>h$],vhe=[9,q,qL,0,()=>ife,()=>afe],yhe=[9,q,YL,0,()=>ofe,()=>sfe],bhe=[9,q,UR,0,()=>ffe,()=>pfe],xhe=e=>({apiVersion:`2016-11-15`,base64Decoder:e?.base64Decoder??vC,base64Encoder:e?.base64Encoder??yC,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??Lie,extensions:e?.extensions??[],httpAuthSchemeProvider:e?.httpAuthSchemeProvider??pie,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new ID}],logger:e?.logger??new OD,protocol:e?.protocol??Oie,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.ec2`,errorTypeRegistries:QY,xmlNamespace:`http://ec2.amazonaws.com/doc/2016-11-15`,version:`2016-11-15`,serviceTarget:`AmazonEC2`},serviceId:e?.serviceId??`EC2`,urlParser:e?.urlParser??UC,utf8Decoder:e?.utf8Decoder??Ux,utf8Encoder:e?.utf8Encoder??Gx}),She=e=>{let t=WD(e),n=()=>t().then(CD),r=xhe(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??UD,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??zD({serviceId:r.serviceId,clientVersion:gie.version}),maxAttempts:e?.maxAttempts??3,region:e?.region??VD(`Region is missing`),requestHandler:DC.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||_E),sha256:e?.sha256??Ax,streamCollector:e?.streamCollector??kC,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},W$=e=>({setRegion(t){e.region=t},region(){return e.region}}),G$=e=>({region:e.region()}),Che=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},whe=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),The=(e,t)=>{let n=Object.assign(W$(e),TD(e),jx(e),Che(e));return t.forEach(e=>e.configure(n)),Object.assign(e,G$(n),ED(n),Mx(n),whe(n))},Ehe=class extends _D{config;constructor(...[e]){let t=She(e||{});super(t),this.initConfig=t;let n=The(mie(uD(JS(nD(dD(TT(hie(t))))))),e?.extensions||[]);this.config=n,this.middlewareStack.use(JC(this.config)),this.middlewareStack.use(QE(this.config)),this.middlewareStack.use(AD(this.config)),this.middlewareStack.use(iD(this.config)),this.middlewareStack.use(ZS(this.config)),this.middlewareStack.use(eC(this.config)),this.middlewareStack.use(rC(this.config)),this.middlewareStack.use(cC(this.config,{httpAuthSchemeParametersProvider:die,identityProviderConfigProvider:async e=>new bT({"aws.auth#sigv4":e.credentials})})),this.middlewareStack.use(pC(this.config))}destroy(){super.destroy()}},K$=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeAddresses`,{}).n(`EC2Client`,`DescribeAddressesCommand`).sc(Hme).build(){},Dhe=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeAvailabilityZones`,{}).n(`EC2Client`,`DescribeAvailabilityZonesCommand`).sc(Ume).build(){},q$=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeImages`,{}).n(`EC2Client`,`DescribeImagesCommand`).sc(Wme).build(){},J$=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeInstances`,{}).n(`EC2Client`,`DescribeInstancesCommand`).sc(Gme).build(){},Ohe=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeInstanceTypes`,{}).n(`EC2Client`,`DescribeInstanceTypesCommand`).sc(Kme).build(){},Y$=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeInternetGateways`,{}).n(`EC2Client`,`DescribeInternetGatewaysCommand`).sc(qme).build(){},X$=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeKeyPairs`,{}).n(`EC2Client`,`DescribeKeyPairsCommand`).sc(Jme).build(){},Z$=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeNatGateways`,{}).n(`EC2Client`,`DescribeNatGatewaysCommand`).sc(Yme).build(){},Q$=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribePlacementGroups`,{}).n(`EC2Client`,`DescribePlacementGroupsCommand`).sc(Xme).build(){},khe=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeRegions`,{}).n(`EC2Client`,`DescribeRegionsCommand`).sc(Zme).build(){},$$=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeRouteTables`,{}).n(`EC2Client`,`DescribeRouteTablesCommand`).sc(Qme).build(){},e1=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeSecurityGroups`,{}).n(`EC2Client`,`DescribeSecurityGroupsCommand`).sc($me).build(){},t1=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeSnapshots`,{}).n(`EC2Client`,`DescribeSnapshotsCommand`).sc(ehe).build(){},n1=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeSubnets`,{}).n(`EC2Client`,`DescribeSubnetsCommand`).sc(the).build(){},r1=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeVolumes`,{}).n(`EC2Client`,`DescribeVolumesCommand`).sc(nhe).build(){},i1=class extends bD.classBuilder().ep(RD).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeVpcs`,{}).n(`EC2Client`,`DescribeVpcsCommand`).sc(rhe).build(){},Ahe=async(e,t,n)=>({operation:DS(t).operation,region:await OS(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()});function jhe(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`elasticloadbalancing`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var Mhe=e=>{let t=[];switch(e.operation){default:t.push(jhe(e))}return t},Nhe=e=>{let t=LD(e);return Object.assign(t,{authSchemePreference:OS(e.authSchemePreference??[])})},Phe=e=>Object.assign(e,{useDualstackEndpoint:e.useDualstackEndpoint??!1,useFipsEndpoint:e.useFipsEndpoint??!1,defaultSigningName:`elasticloadbalancing`}),a1={UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},Fhe={name:`@aws-sdk/client-elastic-load-balancing-v2`,description:`AWS SDK for JavaScript Elastic Load Balancing V2 Client for Node.js, Browser and React Native`,version:`3.1045.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-elastic-load-balancing-v2`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo elastic-load-balancing-v2`,"test:e2e":`yarn g:vitest run -c vitest.config.e2e.mts --mode development`,"test:e2e:watch":`yarn g:vitest watch -c vitest.config.e2e.mts`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.8`,"@aws-sdk/credential-provider-node":`^3.972.39`,"@aws-sdk/middleware-host-header":`^3.972.10`,"@aws-sdk/middleware-logger":`^3.972.10`,"@aws-sdk/middleware-recursion-detection":`^3.972.11`,"@aws-sdk/middleware-user-agent":`^3.972.38`,"@aws-sdk/region-config-resolver":`^3.972.13`,"@aws-sdk/types":`^3.973.8`,"@aws-sdk/util-endpoints":`^3.996.8`,"@aws-sdk/util-user-agent-browser":`^3.972.10`,"@aws-sdk/util-user-agent-node":`^3.973.24`,"@smithy/config-resolver":`^4.4.17`,"@smithy/core":`^3.23.17`,"@smithy/fetch-http-handler":`^5.3.17`,"@smithy/hash-node":`^4.2.14`,"@smithy/invalid-dependency":`^4.2.14`,"@smithy/middleware-content-length":`^4.2.14`,"@smithy/middleware-endpoint":`^4.4.32`,"@smithy/middleware-retry":`^4.5.7`,"@smithy/middleware-serde":`^4.2.20`,"@smithy/middleware-stack":`^4.2.14`,"@smithy/node-config-provider":`^4.3.14`,"@smithy/node-http-handler":`^4.6.1`,"@smithy/protocol-http":`^5.3.14`,"@smithy/smithy-client":`^4.12.13`,"@smithy/types":`^4.14.1`,"@smithy/url-parser":`^4.2.14`,"@smithy/util-base64":`^4.3.2`,"@smithy/util-body-length-browser":`^4.2.2`,"@smithy/util-body-length-node":`^4.2.3`,"@smithy/util-defaults-mode-browser":`^4.3.49`,"@smithy/util-defaults-mode-node":`^4.2.54`,"@smithy/util-endpoints":`^3.4.2`,"@smithy/util-middleware":`^4.2.14`,"@smithy/util-retry":`^4.3.6`,"@smithy/util-utf8":`^4.2.2`,"@smithy/util-waiter":`^4.3.0`,tslib:`^2.6.2`},devDependencies:{"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-elastic-load-balancing-v2`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-elastic-load-balancing-v2`}},o1=`ref`,s1=-1,c1=!0,l1=`isSet`,u1=`PartitionResult`,d1=`booleanEquals`,f1=`getAttr`,p1={[o1]:`Endpoint`},m1={[o1]:u1},h1={},g1=[{[o1]:`Region`}],_1={conditions:[[l1,[p1]],[l1,g1],[`aws.partition`,g1,u1],[d1,[{[o1]:`UseFIPS`},c1]],[d1,[{[o1]:`UseDualStack`},c1]],[d1,[{fn:f1,argv:[m1,`supportsDualStack`]},c1]],[d1,[{fn:f1,argv:[m1,`supportsFIPS`]},c1]],[`stringEquals`,[{fn:f1,argv:[m1,`name`]},`aws-us-gov`]]],results:[[s1],[s1,`Invalid Configuration: FIPS and custom endpoint are not supported`],[s1,`Invalid Configuration: Dualstack and custom endpoint are not supported`],[p1,h1],[`https://elasticloadbalancing-fips.{Region}.{PartitionResult#dualStackDnsSuffix}`,h1],[s1,`FIPS and DualStack are enabled, but this partition does not support one or both`],[`https://elasticloadbalancing.{Region}.amazonaws.com`,h1],[`https://elasticloadbalancing-fips.{Region}.{PartitionResult#dnsSuffix}`,h1],[s1,`FIPS is enabled but this partition does not support FIPS`],[`https://elasticloadbalancing.{Region}.{PartitionResult#dualStackDnsSuffix}`,h1],[s1,`DualStack is enabled but this partition does not support DualStack`],[`https://elasticloadbalancing.{Region}.{PartitionResult#dnsSuffix}`,h1],[s1,`Invalid Configuration: Missing Region`]]},Ihe=2,v1=1e8,Lhe=new Int32Array([-1,1,-1,0,13,3,1,4,v1+12,2,5,v1+12,3,8,6,4,7,v1+11,5,v1+9,v1+10,4,11,9,6,10,v1+8,7,v1+6,v1+7,5,12,v1+5,6,v1+4,v1+5,3,v1+1,14,4,v1+2,v1+3]),Rhe=ET.from(Lhe,Ihe,_1.conditions,_1.results),zhe=new DT({size:50,params:[`Endpoint`,`Region`,`UseDualStack`,`UseFIPS`]}),Bhe=(e,t={})=>zhe.get(e,()=>aE(Rhe,{endpointParams:e,logger:t.logger}));jT.aws=mE;var y1=class e extends xD{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},Vhe=class e extends y1{name=`CertificateNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`CertificateNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Hhe=class e extends y1{name=`ListenerNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`ListenerNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Uhe=class e extends y1{name=`TooManyCertificatesException`;$fault=`client`;Message;constructor(t){super({name:`TooManyCertificatesException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Whe=class e extends y1{name=`DuplicateTagKeysException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateTagKeysException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Ghe=class e extends y1{name=`LoadBalancerNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`LoadBalancerNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Khe=class e extends y1{name=`RuleNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`RuleNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},qhe=class e extends y1{name=`TargetGroupNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`TargetGroupNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Jhe=class e extends y1{name=`TooManyTagsException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTagsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Yhe=class e extends y1{name=`TrustStoreNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`TrustStoreNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Xhe=class e extends y1{name=`InvalidRevocationContentException`;$fault=`client`;Message;constructor(t){super({name:`InvalidRevocationContentException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Zhe=class e extends y1{name=`RevocationContentNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`RevocationContentNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Qhe=class e extends y1{name=`TooManyTrustStoreRevocationEntriesException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTrustStoreRevocationEntriesException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},$he=class e extends y1{name=`AllocationIdNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`AllocationIdNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},ege=class e extends y1{name=`ALPNPolicyNotSupportedException`;$fault=`client`;Message;constructor(t){super({name:`ALPNPolicyNotSupportedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},tge=class e extends y1{name=`AvailabilityZoneNotSupportedException`;$fault=`client`;Message;constructor(t){super({name:`AvailabilityZoneNotSupportedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},nge=class e extends y1{name=`CaCertificatesBundleNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`CaCertificatesBundleNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},rge=class e extends y1{name=`CapacityDecreaseRequestsLimitExceededException`;$fault=`client`;Message;constructor(t){super({name:`CapacityDecreaseRequestsLimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},ige=class e extends y1{name=`CapacityReservationPendingException`;$fault=`client`;Message;constructor(t){super({name:`CapacityReservationPendingException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},age=class e extends y1{name=`CapacityUnitsLimitExceededException`;$fault=`client`;Message;constructor(t){super({name:`CapacityUnitsLimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},oge=class e extends y1{name=`DuplicateListenerException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateListenerException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},sge=class e extends y1{name=`IncompatibleProtocolsException`;$fault=`client`;Message;constructor(t){super({name:`IncompatibleProtocolsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},cge=class e extends y1{name=`InvalidConfigurationRequestException`;$fault=`client`;Message;constructor(t){super({name:`InvalidConfigurationRequestException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},lge=class e extends y1{name=`InvalidLoadBalancerActionException`;$fault=`client`;Message;constructor(t){super({name:`InvalidLoadBalancerActionException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},uge=class e extends y1{name=`SSLPolicyNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`SSLPolicyNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},dge=class e extends y1{name=`TargetGroupAssociationLimitException`;$fault=`client`;Message;constructor(t){super({name:`TargetGroupAssociationLimitException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},fge=class e extends y1{name=`TooManyActionsException`;$fault=`client`;Message;constructor(t){super({name:`TooManyActionsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},pge=class e extends y1{name=`TooManyListenersException`;$fault=`client`;Message;constructor(t){super({name:`TooManyListenersException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},mge=class e extends y1{name=`TooManyRegistrationsForTargetIdException`;$fault=`client`;Message;constructor(t){super({name:`TooManyRegistrationsForTargetIdException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},hge=class e extends y1{name=`TooManyTargetsException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTargetsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},gge=class e extends y1{name=`TooManyUniqueTargetGroupsPerLoadBalancerException`;$fault=`client`;Message;constructor(t){super({name:`TooManyUniqueTargetGroupsPerLoadBalancerException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},_ge=class e extends y1{name=`TrustStoreNotReadyException`;$fault=`client`;Message;constructor(t){super({name:`TrustStoreNotReadyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},vge=class e extends y1{name=`UnsupportedProtocolException`;$fault=`client`;Message;constructor(t){super({name:`UnsupportedProtocolException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},yge=class e extends y1{name=`DuplicateLoadBalancerNameException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateLoadBalancerNameException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},bge=class e extends y1{name=`InvalidSchemeException`;$fault=`client`;Message;constructor(t){super({name:`InvalidSchemeException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},xge=class e extends y1{name=`InvalidSecurityGroupException`;$fault=`client`;Message;constructor(t){super({name:`InvalidSecurityGroupException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Sge=class e extends y1{name=`InvalidSubnetException`;$fault=`client`;Message;constructor(t){super({name:`InvalidSubnetException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Cge=class e extends y1{name=`OperationNotPermittedException`;$fault=`client`;Message;constructor(t){super({name:`OperationNotPermittedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},wge=class e extends y1{name=`ResourceInUseException`;$fault=`client`;Message;constructor(t){super({name:`ResourceInUseException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Tge=class e extends y1{name=`SubnetNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`SubnetNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Ege=class e extends y1{name=`TooManyLoadBalancersException`;$fault=`client`;Message;constructor(t){super({name:`TooManyLoadBalancersException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Dge=class e extends y1{name=`PriorityInUseException`;$fault=`client`;Message;constructor(t){super({name:`PriorityInUseException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Oge=class e extends y1{name=`TooManyRulesException`;$fault=`client`;Message;constructor(t){super({name:`TooManyRulesException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},kge=class e extends y1{name=`TooManyTargetGroupsException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTargetGroupsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Age=class e extends y1{name=`DuplicateTargetGroupNameException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateTargetGroupNameException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},jge=class e extends y1{name=`DuplicateTrustStoreNameException`;$fault=`client`;Message;constructor(t){super({name:`DuplicateTrustStoreNameException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Mge=class e extends y1{name=`InvalidCaCertificatesBundleException`;$fault=`client`;Message;constructor(t){super({name:`InvalidCaCertificatesBundleException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Nge=class e extends y1{name=`TooManyTrustStoresException`;$fault=`client`;Message;constructor(t){super({name:`TooManyTrustStoresException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Pge=class e extends y1{name=`DeleteAssociationSameAccountException`;$fault=`client`;Message;constructor(t){super({name:`DeleteAssociationSameAccountException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Fge=class e extends y1{name=`TrustStoreAssociationNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`TrustStoreAssociationNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Ige=class e extends y1{name=`TrustStoreInUseException`;$fault=`client`;Message;constructor(t){super({name:`TrustStoreInUseException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Lge=class e extends y1{name=`InvalidTargetException`;$fault=`client`;Message;constructor(t){super({name:`InvalidTargetException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Rge=class e extends y1{name=`HealthUnavailableException`;$fault=`server`;Message;constructor(t){super({name:`HealthUnavailableException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},zge=class e extends y1{name=`RevocationIdNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`RevocationIdNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Bge=class e extends y1{name=`ResourceNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`ResourceNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Vge=class e extends y1{name=`InsufficientCapacityException`;$fault=`server`;Message;constructor(t){super({name:`InsufficientCapacityException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Hge=class e extends y1{name=`PriorRequestNotCompleteException`;$fault=`client`;Message;constructor(t){super({name:`PriorRequestNotCompleteException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},Uge=`Action`,Wge=`AdditionalClaims`,Gge=`AuthenticateCognitoActionConfig`,Kge=`AuthenticateCognitoConfig`,b1=`AnomalyDetection`,qge=`AuthorizationEndpoint`,x1=`AllocationId`,Jge=`AllocationIdNotFoundException`,Yge=`ALPNPolicyNotSupportedException`,S1=`AdministrativeOverride`,Xge=`AuthenticateOidcActionConfig`,Zge=`AuthenticateOidcConfig`,C1=`AlpnPolicy`,w1=`AuthenticationRequestExtraParams`,Qge=`AdvertiseTrustStoreCaNames`,T1=`AvailabilityZone`,$ge=`AvailabilityZoneNotSupportedException`,E1=`AvailabilityZones`,D1=`Actions`,O1=`Attributes`,k1=`Certificates`,e_e=`CertificateArn`,t_e=`CaCertificatesBundleNotFoundException`,n_e=`CapacityDecreaseRequestsLimitExceededException`,r_e=`CanonicalHostedZoneId`,i_e=`ClientId`,a_e=`CertificateList`,o_e=`CreateLoadBalancer`,s_e=`CreateLoadBalancerInput`,c_e=`CreateLoadBalancerOutput`,l_e=`CreateListenerInput`,u_e=`CreateListenerOutput`,d_e=`CreateListener`,f_e=`CertificateNotFoundException`,A1=`CustomerOwnedIpv4Pool`,p_e=`CreateRule`,m_e=`CreateRuleInput`,h_e=`CreateRuleOutput`,g_e=`CapacityReservationPendingException`,__e=`ClientSecret`,v_e=`ContentType`,y_e=`CreateTargetGroup`,b_e=`CreateTargetGroupInput`,x_e=`CreateTargetGroupOutput`,S_e=`CreatedTime`,C_e=`CapacityUnitsLimitExceededException`,w_e=`Certificate`,T_e=`Code`,j1=`Conditions`,M1=`Description`,N1=`DefaultActions`,E_e=`DeleteAssociationSameAccountException`,D_e=`DeleteListener`,O_e=`DeleteLoadBalancer`,k_e=`DescribeLoadBalancerAttributes`,A_e=`DescribeLoadBalancerAttributesInput`,j_e=`DescribeLoadBalancerAttributesOutput`,M_e=`DeleteLoadBalancerInput`,N_e=`DescribeLoadBalancersInput`,P_e=`DuplicateLoadBalancerNameException`,F_e=`DeleteLoadBalancerOutput`,I_e=`DescribeLoadBalancersOutput`,L_e=`DescribeLoadBalancers`,R_e=`DuplicateListenerException`,z_e=`DeleteListenerInput`,B_e=`DescribeListenersInput`,V_e=`DeleteListenerOutput`,H_e=`DescribeListenersOutput`,U_e=`DescribeListeners`,W_e=`DNSName`,G_e=`DeleteRule`,K_e=`DeleteRuleInput`,q_e=`DescribeRulesInput`,J_e=`DeleteRuleOutput`,Y_e=`DescribeRulesOutput`,X_e=`DescribeRules`,Z_e=`DurationSeconds`,Q_e=`DeregisterTargets`,$_e=`DeleteTargetGroup`,eve=`DescribeTargetGroupAttributes`,tve=`DescribeTargetGroupAttributesInput`,nve=`DescribeTargetGroupAttributesOutput`,rve=`DeleteTargetGroupInput`,ive=`DescribeTargetGroupsInput`,ave=`DuplicateTargetGroupNameException`,ove=`DeleteTargetGroupOutput`,sve=`DescribeTargetGroupsOutput`,cve=`DescribeTargetGroups`,lve=`DescribeTargetHealth`,uve=`DescribeTargetHealthInput`,dve=`DescribeTargetHealthOutput`,fve=`DeregisterTargetsInput`,pve=`DescribeTagsInput`,mve=`DuplicateTagKeysException`,hve=`DeregisterTargetsOutput`,gve=`DescribeTagsOutput`,_ve=`DuplicateTrustStoreNameException`,vve=`DescribeTags`,yve=`Enabled`,P1=`EnablePrefixForIpv6SourceNat`,bve=`EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic`,xve=`Format`,Sve=`ForwardActionConfig`,Cve=`ForwardConfig`,wve=`FixedResponseActionConfig`,Tve=`FixedResponseConfig`,Eve=`Field`,Dve=`GrpcCode`,Ove=`Host`,kve=`HttpCode`,F1=`HealthCheckEnabled`,I1=`HealthCheckIntervalSeconds`,L1=`HealthCheckProtocol`,R1=`HealthCheckPort`,z1=`HealthCheckPath`,B1=`HealthCheckTimeoutSeconds`,Ave=`HostHeaderConfig`,jve=`HostHeaderConditionConfig`,Mve=`HttpHeaderConditionConfig`,Nve=`HttpHeaderConfig`,Pve=`HttpHeaderName`,V1=`HostHeaderRewriteConfig`,Fve=`HttpRequestMethodConfig`,Ive=`HttpRequestMethodConditionConfig`,H1=`HealthyThresholdCount`,Lve=`HealthUnavailableException`,U1=`Issuer`,Rve=`IpAddress`,W1=`IpAddressType`,zve=`InvalidCaCertificatesBundleException`,Bve=`IgnoreClientCertificateExpiry`,Vve=`InsufficientCapacityException`,Hve=`InvalidConfigurationRequestException`,G1=`IsDefault`,Uve=`Ipv4IpamPoolId`,Wve=`InvalidLoadBalancerActionException`,K1=`IpamPools`,q1=`IPv6Address`,Gve=`IncompatibleProtocolsException`,Kve=`InvalidRevocationContentException`,qve=`InvalidSchemeException`,Jve=`InvalidSubnetException`,Yve=`InvalidSecurityGroupException`,Xve=`InvalidTargetException`,Zve=`Id`,Qve=`Include`,$ve=`JwksEndpoint`,eye=`JwtValidationActionAdditionalClaim`,tye=`JwtValidationActionAdditionalClaims`,nye=`JwtValidationActionConfig`,rye=`JwtValidationConfig`,J1=`Key`,Y1=`Listeners`,X1=`ListenerArn`,iye=`ListenerArns`,Z1=`LoadBalancers`,Q1=`LoadBalancerAddresses`,$1=`LoadBalancerArn`,e0=`LoadBalancerArns`,aye=`LoadBalancerAddress`,oye=`LoadBalancerAttribute`,sye=`LoadBalancerAttributes`,cye=`LoadBalancerName`,lye=`LoadBalancerNotFoundException`,uye=`LoadBalancerState`,dye=`LoadBalancer`,fye=`ListenerNotFoundException`,pye=`Listener`,t0=`Message`,n0=`MutualAuthentication`,mye=`MutualAuthenticationAttributes`,hye=`MessageBody`,gye=`MitigationInEffect`,_ye=`ModifyListener`,vye=`ModifyLoadBalancerAttributes`,yye=`ModifyLoadBalancerAttributesInput`,bye=`ModifyLoadBalancerAttributesOutput`,xye=`ModifyListenerInput`,Sye=`ModifyListenerOutput`,Cye=`ModifyRule`,wye=`ModifyRuleInput`,Tye=`ModifyRuleOutput`,Eye=`ModifyTargetGroupAttributes`,Dye=`ModifyTargetGroupAttributesInput`,Oye=`ModifyTargetGroupAttributesOutput`,r0=`Matcher`,i0=`Marker`,kye=`Mode`,a0=`Name`,o0=`NextMarker`,s0=`Names`,Aye=`Order`,jye=`OutpostId`,Mye=`OperationNotPermittedException`,c0=`OnUnauthenticatedRequest`,l0=`Priority`,u0=`PrivateIPv4Address`,Nye=`PriorityInUseException`,Pye=`PathPatternConfig`,Fye=`PathPatternConditionConfig`,Iye=`PriorRequestNotCompleteException`,d0=`PageSize`,f0=`ProtocolVersion`,Lye=`Path`,p0=`Port`,m0=`Protocol`,Rye=`Query`,zye=`QueryStringConfig`,Bye=`QueryStringConditionConfig`,Vye=`QuicServerId`,Hye=`QueryStringKeyValuePair`,Uye=`QueryStringKeyValuePairList`,h0=`Reason`,Wye=`ResourceArns`,Gye=`RedirectActionConfig`,Kye=`ResourceArn`,g0=`RuleArn`,qye=`RuleArns`,Jye=`RedirectConfig`,Yye=`RewriteConfigList`,Xye=`RuleConditionList`,Zye=`RevocationContentNotFoundException`,Qye=`RewriteConfig`,$ye=`RuleCondition`,ebe=`RevocationIdNotFoundException`,tbe=`ResourceInUseException`,nbe=`ResourceNotFoundException`,rbe=`RuleNotFoundException`,ibe=`RegisterTargetsInput`,abe=`RuleTransformList`,obe=`RegisterTargetsOutput`,sbe=`ResetTransforms`,cbe=`RegisterTargets`,lbe=`RuleTransform`,_0=`RegexValues`,ube=`Result`,dbe=`Regex`,fbe=`Replace`,v0=`Rewrites`,y0=`Rules`,pbe=`Rule`,b0=`State`,x0=`StatusCode`,S0=`SessionCookieName`,C0=`SecurityGroups`,w0=`SubnetId`,mbe=`SourceIpConfig`,hbe=`SourceIpConditionConfig`,T0=`SubnetMappings`,gbe=`SubnetMapping`,_be=`SubnetNotFoundException`,vbe=`SourceNatIpv6Prefixes`,ybe=`SourceNatIpv6Prefix`,E0=`SslPolicy`,bbe=`SSLPolicyNotFoundException`,D0=`SessionTimeout`,O0=`Scope`,k0=`Scheme`,xbe=`Subnets`,A0=`Type`,j0=`TargetControlPort`,M0=`TagDescriptions`,Sbe=`TagDescription`,Cbe=`TargetDescription`,wbe=`TargetDescriptions`,Tbe=`TokenEndpoint`,N0=`TargetGroups`,P0=`TargetGroupArn`,Ebe=`TargetGroupAssociationLimitException`,Dbe=`TargetGroupArns`,Obe=`TargetGroupAttribute`,kbe=`TargetGroupAttributes`,Abe=`TargetGroupList`,jbe=`TargetGroupName`,Mbe=`TargetGroupNotFoundException`,F0=`TargetGroupStickinessConfig`,Nbe=`TargetGroupTuple`,Pbe=`TargetGroup`,I0=`TargetHealth`,L0=`TargetHealthDescriptions`,Fbe=`TargetHealthDescription`,Ibe=`TagList`,Lbe=`TooManyActionsException`,Rbe=`TooManyCertificatesException`,zbe=`TooManyLoadBalancersException`,Bbe=`TooManyListenersException`,Vbe=`TooManyRulesException`,Hbe=`TooManyRegistrationsForTargetIdException`,Ube=`TooManyTagsException`,Wbe=`TooManyTargetsException`,Gbe=`TooManyTargetGroupsException`,Kbe=`TooManyTrustStoresException`,qbe=`TooManyTrustStoreRevocationEntriesException`,Jbe=`TooManyUniqueTargetGroupsPerLoadBalancerException`,Ybe=`TrustStoreArn`,Xbe=`TrustStoreAssociationNotFoundException`,Zbe=`TrustStoreAssociationStatus`,Qbe=`TrustStoreInUseException`,$be=`TrustStoreNotFoundException`,exe=`TrustStoreNotReadyException`,R0=`TargetType`,z0=`Tags`,txe=`Tag`,B0=`Targets`,nxe=`Target`,V0=`Transforms`,rxe=`UseExistingClientSecret`,ixe=`UserInfoEndpoint`,axe=`UserPoolArn`,oxe=`UserPoolClientId`,sxe=`UserPoolDomain`,cxe=`UnsupportedProtocolException`,H0=`UrlRewriteConfig`,U0=`UnhealthyThresholdCount`,W0=`Values`,G0=`VpcId`,K0=`Value`,lxe=`Weight`,uxe=`ZoneName`,q0=`awsQueryError`,J0=`client`,Y0=`error`,X0=`httpError`,Z0=`smithy.ts.sdk.synthetic.com.amazonaws.elasticloadbalancingv2`,Q0=`server`,J=`com.amazonaws.elasticloadbalancingv2`,$0=iw.for(Z0),dxe=[-3,Z0,`ElasticLoadBalancingV2ServiceException`,0,[],[]];$0.registerError(dxe,y1);var e2=iw.for(J),fxe=[-3,J,Jge,{[q0]:[`AllocationIdNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(fxe,$he);var pxe=[-3,J,Yge,{[q0]:[`ALPNPolicyNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(pxe,ege);var mxe=[-3,J,$ge,{[q0]:[`AvailabilityZoneNotSupported`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(mxe,tge);var hxe=[-3,J,t_e,{[q0]:[`CaCertificatesBundleNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(hxe,nge);var gxe=[-3,J,n_e,{[q0]:[`CapacityDecreaseRequestLimitExceeded`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(gxe,rge);var _xe=[-3,J,g_e,{[q0]:[`CapacityReservationPending`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(_xe,ige);var vxe=[-3,J,C_e,{[q0]:[`CapacityUnitsLimitExceeded`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(vxe,age);var yxe=[-3,J,f_e,{[q0]:[`CertificateNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(yxe,Vhe);var bxe=[-3,J,E_e,{[q0]:[`DeleteAssociationSameAccount`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(bxe,Pge);var xxe=[-3,J,R_e,{[q0]:[`DuplicateListener`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(xxe,oge);var Sxe=[-3,J,P_e,{[q0]:[`DuplicateLoadBalancerName`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Sxe,yge);var Cxe=[-3,J,mve,{[q0]:[`DuplicateTagKeys`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Cxe,Whe);var wxe=[-3,J,ave,{[q0]:[`DuplicateTargetGroupName`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(wxe,Age);var Txe=[-3,J,_ve,{[q0]:[`DuplicateTrustStoreName`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Txe,jge);var Exe=[-3,J,Lve,{[q0]:[`HealthUnavailable`,500],[Y0]:Q0,[X0]:500},[t0],[0]];e2.registerError(Exe,Rge);var Dxe=[-3,J,Gve,{[q0]:[`IncompatibleProtocols`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Dxe,sge);var Oxe=[-3,J,Vve,{[q0]:[`InsufficientCapacity`,500],[Y0]:Q0,[X0]:500},[t0],[0]];e2.registerError(Oxe,Vge);var kxe=[-3,J,zve,{[q0]:[`InvalidCaCertificatesBundle`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(kxe,Mge);var Axe=[-3,J,Hve,{[q0]:[`InvalidConfigurationRequest`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Axe,cge);var jxe=[-3,J,Wve,{[q0]:[`InvalidLoadBalancerAction`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(jxe,lge);var Mxe=[-3,J,Kve,{[q0]:[`InvalidRevocationContent`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Mxe,Xhe);var Nxe=[-3,J,qve,{[q0]:[`InvalidScheme`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Nxe,bge);var Pxe=[-3,J,Yve,{[q0]:[`InvalidSecurityGroup`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Pxe,xge);var Fxe=[-3,J,Jve,{[q0]:[`InvalidSubnet`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Fxe,Sge);var Ixe=[-3,J,Xve,{[q0]:[`InvalidTarget`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Ixe,Lge);var Lxe=[-3,J,fye,{[q0]:[`ListenerNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Lxe,Hhe);var Rxe=[-3,J,lye,{[q0]:[`LoadBalancerNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Rxe,Ghe);var zxe=[-3,J,Mye,{[q0]:[`OperationNotPermitted`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(zxe,Cge);var Bxe=[-3,J,Nye,{[q0]:[`PriorityInUse`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Bxe,Dge);var Vxe=[-3,J,Iye,{[q0]:[`PriorRequestNotComplete`,429],[Y0]:J0,[X0]:429},[t0],[0]];e2.registerError(Vxe,Hge);var Hxe=[-3,J,tbe,{[q0]:[`ResourceInUse`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Hxe,wge);var Uxe=[-3,J,nbe,{[q0]:[`ResourceNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Uxe,Bge);var Wxe=[-3,J,Zye,{[q0]:[`RevocationContentNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Wxe,Zhe);var Gxe=[-3,J,ebe,{[q0]:[`RevocationIdNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Gxe,zge);var Kxe=[-3,J,rbe,{[q0]:[`RuleNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Kxe,Khe);var qxe=[-3,J,bbe,{[q0]:[`SSLPolicyNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(qxe,uge);var Jxe=[-3,J,_be,{[q0]:[`SubnetNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Jxe,Tge);var Yxe=[-3,J,Ebe,{[q0]:[`TargetGroupAssociationLimit`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Yxe,dge);var Xxe=[-3,J,Mbe,{[q0]:[`TargetGroupNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Xxe,qhe);var Zxe=[-3,J,Lbe,{[q0]:[`TooManyActions`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Zxe,fge);var Qxe=[-3,J,Rbe,{[q0]:[`TooManyCertificates`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(Qxe,Uhe);var $xe=[-3,J,Bbe,{[q0]:[`TooManyListeners`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError($xe,pge);var eSe=[-3,J,zbe,{[q0]:[`TooManyLoadBalancers`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(eSe,Ege);var tSe=[-3,J,Hbe,{[q0]:[`TooManyRegistrationsForTargetId`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(tSe,mge);var nSe=[-3,J,Vbe,{[q0]:[`TooManyRules`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(nSe,Oge);var rSe=[-3,J,Ube,{[q0]:[`TooManyTags`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(rSe,Jhe);var iSe=[-3,J,Gbe,{[q0]:[`TooManyTargetGroups`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(iSe,kge);var aSe=[-3,J,Wbe,{[q0]:[`TooManyTargets`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(aSe,hge);var oSe=[-3,J,qbe,{[q0]:[`TooManyTrustStoreRevocationEntries`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(oSe,Qhe);var sSe=[-3,J,Kbe,{[q0]:[`TooManyTrustStores`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(sSe,Nge);var cSe=[-3,J,Jbe,{[q0]:[`TooManyUniqueTargetGroupsPerLoadBalancer`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(cSe,gge);var lSe=[-3,J,Xbe,{[q0]:[`AssociationNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(lSe,Fge);var uSe=[-3,J,Qbe,{[q0]:[`TrustStoreInUse`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(uSe,Ige);var dSe=[-3,J,$be,{[q0]:[`TrustStoreNotFound`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(dSe,Yhe);var fSe=[-3,J,exe,{[q0]:[`TrustStoreNotReady`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(fSe,_ge);var pSe=[-3,J,cxe,{[q0]:[`UnsupportedProtocol`,400],[Y0]:J0,[X0]:400},[t0],[0]];e2.registerError(pSe,vge);var mSe=[$0,e2],hSe=[3,J,Uge,0,[A0,P0,Zge,Kge,Aye,Jye,Tve,Cve,rye],[0,0,()=>ySe,()=>vSe,1,()=>ECe,()=>nCe,()=>rCe,()=>lCe],1],gSe=[3,J,S1,0,[b0,h0,M1],[0,0,0]],_Se=[3,J,b1,0,[ube,gye],[0,0]],vSe=[3,J,Gge,0,[axe,oxe,sxe,S0,O0,D0,w1,c0],[0,0,0,0,0,1,128,0],3],ySe=[3,J,Xge,0,[U1,qge,Tbe,ixe,i_e,__e,S0,O0,D0,w1,c0,rxe],[0,0,0,0,0,0,0,0,1,128,0,2],5],bSe=[3,J,T1,0,[uxe,w0,jye,Q1,vbe],[0,0,0,()=>KCe,64]],xSe=[3,J,w_e,0,[e_e,G1],[0,2]],SSe=[3,J,l_e,0,[$1,N1,m0,p0,E0,k1,C1,z0,n0],[0,()=>a2,0,1,0,()=>o2,64,()=>m2,()=>r2],2],CSe=[3,J,u_e,0,[Y1],[()=>s2]],wSe=[3,J,s_e,0,[a0,xbe,T0,C0,k0,z0,A0,W1,A1,P1,K1],[0,64,()=>JCe,64,0,()=>m2,0,0,0,0,()=>t2],1],TSe=[3,J,c_e,0,[Z1],[()=>l2]],ESe=[3,J,m_e,0,[X1,j1,l0,D1,z0,V0],[0,()=>d2,1,()=>a2,()=>m2,()=>p2],4],DSe=[3,J,h_e,0,[y0],[()=>f2]],OSe=[3,J,b_e,0,[a0,m0,f0,p0,G0,L1,R1,F1,z1,I1,B1,H1,U0,r0,R0,z0,W1,j0],[0,0,0,1,0,0,0,2,0,1,1,1,1,()=>n2,0,()=>m2,0,1],1],kSe=[3,J,x_e,0,[N0],[()=>_2]],ASe=[3,J,z_e,0,[X1],[0],1],jSe=[3,J,V_e,0,[],[]],MSe=[3,J,M_e,0,[$1],[0],1],NSe=[3,J,F_e,0,[],[]],PSe=[3,J,K_e,0,[g0],[0],1],FSe=[3,J,J_e,0,[],[]],ISe=[3,J,rve,0,[P0],[0],1],LSe=[3,J,ove,0,[],[]],RSe=[3,J,fve,0,[P0,B0],[0,()=>h2],2],zSe=[3,J,hve,0,[],[]],BSe=[3,J,B_e,0,[$1,iye,i0,d0],[0,64,0,1]],VSe=[3,J,H_e,0,[Y1,o0],[()=>s2,0]],HSe=[3,J,A_e,0,[$1],[0],1],USe=[3,J,j_e,0,[O1],[()=>c2]],WSe=[3,J,N_e,0,[e0,s0,i0,d0],[64,64,0,1]],GSe=[3,J,I_e,0,[Z1,o0],[()=>l2,0]],KSe=[3,J,q_e,0,[X1,qye,i0,d0],[0,64,0,1]],qSe=[3,J,Y_e,0,[y0,o0],[()=>f2,0]],JSe=[3,J,pve,0,[Wye],[64],1],YSe=[3,J,gve,0,[M0],[()=>YCe]],XSe=[3,J,tve,0,[P0],[0],1],ZSe=[3,J,nve,0,[O1],[()=>g2]],QSe=[3,J,ive,0,[$1,Dbe,s0,i0,d0],[0,64,64,0,1]],$Se=[3,J,sve,0,[N0,o0],[()=>_2,0]],eCe=[3,J,uve,0,[P0,B0,Qve],[0,()=>h2,64],1],tCe=[3,J,dve,0,[L0],[()=>ZCe]],nCe=[3,J,wve,0,[x0,hye,v_e],[0,0,0],1],rCe=[3,J,Sve,0,[N0,F0],[()=>XCe,()=>zCe]],iCe=[3,J,jve,0,[W0,_0],[64,64]],aCe=[3,J,V1,0,[v0],[()=>u2]],oCe=[3,J,Mve,0,[Pve,W0,_0],[0,64,64]],sCe=[3,J,Ive,0,[W0],[64]],t2=[3,J,K1,0,[Uve],[0]],cCe=[3,J,eye,0,[xve,a0,W0],[0,0,64],3],lCe=[3,J,nye,0,[$ve,U1,Wge],[0,0,()=>GCe],2],uCe=[3,J,pye,0,[X1,$1,p0,m0,k1,E0,N1,C1,n0],[0,0,1,0,()=>o2,0,()=>a2,64,()=>r2]],dCe=[3,J,dye,0,[$1,W_e,r_e,S_e,cye,k0,G0,b0,A0,E1,C0,W1,A1,bve,P1,K1],[0,0,0,4,0,0,0,()=>mCe,0,()=>WCe,64,0,0,0,0,()=>t2]],fCe=[3,J,aye,0,[Rve,x1,u0,q1],[0,0,0,0]],pCe=[3,J,oye,0,[J1,K0],[0,0]],mCe=[3,J,uye,0,[T_e,h0],[0,0]],n2=[3,J,r0,0,[kve,Dve],[0,0]],hCe=[3,J,xye,0,[X1,p0,m0,E0,k1,N1,C1,n0],[0,1,0,0,()=>o2,()=>a2,64,()=>r2],1],gCe=[3,J,Sye,0,[Y1],[()=>s2]],_Ce=[3,J,yye,0,[$1,O1],[0,()=>c2],2],vCe=[3,J,bye,0,[O1],[()=>c2]],yCe=[3,J,wye,0,[g0,j1,D1,V0,sbe],[0,()=>d2,()=>a2,()=>p2,2],1],bCe=[3,J,Tye,0,[y0],[()=>f2]],xCe=[3,J,Dye,0,[P0,O1],[0,()=>g2],2],SCe=[3,J,Oye,0,[O1],[()=>g2]],r2=[3,J,mye,0,[kye,Ybe,Bve,Zbe,Qge],[0,0,2,0,0]],CCe=[3,J,Fye,0,[W0,_0],[64,64]],wCe=[3,J,Bye,0,[W0],[()=>qCe]],TCe=[3,J,Hye,0,[J1,K0],[0,0]],ECe=[3,J,Gye,0,[x0,m0,p0,Ove,Lye,Rye],[0,0,0,0,0,0],1],DCe=[3,J,ibe,0,[P0,B0],[0,()=>h2],2],OCe=[3,J,obe,0,[],[]],kCe=[3,J,Qye,0,[dbe,fbe],[0,0],2],ACe=[3,J,pbe,0,[g0,l0,j1,D1,G1,V0],[0,0,()=>d2,()=>a2,2,()=>p2]],jCe=[3,J,$ye,0,[Eve,W0,Ave,Pye,Nve,zye,Fve,mbe,_0],[0,64,()=>iCe,()=>CCe,()=>oCe,()=>wCe,()=>sCe,()=>NCe,64]],MCe=[3,J,lbe,0,[A0,V1,H0],[0,()=>aCe,()=>UCe],1],NCe=[3,J,hbe,0,[W0],[64]],PCe=[3,J,gbe,0,[w0,x1,u0,q1,ybe],[0,0,0,0,0]],FCe=[3,J,txe,0,[J1,K0],[0,0],1],ICe=[3,J,Sbe,0,[Kye,z0],[0,()=>m2]],i2=[3,J,Cbe,0,[Zve,p0,T1,Vye],[0,1,0,0],1],LCe=[3,J,Pbe,0,[P0,jbe,m0,p0,G0,L1,R1,F1,I1,B1,H1,U0,z1,r0,e0,R0,f0,W1,j0],[0,0,0,1,0,0,0,2,1,1,1,1,0,()=>n2,64,0,0,0,1]],RCe=[3,J,Obe,0,[J1,K0],[0,0]],zCe=[3,J,F0,0,[yve,Z_e],[2,1]],BCe=[3,J,Nbe,0,[P0,lxe],[0,1]],VCe=[3,J,I0,0,[b0,h0,M1],[0,0,0]],HCe=[3,J,Fbe,0,[nxe,R1,I0,b1,S1],[()=>i2,0,()=>VCe,()=>_Se,()=>gSe]],UCe=[3,J,H0,0,[v0],[()=>u2]],a2=[1,J,D1,0,()=>hSe],WCe=[1,J,E1,0,()=>bSe],o2=[1,J,a_e,0,()=>xSe],GCe=[1,J,tye,0,()=>cCe],s2=[1,J,Y1,0,()=>uCe],KCe=[1,J,Q1,0,()=>fCe],c2=[1,J,sye,0,()=>pCe],l2=[1,J,Z1,0,()=>dCe],qCe=[1,J,Uye,0,()=>TCe],u2=[1,J,Yye,0,()=>kCe],d2=[1,J,Xye,0,()=>jCe],f2=[1,J,y0,0,()=>ACe],p2=[1,J,abe,0,()=>MCe],JCe=[1,J,T0,0,()=>PCe],YCe=[1,J,M0,0,()=>ICe],m2=[1,J,Ibe,0,()=>FCe],h2=[1,J,wbe,0,()=>i2],g2=[1,J,kbe,0,()=>RCe],XCe=[1,J,Abe,0,()=>BCe],_2=[1,J,N0,0,()=>LCe],ZCe=[1,J,L0,0,()=>HCe],QCe=[9,J,d_e,0,()=>SSe,()=>CSe],$Ce=[9,J,o_e,0,()=>wSe,()=>TSe],ewe=[9,J,p_e,0,()=>ESe,()=>DSe],twe=[9,J,y_e,0,()=>OSe,()=>kSe],nwe=[9,J,D_e,0,()=>ASe,()=>jSe],rwe=[9,J,O_e,0,()=>MSe,()=>NSe],iwe=[9,J,G_e,0,()=>PSe,()=>FSe],awe=[9,J,$_e,0,()=>ISe,()=>LSe],owe=[9,J,Q_e,0,()=>RSe,()=>zSe],swe=[9,J,U_e,0,()=>BSe,()=>VSe],cwe=[9,J,k_e,0,()=>HSe,()=>USe],lwe=[9,J,L_e,0,()=>WSe,()=>GSe],uwe=[9,J,X_e,0,()=>KSe,()=>qSe],dwe=[9,J,vve,0,()=>JSe,()=>YSe],fwe=[9,J,eve,0,()=>XSe,()=>ZSe],pwe=[9,J,cve,0,()=>QSe,()=>$Se],mwe=[9,J,lve,0,()=>eCe,()=>tCe],hwe=[9,J,_ye,0,()=>hCe,()=>gCe],gwe=[9,J,vye,0,()=>_Ce,()=>vCe],_we=[9,J,Cye,0,()=>yCe,()=>bCe],vwe=[9,J,Eye,0,()=>xCe,()=>SCe],ywe=[9,J,cbe,0,()=>DCe,()=>OCe],bwe=e=>({apiVersion:`2015-12-01`,base64Decoder:e?.base64Decoder??vC,base64Encoder:e?.base64Encoder??yC,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??Bhe,extensions:e?.extensions??[],httpAuthSchemeProvider:e?.httpAuthSchemeProvider??Mhe,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new ID}],logger:e?.logger??new OD,protocol:e?.protocol??ZD,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.elasticloadbalancingv2`,errorTypeRegistries:mSe,xmlNamespace:`http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/`,version:`2015-12-01`,serviceTarget:`ElasticLoadBalancing_v10`},serviceId:e?.serviceId??`Elastic Load Balancing v2`,urlParser:e?.urlParser??UC,utf8Decoder:e?.utf8Decoder??Ux,utf8Encoder:e?.utf8Encoder??Gx}),xwe=e=>{let t=WD(e),n=()=>t().then(CD),r=bwe(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??UD,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??zD({serviceId:r.serviceId,clientVersion:Fhe.version}),maxAttempts:e?.maxAttempts??3,region:e?.region??VD(`Region is missing`),requestHandler:DC.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||_E),sha256:e?.sha256??Ax,streamCollector:e?.streamCollector??kC,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},Swe=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},Cwe=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),wwe=(e,t)=>{let n=Object.assign(W$(e),TD(e),jx(e),Swe(e));return t.forEach(e=>e.configure(n)),Object.assign(e,G$(n),ED(n),Mx(n),Cwe(n))},Twe=class extends _D{config;constructor(...[e]){let t=xwe(e||{});super(t),this.initConfig=t;let n=wwe(Nhe(uD(JS(nD(dD(TT(Phe(t))))))),e?.extensions||[]);this.config=n,this.middlewareStack.use(JC(this.config)),this.middlewareStack.use(QE(this.config)),this.middlewareStack.use(AD(this.config)),this.middlewareStack.use(iD(this.config)),this.middlewareStack.use(ZS(this.config)),this.middlewareStack.use(eC(this.config)),this.middlewareStack.use(rC(this.config)),this.middlewareStack.use(cC(this.config,{httpAuthSchemeParametersProvider:Ahe,identityProviderConfigProvider:async e=>new bT({"aws.auth#sigv4":e.credentials})})),this.middlewareStack.use(pC(this.config))}destroy(){super.destroy()}},Ewe=class extends bD.classBuilder().ep(a1).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeListeners`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeListenersCommand`).sc(swe).build(){},Dwe=class extends bD.classBuilder().ep(a1).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeLoadBalancerAttributes`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeLoadBalancerAttributesCommand`).sc(cwe).build(){},v2=class extends bD.classBuilder().ep(a1).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeLoadBalancers`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeLoadBalancersCommand`).sc(lwe).build(){},Owe=class extends bD.classBuilder().ep(a1).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeRules`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeRulesCommand`).sc(uwe).build(){},kwe=class extends bD.classBuilder().ep(a1).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeTags`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeTagsCommand`).sc(dwe).build(){},Awe=class extends bD.classBuilder().ep(a1).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeTargetGroupAttributes`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeTargetGroupAttributesCommand`).sc(fwe).build(){},y2=class extends bD.classBuilder().ep(a1).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeTargetGroups`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeTargetGroupsCommand`).sc(pwe).build(){},jwe=class extends bD.classBuilder().ep(a1).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeTargetHealth`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeTargetHealthCommand`).sc(mwe).build(){},Mwe=async(e,t,n)=>({operation:DS(t).operation,region:await OS(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()});function Nwe(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`iam`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var Pwe=e=>{let t=[];switch(e.operation){default:t.push(Nwe(e))}return t},Fwe=e=>{let t=LD(e);return Object.assign(t,{authSchemePreference:OS(e.authSchemePreference??[])})},Iwe=e=>Object.assign(e,{useDualstackEndpoint:e.useDualstackEndpoint??!1,useFipsEndpoint:e.useFipsEndpoint??!1,defaultSigningName:`iam`}),b2={UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},Lwe={name:`@aws-sdk/client-iam`,description:`AWS SDK for JavaScript Iam Client for Node.js, Browser and React Native`,version:`3.1045.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-iam`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo iam`,test:`yarn g:vitest run --passWithNoTests`,"test:e2e":`yarn g:vitest run -c vitest.config.e2e.mts --mode development`,"test:e2e:watch":`yarn g:vitest watch -c vitest.config.e2e.mts`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`,"test:integration":`yarn g:vitest run --passWithNoTests -c vitest.config.integ.mts`,"test:integration:watch":`yarn g:vitest run --passWithNoTests -c vitest.config.integ.mts`,"test:watch":`yarn g:vitest watch --passWithNoTests`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.8`,"@aws-sdk/credential-provider-node":`^3.972.39`,"@aws-sdk/middleware-host-header":`^3.972.10`,"@aws-sdk/middleware-logger":`^3.972.10`,"@aws-sdk/middleware-recursion-detection":`^3.972.11`,"@aws-sdk/middleware-user-agent":`^3.972.38`,"@aws-sdk/region-config-resolver":`^3.972.13`,"@aws-sdk/types":`^3.973.8`,"@aws-sdk/util-endpoints":`^3.996.8`,"@aws-sdk/util-user-agent-browser":`^3.972.10`,"@aws-sdk/util-user-agent-node":`^3.973.24`,"@smithy/config-resolver":`^4.4.17`,"@smithy/core":`^3.23.17`,"@smithy/fetch-http-handler":`^5.3.17`,"@smithy/hash-node":`^4.2.14`,"@smithy/invalid-dependency":`^4.2.14`,"@smithy/middleware-content-length":`^4.2.14`,"@smithy/middleware-endpoint":`^4.4.32`,"@smithy/middleware-retry":`^4.5.7`,"@smithy/middleware-serde":`^4.2.20`,"@smithy/middleware-stack":`^4.2.14`,"@smithy/node-config-provider":`^4.3.14`,"@smithy/node-http-handler":`^4.6.1`,"@smithy/protocol-http":`^5.3.14`,"@smithy/smithy-client":`^4.12.13`,"@smithy/types":`^4.14.1`,"@smithy/url-parser":`^4.2.14`,"@smithy/util-base64":`^4.3.2`,"@smithy/util-body-length-browser":`^4.2.2`,"@smithy/util-body-length-node":`^4.2.3`,"@smithy/util-defaults-mode-browser":`^4.3.49`,"@smithy/util-defaults-mode-node":`^4.2.54`,"@smithy/util-endpoints":`^3.4.2`,"@smithy/util-middleware":`^4.2.14`,"@smithy/util-retry":`^4.3.6`,"@smithy/util-utf8":`^4.2.2`,"@smithy/util-waiter":`^4.3.0`,tslib:`^2.6.2`},devDependencies:{"@smithy/snapshot-testing":`^2.0.8`,"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`,vitest:`^4.0.17`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-iam`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-iam`}},x2=`ref`,S2=`authSchemes`,C2=`name`,w2=`signingRegion`,T2=-1,E2=!0,D2=`isSet`,O2=`PartitionResult`,k2=`booleanEquals`,A2=`stringEquals`,j2=`getAttr`,M2=`sigv4`,N2={[x2]:`Endpoint`},P2={fn:j2,argv:[{[x2]:O2},`name`]},F2={[x2]:O2},I2={[S2]:[{[C2]:M2,[w2]:`us-east-1`}]},L2={[S2]:[{[C2]:M2,[w2]:`cn-north-1`}]},R2={[S2]:[{[C2]:M2,[w2]:`us-gov-west-1`}]},z2={[S2]:[{[C2]:M2,[w2]:`us-iso-east-1`}]},B2={[S2]:[{[C2]:M2,[w2]:`us-isob-east-1`}]},V2={[S2]:[{[C2]:M2,[w2]:`{PartitionResult#implicitGlobalRegion}`}]},H2=[{[x2]:`Region`}],U2={conditions:[[D2,[N2]],[D2,H2],[`aws.partition`,H2,O2],[k2,[{[x2]:`UseFIPS`},E2]],[k2,[{[x2]:`UseDualStack`},E2]],[A2,[P2,`aws`]],[A2,[P2,`aws-cn`]],[A2,[P2,`aws-us-gov`]],[k2,[{fn:j2,argv:[F2,`supportsDualStack`]},E2]],[A2,[P2,`aws-iso`]],[A2,[P2,`aws-iso-b`]],[k2,[{fn:j2,argv:[F2,`supportsFIPS`]},E2]],[A2,[P2,`aws-iso-e`]],[A2,[P2,`aws-iso-f`]],[A2,[P2,`aws-eusc`]]],results:[[T2],[T2,`Invalid Configuration: FIPS and custom endpoint are not supported`],[T2,`Invalid Configuration: Dualstack and custom endpoint are not supported`],[N2,{}],[`https://iam.global.api.aws`,I2],[`https://iam-fips.global.api.aws`,I2],[`https://iam.global.api.amazonwebservices.com.cn`,L2],[`https://iam.cn-north-1.amazonaws.com.cn`,L2],[`https://iam.us-gov.api.aws`,R2],[`https://iam.us-gov.amazonaws.com`,R2],[`https://iam.us-iso-east-1.c2s.ic.gov`,z2],[`https://iam-fips.us-iso-east-1.c2s.ic.gov`,z2],[`https://iam.us-isob-east-1.sc2s.sgov.gov`,B2],[`https://iam-fips.us-isob-east-1.sc2s.sgov.gov`,B2],[`https://iam.eu-isoe-west-1.cloud.adc-e.uk`,{[S2]:[{[C2]:M2,[w2]:`eu-isoe-west-1`}]}],[`https://iam.us-isof-south-1.csp.hci.ic.gov`,{[S2]:[{[C2]:M2,[w2]:`us-isof-south-1`}]}],[`https://iam.eusc-de-east-1.amazonaws.eu`,{[S2]:[{[C2]:M2,[w2]:`eusc-de-east-1`}]}],[`https://iam-fips.{PartitionResult#dualStackDnsSuffix}`,V2],[T2,`FIPS and DualStack are enabled, but this partition does not support one or both`],[`https://iam-fips.{PartitionResult#dnsSuffix}`,V2],[T2,`FIPS is enabled but this partition does not support FIPS`],[`https://iam.{PartitionResult#dualStackDnsSuffix}`,V2],[T2,`DualStack is enabled but this partition does not support DualStack`],[`https://iam.{PartitionResult#dnsSuffix}`,V2],[T2,`Invalid Configuration: Missing Region`]]},Rwe=2,W2=1e8,zwe=new Int32Array([-1,1,-1,0,27,3,1,4,W2+24,2,5,W2+24,3,18,6,4,14,7,6,W2+7,8,7,W2+9,9,9,W2+10,10,10,W2+12,11,12,W2+14,12,13,W2+15,13,14,W2+16,W2+23,5,W2+4,15,6,W2+6,16,7,W2+8,17,8,W2+21,W2+22,4,23,19,7,W2+9,20,9,W2+11,21,10,W2+13,22,11,W2+19,W2+20,5,W2+5,24,7,W2+8,25,8,26,W2+18,11,W2+17,W2+18,3,W2+1,28,4,W2+2,W2+3]),Bwe=ET.from(zwe,Rwe,U2.conditions,U2.results),Vwe=new DT({size:50,params:[`Endpoint`,`Region`,`UseDualStack`,`UseFIPS`]}),Hwe=(e,t={})=>Vwe.get(e,()=>aE(Bwe,{endpointParams:e,logger:t.logger}));jT.aws=mE;var G2=class e extends xD{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},Uwe=class e extends G2{name=`ConcurrentModificationException`;$fault=`client`;constructor(t){super({name:`ConcurrentModificationException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Wwe=class e extends G2{name=`NoSuchEntityException`;$fault=`client`;constructor(t){super({name:`NoSuchEntityException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Gwe=class e extends G2{name=`ServiceFailureException`;$fault=`server`;constructor(t){super({name:`ServiceFailureException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype)}},Kwe=class e extends G2{name=`AccountNotManagementOrDelegatedAdministratorException`;$fault=`client`;Message;constructor(t){super({name:`AccountNotManagementOrDelegatedAdministratorException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},qwe=class e extends G2{name=`InvalidInputException`;$fault=`client`;constructor(t){super({name:`InvalidInputException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Jwe=class e extends G2{name=`LimitExceededException`;$fault=`client`;constructor(t){super({name:`LimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Ywe=class e extends G2{name=`EntityAlreadyExistsException`;$fault=`client`;constructor(t){super({name:`EntityAlreadyExistsException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Xwe=class e extends G2{name=`UnmodifiableEntityException`;$fault=`client`;constructor(t){super({name:`UnmodifiableEntityException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Zwe=class e extends G2{name=`PolicyNotAttachableException`;$fault=`client`;constructor(t){super({name:`PolicyNotAttachableException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},Qwe=class e extends G2{name=`EntityTemporarilyUnmodifiableException`;$fault=`client`;constructor(t){super({name:`EntityTemporarilyUnmodifiableException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},$we=class e extends G2{name=`InvalidUserTypeException`;$fault=`client`;constructor(t){super({name:`InvalidUserTypeException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},eTe=class e extends G2{name=`PasswordPolicyViolationException`;$fault=`client`;constructor(t){super({name:`PasswordPolicyViolationException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},tTe=class e extends G2{name=`OpenIdIdpCommunicationErrorException`;$fault=`client`;constructor(t){super({name:`OpenIdIdpCommunicationErrorException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},nTe=class e extends G2{name=`MalformedPolicyDocumentException`;$fault=`client`;constructor(t){super({name:`MalformedPolicyDocumentException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},rTe=class e extends G2{name=`ServiceNotSupportedException`;$fault=`client`;constructor(t){super({name:`ServiceNotSupportedException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},iTe=class e extends G2{name=`DeleteConflictException`;$fault=`client`;constructor(t){super({name:`DeleteConflictException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},aTe=class e extends G2{name=`OrganizationNotFoundException`;$fault=`client`;Message;constructor(t){super({name:`OrganizationNotFoundException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},oTe=class e extends G2{name=`OrganizationNotInAllFeaturesModeException`;$fault=`client`;Message;constructor(t){super({name:`OrganizationNotInAllFeaturesModeException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},sTe=class e extends G2{name=`ServiceAccessNotEnabledException`;$fault=`client`;Message;constructor(t){super({name:`ServiceAccessNotEnabledException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},cTe=class e extends G2{name=`FeatureDisabledException`;$fault=`client`;constructor(t){super({name:`FeatureDisabledException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},lTe=class e extends G2{name=`InvalidAuthenticationCodeException`;$fault=`client`;constructor(t){super({name:`InvalidAuthenticationCodeException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},uTe=class e extends G2{name=`CallerIsNotManagementAccountException`;$fault=`client`;Message;constructor(t){super({name:`CallerIsNotManagementAccountException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.Message=t.Message}},dTe=class e extends G2{name=`FeatureEnabledException`;$fault=`client`;constructor(t){super({name:`FeatureEnabledException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},fTe=class e extends G2{name=`ReportGenerationLimitExceededException`;$fault=`client`;constructor(t){super({name:`ReportGenerationLimitExceededException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},pTe=class e extends G2{name=`CredentialReportExpiredException`;$fault=`client`;constructor(t){super({name:`CredentialReportExpiredException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},mTe=class e extends G2{name=`CredentialReportNotPresentException`;$fault=`client`;constructor(t){super({name:`CredentialReportNotPresentException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},hTe=class e extends G2{name=`CredentialReportNotReadyException`;$fault=`client`;constructor(t){super({name:`CredentialReportNotReadyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},gTe=class e extends G2{name=`UnrecognizedPublicKeyEncodingException`;$fault=`client`;constructor(t){super({name:`UnrecognizedPublicKeyEncodingException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},_Te=class e extends G2{name=`PolicyEvaluationException`;$fault=`server`;constructor(t){super({name:`PolicyEvaluationException`,$fault:`server`,...t}),Object.setPrototypeOf(this,e.prototype)}},vTe=class e extends G2{name=`KeyPairMismatchException`;$fault=`client`;constructor(t){super({name:`KeyPairMismatchException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},yTe=class e extends G2{name=`MalformedCertificateException`;$fault=`client`;constructor(t){super({name:`MalformedCertificateException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},bTe=class e extends G2{name=`DuplicateCertificateException`;$fault=`client`;constructor(t){super({name:`DuplicateCertificateException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},xTe=class e extends G2{name=`InvalidCertificateException`;$fault=`client`;constructor(t){super({name:`InvalidCertificateException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},STe=class e extends G2{name=`DuplicateSSHPublicKeyException`;$fault=`client`;constructor(t){super({name:`DuplicateSSHPublicKeyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},CTe=class e extends G2{name=`InvalidPublicKeyException`;$fault=`client`;constructor(t){super({name:`InvalidPublicKeyException`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},K2=`Arn`,wTe=`AttachmentCount`,q2=`AccessKey`,J2=`AccessKeyId`,Y2=`AccessKeyMetadata`,TTe=`AccountNotManagementOrDelegatedAdministratorException`,ETe=`AttachedPolicy`,DTe=`AttachedPermissionsBoundary`,OTe=`AttachedPolicies`,kTe=`AttachUserPolicy`,ATe=`AttachUserPolicyRequest`,jTe=`CreateAccessKey`,MTe=`CreateAccessKeyRequest`,NTe=`CreateAccessKeyResponse`,X2=`CreateDate`,PTe=`CallerIsNotManagementAccountException`,FTe=`ConcurrentModificationException`,ITe=`CreatePolicyRequest`,LTe=`CreatePolicyResponse`,RTe=`CreatePolicy`,zTe=`CredentialReportExpiredException`,BTe=`CredentialReportNotPresentException`,VTe=`CredentialReportNotReadyException`,HTe=`CreateUser`,UTe=`CreateUserRequest`,WTe=`CreateUserResponse`,Z2=`Description`,GTe=`DeleteAccessKey`,KTe=`DeleteAccessKeyRequest`,qTe=`DeleteConflictException`,JTe=`DuplicateCertificateException`,YTe=`DeletePolicyRequest`,XTe=`DeletePolicy`,ZTe=`DuplicateSSHPublicKeyException`,QTe=`DeleteUser`,$Te=`DetachUserPolicyRequest`,eEe=`DetachUserPolicy`,tEe=`DeleteUserRequest`,nEe=`DefaultVersionId`,rEe=`Document`,iEe=`EntityAlreadyExistsException`,aEe=`EntityTemporarilyUnmodifiableException`,oEe=`FeatureDisabledException`,sEe=`FeatureEnabledException`,cEe=`GetPolicy`,lEe=`GetPolicyRequest`,uEe=`GetPolicyResponse`,dEe=`GetPolicyVersion`,fEe=`GetPolicyVersionRequest`,pEe=`GetPolicyVersionResponse`,mEe=`GetUser`,hEe=`GetUserRequest`,gEe=`GetUserResponse`,_Ee=`IsAttachable`,vEe=`InvalidAuthenticationCodeException`,yEe=`InvalidCertificateException`,bEe=`IsDefaultVersion`,xEe=`InvalidInputException`,SEe=`InvalidPublicKeyException`,Q2=`IsTruncated`,CEe=`InvalidUserTypeException`,wEe=`Key`,TEe=`KeyPairMismatchException`,EEe=`ListAccessKeys`,DEe=`ListAccessKeysRequest`,OEe=`ListAccessKeysResponse`,kEe=`ListAttachedUserPolicies`,AEe=`ListAttachedUserPoliciesRequest`,jEe=`ListAttachedUserPoliciesResponse`,MEe=`LimitExceededException`,NEe=`ListPoliciesRequest`,PEe=`ListPoliciesResponse`,FEe=`ListPolicies`,IEe=`ListUsers`,LEe=`ListUsersRequest`,REe=`ListUsersResponse`,$2=`Message`,zEe=`MalformedCertificateException`,e4=`MaxItems`,BEe=`MalformedPolicyDocumentException`,t4=`Marker`,VEe=`NoSuchEntityException`,HEe=`OnlyAttached`,UEe=`OpenIdIdpCommunicationErrorException`,WEe=`OrganizationNotFoundException`,GEe=`OrganizationNotInAllFeaturesModeException`,n4=`PolicyArn`,r4=`PermissionsBoundary`,KEe=`PermissionsBoundaryArn`,qEe=`PermissionsBoundaryType`,JEe=`PermissionsBoundaryUsageCount`,YEe=`PolicyDocument`,XEe=`PolicyEvaluationException`,ZEe=`PolicyId`,QEe=`PasswordLastUsed`,i4=`PolicyName`,$Ee=`PolicyNotAttachableException`,eDe=`PasswordPolicyViolationException`,a4=`PathPrefix`,tDe=`PolicyUsageFilter`,o4=`PolicyVersion`,s4=`Path`,c4=`Policy`,nDe=`Policies`,rDe=`ReportGenerationLimitExceededException`,l4=`Status`,iDe=`SecretAccessKey`,aDe=`ServiceAccessNotEnabledException`,oDe=`ServiceFailureException`,sDe=`ServiceNotSupportedException`,cDe=`Scope`,u4=`Tags`,lDe=`Tag`,uDe=`UpdateAccessKey`,dDe=`UpdateAccessKeyRequest`,fDe=`UpdateDate`,pDe=`UnmodifiableEntityException`,mDe=`UserId`,d4=`UserName`,hDe=`UnrecognizedPublicKeyEncodingException`,f4=`User`,gDe=`Users`,p4=`VersionId`,_De=`Value`,vDe=`accessKeyMetadataListType`,yDe=`accessKeySecretType`,bDe=`attachedPoliciesListType`,m4=`awsQueryError`,h4=`client`,g4=`error`,_4=`httpError`,v4=`message`,xDe=`policyListType`,y4=`smithy.ts.sdk.synthetic.com.amazonaws.iam`,b4=`server`,SDe=`tagListType`,CDe=`userListType`,Y=`com.amazonaws.iam`,x4=iw.for(y4),wDe=[-3,y4,`IAMServiceException`,0,[],[]];x4.registerError(wDe,G2);var S4=iw.for(Y),TDe=[-3,Y,TTe,{[g4]:h4,[_4]:400},[$2],[0]];S4.registerError(TDe,Kwe);var EDe=[-3,Y,PTe,{[g4]:h4,[_4]:400},[$2],[0]];S4.registerError(EDe,uTe);var DDe=[-3,Y,FTe,{[m4]:[`ConcurrentModification`,409],[g4]:h4,[_4]:409},[v4],[0]];S4.registerError(DDe,Uwe);var ODe=[-3,Y,zTe,{[m4]:[`ReportExpired`,410],[g4]:h4,[_4]:410},[v4],[0]];S4.registerError(ODe,pTe);var kDe=[-3,Y,BTe,{[m4]:[`ReportNotPresent`,410],[g4]:h4,[_4]:410},[v4],[0]];S4.registerError(kDe,mTe);var ADe=[-3,Y,VTe,{[m4]:[`ReportInProgress`,404],[g4]:h4,[_4]:404},[v4],[0]];S4.registerError(ADe,hTe);var jDe=[-3,Y,qTe,{[m4]:[`DeleteConflict`,409],[g4]:h4,[_4]:409},[v4],[0]];S4.registerError(jDe,iTe);var MDe=[-3,Y,JTe,{[m4]:[`DuplicateCertificate`,409],[g4]:h4,[_4]:409},[v4],[0]];S4.registerError(MDe,bTe);var NDe=[-3,Y,ZTe,{[m4]:[`DuplicateSSHPublicKey`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(NDe,STe);var PDe=[-3,Y,iEe,{[m4]:[`EntityAlreadyExists`,409],[g4]:h4,[_4]:409},[v4],[0]];S4.registerError(PDe,Ywe);var FDe=[-3,Y,aEe,{[m4]:[`EntityTemporarilyUnmodifiable`,409],[g4]:h4,[_4]:409},[v4],[0]];S4.registerError(FDe,Qwe);var IDe=[-3,Y,oEe,{[m4]:[`FeatureDisabled`,404],[g4]:h4,[_4]:404},[v4],[0]];S4.registerError(IDe,cTe);var LDe=[-3,Y,sEe,{[m4]:[`FeatureEnabled`,409],[g4]:h4,[_4]:409},[v4],[0]];S4.registerError(LDe,dTe);var RDe=[-3,Y,vEe,{[m4]:[`InvalidAuthenticationCode`,403],[g4]:h4,[_4]:403},[v4],[0]];S4.registerError(RDe,lTe);var zDe=[-3,Y,yEe,{[m4]:[`InvalidCertificate`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(zDe,xTe);var BDe=[-3,Y,xEe,{[m4]:[`InvalidInput`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(BDe,qwe);var VDe=[-3,Y,SEe,{[m4]:[`InvalidPublicKey`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(VDe,CTe);var HDe=[-3,Y,CEe,{[m4]:[`InvalidUserType`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(HDe,$we);var UDe=[-3,Y,TEe,{[m4]:[`KeyPairMismatch`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(UDe,vTe);var WDe=[-3,Y,MEe,{[m4]:[`LimitExceeded`,409],[g4]:h4,[_4]:409},[v4],[0]];S4.registerError(WDe,Jwe);var GDe=[-3,Y,zEe,{[m4]:[`MalformedCertificate`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(GDe,yTe);var KDe=[-3,Y,BEe,{[m4]:[`MalformedPolicyDocument`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(KDe,nTe);var qDe=[-3,Y,VEe,{[m4]:[`NoSuchEntity`,404],[g4]:h4,[_4]:404},[v4],[0]];S4.registerError(qDe,Wwe);var JDe=[-3,Y,UEe,{[m4]:[`OpenIdIdpCommunicationError`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(JDe,tTe);var YDe=[-3,Y,WEe,{[g4]:h4,[_4]:400},[$2],[0]];S4.registerError(YDe,aTe);var XDe=[-3,Y,GEe,{[g4]:h4,[_4]:400},[$2],[0]];S4.registerError(XDe,oTe);var ZDe=[-3,Y,eDe,{[m4]:[`PasswordPolicyViolation`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(ZDe,eTe);var QDe=[-3,Y,XEe,{[m4]:[`PolicyEvaluation`,500],[g4]:b4,[_4]:500},[v4],[0]];S4.registerError(QDe,_Te);var $De=[-3,Y,$Ee,{[m4]:[`PolicyNotAttachable`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError($De,Zwe);var eOe=[-3,Y,rDe,{[m4]:[`ReportGenerationLimitExceeded`,409],[g4]:h4,[_4]:409},[v4],[0]];S4.registerError(eOe,fTe);var tOe=[-3,Y,aDe,{[g4]:h4,[_4]:400},[$2],[0]];S4.registerError(tOe,sTe);var nOe=[-3,Y,oDe,{[m4]:[`ServiceFailure`,500],[g4]:b4,[_4]:500},[v4],[0]];S4.registerError(nOe,Gwe);var rOe=[-3,Y,sDe,{[m4]:[`NotSupportedService`,404],[g4]:h4,[_4]:404},[v4],[0]];S4.registerError(rOe,rTe);var iOe=[-3,Y,pDe,{[m4]:[`UnmodifiableEntity`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(iOe,Xwe);var aOe=[-3,Y,hDe,{[m4]:[`UnrecognizedPublicKeyEncoding`,400],[g4]:h4,[_4]:400},[v4],[0]];S4.registerError(aOe,gTe);var oOe=[x4,S4],sOe=[0,Y,yDe,8,0],cOe=[3,Y,q2,0,[d4,J2,l4,iDe,X2],[0,0,0,[()=>sOe,0],4],4],lOe=[3,Y,Y2,0,[d4,J2,l4,X2],[0,0,0,4]],uOe=[3,Y,DTe,0,[qEe,KEe],[0,0]],dOe=[3,Y,ETe,0,[i4,n4],[0,0]],fOe=[3,Y,ATe,0,[d4,n4],[0,0],2],pOe=[3,Y,MTe,0,[d4],[0]],mOe=[3,Y,NTe,0,[q2],[[()=>cOe,0]],1],hOe=[3,Y,ITe,0,[i4,YEe,s4,Z2,u4],[0,0,0,0,()=>E4],2],gOe=[3,Y,LTe,0,[c4],[()=>C4]],_Oe=[3,Y,UTe,0,[d4,s4,r4,u4],[0,0,0,()=>E4],1],vOe=[3,Y,WTe,0,[f4],[()=>w4]],yOe=[3,Y,KTe,0,[J2,d4],[0,0],1],bOe=[3,Y,YTe,0,[n4],[0],1],xOe=[3,Y,tEe,0,[d4],[0],1],SOe=[3,Y,$Te,0,[d4,n4],[0,0],2],COe=[3,Y,lEe,0,[n4],[0],1],wOe=[3,Y,uEe,0,[c4],[()=>C4]],TOe=[3,Y,fEe,0,[n4,p4],[0,0],2],EOe=[3,Y,pEe,0,[o4],[()=>LOe]],DOe=[3,Y,hEe,0,[d4],[0]],OOe=[3,Y,gEe,0,[f4],[()=>w4],1],kOe=[3,Y,DEe,0,[d4,t4,e4],[0,0,1]],AOe=[3,Y,OEe,0,[Y2,Q2,t4],[()=>BOe,2,0],1],jOe=[3,Y,AEe,0,[d4,a4,t4,e4],[0,0,0,1],1],MOe=[3,Y,jEe,0,[OTe,Q2,t4],[()=>VOe,2,0]],NOe=[3,Y,NEe,0,[cDe,HEe,a4,tDe,t4,e4],[0,2,0,0,0,1]],POe=[3,Y,PEe,0,[nDe,Q2,t4],[()=>HOe,2,0]],FOe=[3,Y,LEe,0,[a4,t4,e4],[0,0,1]],IOe=[3,Y,REe,0,[gDe,Q2,t4],[()=>UOe,2,0],1],C4=[3,Y,c4,0,[i4,ZEe,K2,s4,nEe,wTe,JEe,_Ee,Z2,X2,fDe,u4],[0,0,0,0,0,1,1,2,0,4,4,()=>E4]],LOe=[3,Y,o4,0,[rEe,p4,bEe,X2],[0,0,2,4]],ROe=[3,Y,lDe,0,[wEe,_De],[0,0],2],zOe=[3,Y,dDe,0,[J2,l4,d4],[0,0,0],2],w4=[3,Y,f4,0,[s4,d4,mDe,K2,X2,QEe,r4,u4],[0,0,0,0,4,4,()=>uOe,()=>E4],5],T4=`unit`,BOe=[1,Y,vDe,0,()=>lOe],VOe=[1,Y,bDe,0,()=>dOe],HOe=[1,Y,xDe,0,()=>C4],E4=[1,Y,SDe,0,()=>ROe],UOe=[1,Y,CDe,0,()=>w4],WOe=[9,Y,kTe,0,()=>fOe,()=>T4],GOe=[9,Y,jTe,0,()=>pOe,()=>mOe],KOe=[9,Y,RTe,0,()=>hOe,()=>gOe],qOe=[9,Y,HTe,0,()=>_Oe,()=>vOe],JOe=[9,Y,GTe,0,()=>yOe,()=>T4],YOe=[9,Y,XTe,0,()=>bOe,()=>T4],XOe=[9,Y,QTe,0,()=>xOe,()=>T4],ZOe=[9,Y,eEe,0,()=>SOe,()=>T4],QOe=[9,Y,cEe,0,()=>COe,()=>wOe],$Oe=[9,Y,dEe,0,()=>TOe,()=>EOe],eke=[9,Y,mEe,0,()=>DOe,()=>OOe],tke=[9,Y,EEe,0,()=>kOe,()=>AOe],nke=[9,Y,kEe,0,()=>jOe,()=>MOe],rke=[9,Y,FEe,0,()=>NOe,()=>POe],ike=[9,Y,IEe,0,()=>FOe,()=>IOe],ake=[9,Y,uDe,0,()=>zOe,()=>T4],oke=e=>({apiVersion:`2010-05-08`,base64Decoder:e?.base64Decoder??vC,base64Encoder:e?.base64Encoder??yC,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??Hwe,extensions:e?.extensions??[],httpAuthSchemeProvider:e?.httpAuthSchemeProvider??Pwe,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new ID}],logger:e?.logger??new OD,protocol:e?.protocol??ZD,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.iam`,errorTypeRegistries:oOe,xmlNamespace:`https://iam.amazonaws.com/doc/2010-05-08/`,version:`2010-05-08`,serviceTarget:`AWSIdentityManagementV20100508`},serviceId:e?.serviceId??`IAM`,urlParser:e?.urlParser??UC,utf8Decoder:e?.utf8Decoder??Ux,utf8Encoder:e?.utf8Encoder??Gx}),ske=e=>{let t=WD(e),n=()=>t().then(CD),r=oke(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??UD,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??zD({serviceId:r.serviceId,clientVersion:Lwe.version}),maxAttempts:e?.maxAttempts??3,region:e?.region??VD(`Region is missing`),requestHandler:DC.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||_E),sha256:e?.sha256??Ax,streamCollector:e?.streamCollector??kC,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},cke=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},lke=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),uke=(e,t)=>{let n=Object.assign(W$(e),TD(e),jx(e),cke(e));return t.forEach(e=>e.configure(n)),Object.assign(e,G$(n),ED(n),Mx(n),lke(n))},dke=class extends _D{config;constructor(...[e]){let t=ske(e||{});super(t),this.initConfig=t;let n=uke(Fwe(uD(JS(nD(dD(TT(Iwe(t))))))),e?.extensions||[]);this.config=n,this.middlewareStack.use(JC(this.config)),this.middlewareStack.use(QE(this.config)),this.middlewareStack.use(AD(this.config)),this.middlewareStack.use(iD(this.config)),this.middlewareStack.use(ZS(this.config)),this.middlewareStack.use(eC(this.config)),this.middlewareStack.use(rC(this.config)),this.middlewareStack.use(cC(this.config,{httpAuthSchemeParametersProvider:Mwe,identityProviderConfigProvider:async e=>new bT({"aws.auth#sigv4":e.credentials})})),this.middlewareStack.use(pC(this.config))}destroy(){super.destroy()}},fke=class extends bD.classBuilder().ep(b2).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`GetPolicy`,{}).n(`IAMClient`,`GetPolicyCommand`).sc(QOe).build(){},pke=class extends bD.classBuilder().ep(b2).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`GetPolicyVersion`,{}).n(`IAMClient`,`GetPolicyVersionCommand`).sc($Oe).build(){},mke=class extends bD.classBuilder().ep(b2).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`GetUser`,{}).n(`IAMClient`,`GetUserCommand`).sc(eke).build(){},hke=class extends bD.classBuilder().ep(b2).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListAccessKeys`,{}).n(`IAMClient`,`ListAccessKeysCommand`).sc(tke).build(){},gke=class extends bD.classBuilder().ep(b2).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListAttachedUserPolicies`,{}).n(`IAMClient`,`ListAttachedUserPoliciesCommand`).sc(nke).build(){},_ke=class extends bD.classBuilder().ep(b2).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListPolicies`,{}).n(`IAMClient`,`ListPoliciesCommand`).sc(rke).build(){},vke=class extends bD.classBuilder().ep(b2).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListUsers`,{}).n(`IAMClient`,`ListUsersCommand`).sc(ike).build(){};function yke(e){return t=>async n=>{let{request:r}=n;if(e.expectContinueHeader!==!1&&Ix.isInstance(r)&&r.body&&e.runtime===`node`&&e.requestHandler?.constructor?.name!==`FetchHttpHandler`){let t=!0;if(typeof e.expectContinueHeader==`number`)try{t=(Number(r.headers?.[`content-length`])??e.bodyLengthChecker?.(r.body)??1/0)>=e.expectContinueHeader}catch{}else t=!!e.expectContinueHeader;t&&(r.headers.Expect=`100-continue`)}return t({...n,request:r})}}var bke={step:`build`,tags:[`SET_EXPECT_HEADER`,`EXPECT_HEADER`],name:`addExpectContinueMiddleware`,override:!0},xke=e=>({applyToStack:t=>{t.add(yke(e),bke)}}),D4={WHEN_SUPPORTED:`WHEN_SUPPORTED`,WHEN_REQUIRED:`WHEN_REQUIRED`},Ske=D4.WHEN_SUPPORTED,Cke={WHEN_SUPPORTED:`WHEN_SUPPORTED`,WHEN_REQUIRED:`WHEN_REQUIRED`},wke=D4.WHEN_SUPPORTED,O4;(function(e){e.MD5=`MD5`,e.CRC32=`CRC32`,e.CRC32C=`CRC32C`,e.CRC64NVME=`CRC64NVME`,e.SHA1=`SHA1`,e.SHA256=`SHA256`})(O4||={});var Tke;(function(e){e.HEADER=`header`,e.TRAILER=`trailer`})(Tke||={});var Eke=O4.CRC32,Dke=function(){function e(){this.crc32=new k4}return e.prototype.update=function(e){cx(e)||this.crc32.update(sx(e))},e.prototype.digest=function(){return _x(this,void 0,void 0,function(){return vx(this,function(e){return[2,lx(this.crc32.digest())]})})},e.prototype.reset=function(){this.crc32=new k4},e}(),k4=function(){function e(){this.checksum=4294967295}return e.prototype.update=function(e){var t,n;try{for(var r=yx(e),i=r.next();!i.done;i=r.next()){var a=i.value;this.checksum=this.checksum>>>8^Oke[(this.checksum^a)&255]}}catch(e){t={error:e}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return this},e.prototype.digest=function(){return(this.checksum^4294967295)>>>0},e}(),Oke=ux([0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117]),kke=e=>{let{requestChecksumCalculation:t,responseChecksumValidation:n,requestStreamBufferSize:r}=e;return Object.assign(e,{requestChecksumCalculation:OS(t??Ske),responseChecksumValidation:OS(n??wke),requestStreamBufferSize:Number(r??0),checksumAlgorithms:e.checksumAlgorithms??{}})},Ake=e=>(t,n)=>async r=>{let i=await e.region(),a=e.region,o=()=>{};n.__s3RegionRedirect&&(Object.defineProperty(e,`region`,{writable:!1,value:async()=>n.__s3RegionRedirect}),o=()=>Object.defineProperty(e,`region`,{writable:!0,value:a}));try{let a=await t(r);if(n.__s3RegionRedirect&&(o(),i!==await e.region()))throw Error(`Region was not restored following S3 region redirect.`);return a}catch(e){throw o(),e}},jke={tags:[`REGION_REDIRECT`,`S3`],name:`regionRedirectEndpointMiddleware`,override:!0,relation:`before`,toMiddleware:`endpointV2Middleware`};function Mke(e){return(t,n)=>async r=>{try{return await t(r)}catch(i){if(e.followRegionRedirects){let a=i?.$metadata?.httpStatusCode,o=n.commandName===`HeadBucketCommand`,s=i?.$response?.headers?.[`x-amz-bucket-region`];if(s&&(a===301||a===400&&(i?.name===`IllegalLocationConstraintException`||o))){try{let t=s;n.logger?.debug(`Redirecting from ${await e.region()} to ${t}`),n.__s3RegionRedirect=t}catch(e){throw Error(`Region redirect failed: `+e)}return t(r)}}throw i}}}var Nke={step:`initialize`,tags:[`REGION_REDIRECT`,`S3`],name:`regionRedirectMiddleware`,override:!0},Pke=e=>({applyToStack:t=>{t.add(Mke(e),Nke),t.addRelativeTo(Ake(e),jke)}}),Fke=class e{data;lastPurgeTime=Date.now();static EXPIRED_CREDENTIAL_PURGE_INTERVAL_MS=3e4;constructor(e={}){this.data=e}get(e){let t=this.data[e];if(t)return t}set(e,t){return this.data[e]=t,t}delete(e){delete this.data[e]}async purgeExpired(){let t=Date.now();if(!(this.lastPurgeTime+e.EXPIRED_CREDENTIAL_PURGE_INTERVAL_MS>t))for(let e in this.data){let n=this.data[e];if(!n.isRefreshing){let r=await n.identity;r.expiration&&r.expiration.getTime()(t.expiration?.getTime()??0){i.set(r,new A4(Promise.resolve(e)))})),t)):i.set(r,new A4(this.getIdentity(r))).identity}async getIdentity(e){await this.cache.purgeExpired().catch(e=>{console.warn(`Error while clearing expired entries in S3ExpressIdentityCache: -`+e)});let t=await this.createSessionFn(e);if(!t.Credentials?.AccessKeyId||!t.Credentials?.SecretAccessKey)throw Error(`s3#createSession response credential missing AccessKeyId or SecretAccessKey.`);return{accessKeyId:t.Credentials.AccessKeyId,secretAccessKey:t.Credentials.SecretAccessKey,sessionToken:t.Credentials.SessionToken,expiration:t.Credentials.Expiration?new Date(t.Credentials.Expiration):void 0}}},Lke=`sigv4-s3express`,j4=`X-Amz-S3session-Token`,M4=j4.toLowerCase(),Rke=class extends FS{async signWithCredentials(e,t,n){let r=N4(t);e.headers[M4]=t.sessionToken;let i=this;return P4(i,r),i.signRequest(e,n??{})}async presignWithCredentials(e,t,n){let r=N4(t);return delete e.headers[M4],e.headers[j4]=t.sessionToken,e.query=e.query??{},e.query[j4]=t.sessionToken,P4(this,r),this.presign(e,n)}};function N4(e){return{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey,expiration:e.expiration}}function P4(e,t){let n=setTimeout(()=>{throw Error(`SignatureV4S3Express credential override was created but not called.`)},10),r=e.credentialProvider;e.credentialProvider=()=>(clearTimeout(n),e.credentialProvider=r,Promise.resolve(t))}var zke=e=>(t,n)=>async r=>{if(n.endpointV2){let t=n.endpointV2,i=t.properties?.authSchemes?.[0]?.name===Lke;if((t.properties?.backend===`S3Express`||t.properties?.bucketType===`Directory`)&&(BE(n,`S3_EXPRESS_BUCKET`,`J`),n.isS3ExpressBucket=!0),i){let t=r.input.Bucket;if(t){let i=await e.s3ExpressIdentityProvider.getS3ExpressIdentity(await e.credentials(),{Bucket:t});n.s3ExpressIdentity=i,Ix.isInstance(r.request)&&i.sessionToken&&(r.request.headers[M4]=i.sessionToken)}}}return t(r)},Bke={name:`s3ExpressMiddleware`,step:`build`,tags:[`S3`,`S3_EXPRESS`],override:!0},Vke=e=>({applyToStack:t=>{t.add(zke(e),Bke)}}),Hke=async(e,t,n,r)=>{let i=await r.signWithCredentials(n,e,{});if(i.headers[`X-Amz-Security-Token`]||i.headers[`x-amz-security-token`])throw Error(`X-Amz-Security-Token must not be set for s3-express requests.`);return i},Uke=e=>e=>{throw e},Wke=(e,t)=>{},Gke=e=>(t,n)=>async r=>{if(!Ix.isInstance(r.request))return t(r);let i=DS(n).selectedHttpAuthScheme;if(!i)throw Error(`No HttpAuthScheme was selected: unable to sign request`);let{httpAuthOption:{signingProperties:a={}},identity:o,signer:s}=i,c;c=n.s3ExpressIdentity?await Hke(n.s3ExpressIdentity,a,r.request,await e.signer()):await s.sign(r.request,o,a);let l=await t({...r,request:c}).catch((s.errorHandler||Uke)(a));return(s.successHandler||Wke)(l.response,a),l},Kke=e=>({applyToStack:t=>{t.addRelativeTo(Gke(e),fC)}}),qke=(e,{session:t})=>{let[n,r]=t,{forcePathStyle:i,useAccelerateEndpoint:a,disableMultiregionAccessPoints:o,followRegionRedirects:s,s3ExpressIdentityProvider:c,bucketEndpoint:l,expectContinueHeader:u}=e;return Object.assign(e,{forcePathStyle:i??!1,useAccelerateEndpoint:a??!1,disableMultiregionAccessPoints:o??!1,followRegionRedirects:s??!1,s3ExpressIdentityProvider:c??new Ike(async e=>n().send(new r({Bucket:e}))),bucketEndpoint:l??!1,expectContinueHeader:u??2097152})};function Jke(e){return new ReadableStream({start(t){t.enqueue(e),t.close()}})}var Yke={CopyObjectCommand:!0,UploadPartCopyCommand:!0,CompleteMultipartUploadCommand:!0},Xke=e=>(t,n)=>async r=>{let i=await t(r),{response:a}=i;if(!Rx.isInstance(a))return i;let{statusCode:o,body:s}=a;if(o<200||o>=300)return i;let c=await Zke(s,e);if(a.body=Jke(c),c.length===0&&Yke[n.commandName]){let e=Error(`S3 aborted request`);throw e.$metadata={httpStatusCode:503},e.name=`InternalError`,e}let l=e.utf8Encoder(c.subarray(c.length-16));return l&&l.endsWith(``)&&(a.statusCode=503),i},Zke=(e=new Uint8Array,t)=>e instanceof Uint8Array?Promise.resolve(e):t.streamCollector(e)||Promise.resolve(new Uint8Array),Qke={relation:`after`,toMiddleware:`deserializerMiddleware`,tags:[`THROW_200_EXCEPTIONS`,`S3`],name:`throw200ExceptionsMiddleware`,override:!0},F4=e=>({applyToStack:t=>{t.addRelativeTo(Xke(e),Qke)}}),$ke=e=>typeof e==`string`&&e.indexOf(`arn:`)===0&&e.split(`:`).length>=6;function eAe(e){return(t,n)=>async r=>{if(e.bucketEndpoint){let e=n.endpointV2;if(e){let t=r.input.Bucket;if(typeof t==`string`)try{let r=new URL(t);n.endpointV2={...e,url:r}}catch(e){let r=`@aws-sdk/middleware-sdk-s3: bucketEndpoint=true was set but Bucket=${t} could not be parsed as URL.`;throw n.logger?.constructor?.name===`NoOpLogger`?console.warn(r):n.logger?.warn?.(r),e}}}return t(r)}}var tAe={name:`bucketEndpointMiddleware`,override:!0,relation:`after`,toMiddleware:`endpointV2Middleware`};function nAe({bucketEndpoint:e}){return t=>async n=>{let{input:{Bucket:r}}=n;if(!e&&typeof r==`string`&&!$ke(r)&&r.indexOf(`/`)>=0){let e=Error(`Bucket name shouldn't contain '/', received '${r}'`);throw e.name=`InvalidBucketName`,e}return t({...n})}}var rAe={step:`initialize`,tags:[`VALIDATE_BUCKET_NAME`],name:`validateBucketNameMiddleware`,override:!0},iAe=e=>({applyToStack:t=>{t.add(nAe(e),rAe),t.addRelativeTo(eAe(e),tAe)}}),aAe=class extends Mie{async serializeRequest(e,t,n){let r=await super.serializeRequest(e,t,n),i=ew.of(e.input),a=i.getSchema(),o=0,s=a[6]??0;if(t&&typeof t==`object`)for(let[e,n]of i.structIterator()){if(++o>s)break;if(e===`Bucket`){if(!t.Bucket&&n.getMergedTraits().httpLabel)throw Error(`No value provided for input HTTP label: Bucket.`);break}}return r}},oAe=e=>Object.assign(e,{eventStreamMarshaller:e.eventStreamSerdeProvider(e)}),I4={CrtSignerV4:null},L4=class{sigv4aSigner;sigv4Signer;signerOptions;static sigv4aDependency(){return typeof I4.CrtSignerV4==`function`?`crt`:typeof IS.SignatureV4a==`function`?`js`:`none`}constructor(e){this.sigv4Signer=new Rke(e),this.signerOptions=e}async sign(e,t={}){return t.signingRegion===`*`?this.getSigv4aSigner().sign(e,t):this.sigv4Signer.sign(e,t)}async signWithCredentials(e,t,n={}){if(n.signingRegion===`*`){let r=this.getSigv4aSigner(),i=I4.CrtSignerV4;if(i&&r instanceof i)return r.signWithCredentials(e,t,n);throw Error(`signWithCredentials with signingRegion '*' is only supported when using the CRT dependency @aws-sdk/signature-v4-crt. Please check whether you have installed the "@aws-sdk/signature-v4-crt" package explicitly. You must also register the package by calling [require("@aws-sdk/signature-v4-crt");] or an ESM equivalent such as [import "@aws-sdk/signature-v4-crt";]. For more information please go to https://github.com/aws/aws-sdk-js-v3#functionality-requiring-aws-common-runtime-crt`)}return this.sigv4Signer.signWithCredentials(e,t,n)}async presign(e,t={}){if(t.signingRegion===`*`){let n=this.getSigv4aSigner(),r=I4.CrtSignerV4;if(r&&n instanceof r)return n.presign(e,t);throw Error(`presign with signingRegion '*' is only supported when using the CRT dependency @aws-sdk/signature-v4-crt. Please check whether you have installed the "@aws-sdk/signature-v4-crt" package explicitly. You must also register the package by calling [require("@aws-sdk/signature-v4-crt");] or an ESM equivalent such as [import "@aws-sdk/signature-v4-crt";]. For more information please go to https://github.com/aws/aws-sdk-js-v3#functionality-requiring-aws-common-runtime-crt`)}return this.sigv4Signer.presign(e,t)}async presignWithCredentials(e,t,n={}){if(n.signingRegion===`*`)throw Error(`Method presignWithCredentials is not supported for [signingRegion=*].`);return this.sigv4Signer.presignWithCredentials(e,t,n)}getSigv4aSigner(){if(!this.sigv4aSigner){let e=I4.CrtSignerV4,t=IS.SignatureV4a;if(this.signerOptions.runtime===`node`){if(!e&&!t)throw Error(`Neither CRT nor JS SigV4a implementation is available. Please load either @aws-sdk/signature-v4-crt or @aws-sdk/signature-v4a. For more information please go to https://github.com/aws/aws-sdk-js-v3#functionality-requiring-aws-common-runtime-crt`);if(e&&typeof e==`function`)this.sigv4aSigner=new e({...this.signerOptions,signingAlgorithm:1});else if(t&&typeof t==`function`)this.sigv4aSigner=new t({...this.signerOptions});else throw Error(`Available SigV4a implementation is not a valid constructor. Please ensure you've properly imported @aws-sdk/signature-v4-crt or @aws-sdk/signature-v4a.For more information please go to https://github.com/aws/aws-sdk-js-v3#functionality-requiring-aws-common-runtime-crt`)}else{if(!t||typeof t!=`function`)throw Error(`JS SigV4a implementation is not available or not a valid constructor. Please check whether you have installed the @aws-sdk/signature-v4a package explicitly. The CRT implementation is not available for browsers. You must also register the package by calling [require('@aws-sdk/signature-v4a');] or an ESM equivalent such as [import '@aws-sdk/signature-v4a';]. For more information please go to https://github.com/aws/aws-sdk-js-v3#using-javascript-non-crt-implementation-of-sigv4a`);this.sigv4aSigner=new t({...this.signerOptions})}}return this.sigv4aSigner}},R4=`ref`,z4=`argv`,B4=`backend`,V4=`authSchemes`,H4=`disableDoubleEncoding`,U4=`signingName`,W4=`signingRegion`,G4=`signingRegionSet`,K4=-1,q4=!0,J4=!1,Y4=`isSet`,X4=`booleanEquals`,Z4=`stringEquals`,Q4=`coalesce`,$4=`substring`,e3=``,t3=`aws.partition`,n3=`partitionResult`,r3=`accessPointSuffix`,i3=`regionPrefix`,a3=e=>`outpostId_ssa_`+e+e3,o3=`hardwareType`,s3=`ite`,c3=`isValidHostLabel`,l3=`sigv4`,u3=`aws.isVirtualHostableS3Bucket`,d3=`url`,f3=`getAttr`,p3=`bucketArn`,m3=`--`,h3=`arnType`,g3=`accesspoint`,_3=e=>`accessPointName_ssa_`+e+e3,v3=`s3-object-lambda`,y3=`s3-outposts`,b3=`bucketPartition`,x3=`us-east-1`,S3=`outpostType`,C3=`name`,w3=`s3`,T3=`{url#scheme}://{Bucket}.{url#authority}{url#path}`,E3=`{url#scheme}://{url#authority}{url#path}`,D3=`{url#scheme}://{url#authority}{url#normalizedPath}{Bucket}`,O3=`https://{Bucket}.s3-accelerate.{partitionResult#dnsSuffix}`,k3=`https://{Bucket}.s3.{partitionResult#dnsSuffix}`,A3=e=>`{url#scheme}://{accessPointName_ssa_`+e+`}-{bucketArn#accountId}.{url#authority}{url#path}`,j3=e=>"Invalid ARN: The access point name may only contain a-z, A-Z, 0-9 and `-`. Found: `{accessPointName_ssa_"+e+"}`",M3=`sigv4a`,N3=`{url#scheme}://{url#authority}{url#normalizedPath}{uri_encoded_bucket}`,P3=`https://s3.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,F3=`https://s3.{partitionResult#dnsSuffix}`,I3={[R4]:`UseFIPS`},L3={[R4]:`UseDualStack`},R3={[R4]:`Bucket`},z3={fn:f3,[z4]:[{[R4]:n3},C3]},B3={[R4]:d3},V3={[R4]:`Region`},H3={[R4]:p3},U3={[R4]:h3},W3={[R4]:`accessPointName_ssa_1`},G3={fn:f3,[z4]:[H3,`region`]},K3={[R4]:o3},q3={fn:f3,[z4]:[H3,`service`]},J3={fn:f3,[z4]:[H3,`accountId`]},Y3={[B4]:`S3Express`,[V4]:[{[H4]:!0,[C3]:`{_s3e_auth}`,[U4]:`s3express`,[W4]:`{Region}`}]},X3={[B4]:`S3Express`,[V4]:[{[H4]:!0,[C3]:l3,[U4]:`s3express`,[W4]:`{Region}`}]},Z3={[V4]:[{[H4]:!0,[C3]:M3,[U4]:y3,[G4]:[`*`]},{[H4]:!0,[C3]:l3,[U4]:y3,[W4]:`{Region}`}]},Q3={[V4]:[{[H4]:!0,[C3]:l3,[U4]:w3,[W4]:x3}]},$3={[V4]:[{[H4]:!0,[C3]:l3,[U4]:w3,[W4]:`{Region}`}]},e6={[V4]:[{[H4]:!0,[C3]:l3,[U4]:v3,[W4]:`{bucketArn#region}`}]},t6={[V4]:[{[H4]:!0,[C3]:l3,[U4]:w3,[W4]:`{bucketArn#region}`}]},n6={[V4]:[{[H4]:!0,[C3]:M3,[U4]:y3,[G4]:[`*`]},{[H4]:!0,[C3]:l3,[U4]:y3,[W4]:`{bucketArn#region}`}]},r6={[V4]:[{[H4]:!0,[C3]:l3,[U4]:v3,[W4]:`{Region}`}]},i6=[V3],a6=[{[R4]:`Endpoint`}],o6=[R3],s6=[R3,0,7,!0],c6=[H3,`resourceId[1]`],sAe=[`*`],l6={conditions:[[Y4,i6],[X4,[{[R4]:`Accelerate`},q4]],[X4,[I3,q4]],[X4,[L3,q4]],[Y4,a6],[Y4,o6],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,0,6,q4]},e3]},`--x-s3`]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:s6},e3]},`--xa-s3`]],[t3,i6,n3],[$4,s6,r3],[Z4,[{[R4]:r3},`--op-s3`]],[$4,[R3,8,12,q4],i3],[$4,[R3,32,49,q4],a3(2)],[$4,[R3,49,50,q4],o3],[X4,[{[R4]:`ForcePathStyle`},q4]],[Z4,[z3,`aws-cn`]],[s3,[L3,`.dualstack`,e3],`_s3e_ds`],[c3,[{[R4]:a3(2)},J4]],[s3,[I3,`-fips`,e3],`_s3e_fips`],[s3,[{fn:Q4,[z4]:[{[R4]:`DisableS3ExpressSessionAuth`},J4]},l3,`sigv4-s3express`],`_s3e_auth`],[u3,[R3,J4]],[`parseURL`,a6,d3],[X4,[{fn:Q4,[z4]:[{[R4]:`UseS3ExpressControlEndpoint`},J4]},q4]],[u3,[R3,q4]],[Z4,[{fn:f3,[z4]:[B3,`scheme`]},`http`]],[c3,[V3,J4]],[`aws.parseArn`,o6,p3],[f3,[{fn:`split`,[z4]:[R3,m3,0]},`[-2]`],`s3expressAvailabilityZoneId`],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,0,4,J4]},e3]},`arn:`]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,16,18,q4]},e3]},m3]],[X4,[{fn:f3,[z4]:[B3,`isIp`]},q4]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,21,23,q4]},e3]},m3]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,27,29,q4]},e3]},m3]],[Z4,[{[R4]:i3},`beta`]],[`uriEncode`,o6,`uri_encoded_bucket`],[c3,[V3,q4]],[X4,[{fn:Q4,[z4]:[{[R4]:`UseObjectLambdaEndpoint`},J4]},q4]],[f3,[H3,`resourceId[0]`],h3],[Z4,[U3,e3]],[Z4,[U3,g3]],[f3,c6,_3(1)],[Z4,[W3,e3]],[Z4,[G3,e3]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,14,16,q4]},e3]},m3]],[Z4,[K3,`e`]],[Z4,[K3,`o`]],[Z4,[V3,`aws-global`]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,19,21,q4]},e3]},m3]],[Z4,[q3,v3]],[X4,[{fn:Q4,[z4]:[{[R4]:`DisableAccessPoints`},J4]},q4]],[Z4,[q3,y3]],[t3,[G3],b3],[c3,[W3,q4]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,26,28,q4]},e3]},m3]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,15,17,q4]},e3]},m3]],[f3,[H3,`resourceId[4]`]],[Z4,[{fn:Q4,[z4]:[{fn:$4,[z4]:[R3,20,22,q4]},e3]},m3]],[X4,[{[R4]:`UseGlobalEndpoint`},q4]],[Z4,[V3,x3]],[f3,c6,a3(1)],[X4,[{fn:Q4,[z4]:[{[R4]:`UseArnRegion`},q4]},q4]],[c3,[{[R4]:a3(1)},J4]],[f3,[H3,`resourceId[2]`],S3],[Z4,[V3,G3]],[Z4,[{fn:f3,[z4]:[{[R4]:b3},C3]},z3]],[X4,[{[R4]:`DisableMultiRegionAccessPoints`},q4]],[c3,[G3,q4]],[Z4,[{fn:f3,[z4]:[H3,`partition`]},z3]],[Z4,[J3,e3]],[Z4,[q3,w3]],[c3,[J3,J4]],[f3,[H3,`resourceId[3]`],_3(2)],[c3,[W3,J4]],[Z4,[{[R4]:S3},g3]],[c3,[{[R4]:_3(2)},J4]]],results:[[K4],[K4,`Accelerate cannot be used with FIPS`],[K4,`Cannot set dual-stack in combination with a custom endpoint.`],[K4,`A custom endpoint cannot be combined with FIPS`],[K4,`A custom endpoint cannot be combined with S3 Accelerate`],[K4,`Partition does not support FIPS`],[K4,`S3Express does not support S3 Accelerate.`],[`{url#scheme}://{url#authority}/{uri_encoded_bucket}{url#path}`,Y3],[T3,Y3],[K4,`S3Express bucket name is not a valid virtual hostable name.`],[`https://s3express-control{_s3e_fips}{_s3e_ds}.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,X3],[`https://{Bucket}.s3express{_s3e_fips}-{s3expressAvailabilityZoneId}{_s3e_ds}.{Region}.{partitionResult#dnsSuffix}`,Y3],[K4,`Unrecognized S3Express bucket name format.`],[E3,Y3],[`https://s3express-control{_s3e_fips}{_s3e_ds}.{Region}.{partitionResult#dnsSuffix}`,X3],[K4,`Expected a endpoint to be specified but no endpoint was found`],[`https://{Bucket}.ec2.{url#authority}`,Z3],[`https://{Bucket}.ec2.s3-outposts.{Region}.{partitionResult#dnsSuffix}`,Z3],[`https://{Bucket}.op-{outpostId_ssa_2}.{url#authority}`,Z3],[`https://{Bucket}.op-{outpostId_ssa_2}.s3-outposts.{Region}.{partitionResult#dnsSuffix}`,Z3],[K4,`Unrecognized hardware type: "Expected hardware type o or e but got {hardwareType}"`],[K4,`Invalid Outposts Bucket alias - it must be a valid bucket name.`],[K4,"Invalid ARN: The outpost Id must only contain a-z, A-Z, 0-9 and `-`."],[K4,"Custom endpoint `{Endpoint}` was not a valid URI"],[K4,`S3 Accelerate cannot be used in this region`],[`https://{Bucket}.s3-fips.dualstack.us-east-1.{partitionResult#dnsSuffix}`,Q3],[`https://{Bucket}.s3-fips.dualstack.{Region}.{partitionResult#dnsSuffix}`,$3],[`https://{Bucket}.s3-fips.us-east-1.{partitionResult#dnsSuffix}`,Q3],[`https://{Bucket}.s3-fips.{Region}.{partitionResult#dnsSuffix}`,$3],[`https://{Bucket}.s3-accelerate.dualstack.us-east-1.{partitionResult#dnsSuffix}`,Q3],[`https://{Bucket}.s3-accelerate.dualstack.{partitionResult#dnsSuffix}`,$3],[`https://{Bucket}.s3.dualstack.us-east-1.{partitionResult#dnsSuffix}`,Q3],[`https://{Bucket}.s3.dualstack.{Region}.{partitionResult#dnsSuffix}`,$3],[D3,Q3],[T3,Q3],[D3,$3],[T3,$3],[O3,Q3],[O3,$3],[k3,Q3],[k3,$3],[`https://{Bucket}.s3.{Region}.{partitionResult#dnsSuffix}`,$3],[K4,`Invalid region: region was not a valid DNS name.`],[K4,`S3 Object Lambda does not support Dual-stack`],[K4,`S3 Object Lambda does not support S3 Accelerate`],[K4,`Access points are not supported for this operation`],[K4,"Invalid configuration: region from ARN `{bucketArn#region}` does not match client region `{Region}` and UseArnRegion is `false`"],[K4,`Invalid ARN: Missing account id`],[A3(1),e6],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-object-lambda-fips.{bucketArn#region}.{bucketPartition#dnsSuffix}`,e6],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-object-lambda.{bucketArn#region}.{bucketPartition#dnsSuffix}`,e6],[K4,j3(1)],[K4,"Invalid ARN: The account id may only contain a-z, A-Z, 0-9 and `-`. Found: `{bucketArn#accountId}`"],[K4,"Invalid region in ARN: `{bucketArn#region}` (invalid DNS name)"],[K4,"Client was configured for partition `{partitionResult#name}` but ARN (`{Bucket}`) has `{bucketPartition#name}`"],[K4,"Invalid ARN: The ARN may only contain a single resource component after `accesspoint`."],[K4,`Invalid ARN: bucket ARN is missing a region`],[K4,"Invalid ARN: Expected a resource of the format `accesspoint:` but no name was provided"],[K4,"Invalid ARN: Object Lambda ARNs only support `accesspoint` arn types, but found: `{arnType}`"],[K4,`Access Points do not support S3 Accelerate`],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-accesspoint-fips.dualstack.{bucketArn#region}.{bucketPartition#dnsSuffix}`,t6],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-accesspoint-fips.{bucketArn#region}.{bucketPartition#dnsSuffix}`,t6],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-accesspoint.dualstack.{bucketArn#region}.{bucketPartition#dnsSuffix}`,t6],[A3(1),t6],[`https://{accessPointName_ssa_1}-{bucketArn#accountId}.s3-accesspoint.{bucketArn#region}.{bucketPartition#dnsSuffix}`,t6],[K4,`Invalid ARN: The ARN was not for the S3 service, found: {bucketArn#service}`],[K4,`S3 MRAP does not support dual-stack`],[K4,`S3 MRAP does not support FIPS`],[K4,`S3 MRAP does not support S3 Accelerate`],[K4,`Invalid configuration: Multi-Region Access Point ARNs are disabled.`],[`https://{accessPointName_ssa_1}.accesspoint.s3-global.{partitionResult#dnsSuffix}`,{[V4]:[{[H4]:q4,name:M3,[U4]:w3,[G4]:sAe}]}],[K4,"Client was configured for partition `{partitionResult#name}` but bucket referred to partition `{bucketArn#partition}`"],[K4,`Invalid Access Point Name`],[K4,`S3 Outposts does not support Dual-stack`],[K4,`S3 Outposts does not support FIPS`],[K4,`S3 Outposts does not support S3 Accelerate`],[K4,`Invalid Arn: Outpost Access Point ARN contains sub resources`],[`https://{accessPointName_ssa_2}-{bucketArn#accountId}.{outpostId_ssa_1}.{url#authority}`,n6],[`https://{accessPointName_ssa_2}-{bucketArn#accountId}.{outpostId_ssa_1}.s3-outposts.{bucketArn#region}.{bucketPartition#dnsSuffix}`,n6],[K4,j3(2)],[K4,"Expected an outpost type `accesspoint`, found {outpostType}"],[K4,`Invalid ARN: expected an access point name`],[K4,`Invalid ARN: Expected a 4-component resource`],[K4,"Invalid ARN: The outpost Id may only contain a-z, A-Z, 0-9 and `-`. Found: `{outpostId_ssa_1}`"],[K4,`Invalid ARN: The Outpost Id was not set`],[K4,`Invalid ARN: Unrecognized format: {Bucket} (type: {arnType})`],[K4,`Invalid ARN: No ARN type specified`],[K4,"Invalid ARN: `{Bucket}` was not a valid ARN"],[K4,`Path-style addressing cannot be used with ARN buckets`],[`https://s3-fips.dualstack.us-east-1.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,Q3],[`https://s3-fips.dualstack.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,$3],[`https://s3-fips.us-east-1.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,Q3],[`https://s3-fips.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,$3],[`https://s3.dualstack.us-east-1.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,Q3],[`https://s3.dualstack.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,$3],[N3,Q3],[N3,$3],[P3,Q3],[P3,$3],[`https://s3.{Region}.{partitionResult#dnsSuffix}/{uri_encoded_bucket}`,$3],[K4,`Path-style addressing cannot be used with S3 Accelerate`],[E3,r6],[`https://s3-object-lambda-fips.{Region}.{partitionResult#dnsSuffix}`,r6],[`https://s3-object-lambda.{Region}.{partitionResult#dnsSuffix}`,r6],[`https://s3-fips.dualstack.us-east-1.{partitionResult#dnsSuffix}`,Q3],[`https://s3-fips.dualstack.{Region}.{partitionResult#dnsSuffix}`,$3],[`https://s3-fips.us-east-1.{partitionResult#dnsSuffix}`,Q3],[`https://s3-fips.{Region}.{partitionResult#dnsSuffix}`,$3],[`https://s3.dualstack.us-east-1.{partitionResult#dnsSuffix}`,Q3],[`https://s3.dualstack.{Region}.{partitionResult#dnsSuffix}`,$3],[E3,Q3],[E3,$3],[F3,Q3],[F3,$3],[`https://s3.{Region}.{partitionResult#dnsSuffix}`,$3],[K4,`A region must be set when sending requests to S3.`]]},cAe=2,X=1e8,lAe=new Int32Array([-1,1,-1,0,3,X+115,1,424,4,2,272,5,3,233,6,4,85,7,5,15,8,8,9,X+115,16,10,13,18,11,13,19,12,13,22,X+14,13,35,14,X+42,36,X+103,435,6,271,16,7,270,17,8,19,18,14,501,106,9,20,24,10,21,24,11,22,24,12,23,24,13,547,24,14,77,25,20,73,26,26,27,78,37,28,X+86,38,X+86,29,39,47,30,48,X+58,31,50,32,X+85,51,33,136,55,X+76,34,59,35,X+84,60,39,36,61,37,X+83,62,38,146,63,41,X+46,61,40,X+83,62,41,150,64,42,X+54,66,43,X+53,70,44,X+52,71,45,X+81,73,46,X+80,74,X+78,X+79,40,48,X+57,41,X+57,49,42,185,50,48,62,51,49,X+45,52,51,53,526,60,56,54,62,X+55,55,63,57,X+46,62,X+55,57,64,58,X+54,66,59,X+53,69,60,X+65,70,61,X+52,72,X+64,X+51,49,X+45,63,51,64,526,60,67,65,62,X+55,66,63,68,X+46,62,X+55,68,64,69,X+54,66,70,X+53,68,X+47,71,70,72,X+52,72,X+50,X+51,25,74,X+42,46,X+39,75,57,76,X+41,58,X+40,X+41,26,X+88,78,28,X+87,79,34,82,80,35,81,545,36,X+103,X+115,46,X+97,83,57,84,X+99,58,X+98,X+99,5,101,86,8,87,X+115,16,88,89,18,91,89,19,90,92,21,97,95,19,93,92,21,98,95,21,97,94,22,X+14,95,35,96,X+42,36,X+103,X+42,22,X+13,98,35,99,X+42,36,X+101,100,46,X+110,X+111,6,214,102,7,208,103,8,119,104,14,118,105,21,106,X+23,26,107,502,37,108,X+86,38,X+86,109,39,112,110,48,X+58,111,50,136,X+85,40,113,X+57,41,X+57,114,42,115,500,48,X+56,116,52,117,X+72,65,X+69,X+72,21,501,X+23,9,120,124,10,121,124,11,122,124,12,123,124,13,202,124,14,195,125,20,190,126,21,127,X+23,23,128,129,24,189,129,26,130,197,37,131,X+86,38,X+86,132,39,159,133,48,X+58,134,50,135,X+85,51,141,136,55,X+76,137,59,138,X+84,60,X+83,139,61,140,X+83,63,X+83,X+46,55,X+76,142,59,143,X+84,60,148,144,61,145,X+83,62,147,146,63,150,X+46,63,153,X+46,61,149,X+83,62,153,150,64,151,X+54,66,152,X+53,70,X+82,X+52,64,154,X+54,66,155,X+53,70,156,X+52,71,157,X+81,73,158,X+80,74,X+77,X+79,40,160,X+57,41,X+57,161,42,185,162,48,174,163,49,X+45,164,51,165,526,60,168,166,62,X+55,167,63,169,X+46,62,X+55,169,64,170,X+54,66,171,X+53,69,172,X+65,70,173,X+52,72,X+63,X+51,49,X+45,175,51,176,526,60,179,177,62,X+55,178,63,180,X+46,62,X+55,180,64,181,X+54,66,182,X+53,68,X+47,183,70,184,X+52,72,X+48,X+51,48,X+56,186,52,187,X+72,65,X+69,188,67,X+70,X+71,25,X+36,X+42,21,191,X+23,25,192,X+42,30,194,193,46,X+34,X+36,46,X+33,X+35,21,196,X+23,26,X+88,197,28,X+87,198,34,201,199,35,200,545,36,X+101,X+115,46,X+95,X+96,17,203,X+22,20,204,X+21,21,205,550,33,206,550,44,X+16,207,45,X+18,X+20,8,209,215,16,210,220,18,211,220,19,212,224,20,213,227,21,231,401,8,218,215,19,216,X+9,20,217,227,21,231,X+9,16,219,220,18,223,220,19,221,224,20,222,227,21,231,X+12,19,226,224,20,225,X+9,21,X+9,X+12,20,230,227,21,228,X+9,30,229,X+9,34,X+7,X+9,21,231,415,30,232,X+8,34,X+7,X+8,4,X+2,234,5,235,480,6,271,236,7,270,237,8,238,491,9,239,243,10,240,243,11,241,243,12,242,243,13,547,243,14,266,244,20,264,245,26,246,267,37,247,X+86,38,X+86,248,39,249,518,40,250,X+57,41,X+57,251,42,538,252,48,X+43,253,49,X+45,254,51,255,526,60,258,256,62,X+55,257,63,259,X+46,62,X+55,259,64,260,X+54,66,261,X+53,69,262,X+65,70,263,X+52,72,X+62,X+51,25,265,X+42,46,X+31,X+32,26,X+88,267,28,X+87,268,34,269,544,46,X+93,X+94,8,397,X+9,8,407,X+9,3,346,273,4,X+3,274,5,284,275,8,276,X+115,15,X+5,277,16,278,281,18,279,281,19,280,281,22,X+14,281,35,282,X+42,36,X+102,283,46,X+106,X+107,6,405,285,7,395,286,8,295,287,14,501,288,26,289,502,37,290,X+86,38,X+86,291,39,292,307,40,293,X+57,41,X+57,294,42,335,500,9,296,300,10,297,300,11,298,300,12,299,300,13,394,300,14,339,301,15,X+5,302,20,337,303,26,304,341,37,305,X+86,38,X+86,306,39,309,307,48,X+58,308,50,X+74,X+85,40,310,X+57,41,X+57,311,42,335,312,48,324,313,49,X+45,314,51,315,526,60,318,316,62,X+55,317,63,319,X+46,62,X+55,319,64,320,X+54,66,321,X+53,69,322,X+65,70,323,X+52,72,X+61,X+51,49,X+45,325,51,326,526,60,329,327,62,X+55,328,63,330,X+46,62,X+55,330,64,331,X+54,66,332,X+53,68,X+47,333,70,334,X+52,72,X+49,X+51,48,X+56,336,52,X+67,X+72,25,338,X+42,46,X+27,X+28,15,X+5,340,26,X+88,341,28,X+87,342,34,345,343,35,344,545,36,X+102,X+115,46,X+91,X+92,4,X+2,347,5,357,348,8,349,X+115,15,X+5,350,16,351,354,18,352,354,19,353,354,22,X+14,354,35,355,X+42,36,X+43,356,46,X+104,X+105,6,405,358,7,395,359,8,360,491,9,361,365,10,362,365,11,363,365,12,364,365,13,394,365,14,389,366,15,X+5,367,20,387,368,26,369,391,37,370,X+86,38,X+86,371,39,372,518,40,373,X+57,41,X+57,374,42,538,375,48,X+43,376,49,X+45,377,51,378,526,60,381,379,62,X+55,380,63,382,X+46,62,X+55,382,64,383,X+54,66,384,X+53,69,385,X+65,70,386,X+52,72,X+60,X+51,25,388,X+42,46,X+25,X+26,15,X+5,390,26,X+88,391,28,X+87,392,34,393,544,46,X+89,X+90,15,X+5,547,8,396,X+9,15,X+5,397,16,398,410,18,399,410,19,400,410,20,401,X+9,27,402,X+12,29,X+11,403,31,X+11,404,32,X+11,422,8,406,X+9,15,X+5,407,16,408,410,18,409,410,19,411,410,20,X+12,X+9,20,414,412,22,413,X+9,34,X+10,X+9,22,416,415,27,419,X+12,27,418,417,34,X+10,X+12,34,X+10,419,43,X+11,420,47,X+11,421,53,X+11,422,54,X+11,423,56,X+11,X+12,2,X+1,425,3,478,426,4,X+4,427,5,438,428,8,429,X+115,16,430,433,18,431,433,19,432,433,22,X+14,433,35,434,X+42,36,X+44,435,46,X+112,436,57,437,X+114,58,X+113,X+114,6,X+6,439,7,X+6,440,8,450,441,14,501,442,26,443,502,37,444,X+86,38,X+86,445,39,446,465,40,447,X+57,41,X+57,448,42,471,449,48,X+44,500,9,451,455,10,452,455,11,453,455,12,454,455,13,547,455,14,473,456,15,460,457,20,458,461,25,459,X+42,46,X+37,X+38,20,540,461,26,462,474,37,463,X+86,38,X+86,464,39,467,465,48,X+58,466,50,X+75,X+85,40,468,X+57,41,X+57,469,42,471,470,48,X+44,524,48,X+44,472,52,X+68,X+72,26,X+88,474,28,X+87,475,34,X+100,476,35,477,545,36,X+44,X+115,4,X+2,479,5,488,480,8,481,X+115,16,482,485,18,483,485,19,484,485,22,X+14,485,35,486,X+42,36,X+43,487,46,X+108,X+109,6,X+6,489,7,X+6,490,8,503,491,14,501,492,26,493,502,37,494,X+86,38,X+86,495,39,496,518,40,497,X+57,41,X+57,498,42,538,499,48,X+43,500,49,X+45,526,26,X+88,502,28,X+87,X+115,9,504,508,10,505,508,11,506,508,12,507,508,13,547,508,14,541,509,15,513,510,20,511,514,25,512,X+42,46,X+29,X+30,20,540,514,26,515,542,37,516,X+86,38,X+86,517,39,520,518,48,X+58,519,50,X+73,X+85,40,521,X+57,41,X+57,522,42,538,523,48,X+43,524,49,X+45,525,51,529,526,60,X+55,527,62,X+55,528,63,X+55,X+46,60,532,530,62,X+55,531,63,533,X+46,62,X+55,533,64,534,X+54,66,535,X+53,69,536,X+65,70,537,X+52,72,X+59,X+51,48,X+43,539,52,X+66,X+72,25,X+24,X+42,26,X+88,542,28,X+87,543,34,X+100,544,35,546,545,36,X+42,X+115,36,X+43,X+115,17,548,X+22,20,549,X+21,33,552,550,44,X+17,551,45,X+19,X+20,44,X+15,553,45,X+15,X+20]),uAe=ET.from(lAe,cAe,l6.conditions,l6.results),dAe=new DT({size:50,params:[`Accelerate`,`Bucket`,`DisableAccessPoints`,`DisableMultiRegionAccessPoints`,`DisableS3ExpressSessionAuth`,`Endpoint`,`ForcePathStyle`,`Region`,`UseArnRegion`,`UseDualStack`,`UseFIPS`,`UseGlobalEndpoint`,`UseObjectLambdaEndpoint`,`UseS3ExpressControlEndpoint`]}),u6=(e,t={})=>dAe.get(e,()=>aE(uAe,{endpointParams:e,logger:t.logger}));jT.aws=mE;var fAe=(e=>async(t,n,r)=>{if(!r)throw Error("Could not find `input` for `defaultEndpointRuleSetHttpAuthSchemeParametersProvider`");let i=await e(t,n,r),a=DS(n)?.commandInstance?.constructor?.getEndpointParameterInstructions;if(!a)throw Error(`getEndpointParameterInstructions() is not defined on '${n.commandName}'`);let o=await cD(r,{getEndpointParameterInstructions:a},t);return Object.assign(i,o)})(async(e,t,n)=>({operation:DS(t).operation,region:await OS(e.region)()||(()=>{throw Error("expected `region` to be configured for `aws.auth#sigv4`")})()}));function d6(e){return{schemeId:`aws.auth#sigv4`,signingProperties:{name:`s3`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}function f6(e){return{schemeId:`aws.auth#sigv4a`,signingProperties:{name:`s3`,region:e.region},propertiesExtractor:(e,t)=>({signingProperties:{config:e,context:t}})}}var pAe=((e,t,n)=>r=>{let i=e(r).properties?.authSchemes;if(!i)return t(r);let a=[];for(let e of i){let{name:t,properties:o={},...s}=e,c=t.toLowerCase();t!==c&&console.warn(`HttpAuthScheme has been normalized with lowercasing: '${t}' to '${c}'`);let l;if(c===`sigv4a`){l=`aws.auth#sigv4a`;let e=i.find(e=>{let t=e.name.toLowerCase();return t!==`sigv4a`&&t.startsWith(`sigv4`)});if(L4.sigv4aDependency()===`none`&&e)continue}else if(c.startsWith(`sigv4`))l=`aws.auth#sigv4`;else throw Error(`Unknown HttpAuthScheme found in '@smithy.rules#endpointRuleSet': '${c}'`);let u=n[l];if(!u)throw Error(`Could not find HttpAuthOption create function for '${l}'`);let d=u(r);d.schemeId=l,d.signingProperties={...d.signingProperties||{},...s,...o},a.push(d)}return a})(u6,e=>{let t=[];switch(e.operation){default:t.push(d6(e)),t.push(f6(e))}return t},{"aws.auth#sigv4":d6,"aws.auth#sigv4a":f6}),mAe=e=>{let t=cie(LD(e));return Object.assign(t,{authSchemePreference:OS(e.authSchemePreference??[])})},hAe=e=>Object.assign(e,{useFipsEndpoint:e.useFipsEndpoint??!1,useDualstackEndpoint:e.useDualstackEndpoint??!1,forcePathStyle:e.forcePathStyle??!1,useAccelerateEndpoint:e.useAccelerateEndpoint??!1,useGlobalEndpoint:e.useGlobalEndpoint??!1,disableMultiregionAccessPoints:e.disableMultiregionAccessPoints??!1,defaultSigningName:`s3`,clientContextParams:e.clientContextParams??{}}),p6={ForcePathStyle:{type:`clientContextParams`,name:`forcePathStyle`},UseArnRegion:{type:`clientContextParams`,name:`useArnRegion`},DisableMultiRegionAccessPoints:{type:`clientContextParams`,name:`disableMultiregionAccessPoints`},Accelerate:{type:`clientContextParams`,name:`useAccelerateEndpoint`},DisableS3ExpressSessionAuth:{type:`clientContextParams`,name:`disableS3ExpressSessionAuth`},UseGlobalEndpoint:{type:`builtInParams`,name:`useGlobalEndpoint`},UseFIPS:{type:`builtInParams`,name:`useFipsEndpoint`},Endpoint:{type:`builtInParams`,name:`endpoint`},Region:{type:`builtInParams`,name:`region`},UseDualStack:{type:`builtInParams`,name:`useDualstackEndpoint`}},m6=class e extends xD{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}},gAe=class e extends m6{name=`NoSuchUpload`;$fault=`client`;constructor(t){super({name:`NoSuchUpload`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},_Ae=class e extends m6{name=`AccessDenied`;$fault=`client`;constructor(t){super({name:`AccessDenied`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},vAe=class e extends m6{name=`ObjectNotInActiveTierError`;$fault=`client`;constructor(t){super({name:`ObjectNotInActiveTierError`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},yAe=class e extends m6{name=`BucketAlreadyExists`;$fault=`client`;constructor(t){super({name:`BucketAlreadyExists`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},bAe=class e extends m6{name=`BucketAlreadyOwnedByYou`;$fault=`client`;constructor(t){super({name:`BucketAlreadyOwnedByYou`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},xAe=class e extends m6{name=`NoSuchBucket`;$fault=`client`;constructor(t){super({name:`NoSuchBucket`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},SAe=class e extends m6{name=`InvalidObjectState`;$fault=`client`;StorageClass;AccessTier;constructor(t){super({name:`InvalidObjectState`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype),this.StorageClass=t.StorageClass,this.AccessTier=t.AccessTier}},CAe=class e extends m6{name=`NoSuchKey`;$fault=`client`;constructor(t){super({name:`NoSuchKey`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},wAe=class e extends m6{name=`NotFound`;$fault=`client`;constructor(t){super({name:`NotFound`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},TAe=class e extends m6{name=`EncryptionTypeMismatch`;$fault=`client`;constructor(t){super({name:`EncryptionTypeMismatch`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},EAe=class e extends m6{name=`InvalidRequest`;$fault=`client`;constructor(t){super({name:`InvalidRequest`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},DAe=class e extends m6{name=`InvalidWriteOffset`;$fault=`client`;constructor(t){super({name:`InvalidWriteOffset`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},OAe=class e extends m6{name=`TooManyParts`;$fault=`client`;constructor(t){super({name:`TooManyParts`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},kAe=class e extends m6{name=`IdempotencyParameterMismatch`;$fault=`client`;constructor(t){super({name:`IdempotencyParameterMismatch`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},AAe=class e extends m6{name=`ObjectAlreadyInActiveTierError`;$fault=`client`;constructor(t){super({name:`ObjectAlreadyInActiveTierError`,$fault:`client`,...t}),Object.setPrototypeOf(this,e.prototype)}},h6=`ACL`,jAe=`AccessDenied`,MAe=`AbortDate`,g6=`AccessKeyId`,NAe=`AbortMultipartUpload`,PAe=`AbortMultipartUploadOutput`,FAe=`AbortMultipartUploadRequest`,IAe=`AcceptRanges`,LAe=`AbortRuleId`,RAe=`AccessTier`,_6=`Bucket`,v6=`BucketArn`,zAe=`BucketAlreadyExists`,BAe=`BucketAlreadyOwnedByYou`,VAe=`BypassGovernanceRetention`,HAe=`BucketInfo`,y6=`BucketKeyEnabled`,UAe=`BucketNamespace`,b6=`BucketRegion`,x6=`Body`,S6=`Buckets`,C6=`ChecksumAlgorithm`,WAe=`CreateBucket`,w6=`CreateBucketConfiguration`,GAe=`CreateBucketOutput`,KAe=`CreateBucketRequest`,T6=`CacheControl`,E6=`ChecksumCRC32`,D6=`ChecksumCRC32C`,O6=`ChecksumCRC64NVME`,k6=`Cache-Control`,qAe=`CreationDate`,A6=`Content-Disposition`,j6=`ContentDisposition`,M6=`Content-Encoding`,N6=`ContentEncoding`,P6=`ContentLanguage`,F6=`Content-Language`,I6=`Content-Length`,L6=`ContentLength`,R6=`Content-MD5`,z6=`ChecksumMD5`,B6=`ContentMD5`,JAe=`CompletedMultipartUpload`,YAe=`CompleteMultipartUploadOutput`,XAe=`CreateMultipartUploadOutput`,ZAe=`CompleteMultipartUploadResult`,QAe=`CompleteMultipartUploadRequest`,$Ae=`CreateMultipartUploadRequest`,V6=`CompleteMultipartUpload`,eje=`CreateMultipartUpload`,tje=`ChecksumMode`,nje=`CommonPrefix`,rje=`CommonPrefixList`,ije=`CompletedPartList`,aje=`CompletedPart`,oje=`CommonPrefixes`,sje=`ContentRange`,cje=`Content-Range`,H6=`ChecksumSHA1`,U6=`ChecksumSHA256`,W6=`ChecksumSHA512`,lje=`CreateSessionOutput`,uje=`CreateSessionResult`,dje=`CreateSessionRequest`,fje=`CreateSession`,G6=`ChecksumType`,K6=`Content-Type`,q6=`ContentType`,J6=`ContinuationToken`,Y6=`ChecksumXXHASH64`,X6=`ChecksumXXHASH3`,Z6=`ChecksumXXHASH128`,pje=`Contents`,Q6=`Credentials`,$6=`DeleteMarker`,mje=`DisplayName`,hje=`DeleteObjectOutput`,gje=`DeleteObjectRequest`,_je=`DeleteObject`,vje=`DataRedundancy`,e8=`Delimiter`,t8=`Expiration`,n8=`ExpectedBucketOwner`,r8=`ExpiresString`,yje=`EncryptionTypeMismatch`,i8=`ETag`,a8=`EncodingType`,o8=`Expires`,bje=`FetchOwner`,s8=`GrantFullControl`,xje=`GetObject`,Sje=`GetObjectOutput`,Cje=`GetObjectRequest`,c8=`GrantRead`,l8=`GrantReadACP`,wje=`GrantWrite`,u8=`GrantWriteACP`,Tje=`ID`,d8=`IfMatch`,Eje=`IfMatchInitiatedTime`,Dje=`IfMatchLastModifiedTime`,Oje=`IfMatchSize`,kje=`If-Modified-Since`,Aje=`IfModifiedSince`,jje=`InitiateMultipartUploadResult`,f8=`If-Match`,p8=`IfNoneMatch`,m8=`If-None-Match`,Mje=`InvalidObjectState`,Nje=`IdempotencyParameterMismatch`,Pje=`InvalidRequest`,Fje=`IsRestoreInProgress`,Ije=`IsTruncated`,Lje=`IfUnmodifiedSince`,Rje=`If-Unmodified-Since`,zje=`InvalidWriteOffset`,h8=`Key`,Bje=`KeyCount`,g8=`Location`,Vje=`ListAllMyBucketsResult`,Hje=`ListBuckets`,Uje=`ListBucketsOutput`,Wje=`ListBucketsRequest`,Gje=`ListBucketResult`,Kje=`LocationConstraint`,qje=`LocationInfo`,_8=`LastModified`,Jje=`Last-Modified`,Yje=`ListObjectsV2`,Xje=`ListObjectsV2Output`,Zje=`ListObjectsV2Request`,v8=`Metadata`,Qje=`MaxBuckets`,$je=`MFA`,y8=`MaxKeys`,eMe=`MissingMeta`,tMe=`MpuObjectSize`,nMe=`MultipartUpload`,b8=`Name`,rMe=`NextContinuationToken`,iMe=`NotFound`,aMe=`NoSuchBucket`,oMe=`NoSuchKey`,sMe=`NoSuchUpload`,x8=`Owner`,cMe=`ObjectAlreadyInActiveTierError`,lMe=`ObjectLockEnabledForBucket`,S8=`ObjectLockLegalHoldStatus`,C8=`ObjectLockMode`,w8=`ObjectLockRetainUntilDate`,uMe=`ObjectList`,dMe=`ObjectNotInActiveTierError`,fMe=`ObjectOwnership`,pMe=`OptionalObjectAttributes`,mMe=`Object`,T8=`Prefix`,hMe=`PartsCount`,E8=`PartNumber`,gMe=`PutObject`,_Me=`PutObjectOutput`,vMe=`PutObjectRequest`,yMe=`PutObjectTagging`,bMe=`PutObjectTaggingOutput`,xMe=`PutObjectTaggingRequest`,SMe=`Parts`,CMe=`Part`,D8=`RequestCharged`,wMe=`ResponseCacheControl`,TMe=`ResponseContentDisposition`,EMe=`ResponseContentEncoding`,DMe=`ResponseContentLanguage`,OMe=`ResponseContentType`,kMe=`ResponseExpires`,AMe=`RestoreExpiryDate`,O8=`RequestPayer`,jMe=`ReplicationStatus`,k8=`RestoreStatus`,A8=`Range`,MMe=`Restore`,j8=`StartAfter`,M8=`SecretAccessKey`,NMe=`StreamingBlob`,N8=`StorageClass`,PMe=`SessionCredentialValue`,FMe=`SessionCredentials`,IMe=`SessionMode`,P8=`ServerSideEncryption`,F8=`SSECustomerAlgorithm`,I8=`SSECustomerKey`,L8=`SSECustomerKeyMD5`,R8=`SSEKMSEncryptionContext`,z8=`SSEKMSKeyId`,B8=`SessionToken`,V8=`Size`,LMe=`Tags`,RMe=`TagCount`,zMe=`TooManyParts`,H8=`TagSet`,U8=`Tag`,W8=`Tagging`,G8=`Type`,K8=`UploadId`,BMe=`UploadPart`,VMe=`UploadPartOutput`,HMe=`UploadPartRequest`,UMe=`Value`,q8=`VersionId`,WMe=`WriteOffsetBytes`,J8=`WebsiteRedirectLocation`,GMe=`accept-ranges`,KMe=`bucket-region`,Y8=`client`,X8=`continuation-token`,qMe=`delimiter`,Z8=`error`,JMe=`encoding-type`,YMe=`fetch-owner`,Q8=`http`,$8=`httpChecksum`,e5=`httpError`,Z=`httpHeader`,t5=`httpPayload`,n5=`httpPrefixHeaders`,r5=`httpQuery`,XMe=`max-buckets`,ZMe=`max-keys`,i5=`prefix`,a5=`partNumber`,QMe=`response-cache-control`,$Me=`response-content-disposition`,eNe=`response-content-encoding`,tNe=`response-content-language`,nNe=`response-content-type`,rNe=`response-expires`,o5=`smithy.ts.sdk.synthetic.com.amazonaws.s3`,iNe=`start-after`,aNe=`streaming`,s5=`uploadId`,c5=`versionId`,l5=`xmlFlattened`,u5=`xmlName`,d5=`x-amz-acl`,oNe=`x-amz-abort-date`,sNe=`x-amz-abort-rule-id`,cNe=`x-amz-bucket-arn`,lNe=`x-amz-bypass-governance-retention`,uNe=`x-amz-bucket-namespace`,dNe=`x-amz-bucket-object-lock-enabled`,f5=`x-amz-checksum-algorithm`,p5=`x-amz-checksum-crc32`,m5=`x-amz-checksum-crc32c`,h5=`x-amz-checksum-crc64nvme`,g5=`x-amz-checksum-md5`,fNe=`x-amz-checksum-mode`,_5=`x-amz-checksum-sha1`,v5=`x-amz-checksum-sha256`,y5=`x-amz-checksum-sha512`,pNe=`x-amz-create-session-mode`,b5=`x-amz-checksum-type`,x5=`x-amz-checksum-xxhash64`,S5=`x-amz-checksum-xxhash3`,C5=`x-amz-checksum-xxhash128`,w5=`x-amz-delete-marker`,T5=`x-amz-expiration`,E5=`x-amz-expected-bucket-owner`,D5=`x-amz-grant-full-control`,O5=`x-amz-grant-read`,k5=`x-amz-grant-read-acp`,mNe=`x-amz-grant-write`,A5=`x-amz-grant-write-acp`,hNe=`x-amz-if-match-initiated-time`,gNe=`x-amz-if-match-last-modified-time`,_Ne=`x-amz-if-match-size`,j5=`x-amz-meta-`,vNe=`x-amz-mfa`,yNe=`x-amz-missing-meta`,bNe=`x-amz-mp-object-size`,xNe=`x-amz-mp-parts-count`,M5=`x-amz-object-lock-legal-hold`,N5=`x-amz-object-lock-mode`,P5=`x-amz-object-lock-retain-until-date`,SNe=`x-amz-object-ownership`,CNe=`x-amz-optional-object-attributes`,wNe=`x-amz-object-size`,TNe=`x-amz-restore`,F5=`x-amz-request-charged`,I5=`x-amz-request-payer`,ENe=`x-amz-replication-status`,L5=`x-amz-storage-class`,R5=`x-amz-sdk-checksum-algorithm`,z5=`x-amz-server-side-encryption`,B5=`x-amz-server-side-encryption-aws-kms-key-id`,V5=`x-amz-server-side-encryption-bucket-key-enabled`,H5=`x-amz-server-side-encryption-context`,U5=`x-amz-server-side-encryption-customer-algorithm`,W5=`x-amz-server-side-encryption-customer-key`,G5=`x-amz-server-side-encryption-customer-key-MD5`,K5=`x-amz-tagging`,DNe=`x-amz-tagging-count`,q5=`x-amz-version-id`,ONe=`x-amz-write-offset-bytes`,J5=`x-amz-website-redirect-location`,Q=`com.amazonaws.s3`,Y5=iw.for(o5),kNe=[-3,o5,`S3ServiceException`,0,[],[]];Y5.registerError(kNe,m6);var X5=iw.for(Q),ANe=[-3,Q,jAe,{[Z8]:Y8,[e5]:403},[],[]];X5.registerError(ANe,_Ae);var jNe=[-3,Q,zAe,{[Z8]:Y8,[e5]:409},[],[]];X5.registerError(jNe,yAe);var MNe=[-3,Q,BAe,{[Z8]:Y8,[e5]:409},[],[]];X5.registerError(MNe,bAe);var NNe=[-3,Q,yje,{[Z8]:Y8,[e5]:400},[],[]];X5.registerError(NNe,TAe);var PNe=[-3,Q,Nje,{[Z8]:Y8,[e5]:400},[],[]];X5.registerError(PNe,kAe);var FNe=[-3,Q,Mje,{[Z8]:Y8,[e5]:403},[N8,RAe],[0,0]];X5.registerError(FNe,SAe);var INe=[-3,Q,Pje,{[Z8]:Y8,[e5]:400},[],[]];X5.registerError(INe,EAe);var LNe=[-3,Q,zje,{[Z8]:Y8,[e5]:400},[],[]];X5.registerError(LNe,DAe);var RNe=[-3,Q,aMe,{[Z8]:Y8,[e5]:404},[],[]];X5.registerError(RNe,xAe);var zNe=[-3,Q,oMe,{[Z8]:Y8,[e5]:404},[],[]];X5.registerError(zNe,CAe);var BNe=[-3,Q,sMe,{[Z8]:Y8,[e5]:404},[],[]];X5.registerError(BNe,gAe);var VNe=[-3,Q,iMe,{[Z8]:Y8},[],[]];X5.registerError(VNe,wAe);var HNe=[-3,Q,cMe,{[Z8]:Y8,[e5]:403},[],[]];X5.registerError(HNe,AAe);var UNe=[-3,Q,dMe,{[Z8]:Y8,[e5]:403},[],[]];X5.registerError(UNe,vAe);var WNe=[-3,Q,zMe,{[Z8]:Y8,[e5]:400},[],[]];X5.registerError(WNe,OAe);var GNe=[Y5,X5],Z5=[0,Q,PMe,8,0],Q5=[0,Q,I8,8,0],$5=[0,Q,R8,8,0],e7=[0,Q,z8,8,0],t7=[0,Q,NMe,{[aNe]:1},42],KNe=[3,Q,PAe,0,[D8],[[0,{[Z]:F5}]]],qNe=[3,Q,FAe,0,[_6,h8,K8,O8,n8,Eje],[[0,1],[0,1],[0,{[r5]:s5}],[0,{[Z]:I5}],[0,{[Z]:E5}],[6,{[Z]:hNe}]],3],JNe=[3,Q,_6,0,[b8,qAe,b6,v6],[0,4,0,0]],YNe=[3,Q,HAe,0,[vje,G8],[0,0]],XNe=[3,Q,nje,0,[T8],[0]],ZNe=[3,Q,JAe,0,[SMe],[[()=>APe,{[l5]:1,[u5]:CMe}]]],QNe=[3,Q,aje,0,[i8,E6,D6,O6,H6,U6,W6,z6,Y6,X6,Z6,E8],[0,0,0,0,0,0,0,0,0,0,0,1]],$Ne=[3,Q,YAe,{[u5]:ZAe},[g8,_6,h8,t8,i8,E6,D6,O6,H6,U6,W6,z6,Y6,X6,Z6,G6,P8,q8,z8,y6,D8],[0,0,0,[0,{[Z]:T5}],0,0,0,0,0,0,0,0,0,0,0,0,[0,{[Z]:z5}],[0,{[Z]:q5}],[()=>e7,{[Z]:B5}],[2,{[Z]:V5}],[0,{[Z]:F5}]]],ePe=[3,Q,QAe,0,[_6,h8,K8,nMe,E6,D6,O6,H6,U6,W6,z6,Y6,X6,Z6,G6,tMe,O8,n8,d8,p8,F8,I8,L8],[[0,1],[0,1],[0,{[r5]:s5}],[()=>ZNe,{[t5]:1,[u5]:V6}],[0,{[Z]:p5}],[0,{[Z]:m5}],[0,{[Z]:h5}],[0,{[Z]:_5}],[0,{[Z]:v5}],[0,{[Z]:y5}],[0,{[Z]:g5}],[0,{[Z]:x5}],[0,{[Z]:S5}],[0,{[Z]:C5}],[0,{[Z]:b5}],[1,{[Z]:bNe}],[0,{[Z]:I5}],[0,{[Z]:E5}],[0,{[Z]:f8}],[0,{[Z]:m8}],[0,{[Z]:U5}],[()=>Q5,{[Z]:W5}],[0,{[Z]:G5}]],3],tPe=[3,Q,w6,0,[Kje,g8,_6,LMe],[0,()=>gPe,()=>YNe,[()=>r7,0]]],nPe=[3,Q,GAe,0,[g8,v6],[[0,{[Z]:g8}],[0,{[Z]:cNe}]]],rPe=[3,Q,KAe,0,[_6,h6,w6,s8,c8,l8,wje,u8,lMe,fMe,UAe],[[0,1],[0,{[Z]:d5}],[()=>tPe,{[t5]:1,[u5]:w6}],[0,{[Z]:D5}],[0,{[Z]:O5}],[0,{[Z]:k5}],[0,{[Z]:mNe}],[0,{[Z]:A5}],[2,{[Z]:dNe}],[0,{[Z]:SNe}],[0,{[Z]:uNe}]],1],iPe=[3,Q,XAe,{[u5]:jje},[MAe,LAe,_6,h8,K8,P8,F8,L8,z8,R8,y6,D8,C6,G6],[[4,{[Z]:oNe}],[0,{[Z]:sNe}],[0,{[u5]:_6}],0,0,[0,{[Z]:z5}],[0,{[Z]:U5}],[0,{[Z]:G5}],[()=>e7,{[Z]:B5}],[()=>$5,{[Z]:H5}],[2,{[Z]:V5}],[0,{[Z]:F5}],[0,{[Z]:f5}],[0,{[Z]:b5}]]],aPe=[3,Q,$Ae,0,[_6,h8,h6,T6,j6,N6,P6,q6,o8,s8,c8,l8,u8,v8,P8,N8,J8,F8,I8,L8,z8,R8,y6,O8,W8,C8,w8,S8,n8,C6,G6],[[0,1],[0,1],[0,{[Z]:d5}],[0,{[Z]:k6}],[0,{[Z]:A6}],[0,{[Z]:M6}],[0,{[Z]:F6}],[0,{[Z]:K6}],[4,{[Z]:o8}],[0,{[Z]:D5}],[0,{[Z]:O5}],[0,{[Z]:k5}],[0,{[Z]:A5}],[128,{[n5]:j5}],[0,{[Z]:z5}],[0,{[Z]:L5}],[0,{[Z]:J5}],[0,{[Z]:U5}],[()=>Q5,{[Z]:W5}],[0,{[Z]:G5}],[()=>e7,{[Z]:B5}],[()=>$5,{[Z]:H5}],[2,{[Z]:V5}],[0,{[Z]:I5}],[0,{[Z]:K5}],[0,{[Z]:N5}],[5,{[Z]:P5}],[0,{[Z]:M5}],[0,{[Z]:E5}],[0,{[Z]:f5}],[0,{[Z]:b5}]],2],oPe=[3,Q,lje,{[u5]:uje},[Q6,P8,z8,R8,y6],[[()=>CPe,{[u5]:Q6}],[0,{[Z]:z5}],[()=>e7,{[Z]:B5}],[()=>$5,{[Z]:H5}],[2,{[Z]:V5}]],1],sPe=[3,Q,dje,0,[_6,IMe,P8,z8,R8,y6],[[0,1],[0,{[Z]:pNe}],[0,{[Z]:z5}],[()=>e7,{[Z]:B5}],[()=>$5,{[Z]:H5}],[2,{[Z]:V5}]],1],cPe=[3,Q,hje,0,[$6,q8,D8],[[2,{[Z]:w5}],[0,{[Z]:q5}],[0,{[Z]:F5}]]],lPe=[3,Q,gje,0,[_6,h8,$je,q8,O8,VAe,n8,d8,Dje,Oje],[[0,1],[0,1],[0,{[Z]:vNe}],[0,{[r5]:c5}],[0,{[Z]:I5}],[2,{[Z]:lNe}],[0,{[Z]:E5}],[0,{[Z]:f8}],[6,{[Z]:gNe}],[1,{[Z]:_Ne}]],2],uPe=[3,Q,Sje,0,[x6,$6,IAe,t8,MMe,_8,L6,i8,E6,D6,O6,H6,U6,W6,z6,Y6,X6,Z6,G6,eMe,q8,T6,j6,N6,P6,sje,q6,o8,r8,J8,P8,v8,F8,L8,z8,y6,N8,D8,jMe,hMe,RMe,C8,w8,S8],[[()=>t7,16],[2,{[Z]:w5}],[0,{[Z]:GMe}],[0,{[Z]:T5}],[0,{[Z]:TNe}],[4,{[Z]:Jje}],[1,{[Z]:I6}],[0,{[Z]:i8}],[0,{[Z]:p5}],[0,{[Z]:m5}],[0,{[Z]:h5}],[0,{[Z]:_5}],[0,{[Z]:v5}],[0,{[Z]:y5}],[0,{[Z]:g5}],[0,{[Z]:x5}],[0,{[Z]:S5}],[0,{[Z]:C5}],[0,{[Z]:b5}],[1,{[Z]:yNe}],[0,{[Z]:q5}],[0,{[Z]:k6}],[0,{[Z]:A6}],[0,{[Z]:M6}],[0,{[Z]:F6}],[0,{[Z]:cje}],[0,{[Z]:K6}],[4,{[Z]:o8}],[0,{[Z]:r8}],[0,{[Z]:J5}],[0,{[Z]:z5}],[128,{[n5]:j5}],[0,{[Z]:U5}],[0,{[Z]:G5}],[()=>e7,{[Z]:B5}],[2,{[Z]:V5}],[0,{[Z]:L5}],[0,{[Z]:F5}],[0,{[Z]:ENe}],[1,{[Z]:xNe}],[1,{[Z]:DNe}],[0,{[Z]:N5}],[5,{[Z]:P5}],[0,{[Z]:M5}]]],dPe=[3,Q,Cje,0,[_6,h8,d8,Aje,p8,Lje,A8,wMe,TMe,EMe,DMe,OMe,kMe,q8,F8,I8,L8,O8,E8,n8,tje],[[0,1],[0,1],[0,{[Z]:f8}],[4,{[Z]:kje}],[0,{[Z]:m8}],[4,{[Z]:Rje}],[0,{[Z]:A8}],[0,{[r5]:QMe}],[0,{[r5]:$Me}],[0,{[r5]:eNe}],[0,{[r5]:tNe}],[0,{[r5]:nNe}],[6,{[r5]:rNe}],[0,{[r5]:c5}],[0,{[Z]:U5}],[()=>Q5,{[Z]:W5}],[0,{[Z]:G5}],[0,{[Z]:I5}],[1,{[r5]:a5}],[0,{[Z]:E5}],[0,{[Z]:fNe}]],2],fPe=[3,Q,Uje,{[u5]:Vje},[S6,x8,J6,T8],[[()=>OPe,0],()=>n7,0,0]],pPe=[3,Q,Wje,0,[Qje,J6,T8,b6],[[1,{[r5]:XMe}],[0,{[r5]:X8}],[0,{[r5]:i5}],[0,{[r5]:KMe}]]],mPe=[3,Q,Xje,{[u5]:Gje},[Ije,pje,b8,T8,e8,y8,oje,a8,Bje,J6,rMe,j8,D8],[2,[()=>jPe,{[l5]:1}],0,0,0,1,[()=>kPe,{[l5]:1}],0,1,0,0,0,[0,{[Z]:F5}]]],hPe=[3,Q,Zje,0,[_6,e8,a8,y8,T8,J6,bje,j8,O8,n8,pMe],[[0,1],[0,{[r5]:qMe}],[0,{[r5]:JMe}],[1,{[r5]:ZMe}],[0,{[r5]:i5}],[0,{[r5]:X8}],[2,{[r5]:YMe}],[0,{[r5]:iNe}],[0,{[Z]:I5}],[0,{[Z]:E5}],[64,{[Z]:CNe}]],1],gPe=[3,Q,qje,0,[G8,b8],[0,0]],_Pe=[3,Q,mMe,0,[h8,_8,i8,C6,G6,V8,N8,x8,k8],[0,4,0,[64,{[l5]:1}],0,1,0,()=>n7,()=>SPe]],n7=[3,Q,x8,0,[mje,Tje],[0,0]],vPe=[3,Q,_Me,0,[t8,i8,E6,D6,O6,H6,U6,W6,z6,Y6,X6,Z6,G6,P8,q8,F8,L8,z8,R8,y6,V8,D8],[[0,{[Z]:T5}],[0,{[Z]:i8}],[0,{[Z]:p5}],[0,{[Z]:m5}],[0,{[Z]:h5}],[0,{[Z]:_5}],[0,{[Z]:v5}],[0,{[Z]:y5}],[0,{[Z]:g5}],[0,{[Z]:x5}],[0,{[Z]:S5}],[0,{[Z]:C5}],[0,{[Z]:b5}],[0,{[Z]:z5}],[0,{[Z]:q5}],[0,{[Z]:U5}],[0,{[Z]:G5}],[()=>e7,{[Z]:B5}],[()=>$5,{[Z]:H5}],[2,{[Z]:V5}],[1,{[Z]:wNe}],[0,{[Z]:F5}]]],yPe=[3,Q,vMe,0,[_6,h8,h6,x6,T6,j6,N6,P6,L6,B6,q6,C6,E6,D6,O6,H6,U6,W6,z6,Y6,X6,Z6,o8,d8,p8,s8,c8,l8,u8,WMe,v8,P8,N8,J8,F8,I8,L8,z8,R8,y6,O8,W8,C8,w8,S8,n8],[[0,1],[0,1],[0,{[Z]:d5}],[()=>t7,16],[0,{[Z]:k6}],[0,{[Z]:A6}],[0,{[Z]:M6}],[0,{[Z]:F6}],[1,{[Z]:I6}],[0,{[Z]:R6}],[0,{[Z]:K6}],[0,{[Z]:R5}],[0,{[Z]:p5}],[0,{[Z]:m5}],[0,{[Z]:h5}],[0,{[Z]:_5}],[0,{[Z]:v5}],[0,{[Z]:y5}],[0,{[Z]:g5}],[0,{[Z]:x5}],[0,{[Z]:S5}],[0,{[Z]:C5}],[4,{[Z]:o8}],[0,{[Z]:f8}],[0,{[Z]:m8}],[0,{[Z]:D5}],[0,{[Z]:O5}],[0,{[Z]:k5}],[0,{[Z]:A5}],[1,{[Z]:ONe}],[128,{[n5]:j5}],[0,{[Z]:z5}],[0,{[Z]:L5}],[0,{[Z]:J5}],[0,{[Z]:U5}],[()=>Q5,{[Z]:W5}],[0,{[Z]:G5}],[()=>e7,{[Z]:B5}],[()=>$5,{[Z]:H5}],[2,{[Z]:V5}],[0,{[Z]:I5}],[0,{[Z]:K5}],[0,{[Z]:N5}],[5,{[Z]:P5}],[0,{[Z]:M5}],[0,{[Z]:E5}]],2],bPe=[3,Q,bMe,0,[q8],[[0,{[Z]:q5}]]],xPe=[3,Q,xMe,0,[_6,h8,W8,q8,B6,C6,n8,O8],[[0,1],[0,1],[()=>TPe,{[t5]:1,[u5]:W8}],[0,{[r5]:c5}],[0,{[Z]:R6}],[0,{[Z]:R5}],[0,{[Z]:E5}],[0,{[Z]:I5}]],3],SPe=[3,Q,k8,0,[Fje,AMe],[2,4]],CPe=[3,Q,FMe,0,[g6,M8,B8,t8],[[0,{[u5]:g6}],[()=>Z5,{[u5]:M8}],[()=>Z5,{[u5]:B8}],[4,{[u5]:t8}]],4],wPe=[3,Q,U8,0,[h8,UMe],[0,0],2],TPe=[3,Q,W8,0,[H8],[[()=>r7,0]],1],EPe=[3,Q,VMe,0,[P8,i8,E6,D6,O6,H6,U6,W6,z6,Y6,X6,Z6,F8,L8,z8,y6,D8],[[0,{[Z]:z5}],[0,{[Z]:i8}],[0,{[Z]:p5}],[0,{[Z]:m5}],[0,{[Z]:h5}],[0,{[Z]:_5}],[0,{[Z]:v5}],[0,{[Z]:y5}],[0,{[Z]:g5}],[0,{[Z]:x5}],[0,{[Z]:S5}],[0,{[Z]:C5}],[0,{[Z]:U5}],[0,{[Z]:G5}],[()=>e7,{[Z]:B5}],[2,{[Z]:V5}],[0,{[Z]:F5}]]],DPe=[3,Q,HMe,0,[_6,h8,E8,K8,x6,L6,B6,C6,E6,D6,O6,H6,U6,W6,z6,Y6,X6,Z6,F8,I8,L8,O8,n8],[[0,1],[0,1],[1,{[r5]:a5}],[0,{[r5]:s5}],[()=>t7,16],[1,{[Z]:I6}],[0,{[Z]:R6}],[0,{[Z]:R5}],[0,{[Z]:p5}],[0,{[Z]:m5}],[0,{[Z]:h5}],[0,{[Z]:_5}],[0,{[Z]:v5}],[0,{[Z]:y5}],[0,{[Z]:g5}],[0,{[Z]:x5}],[0,{[Z]:S5}],[0,{[Z]:C5}],[0,{[Z]:U5}],[()=>Q5,{[Z]:W5}],[0,{[Z]:G5}],[0,{[Z]:I5}],[0,{[Z]:E5}]],4],OPe=[1,Q,S6,0,[()=>JNe,{[u5]:_6}]],kPe=[1,Q,rje,0,()=>XNe],APe=[1,Q,ije,0,()=>QNe],jPe=[1,Q,uMe,0,[()=>_Pe,0]],r7=[1,Q,H8,0,[()=>wPe,{[u5]:U8}]],MPe=[9,Q,NAe,{[Q8]:[`DELETE`,`/{Key+}?x-id=AbortMultipartUpload`,204]},()=>qNe,()=>KNe],NPe=[9,Q,V6,{[Q8]:[`POST`,`/{Key+}`,200]},()=>ePe,()=>$Ne],PPe=[9,Q,WAe,{[Q8]:[`PUT`,`/`,200]},()=>rPe,()=>nPe],FPe=[9,Q,eje,{[Q8]:[`POST`,`/{Key+}?uploads`,200]},()=>aPe,()=>iPe],IPe=[9,Q,fje,{[Q8]:[`GET`,`/?session`,200]},()=>sPe,()=>oPe],LPe=[9,Q,_je,{[Q8]:[`DELETE`,`/{Key+}?x-id=DeleteObject`,204]},()=>lPe,()=>cPe],RPe=[9,Q,xje,{[$8]:`-`,[Q8]:[`GET`,`/{Key+}?x-id=GetObject`,200]},()=>dPe,()=>uPe],zPe=[9,Q,Hje,{[Q8]:[`GET`,`/?x-id=ListBuckets`,200]},()=>pPe,()=>fPe],BPe=[9,Q,Yje,{[Q8]:[`GET`,`/?list-type=2`,200]},()=>hPe,()=>mPe],VPe=[9,Q,gMe,{[$8]:`-`,[Q8]:[`PUT`,`/{Key+}?x-id=PutObject`,200]},()=>yPe,()=>vPe],HPe=[9,Q,yMe,{[$8]:`-`,[Q8]:[`PUT`,`/{Key+}?tagging`,200]},()=>xPe,()=>bPe],UPe=[9,Q,BMe,{[$8]:`-`,[Q8]:[`PUT`,`/{Key+}?x-id=UploadPart`,200]},()=>DPe,()=>EPe],WPe=class extends bD.classBuilder().ep({...p6,DisableS3ExpressSessionAuth:{type:`staticContextParams`,value:!0},Bucket:{type:`contextParams`,name:`Bucket`}}).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions()),F4(n)]}).s(`AmazonS3`,`CreateSession`,{}).n(`S3Client`,`CreateSessionCommand`).sc(IPe).build(){},GPe={name:`@aws-sdk/client-s3`,description:`AWS SDK for JavaScript S3 Client for Node.js, Browser and React Native`,version:`3.1045.0`,scripts:{build:`concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs`,"build:cjs":`node ../../scripts/compilation/inline client-s3`,"build:es":`tsc -p tsconfig.es.json`,"build:include:deps":`yarn g:turbo run build -F="$npm_package_name"`,"build:types":`tsc -p tsconfig.types.json`,"build:types:downlevel":`downlevel-dts dist-types dist-types/ts3.4`,clean:`premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo`,"extract:docs":`api-extractor run --local`,"generate:client":`node ../../scripts/generate-clients/single-service --solo s3`,test:`yarn g:vitest run`,"test:browser":`node ./test/browser-build/esbuild && yarn g:vitest run -c vitest.config.browser.mts`,"test:browser:watch":`node ./test/browser-build/esbuild && yarn g:vitest watch -c vitest.config.browser.mts`,"test:e2e":`yarn g:vitest run -c vitest.config.e2e.mts && yarn test:browser`,"test:e2e:watch":`yarn g:vitest watch -c vitest.config.e2e.mts`,"test:index":`tsc --noEmit ./test/index-types.ts && node ./test/index-objects.spec.mjs`,"test:integration":`yarn g:vitest run -c vitest.config.integ.mts`,"test:integration:watch":`yarn g:vitest watch -c vitest.config.integ.mts`,"test:watch":`yarn g:vitest watch`},main:`./dist-cjs/index.js`,types:`./dist-types/index.d.ts`,module:`./dist-es/index.js`,sideEffects:!1,dependencies:{"@aws-crypto/sha1-browser":`5.2.0`,"@aws-crypto/sha256-browser":`5.2.0`,"@aws-crypto/sha256-js":`5.2.0`,"@aws-sdk/core":`^3.974.8`,"@aws-sdk/credential-provider-node":`^3.972.39`,"@aws-sdk/middleware-bucket-endpoint":`^3.972.10`,"@aws-sdk/middleware-expect-continue":`^3.972.10`,"@aws-sdk/middleware-flexible-checksums":`^3.974.16`,"@aws-sdk/middleware-host-header":`^3.972.10`,"@aws-sdk/middleware-location-constraint":`^3.972.10`,"@aws-sdk/middleware-logger":`^3.972.10`,"@aws-sdk/middleware-recursion-detection":`^3.972.11`,"@aws-sdk/middleware-sdk-s3":`^3.972.37`,"@aws-sdk/middleware-ssec":`^3.972.10`,"@aws-sdk/middleware-user-agent":`^3.972.38`,"@aws-sdk/region-config-resolver":`^3.972.13`,"@aws-sdk/signature-v4-multi-region":`^3.996.25`,"@aws-sdk/types":`^3.973.8`,"@aws-sdk/util-endpoints":`^3.996.8`,"@aws-sdk/util-user-agent-browser":`^3.972.10`,"@aws-sdk/util-user-agent-node":`^3.973.24`,"@smithy/config-resolver":`^4.4.17`,"@smithy/core":`^3.23.17`,"@smithy/eventstream-serde-browser":`^4.2.14`,"@smithy/eventstream-serde-config-resolver":`^4.3.14`,"@smithy/eventstream-serde-node":`^4.2.14`,"@smithy/fetch-http-handler":`^5.3.17`,"@smithy/hash-blob-browser":`^4.2.15`,"@smithy/hash-node":`^4.2.14`,"@smithy/hash-stream-node":`^4.2.14`,"@smithy/invalid-dependency":`^4.2.14`,"@smithy/md5-js":`^4.2.14`,"@smithy/middleware-content-length":`^4.2.14`,"@smithy/middleware-endpoint":`^4.4.32`,"@smithy/middleware-retry":`^4.5.7`,"@smithy/middleware-serde":`^4.2.20`,"@smithy/middleware-stack":`^4.2.14`,"@smithy/node-config-provider":`^4.3.14`,"@smithy/node-http-handler":`^4.6.1`,"@smithy/protocol-http":`^5.3.14`,"@smithy/smithy-client":`^4.12.13`,"@smithy/types":`^4.14.1`,"@smithy/url-parser":`^4.2.14`,"@smithy/util-base64":`^4.3.2`,"@smithy/util-body-length-browser":`^4.2.2`,"@smithy/util-body-length-node":`^4.2.3`,"@smithy/util-defaults-mode-browser":`^4.3.49`,"@smithy/util-defaults-mode-node":`^4.2.54`,"@smithy/util-endpoints":`^3.4.2`,"@smithy/util-middleware":`^4.2.14`,"@smithy/util-retry":`^4.3.6`,"@smithy/util-stream":`^4.5.25`,"@smithy/util-utf8":`^4.2.2`,"@smithy/util-waiter":`^4.3.0`,tslib:`^2.6.2`},devDependencies:{"@aws-sdk/signature-v4-crt":`3.1045.0`,"@smithy/snapshot-testing":`^2.0.8`,"@tsconfig/node20":`20.1.8`,"@types/node":`^20.14.8`,concurrently:`7.0.0`,"downlevel-dts":`0.10.1`,premove:`4.0.0`,typescript:`~5.8.3`,vitest:`^4.0.17`},engines:{node:`>=20.0.0`},typesVersions:{"<4.5":{"dist-types/*":[`dist-types/ts3.4/*`]}},files:[`dist-*/**`],author:{name:`AWS SDK for JavaScript Team`,url:`https://aws.amazon.com/javascript/`},license:`Apache-2.0`,browser:{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.browser`},"react-native":{"./dist-es/runtimeConfig":`./dist-es/runtimeConfig.native`},homepage:`https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3`,repository:{type:`git`,url:`https://github.com/aws/aws-sdk-js-v3.git`,directory:`clients/client-s3`}};function i7(e){return typeof e==`string`?e.length===0:e.byteLength===0}var a7={name:`SHA-1`},o7={name:`HMAC`,hash:a7},KPe=new Uint8Array([218,57,163,238,94,107,75,13,50,85,191,239,149,96,24,144,175,216,7,9]),qPe=function(){function e(e){this.toHash=new Uint8Array,e!==void 0&&(this.key=new Promise(function(t,n){hx().crypto.subtle.importKey(`raw`,s7(e),o7,!1,[`sign`]).then(t,n)}),this.key.catch(function(){}))}return e.prototype.update=function(e){if(!i7(e)){var t=s7(e),n=new Uint8Array(this.toHash.byteLength+t.byteLength);n.set(this.toHash,0),n.set(t,this.toHash.byteLength),this.toHash=n}},e.prototype.digest=function(){var e=this;return this.key?this.key.then(function(t){return hx().crypto.subtle.sign(o7,t,e.toHash).then(function(e){return new Uint8Array(e)})}):i7(this.toHash)?Promise.resolve(KPe):Promise.resolve().then(function(){return hx().crypto.subtle.digest(a7,e.toHash)}).then(function(e){return Promise.resolve(new Uint8Array(e))})},e.prototype.reset=function(){this.toHash=new Uint8Array},e}();function s7(e){return typeof e==`string`?ax(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}var JPe=function(){function e(e){if(Dx(hx()))this.hash=new qPe(e);else throw Error(`SHA1 not supported`)}return e.prototype.update=function(e,t){this.hash.update(sx(e))},e.prototype.digest=function(){return this.hash.digest()},e.prototype.reset=function(){this.hash.reset()},e}(),c7=class e{bytes;constructor(e){if(this.bytes=e,e.byteLength!==8)throw Error(`Int64 buffers must be exactly 8 bytes`)}static fromNumber(t){if(t>0x8000000000000000||t<-0x8000000000000000)throw Error(`${t} is too large (or, if negative, too small) to represent as an Int64`);let n=new Uint8Array(8);for(let e=7,r=Math.abs(Math.round(t));e>-1&&r>0;e--,r/=256)n[e]=r;return t<0&&l7(n),new e(n)}valueOf(){let e=this.bytes.slice(0),t=e[0]&128;return t&&l7(e),parseInt(Hx(e),16)*(t?-1:1)}toString(){return String(this.valueOf())}};function l7(e){for(let t=0;t<8;t++)e[t]^=255;for(let t=7;t>-1&&(e[t]++,e[t]===0);t--);}var YPe=class{toUtf8;fromUtf8;constructor(e,t){this.toUtf8=e,this.fromUtf8=t}format(e){let t=[];for(let n of Object.keys(e)){let r=this.fromUtf8(n);t.push(Uint8Array.from([r.byteLength]),r,this.formatHeaderValue(e[n]))}let n=new Uint8Array(t.reduce((e,t)=>e+t.byteLength,0)),r=0;for(let e of t)n.set(e,r),r+=e.byteLength;return n}formatHeaderValue(e){switch(e.type){case`boolean`:return Uint8Array.from([+!e.value]);case`byte`:return Uint8Array.from([2,e.value]);case`short`:let t=new DataView(new ArrayBuffer(3));return t.setUint8(0,3),t.setInt16(1,e.value,!1),new Uint8Array(t.buffer);case`integer`:let n=new DataView(new ArrayBuffer(5));return n.setUint8(0,4),n.setInt32(1,e.value,!1),new Uint8Array(n.buffer);case`long`:let r=new Uint8Array(9);return r[0]=5,r.set(e.value.bytes,1),r;case`binary`:let i=new DataView(new ArrayBuffer(3+e.value.byteLength));i.setUint8(0,6),i.setUint16(1,e.value.byteLength,!1);let a=new Uint8Array(i.buffer);return a.set(e.value,3),a;case`string`:let o=this.fromUtf8(e.value),s=new DataView(new ArrayBuffer(3+o.byteLength));s.setUint8(0,7),s.setUint16(1,o.byteLength,!1);let c=new Uint8Array(s.buffer);return c.set(o,3),c;case`timestamp`:let l=new Uint8Array(9);return l[0]=8,l.set(c7.fromNumber(e.value.valueOf()).bytes,1),l;case`uuid`:if(!aFe.test(e.value))throw Error(`Invalid UUID received: ${e.value}`);let u=new Uint8Array(17);return u[0]=9,u.set(Vx(e.value.replace(/\-/g,``)),1),u}}parse(e){let t={},n=0;for(;n{if(typeof e!=`number`)throw Error(`Attempted to allocate an event message where size was not a number: `+e);t=e,n=4,r=new Uint8Array(e),new DataView(r.buffer).setUint32(0,e,!1)},o=async function*(){let o=e[Symbol.asyncIterator]();for(;;){let{value:e,done:s}=await o.next();if(s){if(!t)return;if(t===n)yield r;else throw Error(`Truncated event message received.`);return}let c=e.length,l=0;for(;l({[Symbol.asyncIterator]:async function*(){let t=e.getReader();try{for(;;){let{done:e,value:n}=await t.read();if(e)return;yield n}}finally{t.releaseLock()}}}),_Fe=e=>{let t=e[Symbol.asyncIterator]();return new ReadableStream({async pull(e){let{done:n,value:r}=await t.next();if(n)return e.close();e.enqueue(r)}})},vFe=class{universalMarshaller;constructor({utf8Encoder:e,utf8Decoder:t}){this.universalMarshaller=new hFe({utf8Decoder:t,utf8Encoder:e})}deserialize(e,t){let n=yFe(e)?gFe(e):e;return this.universalMarshaller.deserialize(n,t)}serialize(e,t){let n=this.universalMarshaller.serialize(e,t);return typeof ReadableStream==`function`?_Fe(n):n}},yFe=e=>typeof ReadableStream==`function`&&e instanceof ReadableStream,bFe=e=>new vFe(e);async function xFe(e,t,n=1024*1024){let r=e.size,i=0;for(;i{n.update(e)}),n.digest()},CFe=[1732584193,4023233417,2562383102,271733878],wFe=class{state;buffer;bufferLength;bytesHashed;finished;constructor(){this.reset()}update(e){if(TFe(e))return;if(this.finished)throw Error(`Attempted to update an already finished hash.`);let t=EFe(e),n=0,{byteLength:r}=t;for(this.bytesHashed+=r;r>0;)this.buffer.setUint8(this.bufferLength++,t[n++]),r--,this.bufferLength===64&&(this.hashBuffer(),this.bufferLength=0)}async digest(){if(!this.finished){let{buffer:e,bufferLength:t,bytesHashed:n}=this,r=n*8;if(e.setUint8(this.bufferLength++,128),t%64>=56){for(let t=this.bufferLength;t<64;t++)e.setUint8(t,0);this.hashBuffer(),this.bufferLength=0}for(let t=this.bufferLength;t<56;t++)e.setUint8(t,0);e.setUint32(56,r>>>0,!0),e.setUint32(60,Math.floor(r/4294967296),!0),this.hashBuffer(),this.finished=!0}let e=new DataView(new ArrayBuffer(16));for(let t=0;t<4;t++)e.setUint32(t*4,this.state[t],!0);return new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}hashBuffer(){let{buffer:e,state:t}=this,n=t[0],r=t[1],i=t[2],a=t[3];n=h7(n,r,i,a,e.getUint32(0,!0),7,3614090360),a=h7(a,n,r,i,e.getUint32(4,!0),12,3905402710),i=h7(i,a,n,r,e.getUint32(8,!0),17,606105819),r=h7(r,i,a,n,e.getUint32(12,!0),22,3250441966),n=h7(n,r,i,a,e.getUint32(16,!0),7,4118548399),a=h7(a,n,r,i,e.getUint32(20,!0),12,1200080426),i=h7(i,a,n,r,e.getUint32(24,!0),17,2821735955),r=h7(r,i,a,n,e.getUint32(28,!0),22,4249261313),n=h7(n,r,i,a,e.getUint32(32,!0),7,1770035416),a=h7(a,n,r,i,e.getUint32(36,!0),12,2336552879),i=h7(i,a,n,r,e.getUint32(40,!0),17,4294925233),r=h7(r,i,a,n,e.getUint32(44,!0),22,2304563134),n=h7(n,r,i,a,e.getUint32(48,!0),7,1804603682),a=h7(a,n,r,i,e.getUint32(52,!0),12,4254626195),i=h7(i,a,n,r,e.getUint32(56,!0),17,2792965006),r=h7(r,i,a,n,e.getUint32(60,!0),22,1236535329),n=g7(n,r,i,a,e.getUint32(4,!0),5,4129170786),a=g7(a,n,r,i,e.getUint32(24,!0),9,3225465664),i=g7(i,a,n,r,e.getUint32(44,!0),14,643717713),r=g7(r,i,a,n,e.getUint32(0,!0),20,3921069994),n=g7(n,r,i,a,e.getUint32(20,!0),5,3593408605),a=g7(a,n,r,i,e.getUint32(40,!0),9,38016083),i=g7(i,a,n,r,e.getUint32(60,!0),14,3634488961),r=g7(r,i,a,n,e.getUint32(16,!0),20,3889429448),n=g7(n,r,i,a,e.getUint32(36,!0),5,568446438),a=g7(a,n,r,i,e.getUint32(56,!0),9,3275163606),i=g7(i,a,n,r,e.getUint32(12,!0),14,4107603335),r=g7(r,i,a,n,e.getUint32(32,!0),20,1163531501),n=g7(n,r,i,a,e.getUint32(52,!0),5,2850285829),a=g7(a,n,r,i,e.getUint32(8,!0),9,4243563512),i=g7(i,a,n,r,e.getUint32(28,!0),14,1735328473),r=g7(r,i,a,n,e.getUint32(48,!0),20,2368359562),n=_7(n,r,i,a,e.getUint32(20,!0),4,4294588738),a=_7(a,n,r,i,e.getUint32(32,!0),11,2272392833),i=_7(i,a,n,r,e.getUint32(44,!0),16,1839030562),r=_7(r,i,a,n,e.getUint32(56,!0),23,4259657740),n=_7(n,r,i,a,e.getUint32(4,!0),4,2763975236),a=_7(a,n,r,i,e.getUint32(16,!0),11,1272893353),i=_7(i,a,n,r,e.getUint32(28,!0),16,4139469664),r=_7(r,i,a,n,e.getUint32(40,!0),23,3200236656),n=_7(n,r,i,a,e.getUint32(52,!0),4,681279174),a=_7(a,n,r,i,e.getUint32(0,!0),11,3936430074),i=_7(i,a,n,r,e.getUint32(12,!0),16,3572445317),r=_7(r,i,a,n,e.getUint32(24,!0),23,76029189),n=_7(n,r,i,a,e.getUint32(36,!0),4,3654602809),a=_7(a,n,r,i,e.getUint32(48,!0),11,3873151461),i=_7(i,a,n,r,e.getUint32(60,!0),16,530742520),r=_7(r,i,a,n,e.getUint32(8,!0),23,3299628645),n=v7(n,r,i,a,e.getUint32(0,!0),6,4096336452),a=v7(a,n,r,i,e.getUint32(28,!0),10,1126891415),i=v7(i,a,n,r,e.getUint32(56,!0),15,2878612391),r=v7(r,i,a,n,e.getUint32(20,!0),21,4237533241),n=v7(n,r,i,a,e.getUint32(48,!0),6,1700485571),a=v7(a,n,r,i,e.getUint32(12,!0),10,2399980690),i=v7(i,a,n,r,e.getUint32(40,!0),15,4293915773),r=v7(r,i,a,n,e.getUint32(4,!0),21,2240044497),n=v7(n,r,i,a,e.getUint32(32,!0),6,1873313359),a=v7(a,n,r,i,e.getUint32(60,!0),10,4264355552),i=v7(i,a,n,r,e.getUint32(24,!0),15,2734768916),r=v7(r,i,a,n,e.getUint32(52,!0),21,1309151649),n=v7(n,r,i,a,e.getUint32(16,!0),6,4149444226),a=v7(a,n,r,i,e.getUint32(44,!0),10,3174756917),i=v7(i,a,n,r,e.getUint32(8,!0),15,718787259),r=v7(r,i,a,n,e.getUint32(36,!0),21,3951481745),t[0]=n+t[0]&4294967295,t[1]=r+t[1]&4294967295,t[2]=i+t[2]&4294967295,t[3]=a+t[3]&4294967295}reset(){this.state=Uint32Array.from(CFe),this.buffer=new DataView(new ArrayBuffer(64)),this.bufferLength=0,this.bytesHashed=0,this.finished=!1}};function m7(e,t,n,r,i,a){return t=(t+e&4294967295)+(r+a&4294967295)&4294967295,(t<>>32-i)+n&4294967295}function h7(e,t,n,r,i,a,o){return m7(t&n|~t&r,e,t,i,a,o)}function g7(e,t,n,r,i,a,o){return m7(t&r|n&~r,e,t,i,a,o)}function _7(e,t,n,r,i,a,o){return m7(t^n^r,e,t,i,a,o)}function v7(e,t,n,r,i,a,o){return m7(n^(t|~r),e,t,i,a,o)}function TFe(e){return typeof e==`string`?e.length===0:e.byteLength===0}function EFe(e){return typeof e==`string`?Ux(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength/Uint8Array.BYTES_PER_ELEMENT):new Uint8Array(e)}var DFe=e=>({apiVersion:`2006-03-01`,base64Decoder:e?.base64Decoder??vC,base64Encoder:e?.base64Encoder??yC,disableHostPrefix:e?.disableHostPrefix??!1,endpointProvider:e?.endpointProvider??u6,extensions:e?.extensions??[],getAwsChunkedEncodingStream:e?.getAwsChunkedEncodingStream??SC,httpAuthSchemeProvider:e?.httpAuthSchemeProvider??pAe,httpAuthSchemes:e?.httpAuthSchemes??[{schemeId:`aws.auth#sigv4`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4`),signer:new ID},{schemeId:`aws.auth#sigv4a`,identityProvider:e=>e.getIdentityProvider(`aws.auth#sigv4a`),signer:new oie}],logger:e?.logger??new OD,protocol:e?.protocol??aAe,protocolSettings:e?.protocolSettings??{defaultNamespace:`com.amazonaws.s3`,errorTypeRegistries:GNe,xmlNamespace:`http://s3.amazonaws.com/doc/2006-03-01/`,version:`2006-03-01`,serviceTarget:`AmazonS3`},sdkStreamMixin:e?.sdkStreamMixin??PC,serviceId:e?.serviceId??`S3`,signerConstructor:e?.signerConstructor??L4,signingEscapePath:e?.signingEscapePath??!1,urlParser:e?.urlParser??UC,useArnRegion:e?.useArnRegion??void 0,utf8Decoder:e?.utf8Decoder??Ux,utf8Encoder:e?.utf8Encoder??Gx}),OFe=e=>{let t=WD(e),n=()=>t().then(CD),r=DFe(e);return{...r,...e,runtime:`browser`,defaultsMode:t,bodyLengthChecker:e?.bodyLengthChecker??UD,credentialDefaultProvider:e?.credentialDefaultProvider??(e=>()=>Promise.reject(Error(`Credential is missing`))),defaultUserAgentProvider:e?.defaultUserAgentProvider??zD({serviceId:r.serviceId,clientVersion:GPe.version}),eventStreamSerdeProvider:e?.eventStreamSerdeProvider??bFe,maxAttempts:e?.maxAttempts??3,md5:e?.md5??wFe,region:e?.region??VD(`Region is missing`),requestHandler:DC.create(e?.requestHandler??n),retryMode:e?.retryMode??(async()=>(await n()).retryMode||_E),sha1:e?.sha1??JPe,sha256:e?.sha256??Ax,streamCollector:e?.streamCollector??kC,streamHasher:e?.streamHasher??SFe,useDualstackEndpoint:e?.useDualstackEndpoint??(()=>Promise.resolve(!1)),useFipsEndpoint:e?.useFipsEndpoint??(()=>Promise.resolve(!1))}},kFe=e=>{let t=e.httpAuthSchemes,n=e.httpAuthSchemeProvider,r=e.credentials;return{setHttpAuthScheme(e){let n=t.findIndex(t=>t.schemeId===e.schemeId);n===-1?t.push(e):t.splice(n,1,e)},httpAuthSchemes(){return t},setHttpAuthSchemeProvider(e){n=e},httpAuthSchemeProvider(){return n},setCredentials(e){r=e},credentials(){return r}}},AFe=e=>({httpAuthSchemes:e.httpAuthSchemes(),httpAuthSchemeProvider:e.httpAuthSchemeProvider(),credentials:e.credentials()}),jFe=(e,t)=>{let n=Object.assign(W$(e),TD(e),jx(e),kFe(e));return t.forEach(e=>e.configure(n)),Object.assign(e,G$(n),ED(n),Mx(n),AFe(n))},MFe=class extends _D{config;constructor(...[e]){let t=OFe(e||{});super(t),this.initConfig=t;let n=jFe(qke(mAe(oAe(uD(JS(nD(dD(kke(TT(hAe(t))))))))),{session:[()=>this,WPe]}),e?.extensions||[]);this.config=n,this.middlewareStack.use(JC(this.config)),this.middlewareStack.use(QE(this.config)),this.middlewareStack.use(AD(this.config)),this.middlewareStack.use(iD(this.config)),this.middlewareStack.use(ZS(this.config)),this.middlewareStack.use(eC(this.config)),this.middlewareStack.use(rC(this.config)),this.middlewareStack.use(cC(this.config,{httpAuthSchemeParametersProvider:fAe,identityProviderConfigProvider:async e=>new bT({"aws.auth#sigv4":e.credentials,"aws.auth#sigv4a":e.credentials})})),this.middlewareStack.use(pC(this.config)),this.middlewareStack.use(iAe(this.config)),this.middlewareStack.use(xke(this.config)),this.middlewareStack.use(Pke(this.config)),this.middlewareStack.use(Vke(this.config)),this.middlewareStack.use(Kke(this.config))}destroy(){super.destroy()}},NFe=class extends bD.classBuilder().ep(p6).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions()),F4(n)]}).s(`AmazonS3`,`ListBuckets`,{}).n(`S3Client`,`ListBucketsCommand`).sc(zPe).build(){},PFe=class extends bD.classBuilder().ep({...p6,Bucket:{type:`contextParams`,name:`Bucket`},Prefix:{type:`contextParams`,name:`Prefix`}}).m(function(e,t,n,r){return[lD(n,e.getEndpointParameterInstructions()),F4(n)]}).s(`AmazonS3`,`ListObjectsV2`,{}).n(`S3Client`,`ListObjectsV2Command`).sc(BPe).build(){},y7=`ap-southeast-2`,b7=`${window.location.protocol}//localhost:9999`,FFe=`${window.location.protocol}//localhost:8443`,x7=null,S7=null,C7=null,w7=null;function T7(){if(!x7){let e=nx();if(!e)throw Error(`AWS credentials not configured`);x7=new Ehe({endpoint:b7,region:y7,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}}),x7.middlewareStack.add(e=>async t=>(Ix.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/awsgw${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return x7}function E7(){if(!S7){let e=nx();if(!e)throw Error(`AWS credentials not configured`);S7=new Twe({endpoint:b7,region:y7,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}}),S7.middlewareStack.add(e=>async t=>(Ix.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/awsgw${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return S7}function D7(){if(!C7){let e=nx();if(!e)throw Error(`AWS credentials not configured`);C7=new dke({endpoint:b7,region:y7,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}}),C7.middlewareStack.add(e=>async t=>(Ix.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/awsgw${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return C7}function O7(){if(!w7){let e=nx();if(!e)throw Error(`AWS credentials not configured`);w7=new MFe({endpoint:FFe,region:y7,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey},forcePathStyle:!0}),w7.middlewareStack.add(e=>async t=>(Ix.isInstance(t.request)&&t.request.path.endsWith(`/`)&&t.request.path!==`/`&&(t.request.path=t.request.path.slice(0,-1)),await e(t)),{step:`build`,name:`removeTrailingSlash`}),w7.middlewareStack.add(e=>async t=>(Ix.isInstance(t.request)&&(t.request.hostname=window.location.hostname,t.request.port=Number(window.location.port)||443,t.request.path=`/proxy/s3${t.request.path}`),await e(t)),{step:`finalizeRequest`,name:`proxyRewrite`,override:!0})}return w7}function k7(){x7=null,S7=null,C7=null,w7=null}var A7=!1;function j7(e,t){A7||!qS(e)||(A7=!0,ix(),k7(),t.clear(),window.location.pathname!==`/login`&&(window.location.href=`/login?reason=expired`))}function IFe(){let e=new ke({queryCache:new Oe({onError:t=>j7(t,e)}),mutationCache:new Ee({onError:t=>j7(t,e)}),defaultOptions:{queries:{staleTime:5e3,refetchIntervalInBackground:!1}}});return e}function LFe(){let e=(0,da.c)(8),{theme:t,setTheme:n}=ha(),r;e[0]!==n||e[1]!==t?(r=()=>{n(t===`light`?`dark`:t===`dark`?`light`:`dark`)},e[0]=n,e[1]=t,e[2]=r):r=e[2];let i=r,a,o,s;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,F.jsx)(Ho,{className:`size-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90`}),o=(0,F.jsx)(Po,{className:`absolute size-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0`}),s=(0,F.jsx)(`span`,{className:`sr-only`,children:`Toggle theme`}),e[3]=a,e[4]=o,e[5]=s):(a=e[3],o=e[4],s=e[5]);let c;return e[6]===i?c=e[7]:(c=(0,F.jsxs)(gl,{className:`rounded-full`,onClick:i,size:`icon-sm`,variant:`ghost`,children:[a,o,s]}),e[6]=i,e[7]=c),c}function M7(){let e=(0,da.c)(7),{isAdmin:t,license:n}=GS(),r=t&&n===`open-source`,i,a;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(i=rl(`z-50 flex h-10 shrink-0 items-center gap-2 border-b border-sidebar-border bg-sidebar px-4 text-sidebar-foreground`,`sticky top-0`),a=(0,F.jsx)(Wg,{className:`-ml-1`}),e[0]=i,e[1]=a):(i=e[0],a=e[1]);let o;e[2]===r?o=e[3]:(o=r&&(0,F.jsx)(`a`,{href:`https://mulgadc.com/purchase`,target:`_blank`,rel:`noopener noreferrer`,className:rl(hl({variant:`outline`,size:`sm`})),children:`Upgrade`}),e[2]=r,e[3]=o);let s;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,F.jsx)(LFe,{}),e[4]=s):s=e[4];let c;return e[5]===o?c=e[6]:(c=(0,F.jsxs)(`header`,{className:i,children:[a,(0,F.jsxs)(`div`,{className:`flex w-full items-center justify-end gap-2`,children:[o,s]})]}),e[5]=o,e[6]=c),c}function RFe(){let e=(0,da.c)(3),t,n;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,F.jsx)(`h1`,{className:`text-4xl font-bold`,children:`404`}),n=(0,F.jsx)(`p`,{className:`text-muted-foreground`,children:`Page not found`}),e[0]=t,e[1]=n):(t=e[0],n=e[1]);let r;return e[2]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,F.jsxs)(`div`,{className:`flex flex-1 flex-col items-center justify-center gap-4`,children:[t,n,(0,F.jsx)(Di,{to:`/`,children:(0,F.jsx)(gl,{variant:`outline`,children:`Go back home`})})]}),e[2]=r):r=e[2],r}function N7(){let e=(0,da.c)(172),t;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t={select:zFe},e[0]=t):t=e[0];let n=Qi(t),r=di(),i=Fe(),{isAdmin:a}=GS(),o;e[1]!==r||e[2]!==i?(o=function(){ix(),k7(),i.clear(),r({to:`/login`})},e[1]=r,e[2]=i,e[3]=o):o=e[3];let s=o,c;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,F.jsxs)(Kg,{className:`flex flex-row items-center gap-2 px-4 py-3`,children:[(0,F.jsx)(`img`,{src:`/mulga-logo.svg`,alt:`Mulga`,className:`size-6 shrink-0 dark:invert`}),(0,F.jsx)(`span`,{className:`truncate text-sm font-semibold group-data-[collapsible=icon]:hidden`,children:`Spinifex`})]}),e[4]=c):c=e[4];let l;e[5]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,F.jsx)(Zg,{children:`General`}),e[5]=l):l=e[5];let u=n===`/`,d,f;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,F.jsx)(Do,{className:`size-4`}),f=(0,F.jsx)(`span`,{children:`Dashboard`}),e[6]=d,e[7]=f):(d=e[6],f=e[7]);let p;e[8]===u?p=e[9]:(p=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/`,children:(0,F.jsxs)(t_,{isActive:u,tooltip:`Dashboard`,children:[d,f]})})}),e[8]=u,e[9]=p);let m;e[10]!==a||e[11]!==n?(m=a&&(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/nodes`,children:(0,F.jsxs)(t_,{isActive:n.startsWith(`/nodes`),tooltip:`Nodes`,children:[(0,F.jsx)(zo,{className:`size-4`}),(0,F.jsx)(`span`,{children:`Nodes`})]})})}),e[10]=a,e[11]=n,e[12]=m):m=e[12];let h;e[13]!==p||e[14]!==m?(h=(0,F.jsxs)(Xg,{children:[l,(0,F.jsxs)(Qg,{children:[p,m]})]}),e[13]=p,e[14]=m,e[15]=h):h=e[15];let g;e[16]===Symbol.for(`react.memo_cache_sentinel`)?(g=(0,F.jsx)(Zg,{children:`EC2`}),e[16]=g):g=e[16];let _;e[17]===n?_=e[18]:(_=n.startsWith(`/ec2/describe-instances`)||n.startsWith(`/ec2/run-instances`),e[17]=n,e[18]=_);let v,y;e[19]===Symbol.for(`react.memo_cache_sentinel`)?(v=(0,F.jsx)(zo,{className:`size-4`}),y=(0,F.jsx)(`span`,{children:`Instances`}),e[19]=v,e[20]=y):(v=e[19],y=e[20]);let b;e[21]===_?b=e[22]:(b=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-instances`,children:(0,F.jsxs)(t_,{isActive:_,tooltip:`Instances`,children:[v,y]})})}),e[21]=_,e[22]=b);let x;e[23]===n?x=e[24]:(x=n.startsWith(`/ec2/describe-images`),e[23]=n,e[24]=x);let S,C;e[25]===Symbol.for(`react.memo_cache_sentinel`)?(S=(0,F.jsx)(Oo,{className:`size-4`}),C=(0,F.jsx)(`span`,{children:`Images`}),e[25]=S,e[26]=C):(S=e[25],C=e[26]);let w;e[27]===x?w=e[28]:(w=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-images`,children:(0,F.jsxs)(t_,{isActive:x,tooltip:`Images`,children:[S,C]})})}),e[27]=x,e[28]=w);let T;e[29]===n?T=e[30]:(T=n.startsWith(`/ec2/describe-key-pairs`),e[29]=n,e[30]=T);let E,ee;e[31]===Symbol.for(`react.memo_cache_sentinel`)?(E=(0,F.jsx)(ko,{className:`size-4`}),ee=(0,F.jsx)(`span`,{children:`Key Pairs`}),e[31]=E,e[32]=ee):(E=e[31],ee=e[32]);let D;e[33]===T?D=e[34]:(D=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-key-pairs`,children:(0,F.jsxs)(t_,{isActive:T,tooltip:`Key Pairs`,children:[E,ee]})})}),e[33]=T,e[34]=D);let O;e[35]===n?O=e[36]:(O=n.startsWith(`/ec2/describe-addresses`)||n.startsWith(`/ec2/allocate-address`),e[35]=n,e[36]=O);let te,ne;e[37]===Symbol.for(`react.memo_cache_sentinel`)?(te=(0,F.jsx)(No,{className:`size-4`}),ne=(0,F.jsx)(`span`,{children:`Elastic IPs`}),e[37]=te,e[38]=ne):(te=e[37],ne=e[38]);let re;e[39]===O?re=e[40]:(re=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-addresses`,children:(0,F.jsxs)(t_,{isActive:O,tooltip:`Elastic IPs`,children:[te,ne]})})}),e[39]=O,e[40]=re);let k;e[41]===n?k=e[42]:(k=n.startsWith(`/ec2/describe-volumes`)||n.startsWith(`/ec2/create-volume`)||n.startsWith(`/ec2/modify-volume`),e[41]=n,e[42]=k);let A,ie;e[43]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,F.jsx)(Eo,{className:`size-4`}),ie=(0,F.jsx)(`span`,{children:`Volumes`}),e[43]=A,e[44]=ie):(A=e[43],ie=e[44]);let j;e[45]===k?j=e[46]:(j=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-volumes`,children:(0,F.jsxs)(t_,{isActive:k,tooltip:`Volumes`,children:[A,ie]})})}),e[45]=k,e[46]=j);let ae;e[47]===n?ae=e[48]:(ae=n.startsWith(`/ec2/describe-snapshots`)||n.startsWith(`/ec2/create-snapshot`),e[47]=n,e[48]=ae);let oe,se;e[49]===Symbol.for(`react.memo_cache_sentinel`)?(oe=(0,F.jsx)(So,{className:`size-4`}),se=(0,F.jsx)(`span`,{children:`Snapshots`}),e[49]=oe,e[50]=se):(oe=e[49],se=e[50]);let ce;e[51]===ae?ce=e[52]:(ce=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-snapshots`,children:(0,F.jsxs)(t_,{isActive:ae,tooltip:`Snapshots`,children:[oe,se]})})}),e[51]=ae,e[52]=ce);let M;e[53]===n?M=e[54]:(M=n.startsWith(`/ec2/describe-placement-groups`)||n.startsWith(`/ec2/create-placement-group`),e[53]=n,e[54]=M);let le,ue;e[55]===Symbol.for(`react.memo_cache_sentinel`)?(le=(0,F.jsx)(Ao,{className:`size-4`}),ue=(0,F.jsx)(`span`,{children:`Placement Groups`}),e[55]=le,e[56]=ue):(le=e[55],ue=e[56]);let N;e[57]===M?N=e[58]:(N=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-placement-groups`,children:(0,F.jsxs)(t_,{isActive:M,tooltip:`Placement Groups`,children:[le,ue]})})}),e[57]=M,e[58]=N);let de;e[59]===n?de=e[60]:(de=n.startsWith(`/ec2/describe-load-balancers`)||n.startsWith(`/ec2/create-load-balancer`),e[59]=n,e[60]=de);let fe,pe;e[61]===Symbol.for(`react.memo_cache_sentinel`)?(fe=(0,F.jsx)(Wo,{className:`size-4`}),pe=(0,F.jsx)(`span`,{children:`Load Balancers`}),e[61]=fe,e[62]=pe):(fe=e[61],pe=e[62]);let me;e[63]===de?me=e[64]:(me=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-load-balancers`,children:(0,F.jsxs)(t_,{isActive:de,tooltip:`Load Balancers`,children:[fe,pe]})})}),e[63]=de,e[64]=me);let he;e[65]===n?he=e[66]:(he=n.startsWith(`/ec2/describe-target-groups`)||n.startsWith(`/ec2/create-target-group`),e[65]=n,e[66]=he);let ge,_e;e[67]===Symbol.for(`react.memo_cache_sentinel`)?(ge=(0,F.jsx)(wo,{className:`size-4`}),_e=(0,F.jsx)(`span`,{children:`Target Groups`}),e[67]=ge,e[68]=_e):(ge=e[67],_e=e[68]);let ve;e[69]===he?ve=e[70]:(ve=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-target-groups`,children:(0,F.jsxs)(t_,{isActive:he,tooltip:`Target Groups`,children:[ge,_e]})})}),e[69]=he,e[70]=ve);let ye;e[71]!==b||e[72]!==w||e[73]!==D||e[74]!==re||e[75]!==j||e[76]!==ce||e[77]!==N||e[78]!==me||e[79]!==ve?(ye=(0,F.jsxs)(Xg,{children:[g,(0,F.jsxs)(Qg,{children:[b,w,D,re,j,ce,N,me,ve]})]}),e[71]=b,e[72]=w,e[73]=D,e[74]=re,e[75]=j,e[76]=ce,e[77]=N,e[78]=me,e[79]=ve,e[80]=ye):ye=e[80];let be;e[81]===Symbol.for(`react.memo_cache_sentinel`)?(be=(0,F.jsx)(Zg,{children:`Networking`}),e[81]=be):be=e[81];let xe;e[82]===n?xe=e[83]:(xe=n.startsWith(`/ec2/describe-vpcs`)||n.startsWith(`/ec2/create-vpc`),e[82]=n,e[83]=xe);let Se,Ce;e[84]===Symbol.for(`react.memo_cache_sentinel`)?(Se=(0,F.jsx)(Fo,{className:`size-4`}),Ce=(0,F.jsx)(`span`,{children:`VPCs`}),e[84]=Se,e[85]=Ce):(Se=e[84],Ce=e[85]);let we;e[86]===xe?we=e[87]:(we=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-vpcs`,children:(0,F.jsxs)(t_,{isActive:xe,tooltip:`VPCs`,children:[Se,Ce]})})}),e[86]=xe,e[87]=we);let Te;e[88]===n?Te=e[89]:(Te=n.startsWith(`/ec2/describe-subnets`)||n.startsWith(`/ec2/create-subnet`),e[88]=n,e[89]=Te);let Ee,De;e[90]===Symbol.for(`react.memo_cache_sentinel`)?(Ee=(0,F.jsx)(jo,{className:`size-4`}),De=(0,F.jsx)(`span`,{children:`Subnets`}),e[90]=Ee,e[91]=De):(Ee=e[90],De=e[91]);let Oe;e[92]===Te?Oe=e[93]:(Oe=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-subnets`,children:(0,F.jsxs)(t_,{isActive:Te,tooltip:`Subnets`,children:[Ee,De]})})}),e[92]=Te,e[93]=Oe);let ke;e[94]===n?ke=e[95]:(ke=n.startsWith(`/ec2/describe-security-groups`)||n.startsWith(`/ec2/create-security-group`),e[94]=n,e[95]=ke);let Ae,je;e[96]===Symbol.for(`react.memo_cache_sentinel`)?(Ae=(0,F.jsx)(Bo,{className:`size-4`}),je=(0,F.jsx)(`span`,{children:`Security Groups`}),e[96]=Ae,e[97]=je):(Ae=e[96],je=e[97]);let Me;e[98]===ke?Me=e[99]:(Me=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-security-groups`,children:(0,F.jsxs)(t_,{isActive:ke,tooltip:`Security Groups`,children:[Ae,je]})})}),e[98]=ke,e[99]=Me);let Ne;e[100]===n?Ne=e[101]:(Ne=n.startsWith(`/ec2/describe-route-tables`)||n.startsWith(`/ec2/create-route-table`),e[100]=n,e[101]=Ne);let P,Pe;e[102]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,F.jsx)(Lo,{className:`size-4`}),Pe=(0,F.jsx)(`span`,{children:`Route Tables`}),e[102]=P,e[103]=Pe):(P=e[102],Pe=e[103]);let Ie;e[104]===Ne?Ie=e[105]:(Ie=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-route-tables`,children:(0,F.jsxs)(t_,{isActive:Ne,tooltip:`Route Tables`,children:[P,Pe]})})}),e[104]=Ne,e[105]=Ie);let I;e[106]===n?I=e[107]:(I=n.startsWith(`/ec2/describe-internet-gateways`),e[106]=n,e[107]=I);let Le,Re;e[108]===Symbol.for(`react.memo_cache_sentinel`)?(Le=(0,F.jsx)(To,{className:`size-4`}),Re=(0,F.jsx)(`span`,{children:`Internet Gateways`}),e[108]=Le,e[109]=Re):(Le=e[108],Re=e[109]);let ze;e[110]===I?ze=e[111]:(ze=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-internet-gateways`,children:(0,F.jsxs)(t_,{isActive:I,tooltip:`Internet Gateways`,children:[Le,Re]})})}),e[110]=I,e[111]=ze);let Be;e[112]===n?Be=e[113]:(Be=n.startsWith(`/ec2/describe-nat-gateways`)||n.startsWith(`/ec2/create-nat-gateway`),e[112]=n,e[113]=Be);let Ve,He;e[114]===Symbol.for(`react.memo_cache_sentinel`)?(Ve=(0,F.jsx)(Ro,{className:`size-4`}),He=(0,F.jsx)(`span`,{children:`NAT Gateways`}),e[114]=Ve,e[115]=He):(Ve=e[114],He=e[115]);let Ue;e[116]===Be?Ue=e[117]:(Ue=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/ec2/describe-nat-gateways`,children:(0,F.jsxs)(t_,{isActive:Be,tooltip:`NAT Gateways`,children:[Ve,He]})})}),e[116]=Be,e[117]=Ue);let We;e[118]!==we||e[119]!==Oe||e[120]!==Me||e[121]!==Ie||e[122]!==ze||e[123]!==Ue?(We=(0,F.jsxs)(Xg,{children:[be,(0,F.jsxs)(Qg,{children:[we,Oe,Me,Ie,ze,Ue]})]}),e[118]=we,e[119]=Oe,e[120]=Me,e[121]=Ie,e[122]=ze,e[123]=Ue,e[124]=We):We=e[124];let Ge;e[125]===Symbol.for(`react.memo_cache_sentinel`)?(Ge=(0,F.jsx)(Zg,{children:`IAM`}),e[125]=Ge):Ge=e[125];let Ke;e[126]===n?Ke=e[127]:(Ke=n.startsWith(`/iam/list-users`)||n.startsWith(`/iam/create-user`),e[126]=n,e[127]=Ke);let qe,Je;e[128]===Symbol.for(`react.memo_cache_sentinel`)?(qe=(0,F.jsx)(Uo,{className:`size-4`}),Je=(0,F.jsx)(`span`,{children:`Users`}),e[128]=qe,e[129]=Je):(qe=e[128],Je=e[129]);let Ye;e[130]===Ke?Ye=e[131]:(Ye=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/iam/list-users`,children:(0,F.jsxs)(t_,{isActive:Ke,tooltip:`Users`,children:[qe,Je]})})}),e[130]=Ke,e[131]=Ye);let Xe;e[132]===n?Xe=e[133]:(Xe=n.startsWith(`/iam/list-policies`)||n.startsWith(`/iam/create-policy`),e[132]=n,e[133]=Xe);let Ze,Qe;e[134]===Symbol.for(`react.memo_cache_sentinel`)?(Ze=(0,F.jsx)(Vo,{className:`size-4`}),Qe=(0,F.jsx)(`span`,{children:`Policies`}),e[134]=Ze,e[135]=Qe):(Ze=e[134],Qe=e[135]);let $e;e[136]===Xe?$e=e[137]:($e=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/iam/list-policies`,children:(0,F.jsxs)(t_,{isActive:Xe,tooltip:`Policies`,children:[Ze,Qe]})})}),e[136]=Xe,e[137]=$e);let et;e[138]!==Ye||e[139]!==$e?(et=(0,F.jsxs)(Xg,{children:[Ge,(0,F.jsxs)(Qg,{children:[Ye,$e]})]}),e[138]=Ye,e[139]=$e,e[140]=et):et=e[140];let tt;e[141]===Symbol.for(`react.memo_cache_sentinel`)?(tt=(0,F.jsx)(Zg,{children:`S3`}),e[141]=tt):tt=e[141];let nt;e[142]===n?nt=e[143]:(nt=n.startsWith(`/s3/ls`),e[142]=n,e[143]=nt);let rt,it;e[144]===Symbol.for(`react.memo_cache_sentinel`)?(rt=(0,F.jsx)(zo,{className:`size-4`}),it=(0,F.jsx)(`span`,{children:`Buckets`}),e[144]=rt,e[145]=it):(rt=e[144],it=e[145]);let at;e[146]===nt?at=e[147]:(at=(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/s3/ls`,children:(0,F.jsxs)(t_,{isActive:nt,tooltip:`Buckets`,children:[rt,it]})})}),e[146]=nt,e[147]=at);let ot;e[148]!==a||e[149]!==n?(ot=a&&(0,F.jsx)($g,{children:(0,F.jsx)(Di,{to:`/s3/service-metrics`,children:(0,F.jsxs)(t_,{isActive:n.startsWith(`/s3/service-metrics`),tooltip:`Service Metrics`,children:[(0,F.jsx)(bo,{className:`size-4`}),(0,F.jsx)(`span`,{children:`Service Metrics`})]})})}),e[148]=a,e[149]=n,e[150]=ot):ot=e[150];let st;e[151]!==at||e[152]!==ot?(st=(0,F.jsxs)(Xg,{children:[tt,(0,F.jsxs)(Qg,{children:[at,ot]})]}),e[151]=at,e[152]=ot,e[153]=st):st=e[153];let ct;e[154]===a?ct=e[155]:(ct=a&&(0,F.jsxs)(Xg,{children:[(0,F.jsx)(Zg,{children:`Documentation`}),(0,F.jsx)(Qg,{children:(0,F.jsx)($g,{children:(0,F.jsx)(`a`,{href:`https://docs.mulgadc.com`,target:`_blank`,rel:`noopener noreferrer`,children:(0,F.jsxs)(t_,{tooltip:`Documentation`,children:[(0,F.jsx)(xo,{className:`size-4`}),(0,F.jsx)(`span`,{children:`Docs`})]})})})})]}),e[154]=a,e[155]=ct);let lt;e[156]!==ye||e[157]!==We||e[158]!==et||e[159]!==h||e[160]!==st||e[161]!==ct?(lt=(0,F.jsxs)(Yg,{children:[h,ye,We,et,st,ct]}),e[156]=ye,e[157]=We,e[158]=et,e[159]=h,e[160]=st,e[161]=ct,e[162]=lt):lt=e[162];let ut;e[163]===Symbol.for(`react.memo_cache_sentinel`)?(ut=(0,F.jsx)(Jg,{className:`mx-0 w-full`}),e[163]=ut):ut=e[163];let dt,ft;e[164]===Symbol.for(`react.memo_cache_sentinel`)?(dt=(0,F.jsx)(Mo,{className:`size-4`}),ft=(0,F.jsx)(`span`,{children:`Sign out`}),e[164]=dt,e[165]=ft):(dt=e[164],ft=e[165]);let pt;e[166]===s?pt=e[167]:(pt=(0,F.jsxs)(qg,{children:[ut,(0,F.jsx)(Qg,{children:(0,F.jsx)($g,{children:(0,F.jsxs)(t_,{onClick:s,tooltip:`Sign out`,children:[dt,ft]})})})]}),e[166]=s,e[167]=pt);let mt;e[168]===Symbol.for(`react.memo_cache_sentinel`)?(mt=(0,F.jsx)(Gg,{}),e[168]=mt):mt=e[168];let ht;return e[169]!==lt||e[170]!==pt?(ht=(0,F.jsxs)(Ug,{collapsible:`icon`,children:[c,lt,pt,mt]}),e[169]=lt,e[170]=pt,e[171]=ht):ht=e[171],ht}function zFe(e){return e.pathname}function BFe(e){let t=(0,da.c)(18),{error:n,reset:r}=e,i=jr(),a=Fe(),o;t[0]!==a||t[1]!==r||t[2]!==i?(o=function(){a.invalidateQueries(),r(),i.invalidate()},t[0]=a,t[1]=r,t[2]=i,t[3]=o):o=t[3];let s=o,c;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,F.jsx)(N7,{}),t[4]=c):c=t[4];let l;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,F.jsx)(M7,{}),t[5]=l):l=t[5];let u,d;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(u=(0,F.jsx)(`div`,{className:`flex justify-center`,children:(0,F.jsx)(Co,{className:`size-12 text-destructive`})}),d=(0,F.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Something went wrong`}),t[6]=u,t[7]=d):(u=t[6],d=t[7]);let f=n.message||`An unexpected error occurred`,p;t[8]===f?p=t[9]:(p=(0,F.jsx)(`p`,{className:`text-muted-foreground`,children:f}),t[8]=f,t[9]=p);let m;t[10]===s?m=t[11]:(m=(0,F.jsx)(gl,{onClick:s,variant:`default`,children:`Try Again`}),t[10]=s,t[11]=m);let h;t[12]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,F.jsx)(Di,{to:`/`,children:(0,F.jsx)(gl,{variant:`outline`,children:`Go Home`})}),t[12]=h):h=t[12];let g;t[13]===m?g=t[14]:(g=(0,F.jsxs)(`div`,{className:`flex justify-center gap-2`,children:[m,h]}),t[13]=m,t[14]=g);let _;return t[15]!==g||t[16]!==p?(_=(0,F.jsxs)(F.Fragment,{children:[c,(0,F.jsxs)(`main`,{className:`flex flex-1 flex-col`,children:[l,(0,F.jsx)(`div`,{className:`flex flex-1 items-center justify-center p-6 pt-0`,children:(0,F.jsxs)(`div`,{className:`max-w-md space-y-4 text-center`,children:[u,d,p,g]})})]})]}),t[15]=g,t[16]=p,t[17]=_):_=t[17],_}var P7=ji()({head:()=>({meta:[{title:`Mulga`}]}),component:()=>(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(ra,{}),(0,F.jsx)(N7,{}),(0,F.jsxs)(`main`,{className:`flex flex-1 flex-col`,children:[(0,F.jsx)(M7,{}),(0,F.jsx)(`div`,{className:`flex flex-1 flex-col p-8`,children:(0,F.jsx)(Ui,{})})]})]}),errorComponent:BFe,notFoundComponent:RFe}),F7=L(`/login`)({validateSearch:e=>({reason:e.reason===`expired`?`expired`:void 0}),beforeLoad:()=>{if(nx())throw yn({to:`/`})},component:R(()=>H(()=>import(`./login.js`),__vite__mapDeps([0,1,2,3,4])),`component`)}),VFe=L(`/_auth`)({beforeLoad:()=>{if(!nx())throw yn({to:`/login`})},component:R(()=>H(()=>import(`./route.js`),[]),`component`)}),I7=I({queryKey:[`ec2`,`availabilityZones`],queryFn:async()=>{let e=new Dhe({});return await T7().send(e)},staleTime:3e5}),L7=I({queryKey:[`ec2`,`instances`],queryFn:async()=>{let e=new J$({});return await T7().send(e)},refetchInterval:5e3}),R7=e=>I({queryKey:[`ec2`,`instances`,e],queryFn:async()=>{let t=new J$({InstanceIds:[e]});return await T7().send(t)},refetchInterval:5e3}),z7=I({queryKey:[`ec2`,`instances`,`types`],queryFn:async()=>{let e=new Ohe({Filters:[{Name:`capacity`,Values:[`true`]}]});return await T7().send(e)},refetchInterval:5e3}),B7=I({queryKey:[`ec2`,`images`],queryFn:async()=>{let e=new q$({});return await T7().send(e)}}),V7=e=>I({queryKey:[`ec2`,`images`,e??`none`],queryFn:async()=>{if(!e)return{Images:[],$metadata:{}};let t=new q$({ImageIds:[e]});return await T7().send(t)}}),H7=I({queryKey:[`ec2`,`keypairs`],queryFn:async()=>{let e=new X$({});return await T7().send(e)}}),U7=e=>I({queryKey:[`ec2`,`keypairs`,e],queryFn:async()=>{let t=new X$({KeyPairIds:[e]});return await T7().send(t)}}),W7=I({queryKey:[`ec2`,`regions`],queryFn:async()=>{let e=new khe({});return await T7().send(e)},staleTime:3e5}),G7=I({queryKey:[`ec2`,`subnets`],queryFn:async()=>{let e=new n1({});return await T7().send(e)}}),K7=e=>I({queryKey:[`ec2`,`subnets`,e],queryFn:async()=>{let t=new n1({SubnetIds:[e]});return await T7().send(t)}}),q7=I({queryKey:[`ec2`,`snapshots`],queryFn:async()=>{let e=new t1({});return await T7().send(e)},refetchInterval:5e3}),J7=e=>I({queryKey:[`ec2`,`snapshots`,e],queryFn:async()=>{let t=new t1({SnapshotIds:[e]});return await T7().send(t)},refetchInterval:5e3}),Y7=I({queryKey:[`ec2`,`internetGateways`],queryFn:async()=>{let e=new Y$({});return await T7().send(e)},refetchInterval:5e3}),X7=e=>I({queryKey:[`ec2`,`internetGateways`,e],queryFn:async()=>{let t=new Y$({InternetGatewayIds:[e]});return await T7().send(t)},refetchInterval:5e3}),Z7=I({queryKey:[`ec2`,`routeTables`],queryFn:async()=>{let e=new $$({});return await T7().send(e)},refetchInterval:5e3}),Q7=e=>I({queryKey:[`ec2`,`routeTables`,e],queryFn:async()=>{let t=new $$({RouteTableIds:[e]});return await T7().send(t)},refetchInterval:5e3}),$7=I({queryKey:[`ec2`,`vpcs`],queryFn:async()=>{let e=new i1({});return await T7().send(e)}}),e9=e=>I({queryKey:[`ec2`,`vpcs`,e],queryFn:async()=>{let t=new i1({VpcIds:[e]});return await T7().send(t)}}),t9=I({queryKey:[`ec2`,`placementGroups`],queryFn:async()=>{let e=new Q$({});return await T7().send(e)}}),n9=e=>I({queryKey:[`ec2`,`placementGroups`,e],queryFn:async()=>{let t=new Q$({GroupIds:[e]});return await T7().send(t)}}),r9=I({queryKey:[`ec2`,`securityGroups`],queryFn:async()=>{let e=new e1({});return await T7().send(e)}}),i9=e=>I({queryKey:[`ec2`,`securityGroups`,e],queryFn:async()=>{let t=new e1({GroupIds:[e]});return await T7().send(t)}}),a9=I({queryKey:[`ec2`,`addresses`],queryFn:async()=>{let e=new K$({});return await T7().send(e)},refetchInterval:5e3}),o9=e=>I({queryKey:[`ec2`,`addresses`,e],queryFn:async()=>{let t=new K$({AllocationIds:[e]});return await T7().send(t)},refetchInterval:5e3}),s9=I({queryKey:[`ec2`,`natGateways`],queryFn:async()=>{let e=new Z$({});return await T7().send(e)},refetchInterval:5e3}),c9=e=>I({queryKey:[`ec2`,`natGateways`,e],queryFn:async()=>{let t=new Z$({NatGatewayIds:[e]});return await T7().send(t)},refetchInterval:5e3}),l9=I({queryKey:[`ec2`,`volumes`],queryFn:async()=>{let e=new r1({});return await T7().send(e)},refetchInterval:5e3}),u9=e=>I({queryKey:[`ec2`,`volumes`,e],queryFn:async()=>{let t=new r1({VolumeIds:[e]});return await T7().send(t)},refetchInterval:5e3}),d9=I({queryKey:[`s3`,`buckets`],queryFn:async()=>{let e=new NFe({});return await O7().send(e)}}),f9=(e,t)=>I({queryKey:[`s3`,`buckets`,e,`objects`,t??``],queryFn:async()=>{let n=new PFe({Bucket:e,Prefix:t,Delimiter:`/`});return await O7().send(n)}}),HFe=L(`/_auth/`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData(L7),e.queryClient.ensureQueryData(l9),e.queryClient.ensureQueryData($7),e.queryClient.ensureQueryData(H7),e.queryClient.ensureQueryData(d9),e.queryClient.ensureQueryData(r9),e.queryClient.ensureQueryData(s9),e.queryClient.ensureQueryData(a9),e.queryClient.ensureQueryData(Y7)])},head:()=>({meta:[{title:`Dashboard | Mulga`}]}),component:R(()=>H(()=>import(`./_auth.js`),__vite__mapDeps([5,6,7,8,9,10,11,12,3,13])),`component`)}),UFe=L(`/_auth/nodes`)({head:()=>({meta:[{title:`Nodes | Mulga`}]}),component:R(()=>H(()=>import(`./nodes.js`),__vite__mapDeps([14,6,7,10,12,3,13])),`component`)}),WFe=L(`/_auth/s3/service-metrics`)({head:()=>({meta:[{title:`Service Metrics | S3 | Mulga`}]}),component:R(()=>H(()=>import(`./service-metrics.js`),__vite__mapDeps([15,6,7,10,12,3,13])),`component`)}),GFe=L(`/_auth/s3/ls/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(d9)},head:()=>({meta:[{title:`Buckets | S3 | Mulga`}]}),component:R(()=>H(()=>import(`./ls.js`),__vite__mapDeps([16,17,7,18,10])),`component`)}),KFe=L(`/_auth/s3/(buckets)/create-bucket`)({head:()=>({meta:[{title:`Create Bucket | S3 | Mulga`}]}),component:R(()=>H(()=>import(`./create-bucket.js`),__vite__mapDeps([19,20,21,2,22,23,24,8,25,26,27,10,4])),`component`)}),qFe=L(`/_auth/iam/(users)/create-user`)({head:()=>({meta:[{title:`Create User | IAM | Mulga`}]}),component:R(()=>H(()=>import(`./create-user.js`),__vite__mapDeps([28,29,21,2,22,23,24,8,25,26,27,10,4,30])),`component`)}),JFe=L(`/_auth/iam/(policies)/create-policy`)({head:()=>({meta:[{title:`Create Policy | IAM | Mulga`}]}),component:R(()=>H(()=>import(`./create-policy.js`),__vite__mapDeps([31,29,21,2,22,23,24,8,25,26,27,10,4,32,30])),`component`)}),YFe=L(`/_auth/ec2/(vpc)/create-vpc`)({head:()=>({meta:[{title:`Create VPC | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-vpc.js`),__vite__mapDeps([33,34,21,35,24,8,2,22,9,23,25,26,27,10,4,36])),`component`)}),XFe=L(`/_auth/ec2/(volumes)/create-volume`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(I7)},head:()=>({meta:[{title:`Create Volume | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-volume.js`),__vite__mapDeps([37,34,21,35,24,8,2,17,7,22,23,25,26,27,10,4,36])),`component`)}),ZFe=L(`/_auth/ec2/(target-groups)/create-target-group`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData($7)},head:()=>({meta:[{title:`Create Target Group | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-target-group.js`),__vite__mapDeps([38,39,21,2,17,7,22,23,24,8,25,26,40,1,35,41,42,4,27,10,43])),`component`)}),QFe=L(`/_auth/ec2/(subnet)/create-subnet`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData($7),e.queryClient.ensureQueryData(I7)])},head:()=>({meta:[{title:`Create Subnet | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-subnet.js`),__vite__mapDeps([44,34,21,35,24,8,2,17,7,22,23,25,26,27,10,4,36])),`component`)}),p9=L(`/_auth/ec2/(snapshots)/create-snapshot`)({validateSearch:e=>({volumeId:typeof e.volumeId==`string`?e.volumeId:void 0}),loader:async({context:e})=>{await e.queryClient.ensureQueryData(l9)},head:()=>({meta:[{title:`Create Snapshot | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-snapshot.js`),__vite__mapDeps([45,34,21,35,24,8,2,17,7,22,23,25,26,27,10,4,32,36])),`component`)}),$Fe=L(`/_auth/ec2/(security-groups)/create-security-group`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData($7)},head:()=>({meta:[{title:`Create Security Group | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-security-group.js`),__vite__mapDeps([46,34,21,35,24,8,2,17,7,22,23,25,26,27,10,4,36])),`component`)}),eIe=L(`/_auth/ec2/(route-tables)/create-route-table`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData($7)},head:()=>({meta:[{title:`Create Route Table | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-route-table.js`),__vite__mapDeps([47,34,21,35,24,8,2,17,7,22,27,10,4,36])),`component`)}),tIe=L(`/_auth/ec2/(placement-groups)/create-placement-group`)({head:()=>({meta:[{title:`Create Placement Group | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-placement-group.js`),__vite__mapDeps([48,34,21,35,24,8,2,22,23,25,26,27,10,4,36])),`component`)}),nIe=L(`/_auth/ec2/(nat-gateways)/create-nat-gateway`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData(G7),e.queryClient.ensureQueryData(a9)])},head:()=>({meta:[{title:`Create NAT Gateway | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-nat-gateway.js`),__vite__mapDeps([49,34,21,35,24,8,2,17,7,22,23,25,26,27,10,4,36])),`component`)}),m9=5e3,h9=I({queryKey:[`elbv2`,`loadBalancers`],queryFn:async()=>{let e=new v2({});return await E7().send(e)},refetchInterval:e=>(e.state.data?.LoadBalancers??[]).some(e=>e.State?.Code===`provisioning`)?m9:!1}),g9=e=>I({queryKey:[`elbv2`,`loadBalancers`,e],queryFn:async()=>{let t=new v2({LoadBalancerArns:[e]});return await E7().send(t)},refetchInterval:e=>(e.state.data?.LoadBalancers?.[0])?.State?.Code===`provisioning`?m9:!1}),_9=e=>I({queryKey:[`elbv2`,`loadBalancers`,e,`attributes`],queryFn:async()=>{let t=new Dwe({LoadBalancerArn:e});return await E7().send(t)}}),v9=I({queryKey:[`elbv2`,`targetGroups`],queryFn:async()=>{let e=new y2({});return await E7().send(e)}}),y9=e=>I({queryKey:[`elbv2`,`targetGroups`,e],queryFn:async()=>{let t=new y2({TargetGroupArns:[e]});return await E7().send(t)}}),b9=e=>I({queryKey:[`elbv2`,`targetGroups`,e,`attributes`],queryFn:async()=>{let t=new Awe({TargetGroupArn:e});return await E7().send(t)}}),x9=e=>I({queryKey:[`elbv2`,`listeners`,e],queryFn:async()=>{let t=new Ewe({LoadBalancerArn:e});return await E7().send(t)}}),rIe=e=>I({queryKey:[`elbv2`,`listeners`,e,`rules`],queryFn:async()=>{let t=new Owe({ListenerArn:e});return await E7().send(t)}}),iIe=e=>I({queryKey:[`elbv2`,`targetGroups`,e,`health`],queryFn:async()=>{let t=new jwe({TargetGroupArn:e});return await E7().send(t)}}),S9=e=>I({queryKey:[`elbv2`,`tags`,...e],queryFn:async()=>{let t=new kwe({ResourceArns:e});return await E7().send(t)},enabled:e.length>0}),aIe=L(`/_auth/ec2/(load-balancers)/create-load-balancer`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData($7),e.queryClient.ensureQueryData(G7),e.queryClient.ensureQueryData(r9),e.queryClient.ensureQueryData(v9)])},head:()=>({meta:[{title:`Create Load Balancer | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-load-balancer.js`),__vite__mapDeps([50,39,21,35,24,8,2,17,7,22,41,23,25,26,42,40,1,4,27,10,43])),`component`)}),oIe=L(`/_auth/ec2/(key)/import-key-pair`)({head:()=>({meta:[{title:`Import Key Pair | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./import-key-pair.js`),__vite__mapDeps([51,34,21,2,22,23,24,8,25,26,27,10,4,32,36])),`component`)}),sIe=L(`/_auth/ec2/(key)/create-key-pair`)({head:()=>({meta:[{title:`Create Key Pair | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-key-pair.js`),__vite__mapDeps([52,34,21,53,2,22,24,54,23,8,25,26,55,27,10,4,36])),`component`)}),cIe=L(`/_auth/ec2/(internet-gateways)/create-internet-gateway`)({head:()=>({meta:[{title:`Create Internet Gateway | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./create-internet-gateway.js`),__vite__mapDeps([56,34,21,2,22,23,24,8,25,26,27,10,4,36])),`component`)}),lIe=L(`/_auth/ec2/(instances)/run-instances`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData(B7),e.queryClient.ensureQueryData(H7),e.queryClient.ensureQueryData(z7),e.queryClient.ensureQueryData(G7),e.queryClient.ensureQueryData(t9),e.queryClient.ensureQueryData($7),e.queryClient.ensureQueryData(r9)])},head:()=>({meta:[{title:`Run Instances | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./run-instances.js`),__vite__mapDeps([57,34,21,1,35,24,8,2,17,7,22,23,25,26,10,4,36])),`component`)}),uIe=L(`/_auth/ec2/(elastic-ips)/allocate-address`)({head:()=>({meta:[{title:`Allocate Elastic IP | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./allocate-address.js`),__vite__mapDeps([58,34,21,2,22,23,24,8,25,26,27,10,4,36])),`component`)}),dIe=L(`/_auth/s3/ls/$bucket`)({component:R(()=>H(()=>import(`./route2.js`),[]),`component`)}),C9=L(`/_auth/s3/ls/$bucket/`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(f9(t.bucket))},head:({params:e})=>({meta:[{title:`${e.bucket} | S3 | Mulga`}]}),component:R(()=>H(()=>import(`./_bucket.js`),__vite__mapDeps([59,20,21,60,54,42,61,53,17,7,22,10])),`component`)}),w9=I({queryKey:[`iam`,`users`],queryFn:async()=>{let e=new vke({});return await D7().send(e)},staleTime:3e5}),T9=e=>I({queryKey:[`iam`,`users`,e],queryFn:async()=>{let t=new mke({UserName:e});return await D7().send(t)},staleTime:3e5}),E9=e=>I({queryKey:[`iam`,`access-keys`,e],queryFn:async()=>{let t=new hke({UserName:e});return await D7().send(t)},staleTime:3e5}),D9=I({queryKey:[`iam`,`policies`],queryFn:async()=>{let e=new _ke({Scope:`Local`});return await D7().send(e)},staleTime:3e5}),O9=e=>I({queryKey:[`iam`,`policies`,e],queryFn:async()=>{let t=new fke({PolicyArn:e});return await D7().send(t)},staleTime:3e5}),k9=(e,t)=>I({queryKey:[`iam`,`policy-versions`,e,t],queryFn:async()=>{let n=new pke({PolicyArn:e,VersionId:t});return await D7().send(n)},staleTime:3e5}),A9=e=>I({queryKey:[`iam`,`attached-user-policies`,e],queryFn:async()=>{let t=new gke({UserName:e});return await D7().send(t)},staleTime:3e5}),fIe=L(`/_auth/iam/(users)/list-users/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(w9)},head:()=>({meta:[{title:`Users | IAM | Mulga`}]}),component:R(()=>H(()=>import(`./list-users.js`),__vite__mapDeps([62,17,7,18,10])),`component`)}),pIe=L(`/_auth/iam/(policies)/list-policies/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(D9)},head:()=>({meta:[{title:`Policies | IAM | Mulga`}]}),component:R(()=>H(()=>import(`./list-policies.js`),__vite__mapDeps([63,17,7,18,10])),`component`)}),mIe=L(`/_auth/ec2/(vpc)/describe-vpcs/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData($7)},head:()=>({meta:[{title:`VPCs | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-vpcs.js`),__vite__mapDeps([64,17,7,18,10,11])),`component`)}),hIe=L(`/_auth/ec2/(volumes)/describe-volumes/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(l9)},head:()=>({meta:[{title:`Volumes | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-volumes.js`),__vite__mapDeps([65,17,7,18,10,11])),`component`)}),gIe=L(`/_auth/ec2/(target-groups)/describe-target-groups/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(v9)},head:()=>({meta:[{title:`Target Groups | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-target-groups.js`),__vite__mapDeps([66,6,7,17,10])),`component`)}),_Ie=L(`/_auth/ec2/(subnet)/describe-subnets/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(G7)},head:()=>({meta:[{title:`Subnets | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-subnets.js`),__vite__mapDeps([67,17,7,18,10,11])),`component`)}),vIe=L(`/_auth/ec2/(snapshots)/describe-snapshots/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(q7)},head:()=>({meta:[{title:`Snapshots | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-snapshots.js`),__vite__mapDeps([68,17,7,18,10,11])),`component`)}),yIe=L(`/_auth/ec2/(security-groups)/describe-security-groups/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(r9)},head:()=>({meta:[{title:`Security Groups | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-security-groups.js`),__vite__mapDeps([69,17,7,18,10])),`component`)}),bIe=L(`/_auth/ec2/(route-tables)/describe-route-tables/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(Z7)},head:()=>({meta:[{title:`Route Tables | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-route-tables.js`),__vite__mapDeps([70,17,7,18,10,12])),`component`)}),xIe=L(`/_auth/ec2/(placement-groups)/describe-placement-groups/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(t9)},head:()=>({meta:[{title:`Placement Groups | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-placement-groups.js`),__vite__mapDeps([71,17,7,18,10,11])),`component`)}),SIe=L(`/_auth/ec2/(nat-gateways)/describe-nat-gateways/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(s9)},head:()=>({meta:[{title:`NAT Gateways | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-nat-gateways.js`),__vite__mapDeps([72,17,7,18,10,11])),`component`)}),CIe=L(`/_auth/ec2/(load-balancers)/describe-load-balancers/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(h9)},head:()=>({meta:[{title:`Load Balancers | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-load-balancers.js`),__vite__mapDeps([73,17,7,10,11])),`component`)}),wIe=L(`/_auth/ec2/(key)/describe-key-pairs/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(H7)},head:()=>({meta:[{title:`Key Pairs | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-key-pairs.js`),__vite__mapDeps([74,17,7,18,10])),`component`)}),TIe=L(`/_auth/ec2/(internet-gateways)/describe-internet-gateways/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(Y7)},head:()=>({meta:[{title:`Internet Gateways | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-internet-gateways.js`),__vite__mapDeps([75,17,7,18,10])),`component`)}),EIe=L(`/_auth/ec2/(instances)/describe-instances/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(L7)},head:()=>({meta:[{title:`Instances | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-instances.js`),__vite__mapDeps([76,17,7,18,10,11])),`component`)}),DIe=L(`/_auth/ec2/(images)/describe-images/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(B7)},head:()=>({meta:[{title:`Images | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-images.js`),__vite__mapDeps([77,17,7,18,10,11,78])),`component`)}),OIe=L(`/_auth/ec2/(elastic-ips)/describe-addresses/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(a9)},head:()=>({meta:[{title:`Elastic IPs | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./describe-addresses.js`),__vite__mapDeps([79,17,7,18,10])),`component`)}),j9=L(`/_auth/s3/ls/$bucket/$`)({loader:async({context:e,params:t})=>{let n=fl(t._splat??``);await e.queryClient.ensureQueryData(f9(t.bucket,n))},head:({params:e})=>({meta:[{title:`${pl(e._splat??``,`${e.bucket}/`)} | S3 | Mulga`}]}),component:R(()=>H(()=>import(`./_.js`),__vite__mapDeps([80,20,21,60,54,42,61,53,17,7,22])),`component`)}),M9=L(`/_auth/iam/(users)/list-users/$userName`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(T9(t.userName)),e.queryClient.ensureQueryData(E9(t.userName)),e.queryClient.ensureQueryData(A9(t.userName)),e.queryClient.ensureQueryData(D9)])},head:({params:e})=>({meta:[{title:`${e.userName} | IAM | Mulga`}]}),component:R(()=>H(()=>import(`./_userName.js`),__vite__mapDeps([81,29,21,53,17,7,22,24,42,55,61,82,10,11,26])),`component`)}),N9=L(`/_auth/iam/(policies)/list-policies/$policyArn`)({loader:async({context:e,params:t})=>{let n=decodeURIComponent(t.policyArn),r=await e.queryClient.ensureQueryData(O9(n)),i=r.Policy?.DefaultVersionId;return i&&await e.queryClient.ensureQueryData(k9(n,i)),r},head:({loaderData:e})=>({meta:[{title:`${e?.Policy?.PolicyName??`Policy`} | IAM | Mulga`}]}),component:R(()=>H(()=>import(`./_policyArn.js`),__vite__mapDeps([83,29,21,17,7,22,42,61,53,82,10])),`component`)}),P9=L(`/_auth/ec2/(vpc)/describe-vpcs/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(e9(t.id)),e.queryClient.ensureQueryData(G7)])},head:({params:e})=>({meta:[{title:`${e.id} | VPC | Mulga`}]}),component:R(()=>H(()=>import(`./_id16.js`),__vite__mapDeps([84,34,21,17,7,22,42,61,53,82,10,11])),`component`)}),F9=L(`/_auth/ec2/(volumes)/modify-volume/$id`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(u9(t.id))},head:({params:e})=>({meta:[{title:`Resize ${e.id} | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./_id15.js`),__vite__mapDeps([85,34,21,2,17,7,22,27,10,4,36])),`component`)}),I9=L(`/_auth/ec2/(volumes)/describe-volumes/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(u9(t.id)),e.queryClient.ensureQueryData(L7)])},head:({params:e})=>({meta:[{title:`${e.id} | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./_id14.js`),__vite__mapDeps([86,34,21,53,35,24,8,17,7,22,42,61,82,10,11,4])),`component`)}),L9=L(`/_auth/ec2/(target-groups)/describe-target-groups/$id`)({loader:async({context:e,params:t})=>{let n=decodeURIComponent(t.id);await Promise.all([e.queryClient.ensureQueryData(y9(n)),e.queryClient.ensureQueryData(b9(n)),e.queryClient.ensureQueryData(S9([n])),e.queryClient.ensureQueryData(L7)])},head:({params:e})=>({meta:[{title:`${decodeURIComponent(e.id)} | Target Group | Mulga`}]}),component:R(()=>H(()=>import(`./_id13.js`),__vite__mapDeps([87,39,21,53,88,35,24,8,4,6,7,17,22,42,61,82,10])),`component`)}),R9=L(`/_auth/ec2/(subnet)/describe-subnets/$id`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(K7(t.id))},head:({params:e})=>({meta:[{title:`${e.id} | Subnet | Mulga`}]}),component:R(()=>H(()=>import(`./_id12.js`),__vite__mapDeps([89,34,21,17,7,22,42,61,53,82,10,11])),`component`)}),z9=L(`/_auth/ec2/(snapshots)/describe-snapshots/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(J7(t.id)),e.queryClient.ensureQueryData(W7)])},head:({params:e})=>({meta:[{title:`${e.id} | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./_id11.js`),__vite__mapDeps([90,34,21,53,17,7,22,25,42,61,82,10,11,4])),`component`)}),B9=L(`/_auth/ec2/(security-groups)/describe-security-groups/$id`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(i9(t.id))},head:({params:e})=>({meta:[{title:`${e.id} | Security Group | Mulga`}]}),component:R(()=>H(()=>import(`./_id10.js`),__vite__mapDeps([91,34,21,53,35,24,8,2,17,7,22,41,42,61,82,10,4,36])),`component`)}),V9=L(`/_auth/ec2/(route-tables)/describe-route-tables/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(Q7(t.id)),e.queryClient.ensureQueryData(Y7),e.queryClient.ensureQueryData(s9),e.queryClient.ensureQueryData(G7)])},head:({params:e})=>({meta:[{title:`${e.id} | Route Table | Mulga`}]}),component:R(()=>H(()=>import(`./_id9.js`),__vite__mapDeps([92,34,21,35,24,8,2,17,7,22,42,61,53,82,10,12,4,36])),`component`)}),H9=L(`/_auth/ec2/(placement-groups)/describe-placement-groups/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(n9(t.id)),e.queryClient.ensureQueryData(L7)])},head:({params:e})=>({meta:[{title:`${e.id} | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./_id8.js`),__vite__mapDeps([93,34,21,17,7,22,42,61,53,82,10,11])),`component`)}),U9=L(`/_auth/ec2/(nat-gateways)/describe-nat-gateways/$id`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(c9(t.id))},head:({params:e})=>({meta:[{title:`${e.id} | NAT Gateway | Mulga`}]}),component:R(()=>H(()=>import(`./_id7.js`),__vite__mapDeps([94,34,21,17,7,22,42,61,53,82,10,11])),`component`)}),W9=L(`/_auth/ec2/(load-balancers)/describe-load-balancers/$id`)({loader:async({context:e,params:t})=>{let n=decodeURIComponent(t.id);await Promise.all([e.queryClient.ensureQueryData(g9(n)),e.queryClient.ensureQueryData(x9(n)),e.queryClient.ensureQueryData(_9(n)),e.queryClient.ensureQueryData(S9([n])),e.queryClient.ensureQueryData(v9),e.queryClient.ensureQueryData(G7)])},head:({params:e})=>({meta:[{title:`${decodeURIComponent(e.id)} | Load Balancer | Mulga`}]}),component:R(()=>H(()=>import(`./_id6.js`),__vite__mapDeps([95,39,21,53,35,24,8,88,4,2,17,7,22,42,61,82,10,11,43])),`component`)}),G9=L(`/_auth/ec2/(key)/describe-key-pairs/$id`)({loader:async({context:e,params:t})=>await e.queryClient.ensureQueryData(U7(t.id)),head:({loaderData:e})=>({meta:[{title:`${e?.KeyPairs?.[0]?.KeyName??`Key Pair`} | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./_id5.js`),__vite__mapDeps([96,34,21,17,7,22,42,61,53,82,10])),`component`)}),K9=L(`/_auth/ec2/(internet-gateways)/describe-internet-gateways/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(X7(t.id)),e.queryClient.ensureQueryData($7)])},head:({params:e})=>({meta:[{title:`${e.id} | Internet Gateway | Mulga`}]}),component:R(()=>H(()=>import(`./_id4.js`),__vite__mapDeps([97,34,21,35,24,8,17,7,22,42,61,53,82,10])),`component`)}),q9=L(`/_auth/ec2/(instances)/describe-instances/$id`)({loader:async({context:e,params:t})=>{let[n]=await Promise.all([e.queryClient.ensureQueryData(R7(t.id)),e.queryClient.ensureQueryData(z7)]),r=n.Reservations?.[0]?.Instances?.[0]?.ImageId;return await e.queryClient.ensureQueryData(V7(r)),n},head:({loaderData:e})=>({meta:[{title:`${e?.Reservations?.[0]?.Instances?.[0]?.InstanceId??`Instance`} | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./_id3.js`),__vite__mapDeps([98,34,21,53,35,24,8,6,7,17,22,42,61,82,10,11,4,32,13,99])),`component`)}),J9=L(`/_auth/ec2/(images)/describe-images/$id`)({loader:async({context:e,params:t})=>await e.queryClient.ensureQueryData(V7(t.id)),head:({loaderData:e})=>({meta:[{title:`${e?.Images?.[0]?.Name??`Image`} | EC2 | Mulga`}]}),component:R(()=>H(()=>import(`./_id2.js`),__vite__mapDeps([100,17,7,22,10,11,78,99,82])),`component`)}),Y9=L(`/_auth/ec2/(elastic-ips)/describe-addresses/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(o9(t.id)),e.queryClient.ensureQueryData(L7)])},head:({params:e})=>({meta:[{title:`${e.id} | Elastic IP | Mulga`}]}),component:R(()=>H(()=>import(`./_id.js`),__vite__mapDeps([101,34,21,35,24,8,17,7,22,42,61,53,82,10])),`component`)}),kIe=F7.update({id:`/login`,path:`/login`,getParentRoute:()=>P7}),$=VFe.update({id:`/_auth`,getParentRoute:()=>P7}),AIe=HFe.update({id:`/`,path:`/`,getParentRoute:()=>$}),jIe=UFe.update({id:`/nodes`,path:`/nodes`,getParentRoute:()=>$}),MIe=WFe.update({id:`/s3/service-metrics`,path:`/s3/service-metrics`,getParentRoute:()=>$}),NIe=GFe.update({id:`/s3/ls/`,path:`/s3/ls/`,getParentRoute:()=>$}),PIe=KFe.update({id:`/s3/(buckets)/create-bucket`,path:`/s3/create-bucket`,getParentRoute:()=>$}),FIe=qFe.update({id:`/iam/(users)/create-user`,path:`/iam/create-user`,getParentRoute:()=>$}),IIe=JFe.update({id:`/iam/(policies)/create-policy`,path:`/iam/create-policy`,getParentRoute:()=>$}),LIe=YFe.update({id:`/ec2/(vpc)/create-vpc`,path:`/ec2/create-vpc`,getParentRoute:()=>$}),RIe=XFe.update({id:`/ec2/(volumes)/create-volume`,path:`/ec2/create-volume`,getParentRoute:()=>$}),zIe=ZFe.update({id:`/ec2/(target-groups)/create-target-group`,path:`/ec2/create-target-group`,getParentRoute:()=>$}),BIe=QFe.update({id:`/ec2/(subnet)/create-subnet`,path:`/ec2/create-subnet`,getParentRoute:()=>$}),VIe=p9.update({id:`/ec2/(snapshots)/create-snapshot`,path:`/ec2/create-snapshot`,getParentRoute:()=>$}),HIe=$Fe.update({id:`/ec2/(security-groups)/create-security-group`,path:`/ec2/create-security-group`,getParentRoute:()=>$}),UIe=eIe.update({id:`/ec2/(route-tables)/create-route-table`,path:`/ec2/create-route-table`,getParentRoute:()=>$}),WIe=tIe.update({id:`/ec2/(placement-groups)/create-placement-group`,path:`/ec2/create-placement-group`,getParentRoute:()=>$}),GIe=nIe.update({id:`/ec2/(nat-gateways)/create-nat-gateway`,path:`/ec2/create-nat-gateway`,getParentRoute:()=>$}),KIe=aIe.update({id:`/ec2/(load-balancers)/create-load-balancer`,path:`/ec2/create-load-balancer`,getParentRoute:()=>$}),qIe=oIe.update({id:`/ec2/(key)/import-key-pair`,path:`/ec2/import-key-pair`,getParentRoute:()=>$}),JIe=sIe.update({id:`/ec2/(key)/create-key-pair`,path:`/ec2/create-key-pair`,getParentRoute:()=>$}),YIe=cIe.update({id:`/ec2/(internet-gateways)/create-internet-gateway`,path:`/ec2/create-internet-gateway`,getParentRoute:()=>$}),XIe=lIe.update({id:`/ec2/(instances)/run-instances`,path:`/ec2/run-instances`,getParentRoute:()=>$}),ZIe=uIe.update({id:`/ec2/(elastic-ips)/allocate-address`,path:`/ec2/allocate-address`,getParentRoute:()=>$}),X9=dIe.update({id:`/s3/ls/$bucket`,path:`/s3/ls/$bucket`,getParentRoute:()=>$}),QIe=C9.update({id:`/`,path:`/`,getParentRoute:()=>X9}),$Ie=fIe.update({id:`/iam/(users)/list-users/`,path:`/iam/list-users/`,getParentRoute:()=>$}),eLe=pIe.update({id:`/iam/(policies)/list-policies/`,path:`/iam/list-policies/`,getParentRoute:()=>$}),tLe=mIe.update({id:`/ec2/(vpc)/describe-vpcs/`,path:`/ec2/describe-vpcs/`,getParentRoute:()=>$}),nLe=hIe.update({id:`/ec2/(volumes)/describe-volumes/`,path:`/ec2/describe-volumes/`,getParentRoute:()=>$}),rLe=gIe.update({id:`/ec2/(target-groups)/describe-target-groups/`,path:`/ec2/describe-target-groups/`,getParentRoute:()=>$}),iLe=_Ie.update({id:`/ec2/(subnet)/describe-subnets/`,path:`/ec2/describe-subnets/`,getParentRoute:()=>$}),aLe=vIe.update({id:`/ec2/(snapshots)/describe-snapshots/`,path:`/ec2/describe-snapshots/`,getParentRoute:()=>$}),oLe=yIe.update({id:`/ec2/(security-groups)/describe-security-groups/`,path:`/ec2/describe-security-groups/`,getParentRoute:()=>$}),sLe=bIe.update({id:`/ec2/(route-tables)/describe-route-tables/`,path:`/ec2/describe-route-tables/`,getParentRoute:()=>$}),cLe=xIe.update({id:`/ec2/(placement-groups)/describe-placement-groups/`,path:`/ec2/describe-placement-groups/`,getParentRoute:()=>$}),lLe=SIe.update({id:`/ec2/(nat-gateways)/describe-nat-gateways/`,path:`/ec2/describe-nat-gateways/`,getParentRoute:()=>$}),uLe=CIe.update({id:`/ec2/(load-balancers)/describe-load-balancers/`,path:`/ec2/describe-load-balancers/`,getParentRoute:()=>$}),dLe=wIe.update({id:`/ec2/(key)/describe-key-pairs/`,path:`/ec2/describe-key-pairs/`,getParentRoute:()=>$}),fLe=TIe.update({id:`/ec2/(internet-gateways)/describe-internet-gateways/`,path:`/ec2/describe-internet-gateways/`,getParentRoute:()=>$}),pLe=EIe.update({id:`/ec2/(instances)/describe-instances/`,path:`/ec2/describe-instances/`,getParentRoute:()=>$}),mLe=DIe.update({id:`/ec2/(images)/describe-images/`,path:`/ec2/describe-images/`,getParentRoute:()=>$}),hLe=OIe.update({id:`/ec2/(elastic-ips)/describe-addresses/`,path:`/ec2/describe-addresses/`,getParentRoute:()=>$}),gLe=j9.update({id:`/$`,path:`/$`,getParentRoute:()=>X9}),_Le=M9.update({id:`/iam/(users)/list-users/$userName`,path:`/iam/list-users/$userName`,getParentRoute:()=>$}),vLe=N9.update({id:`/iam/(policies)/list-policies/$policyArn`,path:`/iam/list-policies/$policyArn`,getParentRoute:()=>$}),yLe=P9.update({id:`/ec2/(vpc)/describe-vpcs/$id`,path:`/ec2/describe-vpcs/$id`,getParentRoute:()=>$}),bLe=F9.update({id:`/ec2/(volumes)/modify-volume/$id`,path:`/ec2/modify-volume/$id`,getParentRoute:()=>$}),xLe=I9.update({id:`/ec2/(volumes)/describe-volumes/$id`,path:`/ec2/describe-volumes/$id`,getParentRoute:()=>$}),SLe=L9.update({id:`/ec2/(target-groups)/describe-target-groups/$id`,path:`/ec2/describe-target-groups/$id`,getParentRoute:()=>$}),CLe=R9.update({id:`/ec2/(subnet)/describe-subnets/$id`,path:`/ec2/describe-subnets/$id`,getParentRoute:()=>$}),wLe=z9.update({id:`/ec2/(snapshots)/describe-snapshots/$id`,path:`/ec2/describe-snapshots/$id`,getParentRoute:()=>$}),Z9=B9.update({id:`/ec2/(security-groups)/describe-security-groups/$id`,path:`/ec2/describe-security-groups/$id`,getParentRoute:()=>$}),TLe=V9.update({id:`/ec2/(route-tables)/describe-route-tables/$id`,path:`/ec2/describe-route-tables/$id`,getParentRoute:()=>$}),ELe=H9.update({id:`/ec2/(placement-groups)/describe-placement-groups/$id`,path:`/ec2/describe-placement-groups/$id`,getParentRoute:()=>$}),DLe=U9.update({id:`/ec2/(nat-gateways)/describe-nat-gateways/$id`,path:`/ec2/describe-nat-gateways/$id`,getParentRoute:()=>$}),OLe=W9.update({id:`/ec2/(load-balancers)/describe-load-balancers/$id`,path:`/ec2/describe-load-balancers/$id`,getParentRoute:()=>$}),kLe=G9.update({id:`/ec2/(key)/describe-key-pairs/$id`,path:`/ec2/describe-key-pairs/$id`,getParentRoute:()=>$}),ALe=K9.update({id:`/ec2/(internet-gateways)/describe-internet-gateways/$id`,path:`/ec2/describe-internet-gateways/$id`,getParentRoute:()=>$}),jLe=q9.update({id:`/ec2/(instances)/describe-instances/$id`,path:`/ec2/describe-instances/$id`,getParentRoute:()=>$}),MLe=J9.update({id:`/ec2/(images)/describe-images/$id`,path:`/ec2/describe-images/$id`,getParentRoute:()=>$}),NLe=Y9.update({id:`/ec2/(elastic-ips)/describe-addresses/$id`,path:`/ec2/describe-addresses/$id`,getParentRoute:()=>$}),PLe={AuthS3LsBucketSplatRoute:gLe,AuthS3LsBucketIndexRoute:QIe},FLe={AuthNodesRoute:jIe,AuthIndexRoute:AIe,AuthS3ServiceMetricsRoute:MIe,AuthS3LsBucketRouteRoute:X9._addFileChildren(PLe),AuthEc2elasticIpsAllocateAddressRoute:ZIe,AuthEc2instancesRunInstancesRoute:XIe,AuthEc2internetGatewaysCreateInternetGatewayRoute:YIe,AuthEc2keyCreateKeyPairRoute:JIe,AuthEc2keyImportKeyPairRoute:qIe,AuthEc2loadBalancersCreateLoadBalancerRoute:KIe,AuthEc2natGatewaysCreateNatGatewayRoute:GIe,AuthEc2placementGroupsCreatePlacementGroupRoute:WIe,AuthEc2routeTablesCreateRouteTableRoute:UIe,AuthEc2securityGroupsCreateSecurityGroupRoute:HIe,AuthEc2snapshotsCreateSnapshotRoute:VIe,AuthEc2subnetCreateSubnetRoute:BIe,AuthEc2targetGroupsCreateTargetGroupRoute:zIe,AuthEc2volumesCreateVolumeRoute:RIe,AuthEc2vpcCreateVpcRoute:LIe,AuthIampoliciesCreatePolicyRoute:IIe,AuthIamusersCreateUserRoute:FIe,AuthS3bucketsCreateBucketRoute:PIe,AuthS3LsIndexRoute:NIe,AuthEc2elasticIpsDescribeAddressesIdRoute:NLe,AuthEc2imagesDescribeImagesIdRoute:MLe,AuthEc2instancesDescribeInstancesIdRoute:jLe,AuthEc2internetGatewaysDescribeInternetGatewaysIdRoute:ALe,AuthEc2keyDescribeKeyPairsIdRoute:kLe,AuthEc2loadBalancersDescribeLoadBalancersIdRoute:OLe,AuthEc2natGatewaysDescribeNatGatewaysIdRoute:DLe,AuthEc2placementGroupsDescribePlacementGroupsIdRoute:ELe,AuthEc2routeTablesDescribeRouteTablesIdRoute:TLe,AuthEc2securityGroupsDescribeSecurityGroupsIdRoute:Z9,AuthEc2snapshotsDescribeSnapshotsIdRoute:wLe,AuthEc2subnetDescribeSubnetsIdRoute:CLe,AuthEc2targetGroupsDescribeTargetGroupsIdRoute:SLe,AuthEc2volumesDescribeVolumesIdRoute:xLe,AuthEc2volumesModifyVolumeIdRoute:bLe,AuthEc2vpcDescribeVpcsIdRoute:yLe,AuthIampoliciesListPoliciesPolicyArnRoute:vLe,AuthIamusersListUsersUserNameRoute:_Le,AuthEc2elasticIpsDescribeAddressesIndexRoute:hLe,AuthEc2imagesDescribeImagesIndexRoute:mLe,AuthEc2instancesDescribeInstancesIndexRoute:pLe,AuthEc2internetGatewaysDescribeInternetGatewaysIndexRoute:fLe,AuthEc2keyDescribeKeyPairsIndexRoute:dLe,AuthEc2loadBalancersDescribeLoadBalancersIndexRoute:uLe,AuthEc2natGatewaysDescribeNatGatewaysIndexRoute:lLe,AuthEc2placementGroupsDescribePlacementGroupsIndexRoute:cLe,AuthEc2routeTablesDescribeRouteTablesIndexRoute:sLe,AuthEc2securityGroupsDescribeSecurityGroupsIndexRoute:oLe,AuthEc2snapshotsDescribeSnapshotsIndexRoute:aLe,AuthEc2subnetDescribeSubnetsIndexRoute:iLe,AuthEc2targetGroupsDescribeTargetGroupsIndexRoute:rLe,AuthEc2volumesDescribeVolumesIndexRoute:nLe,AuthEc2vpcDescribeVpcsIndexRoute:tLe,AuthIampoliciesListPoliciesIndexRoute:eLe,AuthIamusersListUsersIndexRoute:$Ie},ILe={AuthRouteRoute:$._addFileChildren(FLe),LoginRoute:kIe},LLe=P7._addFileChildren(ILe)._addFileTypes(),Q9=IFe(),RLe=Ji({routeTree:LLe,context:{queryClient:Q9},defaultPreload:`intent`,scrollRestoration:!0,defaultStructuralSharing:!0,defaultPreloadStaleTime:0}),$9=document.querySelector(`#app`);$9&&!$9.innerHTML&&ua.createRoot($9).render((0,F.jsx)(P.StrictMode,{children:(0,F.jsx)(ma,{defaultTheme:`dark`,storageKey:`spinifex-ui-theme`,children:(0,F.jsx)(Ie,{client:Q9,children:(0,F.jsx)(WS,{children:(0,F.jsx)(Bg,{children:(0,F.jsx)(Zi,{router:RLe})})})})})}));export{c9 as $,rl as $a,Zd as $i,ghe as $n,b as $o,pg as $r,rwe as $t,_9 as A,pu as Aa,Mp as Ai,jme as An,Ga as Ao,cx as Ar,UPe as At,d9 as B,Jl as Ba,Pf as Bi,Vme as Bn,I as Bo,fre as Br,KOe as Bt,O9 as C,qu as Ca,Wh as Ci,wme as Cn,So as Co,Rx as Cr,NPe as Ct,C9 as D,Wu as Da,Um as Di,Ome as Dn,Za as Do,yx as Dr,RPe as Dt,w9 as E,ed as Ea,qm as Ei,Dme as En,ro as Eo,vx as Er,LPe as Et,y9 as F,iu as Fa,ip as Fi,Ime as Fn,la as Fo,rx as Fr,Eke as Ft,B7 as G,jl as Ga,xf as Gi,che as Gn,xe as Go,Q_ as Gr,ZOe as Gt,a9 as H,Xl as Ha,Tf as Hi,ahe as Hn,Ne as Ho,Tre as Hr,JOe as Ht,v9 as I,Ql as Ia,Wf as Ii,Lme as In,Ui as Io,Lb as Ir,D4 as It,L7 as J,Cl as Ja,ff as Ji,dhe as Jn,ce as Jo,Fg as Jr,QCe as Jt,R7 as K,Al as Ka,bf as Ki,lhe as Kn,N as Ko,G_ as Kr,ake as Kt,iIe as L,$l as La,zf as Li,Rme as Ln,Di as Lo,Nb as Lr,Cke as Lt,h9 as M,du as Ma,hp as Mi,Nme as Mn,Fa as Mo,ex as Mr,F4 as Mt,S9 as N,cu as Na,gp as Ni,Pme as Nn,Na as No,ix as Nr,Dke as Nt,rIe as O,gu as Oa,Nm as Oi,kme as On,Ka as Oo,ux as Or,VPe as Ot,b9 as P,ou as Pa,dp as Pi,Fme as Pn,va as Po,nx as Pr,O4 as Pt,H7 as Q,pl as Qa,ef as Qi,hhe as Qn,k as Qo,hg as Qr,nwe as Qt,p9 as R,eu as Ra,Bf as Ri,zme as Rn,di as Ro,hre as Rr,WOe as Rt,D9 as S,Ju as Sa,Uh as Si,Cme as Sn,To as So,Ux as Sr,MPe as St,T9 as T,Ku as Ta,oh as Ti,Eme as Tn,co as To,_x as Tr,FPe as Tt,I7 as U,Ll as Ua,wf as Ui,ohe as Un,je as Uo,Eb as Ur,YOe as Ut,o9 as V,Yl as Va,Ef as Vi,ihe as Vn,Fe as Vo,Pb as Vr,qOe as Vt,V7 as W,Ml as Wa,Sf as Wi,she as Wn,Te as Wo,X_ as Wr,XOe as Wt,Y7 as X,gl as Xa,cf as Xi,phe as Xn,_ as Xo,kg as Xr,ewe as Xt,X7 as Y,xl as Ya,lf as Yi,fhe as Yn,w as Yo,Ng as Yr,$Ce as Yt,U7 as Z,hl as Za,af as Zi,mhe as Zn,h as Zo,_g as Zr,twe as Zt,N9 as _,Zu as _a,Ph as _i,vme as _n,zo as _o,VS as _r,k7 as _t,G9 as a,rf as aa,Zh as ai,_we as an,ll as ao,OD as ar,u as as,Z7 as at,E9 as b,$u as ba,Ih as bi,xme as bn,ko as bo,Gx as br,D7 as bt,H9 as c,Vd as ca,Jh as ci,a1 as cn,ws as co,sD as cr,c as cs,J7 as ct,z9 as d,Fd as da,Rh as di,fme as dn,ys as do,Ew as dr,G7 as dt,Yd as ea,dg as ei,iwe as en,fl as eo,_he as er,y as es,s9 as et,R9 as f,Rd as fa,jh as fi,pme as fn,Jo as fo,LC as fr,u9 as ft,P9 as g,Ed as ga,Nh as gi,_me as gn,Vo as go,GS as gr,F7 as gt,F9 as h,kd as ha,Bh as hi,gme as hn,Wo as ho,yC as hr,$7 as ht,K9 as i,Gd as ia,Qh as ii,gwe as in,ol as io,RD as ir,f as is,Q7 as it,g9 as j,mu as ja,Tp as ji,Mme as jn,Ia as jo,sx as jr,p6 as jt,x9 as k,hu as ka,gm as ki,Ame as kn,qa as ko,lx as kr,HPe as kt,V9 as l,Bd as la,zh as li,J$ as ln,Ts as lo,oD as lr,q7 as lt,I9 as m,Nd as ma,kh as mi,hme as mn,Go as mo,xC as mr,e9 as mt,J9 as n,Wd as na,lg as ni,owe as nn,al as no,yhe as nr,oe as ns,t9 as nt,W9 as o,Ud as oa,Xh as oi,vwe as on,dl as oo,bD as or,l as os,i9 as ot,L9 as p,Md as pa,Mh as pi,mme as pn,Qo as po,CC as pr,l9 as pt,z7 as q,Ol as qa,df as qi,uhe as qn,M as qo,Ig as qr,b2 as qt,q9 as r,Jd as ra,sg as ri,hwe as rn,cl as ro,bhe as rr,v as rs,W7 as rt,U9 as s,Hd as sa,Yh as si,ywe as sn,Ds as so,lD as sr,o as ss,r9 as st,Y9 as t,$d as ta,ug as ti,awe as tn,ml as to,vhe as tr,D as ts,n9 as tt,B9 as u,zd as ua,Ah as ui,dme as un,Cs as uo,BE as ur,K7 as ut,M9 as v,Yu as va,Fh as vi,yme as vn,Ro as vo,FS as vr,T7 as vt,k9 as w,nd as wa,mh as wi,Tme as wn,yo as wo,Ix as wr,PPe as wt,A9 as x,Xu as xa,Lh as xi,Sme as xn,Eo as xo,Wx as xr,O7 as xt,j9 as y,Qu as ya,Vh as yi,bme as yn,Fo as yo,_S as yr,E7 as yt,f9 as z,Zl as za,If as zi,Bme as zn,yn as zo,jb as zr,GOe as zt}; \ No newline at end of file +`+e.stack}}var ye=Object.prototype.hasOwnProperty,be=t.unstable_scheduleCallback,xe=t.unstable_cancelCallback,Se=t.unstable_shouldYield,Ce=t.unstable_requestPaint,we=t.unstable_now,Te=t.unstable_getCurrentPriorityLevel,Ee=t.unstable_ImmediatePriority,De=t.unstable_UserBlockingPriority,Oe=t.unstable_NormalPriority,ke=t.unstable_LowPriority,Ae=t.unstable_IdlePriority,je=t.log,Me=t.unstable_setDisableYieldValue,Ne=null,Pe=null;function Fe(e){if(typeof je==`function`&&Me(e),Pe&&typeof Pe.setStrictMode==`function`)try{Pe.setStrictMode(Ne,e)}catch{}}var Ie=Math.clz32?Math.clz32:R,Le=Math.log,Re=Math.LN2;function R(e){return e>>>=0,e===0?32:31-(Le(e)/Re|0)|0}var ze=256,Be=262144,Ve=4194304;function He(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ue(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=He(n))):i=He(o):i=He(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=He(n))):i=He(o)):i=He(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function We(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Ge(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Ke(){var e=Ve;return Ve<<=1,!(Ve&62914560)&&(Ve=4194304),e}function qe(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function Je(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Ye(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),cn=!1;if(sn)try{var B={};Object.defineProperty(B,"passive",{get:function(){cn=!0}}),window.addEventListener(`test`,B,B),window.removeEventListener(`test`,B,B)}catch{cn=!1}var ln=null,un=null,dn=null;function fn(){if(dn)return dn;var e,t=un,n=t.length,r,i=`value`in ln?ln.value:ln.textContent,a=i.length;for(e=0;e=Hn),Gn=` `,Kn=!1;function qn(e,t){switch(e){case`keyup`:return Bn.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function Jn(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var Yn=!1;function Xn(e,t){switch(e){case`compositionend`:return Jn(t);case`keypress`:return t.which===32?(Kn=!0,Gn):null;case`textInput`:return e=t.data,e===Gn&&Kn?null:e;default:return null}}function Zn(e,t){if(Yn)return e===`compositionend`||!Vn&&qn(e,t)?(e=fn(),dn=un=ln=null,Yn=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=yr(n)}}function xr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?xr(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Sr(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Pt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=Pt(e.document)}return t}function Cr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var wr=sn&&`documentMode`in document&&11>=document.documentMode,Tr=null,Er=null,Dr=null,Or=!1;function kr(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Or||Tr==null||Tr!==Pt(r)||(r=Tr,`selectionStart`in r&&Cr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Dr&&vr(Dr,r)||(Dr=r,r=Dd(Er,`onSelect`),0>=o,i-=o,yi=1<<32-Ie(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),G&&xi(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(i,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(i,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(i,h),G&&xi(i,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(i,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return G&&xi(i,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,i,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(i,e)}),G&&xi(i,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===_&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case h:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===_){if(r.tag===7){n(e,r.sibling),c=i(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===E&&xa(l)===r.type){n(e,r.sibling),c=i(r,o.props),Oa(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===_?(c=oi(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=ai(o.type,o.key,o.props,null,e.mode,c),Oa(c,o),c.return=e,e=c)}return s(e);case g:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=i(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=li(o,e.mode,c),c.return=e,e=c}return s(e);case E:return o=xa(o),b(e,r,o,c)}if(ee(o))return v(e,r,o,c);if(A(o)){if(l=A(o),typeof l!=`function`)throw Error(a(150));return o=l.call(o),y(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,Da(o),c);if(o.$$typeof===x)return b(e,r,Yi(e,o),c);ka(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=i(r,o),c.return=e,e=c):(n(e,r),c=si(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{Ea=0;var i=b(e,t,n,r);return Ta=null,i}catch(t){if(t===ha||t===_a)throw t;var a=ti(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var ja=Aa(!0),Ma=Aa(!1),Na=!1;function Pa(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Fa(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Ia(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function La(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,Il&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=Qr(e),Zr(e,null,n),t}return Jr(e,r,t,n),Qr(e)}function Ra(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ze(e,n)}}function za(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Ba=!1;function Va(){if(Ba){var e=oa;if(e!==null)throw e}}function K(e,t,n,r){Ba=!1;var i=e.updateQueue;Na=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,m=f!==s.lane;if(m?(Rl&f)===f:(r&f)===f){f!==0&&f===aa&&(Ba=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=p({},d,f);break a;case 2:Na=!0}}f=s.callback,f!==null&&(e.flags|=64,m&&(e.flags|=8192),m=i.callbacks,m===null?i.callbacks=[f]:m.push(f))}else m={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=m,c=d):u=u.next=m,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;m=s,s=m.next,m.next=null,i.lastBaseUpdate=m,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),Kl|=o,e.lanes=o,e.memoizedState=d}}function Ha(e,t){if(typeof e!=`function`)throw Error(a(191,e));e.call(t)}function q(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=N.T,s={};N.T=s,ks(e,!1,t,n);try{var c=i(),l=N.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Os(e,t,la(c,r),pu(e)):Os(e,t,r,pu(e))}catch(n){Os(e,t,{then:function(){},status:`rejected`,reason:n},pu())}finally{P.p=a,o!==null&&s.types!==null&&(o.types=s.types),N.T=o}}function vs(){}function ys(e,t,n,r){if(e.tag!==5)throw Error(a(476));var i=bs(e).queue;_s(e,i,t,te,n===null?vs:function(){return xs(e),n(r)})}function bs(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:te,baseState:te,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ao,lastRenderedState:te},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ao,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function xs(e){var t=bs(e);t.next===null&&(t=e.alternate.memoizedState),Os(e,t.next.queue,{},pu())}function Ss(){return Ji($f)}function Cs(){return To().memoizedState}function ws(){return To().memoizedState}function Ts(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=pu();e=Ia(n);var r=La(t,e,n);r!==null&&(hu(r,t,n),Ra(r,t,n)),t={cache:ta()},e.payload=t;return}t=t.return}}function Es(e,t,n){var r=pu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},As(e)?js(t,n):(n=Yr(e,t,n,r),n!==null&&(hu(n,e,r),Ms(n,t,r)))}function Ds(e,t,n){Os(e,t,n,pu())}function Os(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(As(e))js(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,_r(s,o))return Jr(e,t,i,0),Ll===null&&qr(),!1}catch{}if(n=Yr(e,t,i,r),n!==null)return hu(n,e,r),Ms(n,t,r),!0}return!1}function ks(e,t,n,r){if(r={lane:2,revertLane:fd(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},As(e)){if(t)throw Error(a(479))}else t=Yr(e,n,r,2),t!==null&&hu(t,e,2)}function As(e){var t=e.alternate;return e===J||t!==null&&t===J}function js(e,t){so=oo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ms(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ze(e,n)}}var Ns={readContext:Ji,use:Oo,useCallback:ho,useContext:ho,useEffect:ho,useImperativeHandle:ho,useLayoutEffect:ho,useInsertionEffect:ho,useMemo:ho,useReducer:ho,useRef:ho,useState:ho,useDebugValue:ho,useDeferredValue:ho,useTransition:ho,useSyncExternalStore:ho,useId:ho,useHostTransitionStatus:ho,useFormState:ho,useActionState:ho,useOptimistic:ho,useMemoCache:ho,useCacheRefresh:ho};Ns.useEffectEvent=ho;var Ps={readContext:Ji,use:Oo,useCallback:function(e,t){return wo().memoizedState=[e,t===void 0?null:t],e},useContext:Ji,useEffect:is,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),ns(4194308,4,us.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ns(4194308,4,e,t)},useInsertionEffect:function(e,t){ns(4,2,e,t)},useMemo:function(e,t){var n=wo();t=t===void 0?null:t;var r=e();if(co){Fe(!0);try{e()}finally{Fe(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=wo();if(n!==void 0){var i=n(t);if(co){Fe(!0);try{n(t)}finally{Fe(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=Es.bind(null,J,e),[r.memoizedState,e]},useRef:function(e){var t=wo();return e={current:e},t.memoizedState=e},useState:function(e){e=Bo(e);var t=e.queue,n=Ds.bind(null,J,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:fs,useDeferredValue:function(e,t){return hs(wo(),e,t)},useTransition:function(){var e=Bo(!1);return e=_s.bind(null,J,e.queue,!0,!1),wo().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=J,i=wo();if(G){if(n===void 0)throw Error(a(407));n=n()}else{if(n=t(),Ll===null)throw Error(a(349));Rl&127||Fo(r,t,n)}i.memoizedState=n;var o={value:n,getSnapshot:t};return i.queue=o,is(Lo.bind(null,r,o,e),[e]),r.flags|=2048,es(9,{destroy:void 0},Io.bind(null,r,o,n,t),null),n},useId:function(){var e=wo(),t=Ll.identifierPrefix;if(G){var n=bi,r=yi;n=(r&~(1<<32-Ie(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=lo++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(i,{is:r.is}):s.createElement(i)}}o[rt]=t,o[it]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(Id(o,i,r),i){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Oc(t)}}return Nc(t),kc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Oc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(a(166));if(e=I.current,Pi(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,i=Ei,i!==null)switch(i.tag){case 27:case 5:r=i.memoizedProps}e[rt]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Nd(e.nodeValue,n)),e||ji(t,!0)}else e=Hd(e).createTextNode(r),e[rt]=t,t.stateNode=e}return Nc(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=Pi(t),n!==null){if(e===null){if(!r)throw Error(a(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(a(557));e[rt]=t}else Fi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Nc(t),e=!1}else n=Ii(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(eo(t),t):(eo(t),null);if(t.flags&128)throw Error(a(558))}return Nc(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(i=Pi(t),r!==null&&r.dehydrated!==null){if(e===null){if(!i)throw Error(a(318));if(i=t.memoizedState,i=i===null?null:i.dehydrated,!i)throw Error(a(317));i[rt]=t}else Fi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Nc(t),i=!1}else i=Ii(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=i),i=!0;if(!i)return t.flags&256?(eo(t),t):(eo(t),null)}return eo(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,i=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(i=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==i&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),jc(t,t.updateQueue),Nc(t),null);case 4:return ue(),e===null&&Cd(t.stateNode.containerInfo),Nc(t),null;case 10:return Hi(t.type),Nc(t),null;case 19:if(ae(to),r=t.memoizedState,r===null)return Nc(t),null;if(i=(t.flags&128)!=0,o=r.rendering,o===null)if(i)Mc(r,!1);else{if(Gl!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=no(e),o!==null){for(t.flags|=128,Mc(r,!1),e=o.updateQueue,t.updateQueue=e,jc(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)ii(n,e),n=n.sibling;return F(to,to.current&1|2),G&&xi(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&we()>X&&(t.flags|=128,i=!0,Mc(r,!1),t.lanes=4194304)}else{if(!i)if(e=no(o),e!==null){if(t.flags|=128,i=!0,e=e.updateQueue,t.updateQueue=e,jc(t,e),Mc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!G)return Nc(t),null}else 2*we()-r.renderingStartTime>X&&n!==536870912&&(t.flags|=128,i=!0,Mc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(Nc(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=we(),e.sibling=null,n=to.current,F(to,i?n&1|2:n&1),G&&xi(t,r.treeForkCount),e);case 22:case 23:return eo(t),qa(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(Nc(t),t.subtreeFlags&6&&(t.flags|=8192)):Nc(t),n=t.updateQueue,n!==null&&jc(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&ae(da),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),Hi(ea),Nc(t),null;case 25:return null;case 30:return null}throw Error(a(156,t.tag))}function Fc(e,t){switch(wi(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Hi(ea),ue(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return fe(t),null;case 31:if(t.memoizedState!==null){if(eo(t),t.alternate===null)throw Error(a(340));Fi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(eo(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(a(340));Fi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return ae(to),null;case 4:return ue(),null;case 10:return Hi(t.type),null;case 22:case 23:return eo(t),qa(),e!==null&&ae(da),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return Hi(ea),null;case 25:return null;default:return null}}function Ic(e,t){switch(wi(t),t.tag){case 3:Hi(ea),ue();break;case 26:case 27:case 5:fe(t);break;case 4:ue();break;case 31:t.memoizedState!==null&&eo(t);break;case 13:eo(t);break;case 19:ae(to);break;case 10:Hi(t.type);break;case 22:case 23:eo(t),qa(),e!==null&&ae(da);break;case 24:Hi(ea)}}function Lc(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){Gu(t,t.return,e)}}function Rc(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){Gu(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){Gu(t,t.return,e)}}function zc(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{q(t,n)}catch(t){Gu(e,e.return,t)}}}function Bc(e,t,n){n.props=Vs(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){Gu(e,t,n)}}function Vc(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){Gu(e,t,n)}}function Hc(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){Gu(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){Gu(e,t,n)}else n.current=null}function Uc(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){Gu(e,e.return,t)}}function Wc(e,t,n){try{var r=e.stateNode;Ld(r,e.type,n,t),r[it]=t}catch(t){Gu(e,e.return,t)}}function Gc(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&$d(e.type)||e.tag===4}function Kc(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||Gc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&$d(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function qc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Zt));else if(r!==4&&(r===27&&$d(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(qc(e,t,n),e=e.sibling;e!==null;)qc(e,t,n),e=e.sibling}function Jc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&$d(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(Jc(e,t,n),e=e.sibling;e!==null;)Jc(e,t,n),e=e.sibling}function Yc(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);Id(t,r,n),t[rt]=e,t[it]=n}catch(t){Gu(e,e.return,t)}}var Xc=!1,Zc=!1,Qc=!1,$c=typeof WeakSet==`function`?WeakSet:Set,el=null;function tl(e,t){if(e=e.containerInfo,Bd=cp,e=Sr(e),Cr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var i=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||i!==0&&f.nodeType!==3||(c=s+i),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===i&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(Vd={focusedElem:e,selectionRange:n},cp=!1,el=t;el!==null;)if(t=el,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,el=e;else for(;el!==null;){switch(t=el,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),Id(o,r,n),o[rt]=e,_t(o),r=o;break a;case`link`:var s=Hf(`link`,`href`,i).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=br(s,h),v=br(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,N.T=null,n=lu,lu=null;var o=au,s=su;if(iu=0,ou=au=null,su=0,Il&6)throw Error(a(331));var c=Il;if(Il|=4,jl(o.current),Cl(o,o.current,s,n),Il=c,ad(0,!1),Pe&&typeof Pe.onPostCommitFiberRoot==`function`)try{Pe.onPostCommitFiberRoot(Ne,o)}catch{}return!0}finally{P.p=i,N.T=r,Vu(e,t)}}function Wu(e,t,n){t=di(n,t),t=qs(e.stateNode,t,2),e=La(e,t,2),e!==null&&(Je(e,2),id(e))}function Gu(e,t,n){if(e.tag===3)Wu(e,e,n);else for(;t!==null;){if(t.tag===3){Wu(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(ru===null||!ru.has(r))){e=di(n,e),n=Js(2),r=La(t,n,2),r!==null&&(Ys(n,r,t,e),Je(r,2),id(r));break}}t=t.return}}function Ku(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new Fl;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(Ul=!0,i.add(n),e=qu.bind(null,e,t,n),t.then(e,e))}function qu(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,Ll===e&&(Rl&n)===n&&(Gl===4||Gl===3&&(Rl&62914560)===Rl&&300>we()-eu?!(Il&2)&&Su(e,0):Jl|=n,Xl===Rl&&(Xl=0)),id(e)}function Ju(e,t){t===0&&(t=Ke()),e=Xr(e,t),e!==null&&(Je(e,t),id(e))}function Yu(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Ju(e,n)}function Xu(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,i=e.memoizedState;i!==null&&(n=i.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(a(314))}r!==null&&r.delete(t),Ju(e,n)}function Zu(e,t){return be(e,t)}var Qu=null,$u=null,ed=!1,td=!1,nd=!1,rd=0;function id(e){e!==$u&&e.next===null&&($u===null?Qu=$u=e:$u=$u.next=e),td=!0,ed||(ed=!0,dd())}function ad(e,t){if(!nd&&td){nd=!0;do for(var n=!1,r=Qu;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Ie(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,ud(r,a))}else a=Rl,a=Ue(r,r===Ll?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||We(r,a)||(n=!0,ud(r,a));r=r.next}while(n);nd=!1}}function od(){sd()}function sd(){td=ed=!1;var e=0;rd!==0&&qd()&&(e=rd);for(var t=we(),n=null,r=Qu;r!==null;){var i=r.next,a=cd(r,t);a===0?(r.next=null,n===null?Qu=i:n.next=i,i===null&&($u=n)):(n=r,(e!==0||a&3)&&(td=!0)),r=i}iu!==0&&iu!==5||ad(e,!1),rd!==0&&(rd=0)}function cd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&Rd(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function Sf(e,t,n){var r=xf;if(r&&typeof t==`string`&&t){var i=It(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),gf.has(i)||(gf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),Id(t,`link`,e),_t(t),r.head.appendChild(t)))}}function Cf(e){vf.D(e),Sf(`dns-prefetch`,e,null)}function wf(e,t){vf.C(e,t),Sf(`preconnect`,e,t)}function Tf(e,t,n){vf.L(e,t,n);var r=xf;if(r&&e&&t){var i=`link[rel="preload"][as="`+It(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+It(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+It(n.imageSizes)+`"]`)):i+=`[href="`+It(e)+`"]`;var a=i;switch(t){case`style`:a=jf(e);break;case`script`:a=Ff(e)}hf.has(a)||(e=p({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),hf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(Mf(a))||t===`script`&&r.querySelector(If(a))||(t=r.createElement(`link`),Id(t,`link`,e),_t(t),r.head.appendChild(t)))}}function Ef(e,t){vf.m(e,t);var n=xf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+It(r)+`"][href="`+It(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=Ff(e)}if(!hf.has(a)&&(e=p({rel:`modulepreload`,href:e},t),hf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(If(a)))return}r=n.createElement(`link`),Id(r,`link`,e),_t(r),n.head.appendChild(r)}}}function Df(e,t,n){vf.S(e,t,n);var r=xf;if(r&&e){var i=gt(r).hoistableStyles,a=jf(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(Mf(a)))s.loading=5;else{e=p({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=hf.get(a))&&zf(e,n);var c=o=r.createElement(`link`);_t(c),Id(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Rf(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Of(e,t){vf.X(e,t);var n=xf;if(n&&e){var r=gt(n).hoistableScripts,i=Ff(e),a=r.get(i);a||(a=n.querySelector(If(i)),a||(e=p({src:e,async:!0},t),(t=hf.get(i))&&Bf(e,t),a=n.createElement(`script`),_t(a),Id(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function kf(e,t){vf.M(e,t);var n=xf;if(n&&e){var r=gt(n).hoistableScripts,i=Ff(e),a=r.get(i);a||(a=n.querySelector(If(i)),a||(e=p({src:e,async:!0,type:`module`},t),(t=hf.get(i))&&Bf(e,t),a=n.createElement(`script`),_t(a),Id(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Af(e,t,n,r){var i=(i=I.current)?_f(i):null;if(!i)throw Error(a(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=jf(n.href),n=gt(i).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=jf(n.href);var o=gt(i).hoistableStyles,s=o.get(e);if(s||(i=i.ownerDocument||i,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=i.querySelector(Mf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),hf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},hf.set(e,n),o||Pf(i,e,n,s.state))),t&&r===null)throw Error(a(528,``));return s}if(t&&r!==null)throw Error(a(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=Ff(n),n=gt(i).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(a(444,e))}}function jf(e){return`href="`+It(e)+`"`}function Mf(e){return`link[rel="stylesheet"][`+e+`]`}function Nf(e){return p({},e,{"data-precedence":e.precedence,precedence:null})}function Pf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),Id(t,`link`,n),_t(t),e.head.appendChild(t))}function Ff(e){return`[src="`+It(e)+`"]`}function If(e){return`script[async]`+e}function Lf(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+It(n.href)+`"]`);if(r)return t.instance=r,_t(r),r;var i=p({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),_t(r),Id(r,`style`,i),Rf(r,n.precedence,e),t.instance=r;case`stylesheet`:i=jf(n.href);var o=e.querySelector(Mf(i));if(o)return t.state.loading|=4,t.instance=o,_t(o),o;r=Nf(n),(i=hf.get(i))&&zf(r,i),o=(e.ownerDocument||e).createElement(`link`),_t(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),Id(o,`link`,r),t.state.loading|=4,Rf(o,n.precedence,e),t.instance=o;case`script`:return o=Ff(n.src),(i=e.querySelector(If(o)))?(t.instance=i,_t(i),i):(r=n,(i=hf.get(o))&&(r=p({},n),Bf(r,i)),e=e.ownerDocument||e,i=e.createElement(`script`),_t(i),Id(i,`link`,r),e.head.appendChild(i),t.instance=i);case`void`:return null;default:throw Error(a(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Rf(r,n.precedence,e));return t.instance}function Rf(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function Wf(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function Gf(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function Kf(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=jf(r.href),a=t.querySelector(Mf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=Yf.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,_t(a);return}a=t.ownerDocument||t,r=Nf(r),(i=hf.get(i))&&zf(r,i),a=a.createElement(`link`),_t(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),Id(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=Yf.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var qf=0;function Jf(e,t){return e.stylesheets&&e.count===0&&Zf(e,e.stylesheets),0qf?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function Yf(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Zf(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Xf=null;function Zf(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Xf=new Map,t.forEach(Qf,e),Xf=null,Yf.call(e))}function Qf(e,t){if(!(t.state.loading&4)){var n=Xf.get(e);if(n)var r=n.get(null);else{n=new Map,Xf.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=mo()}))(),1),go=i(),_o=(0,U.createContext)(void 0);function vo(e,t){try{let t=localStorage.getItem(e);if(t===`light`||t===`dark`||t===`system`)return t}catch{}return t}function yo(e){let t=(0,go.c)(14),{children:n,defaultTheme:r,storageKey:i}=e,a=r===void 0?`dark`:r,o=i===void 0?`ui-theme`:i,s;t[0]!==a||t[1]!==o?(s=()=>vo(o,a),t[0]=a,t[1]=o,t[2]=s):s=t[2];let[c,l]=(0,U.useState)(s),u,d;t[3]===c?(u=t[4],d=t[5]):(u=()=>{let e=window.document.documentElement;if(e.classList.remove(`light`,`dark`),c===`system`){let t=window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`;e.classList.add(t);return}e.classList.add(c)},d=[c],t[3]=c,t[4]=u,t[5]=d),(0,U.useEffect)(u,d);let f;t[6]===o?f=t[7]:(f=e=>{try{localStorage.setItem(o,e)}catch{}l(e)},t[6]=o,t[7]=f);let p;t[8]!==f||t[9]!==c?(p={theme:c,setTheme:f},t[8]=f,t[9]=c,t[10]=p):p=t[10];let m=p,h;return t[11]!==n||t[12]!==m?(h=(0,W.jsx)(_o.Provider,{value:m,children:n}),t[11]=n,t[12]=m,t[13]=h):h=t[13],h}function bo(){let e=(0,U.useContext)(_o);if(e===void 0)throw Error(`useTheme must be used within a ThemeProvider`);return e}function xo(e){return Ft(e.defaultTagName??`div`,e,e)}var So=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),Co=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),wo=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),To=e=>{let t=wo(e);return t.charAt(0).toUpperCase()+t.slice(1)},Eo={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`},Do=e=>{for(let t in e)if(t.startsWith(`aria-`)||t===`role`||t===`title`)return!0;return!1},Oo=(0,U.createContext)({}),ko=()=>(0,U.useContext)(Oo),Ao=(0,U.forwardRef)(({color:e,size:t,strokeWidth:n,absoluteStrokeWidth:r,className:i=``,children:a,iconNode:o,...s},c)=>{let{size:l=24,strokeWidth:u=2,absoluteStrokeWidth:d=!1,color:f=`currentColor`,className:p=``}=ko()??{},m=r??d?Number(n??u)*24/Number(t??l):n??u;return(0,U.createElement)(`svg`,{ref:c,...Eo,width:t??l??Eo.width,height:t??l??Eo.height,stroke:e??f,strokeWidth:m,className:So(`lucide`,p,i),...!a&&!Do(s)&&{"aria-hidden":`true`},...s},[...o.map(([e,t])=>(0,U.createElement)(e,t)),...Array.isArray(a)?a:[a]])}),jo=(e,t)=>{let n=(0,U.forwardRef)(({className:n,...r},i)=>(0,U.createElement)(Ao,{ref:i,iconNode:t,className:So(`lucide-${Co(To(e))}`,`lucide-${e}`,n),...r}));return n.displayName=To(e),n},Mo=jo(`activity`,[[`path`,{d:`M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2`,key:`169zse`}]]),No=jo(`book-open`,[[`path`,{d:`M12 7v14`,key:`1akyts`}],[`path`,{d:`M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z`,key:`ruj8y`}]]),Po=jo(`boxes`,[[`path`,{d:`M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z`,key:`lc1i9w`}],[`path`,{d:`m7 16.5-4.74-2.85`,key:`1o9zyk`}],[`path`,{d:`m7 16.5 5-3`,key:`va8pkn`}],[`path`,{d:`M7 16.5v5.17`,key:`jnp8gn`}],[`path`,{d:`M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z`,key:`8zsnat`}],[`path`,{d:`m17 16.5-5-3`,key:`8arw3v`}],[`path`,{d:`m17 16.5 4.74-2.85`,key:`8rfmw`}],[`path`,{d:`M17 16.5v5.17`,key:`k6z78m`}],[`path`,{d:`M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z`,key:`1xygjf`}],[`path`,{d:`M12 8 7.26 5.15`,key:`1vbdud`}],[`path`,{d:`m12 8 4.74-2.85`,key:`3rx089`}],[`path`,{d:`M12 13.5V8`,key:`1io7kd`}]]),Fo=jo(`camera`,[[`path`,{d:`M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z`,key:`18u6gg`}],[`circle`,{cx:`12`,cy:`13`,r:`3`,key:`1vg3eu`}]]),Io=jo(`circle-alert`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`12`,x2:`12`,y1:`8`,y2:`12`,key:`1pkeuh`}],[`line`,{x1:`12`,x2:`12.01`,y1:`16`,y2:`16`,key:`4dfq90`}]]),Lo=jo(`crosshair`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`22`,x2:`18`,y1:`12`,y2:`12`,key:`l9bcsi`}],[`line`,{x1:`6`,x2:`2`,y1:`12`,y2:`12`,key:`13hhkx`}],[`line`,{x1:`12`,x2:`12`,y1:`6`,y2:`2`,key:`10w3f3`}],[`line`,{x1:`12`,x2:`12`,y1:`22`,y2:`18`,key:`15g9kq`}]]),Ro=jo(`globe`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20`,key:`13o1zl`}],[`path`,{d:`M2 12h20`,key:`9i4pu4`}]]),zo=jo(`hard-drive`,[[`path`,{d:`M10 16h.01`,key:`1bzywj`}],[`path`,{d:`M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`18tbho`}],[`path`,{d:`M21.946 12.013H2.054`,key:`zqlbp7`}],[`path`,{d:`M6 16h.01`,key:`1pmjb7`}]]),Bo=jo(`house`,[[`path`,{d:`M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8`,key:`5wwlr5`}],[`path`,{d:`M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z`,key:`r6nss1`}]]),Vo=jo(`id-card`,[[`path`,{d:`M16 10h2`,key:`8sgtl7`}],[`path`,{d:`M16 14h2`,key:`epxaof`}],[`path`,{d:`M6.17 15a3 3 0 0 1 5.66 0`,key:`n6f512`}],[`circle`,{cx:`9`,cy:`11`,r:`2`,key:`yxgjnd`}],[`rect`,{x:`2`,y:`5`,width:`20`,height:`14`,rx:`2`,key:`qneu4z`}]]),Ho=jo(`image`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,ry:`2`,key:`1m3agn`}],[`circle`,{cx:`9`,cy:`9`,r:`2`,key:`af1f0g`}],[`path`,{d:`m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21`,key:`1xmnt7`}]]),Uo=jo(`key`,[[`path`,{d:`m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4`,key:`g0fldk`}],[`path`,{d:`m21 2-9.6 9.6`,key:`1j0ho8`}],[`circle`,{cx:`7.5`,cy:`15.5`,r:`5.5`,key:`yqb3hr`}]]),Wo=jo(`layers`,[[`path`,{d:`M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z`,key:`zw3jo`}],[`path`,{d:`M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12`,key:`1wduqc`}],[`path`,{d:`M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17`,key:`kqbvx6`}]]),Go=jo(`layout-grid`,[[`rect`,{width:`7`,height:`7`,x:`3`,y:`3`,rx:`1`,key:`1g98yp`}],[`rect`,{width:`7`,height:`7`,x:`14`,y:`3`,rx:`1`,key:`6d4xhi`}],[`rect`,{width:`7`,height:`7`,x:`14`,y:`14`,rx:`1`,key:`nxv5o0`}],[`rect`,{width:`7`,height:`7`,x:`3`,y:`14`,rx:`1`,key:`1bb6yr`}]]),Ko=jo(`log-out`,[[`path`,{d:`m16 17 5-5-5-5`,key:`1bji2h`}],[`path`,{d:`M21 12H9`,key:`dn1m92`}],[`path`,{d:`M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4`,key:`1uf3rs`}]]),qo=jo(`map-pin`,[[`path`,{d:`M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0`,key:`1r0f0z`}],[`circle`,{cx:`12`,cy:`10`,r:`3`,key:`ilqhr7`}]]),Jo=jo(`moon`,[[`path`,{d:`M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401`,key:`kfwtm`}]]),Yo=jo(`network`,[[`rect`,{x:`16`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`4q2zg0`}],[`rect`,{x:`2`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`8cvhb9`}],[`rect`,{x:`9`,y:`2`,width:`6`,height:`6`,rx:`1`,key:`1egb70`}],[`path`,{d:`M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3`,key:`1jsf9p`}],[`path`,{d:`M12 12V8`,key:`2874zd`}]]),Xo=jo(`panel-left`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}]]),Zo=jo(`router`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,key:`w68u3i`}],[`path`,{d:`M6.01 18H6`,key:`19vcac`}],[`path`,{d:`M10.01 18H10`,key:`uamcmx`}],[`path`,{d:`M15 10v4`,key:`qjz1xs`}],[`path`,{d:`M17.84 7.17a4 4 0 0 0-5.66 0`,key:`1rif40`}],[`path`,{d:`M20.66 4.34a8 8 0 0 0-11.31 0`,key:`6a5xfq`}]]),Qo=jo(`route`,[[`circle`,{cx:`6`,cy:`19`,r:`3`,key:`1kj8tv`}],[`path`,{d:`M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15`,key:`1d8sl`}],[`circle`,{cx:`18`,cy:`5`,r:`3`,key:`gq8acd`}]]),$o=jo(`server`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`2`,rx:`2`,ry:`2`,key:`ngkwjq`}],[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,ry:`2`,key:`iecqi9`}],[`line`,{x1:`6`,x2:`6.01`,y1:`6`,y2:`6`,key:`16zg32`}],[`line`,{x1:`6`,x2:`6.01`,y1:`18`,y2:`18`,key:`nzw8ys`}]]),es=jo(`shield-check`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),ts=jo(`shield`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}]]),ns=jo(`sun`,[[`circle`,{cx:`12`,cy:`12`,r:`4`,key:`4exip2`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`m4.93 4.93 1.41 1.41`,key:`149t6j`}],[`path`,{d:`m17.66 17.66 1.41 1.41`,key:`ptbguv`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`m6.34 17.66-1.41 1.41`,key:`1m8zz5`}],[`path`,{d:`m19.07 4.93-1.41 1.41`,key:`1shlcs`}]]),rs=jo(`user-cog`,[[`path`,{d:`M10 15H6a4 4 0 0 0-4 4v2`,key:`1nfge6`}],[`path`,{d:`m14.305 16.53.923-.382`,key:`1itpsq`}],[`path`,{d:`m15.228 13.852-.923-.383`,key:`eplpkm`}],[`path`,{d:`m16.852 12.228-.383-.923`,key:`13v3q0`}],[`path`,{d:`m16.852 17.772-.383.924`,key:`1i8mnm`}],[`path`,{d:`m19.148 12.228.383-.923`,key:`1q8j1v`}],[`path`,{d:`m19.53 18.696-.382-.924`,key:`vk1qj3`}],[`path`,{d:`m20.772 13.852.924-.383`,key:`n880s0`}],[`path`,{d:`m20.772 16.148.924.383`,key:`1g6xey`}],[`circle`,{cx:`18`,cy:`15`,r:`3`,key:`gjjjvw`}],[`circle`,{cx:`9`,cy:`7`,r:`4`,key:`nufk8`}]]),is=jo(`users`,[[`path`,{d:`M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2`,key:`1yyitq`}],[`path`,{d:`M16 3.128a4 4 0 0 1 0 7.744`,key:`16gr8j`}],[`path`,{d:`M22 21v-2a4 4 0 0 0-3-3.87`,key:`kshegd`}],[`circle`,{cx:`9`,cy:`7`,r:`4`,key:`nufk8`}]]),as=jo(`waypoints`,[[`path`,{d:`m10.586 5.414-5.172 5.172`,key:`4mc350`}],[`path`,{d:`m18.586 13.414-5.172 5.172`,key:`8c96vv`}],[`path`,{d:`M6 12h12`,key:`8npq4p`}],[`circle`,{cx:`12`,cy:`20`,r:`2`,key:`144qzu`}],[`circle`,{cx:`12`,cy:`4`,r:`2`,key:`muu5ef`}],[`circle`,{cx:`20`,cy:`12`,r:`2`,key:`1xzzfp`}],[`circle`,{cx:`4`,cy:`12`,r:`2`,key:`1hvhnz`}]]),os=jo(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]),ss=function(e){return e.disabled=`data-disabled`,e.valid=`data-valid`,e.invalid=`data-invalid`,e.touched=`data-touched`,e.dirty=`data-dirty`,e.filled=`data-filled`,e.focused=`data-focused`,e}({}),cs={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},ls={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},us={disabled:!1,...ls},ds={valid(e){return e===null?null:e?{[ss.valid]:``}:{[ss.invalid]:``}}},fs={invalid:void 0,name:void 0,validityData:{state:cs,errors:[],error:``,value:``,initialValue:null},setValidityData:It,disabled:void 0,touched:ls.touched,setTouched:It,dirty:ls.dirty,setDirty:It,filled:ls.filled,setFilled:It,focused:ls.focused,setFocused:It,validate:()=>null,validationMode:`onSubmit`,validationDebounceTime:0,shouldValidateOnChange:()=>!1,state:us,markedDirtyRef:{current:!1},registerFieldControl:It,validation:{getValidationProps:(e=jt)=>e,getInputValidationProps:(e=jt)=>e,inputRef:{current:null},commit:async()=>{}}},ps=U.createContext(fs);function ms(e=!0){let t=U.useContext(ps);if(t.setValidityData===It&&!e)throw Error(zt(28));return t}var hs=U.createContext({controlId:void 0,registerControlId:It,labelId:void 0,setLabelId:It,messageIds:[],setMessageIds:It,getDescriptionProps:e=>e});function gs(){return U.useContext(hs)}function _s(e={}){let{id:t,implicit:n=!1,controlRef:r}=e,{controlId:i,registerControlId:a}=gs(),o=vn(t),s=n?i:void 0,c=Pt(()=>Symbol(`labelable-control`)),l=U.useRef(!1),u=U.useRef(t!=null),d=dn(()=>{!l.current||a===It||(l.current=!1,a(c.current,void 0))});return B(()=>{if(a===It)return;let e;if(n){let n=r?.current;e=V(n)&&n.closest(`label`)!=null?t??null:s??o}else if(t!=null)u.current=!0,e=t;else if(u.current)e=o;else{d();return}if(e===void 0){d();return}l.current=!0,a(c.current,e)},[t,r,s,a,n,o,c,d]),U.useEffect(()=>d,[d]),i??o}var vs=typeof navigator<`u`,ys=Os(),bs=As(),xs=ks(),Ss=typeof CSS>`u`||!CSS.supports?!1:CSS.supports(`-webkit-backdrop-filter:none`),Cs=ys.platform===`MacIntel`&&ys.maxTouchPoints>1?!0:/iP(hone|ad|od)|iOS/.test(ys.platform);vs&&/firefox/i.test(xs);var ws=vs&&/apple/i.test(navigator.vendor);vs&&/Edg/i.test(xs);var Ts=vs&&/android/i.test(bs)||/android/i.test(xs),Es=vs&&bs.toLowerCase().startsWith(`mac`)&&!navigator.maxTouchPoints,Ds=xs.includes(`jsdom/`);function Os(){if(!vs)return{platform:``,maxTouchPoints:-1};let e=navigator.userAgentData;return e?.platform?{platform:e.platform,maxTouchPoints:navigator.maxTouchPoints}:{platform:navigator.platform??``,maxTouchPoints:navigator.maxTouchPoints??-1}}function ks(){if(!vs)return``;let e=navigator.userAgentData;return e&&Array.isArray(e.brands)?e.brands.map(({brand:e,version:t})=>`${e}/${t}`).join(` `):navigator.userAgent}function As(){if(!vs)return``;let e=navigator.userAgentData;return e?.platform?e.platform:navigator.platform??``}var js=`data-base-ui-focusable`,Ms=`input:not([type='hidden']):not([disabled]),[contenteditable]:not([contenteditable='false']),textarea:not([disabled])`,Ns=`ArrowLeft`,Ps=`ArrowRight`,Fs=`ArrowUp`,Is=`ArrowDown`;function Ls(e,t){if(!V(e))return!1;let n=e;if(t.hasElement(n))return!n.hasAttribute(`data-trigger-disabled`);for(let[,e]of t.entries())if(H(e,n))return!e.hasAttribute(`data-trigger-disabled`);return!1}function Rs(e,t){if(t==null)return!1;if(`composedPath`in e)return e.composedPath().includes(t);let n=e;return n.target!=null&&t.contains(n.target)}function zs(e){return e.matches(`html,body`)}function Bs(e){return en(e)&&e.matches(`input:not([type='hidden']):not([disabled]),[contenteditable]:not([contenteditable='false']),textarea:not([disabled])`)}function Vs(e){return e?.closest(`button,a[href],[role="button"],select,[tabindex]:not([tabindex="-1"]),${Ms}`)!=null}function Hs(e){return e?e.getAttribute(`role`)===`combobox`&&Bs(e):!1}function Us(e){if(!e||Ds)return!0;try{return e.matches(`:focus-visible`)}catch{return!0}}function Ws(e){return e?e.hasAttribute(`data-base-ui-focusable`)?e:e.querySelector(`[data-base-ui-focusable]`)||e:null}function Gs(e,t,n=!0){return e.filter(e=>e.parentId===t).flatMap(t=>[...!n||t.context?.open?[t]:[],...Gs(e,t.id,n)])}function Ks(e,t){let n=[],r=e.find(e=>e.id===t)?.parentId;for(;r;){let t=e.find(e=>e.id===r);r=t?.parentId,t&&(n=n.concat(t))}return n}function qs(e){e.preventDefault(),e.stopPropagation()}function Js(e){return`nativeEvent`in e}function Ys(e){return e.pointerType===``&&e.isTrusted?!0:Ts&&e.pointerType?e.type===`click`&&e.buttons===1:e.detail===0&&!e.pointerType}function Xs(e){return Ds?!1:!Ts&&e.width===0&&e.height===0||Ts&&e.width===1&&e.height===1&&e.pressure===0&&e.detail===0&&e.pointerType===`mouse`||e.width<1&&e.height<1&&e.pressure===0&&e.detail===0&&e.pointerType===`touch`}function Zs(e,t){let n=[`mouse`,`pen`];return t||n.push(``,void 0),n.includes(e)}function Qs(e){let t=e.type;return t===`click`||t===`mousedown`||t===`keydown`||t===`keyup`}var $s=[`top`,`right`,`bottom`,`left`],ec=Math.min,tc=Math.max,nc=Math.round,rc=Math.floor,ic=e=>({x:e,y:e}),ac={left:`right`,right:`left`,bottom:`top`,top:`bottom`};function oc(e,t,n){return tc(e,ec(t,n))}function sc(e,t){return typeof e==`function`?e(t):e}function cc(e){return e.split(`-`)[0]}function lc(e){return e.split(`-`)[1]}function uc(e){return e===`x`?`y`:`x`}function dc(e){return e===`y`?`height`:`width`}function fc(e){let t=e[0];return t===`t`||t===`b`?`y`:`x`}function pc(e){return uc(fc(e))}function mc(e,t,n){n===void 0&&(n=!1);let r=lc(e),i=pc(e),a=dc(i),o=i===`x`?r===(n?`end`:`start`)?`right`:`left`:r===`start`?`bottom`:`top`;return t.reference[a]>t.floating[a]&&(o=Cc(o)),[o,Cc(o)]}function hc(e){let t=Cc(e);return[gc(e),t,gc(t)]}function gc(e){return e.includes(`start`)?e.replace(`start`,`end`):e.replace(`end`,`start`)}var _c=[`left`,`right`],vc=[`right`,`left`],yc=[`top`,`bottom`],bc=[`bottom`,`top`];function xc(e,t,n){switch(e){case`top`:case`bottom`:return n?t?vc:_c:t?_c:vc;case`left`:case`right`:return t?yc:bc;default:return[]}}function Sc(e,t,n,r){let i=lc(e),a=xc(cc(e),n===`start`,r);return i&&(a=a.map(e=>e+`-`+i),t&&(a=a.concat(a.map(gc)))),a}function Cc(e){let t=cc(e);return ac[t]+e.slice(t.length)}function wc(e){return{top:0,right:0,bottom:0,left:0,...e}}function Tc(e){return typeof e==`number`?{top:e,right:e,bottom:e,left:e}:wc(e)}function Ec(e){let{x:t,y:n,width:r,height:i}=e;return{width:r,height:i,top:n,left:t,right:t+r,bottom:n+i,x:t,y:n}}function Dc(e,t,n){return Math.floor(e/t)!==n}function Oc(e,t){return t<0||t>=e.length}function kc(e,t){return jc(e.current,{disabledIndices:t})}function Ac(e,t){return jc(e.current,{decrement:!0,startingIndex:e.current.length,disabledIndices:t})}function jc(e,{startingIndex:t=-1,decrement:n=!1,disabledIndices:r,amount:i=1}={}){let a=t;do a+=n?-i:i;while(a>=0&&a<=e.length-1&&Ic(e,a,r));return a}function Mc(e,{event:t,orientation:n,loopFocus:r,onLoop:i,rtl:a,cols:o,disabledIndices:s,minIndex:c,maxIndex:l,prevIndex:u,stopEvent:d=!1}){let f=u,p;if(t.key===`ArrowUp`?p=`up`:t.key===`ArrowDown`&&(p=`down`),p){let n=[],a=[],m=!1,h=0;{let t=null,r=-1;e.forEach((e,i)=>{if(e==null)return;h+=1;let o=e.closest(`[role="row"]`);o&&(m=!0),(o!==t||r===-1)&&(t=o,r+=1,n[r]=[]),n[r].push(i),a[i]=r})}let g=!1,_=0;if(m)for(let e of n){let t=e.length;t>_&&(_=t),t!==o&&(g=!0)}let v=g&&h{if(!g||u===-1)return;let c=a[u];if(c==null)return;let l=n[c].indexOf(u),d=o===`up`?-1:1;for(let o=c+d,f=0;f=n.length){if(!r||v)return;if(o=o<0?n.length-1:0,i){let e=Math.min(l,n[o].length-1);o=a[i(t,u,n[o][e]??n[o][0])]??o}}let c=n[o];for(let t=Math.min(l,c.length-1);t>=0;--t){let n=c[t];if(!Ic(e,n,s))return n}}},x=t=>{if(!v||u===-1)return;let n=u%y,i=t===`up`?-y:y,a=l-l%y,o=rc(l/y)+1;for(let t=u-n+i,c=0;cl){if(!r)return;t=t<0?a:0}let i=Math.min(t+y-1,l);for(let r=Math.min(t+n,i);r>=t;--r)if(!Ic(e,r,s))return r}};d&&qs(t);let S=b(p)??x(p);if(S!==void 0)f=S;else if(u===-1)f=p===`up`?l:c;else if(f=jc(e,{startingIndex:u,amount:y,decrement:p===`up`,disabledIndices:s}),r){if(p===`up`&&(u-ye?r:r-y,i&&(f=i(t,u,f))}p===`down`&&u+y>l&&(f=jc(e,{startingIndex:u%y-y,amount:y,disabledIndices:s}),i&&(f=i(t,u,f)))}Oc(e,f)&&(f=u)}if(n===`both`){let n=rc(u/o);t.key===(a?`ArrowLeft`:`ArrowRight`)&&(d&&qs(t),u%o===o-1?r&&(f=jc(e,{startingIndex:u-u%o-1,disabledIndices:s}),i&&(f=i(t,u,f))):(f=jc(e,{startingIndex:u,disabledIndices:s}),r&&Dc(f,o,n)&&(f=jc(e,{startingIndex:u-u%o-1,disabledIndices:s}),i&&(f=i(t,u,f)))),Dc(f,o,n)&&(f=u)),t.key===(a?`ArrowRight`:`ArrowLeft`)&&(d&&qs(t),u%o===0?r&&(f=jc(e,{startingIndex:u+(o-u%o),decrement:!0,disabledIndices:s}),i&&(f=i(t,u,f))):(f=jc(e,{startingIndex:u,decrement:!0,disabledIndices:s}),r&&Dc(f,o,n)&&(f=jc(e,{startingIndex:u+(o-u%o),decrement:!0,disabledIndices:s}),i&&(f=i(t,u,f)))),Dc(f,o,n)&&(f=u));let c=rc(l/o)===n;Oc(e,f)&&(r&&c?(f=t.key===(a?`ArrowRight`:`ArrowLeft`)?l:jc(e,{startingIndex:u-u%o-1,disabledIndices:s}),i&&(f=i(t,u,f))):f=u)}return f}function Nc(e,t,n){let r=[],i=0;return e.forEach(({width:e,height:a},o)=>{let s=!1;for(n&&(i=0);!s;){let n=[];for(let r=0;rr[e]==null)?(n.forEach(e=>{r[e]=o}),s=!0):i+=1}}),[...r]}function Pc(e,t,n,r,i){if(e===-1)return-1;let a=n.indexOf(e),o=t[e];switch(i){case`tl`:return a;case`tr`:return o?a+o.width-1:a;case`bl`:return o?a+(o.height-1)*r:a;case`br`:return n.lastIndexOf(e);default:return-1}}function Fc(e,t){return t.flatMap((t,n)=>e.includes(t)?[n]:[])}function Ic(e,t,n){if(typeof n==`function`?n(t):n?.includes(t)??!1)return!0;let r=e[t];return r?Rc(r)?!n&&(r.hasAttribute(`disabled`)||r.getAttribute(`aria-disabled`)===`true`):!0:!1}function Lc(e){return e.visibility===`hidden`||e.visibility===`collapse`}function Rc(e,t=e?on(e):null){return!e||!e.isConnected||!t||Lc(t)?!1:typeof e.checkVisibility==`function`?e.checkVisibility():t.display!==`none`&&t.display!==`contents`}var zc=`a[href],button,input,select,textarea,summary,details,iframe,object,embed,[tabindex],[contenteditable]:not([contenteditable="false"]),audio[controls],video[controls]`;function Bc(e){let t=e.assignedSlot;if(t)return t;if(e.parentElement)return e.parentElement;let n=e.getRootNode();return hn(n)?n.host:null}function Vc(e){for(let t of Array.from(e.children))if(ln(t)===`summary`)return t;return null}function Hc(e,t){let n=Vc(t);return!!n&&(e===n||H(n,e))}function Uc(e){let t=e?ln(e):``;return e!=null&&e.matches(zc)&&(t!==`summary`||e.parentElement!=null&&ln(e.parentElement)===`details`&&Vc(e.parentElement)===e)&&(t!==`details`||Vc(e)==null)&&(t!==`input`||e.type!==`hidden`)}function Wc(e){if(!Uc(e)||!e.isConnected||e.matches(`:disabled`))return!1;for(let t=e;t;t=Bc(t)){let n=t!==e,r=ln(t)===`slot`;if(t.hasAttribute(`inert`)||n&&ln(t)===`details`&&!t.open&&!Hc(e,t)||t.hasAttribute(`hidden`)||!r&&!Gc(t,n))return!1}return!0}function Gc(e,t){let n=on(e);return t?n.display!==`none`:Rc(e,n)}function Kc(e){let t=e.tabIndex;if(t<0){let t=ln(e);if(t===`details`||t===`audio`||t===`video`||en(e)&&e.isContentEditable)return 0}return t}function qc(e){if(ln(e)!==`input`)return null;let t=e;return t.type===`radio`&&t.name!==``?t:null}function Jc(e,t){let n=qc(e);if(!n)return!0;let r=t.find(e=>{let t=qc(e);return t?.name===n.name&&t.form===n.form&&t.checked});return r?r===n:t.find(e=>{let t=qc(e);return t?.name===n.name&&t.form===n.form})===n}function Yc(e){if(en(e)&&ln(e)===`slot`){let t=e.assignedElements({flatten:!0});if(t.length>0)return t}return en(e)&&e.shadowRoot?Array.from(e.shadowRoot.children):Array.from(e.children)}function Xc(e,t){Yc(e).forEach(e=>{Uc(e)&&t.push(e),Xc(e,t)})}function Zc(e,t,n){Yc(e).forEach(e=>{en(e)&&e.matches(t)&&n.push(e),Zc(e,t,n)})}function Qc(e){return Wc(e)&&Kc(e)>=0}function $c(e){let t=[];return Xc(e,t),t.filter(Wc)}function el(e){let t=$c(e);return t.filter(e=>Kc(e)>=0&&Jc(e,t))}function tl(e,t){let n=el(e),r=n.length;if(r===0)return;let i=jn(Mn(e)),a=n.indexOf(i);return n[a===-1?t===1?0:r-1:a+t]}function nl(e){return tl(Mn(e).body,1)||e}function rl(e){return tl(Mn(e).body,-1)||e}function il(e,t){let n=t||e.currentTarget,r=e.relatedTarget;return!r||!H(n,r)}function al(e){el(e).forEach(e=>{e.dataset.tabindex=e.getAttribute(`tabindex`)||``,e.setAttribute(`tabindex`,`-1`)})}function ol(e){let t=[];Zc(e,`[data-tabindex]`,t),t.forEach(e=>{let t=e.dataset.tabindex;delete e.dataset.tabindex,t?e.setAttribute(`tabindex`,t):e.removeAttribute(`tabindex`)})}var sl=[];function cl(e){U.useEffect(e,sl)}var ll=0,ul=class e{static create(){return new e}currentId=ll;start(e,t){this.clear(),this.currentId=setTimeout(()=>{this.currentId=ll,t()},e)}isStarted(){return this.currentId!==ll}clear=()=>{this.currentId!==ll&&(clearTimeout(this.currentId),this.currentId=ll)};disposeEffect=()=>this.clear};function dl(){let e=Pt(ul.create).current;return cl(e.disposeEffect),e}var fl=null;globalThis.requestAnimationFrame;var pl=new class{callbacks=[];callbacksCount=0;nextId=1;startId=1;isScheduled=!1;tick=e=>{this.isScheduled=!1;let t=this.callbacks,n=this.callbacksCount;if(this.callbacks=[],this.callbacksCount=0,this.startId=this.nextId,n>0)for(let n=0;n=this.callbacks.length||(this.callbacks[t]=null,--this.callbacksCount)}},ml=class e{static create(){return new e}static request(e){return pl.request(e)}static cancel(e){return pl.cancel(e)}currentId=fl;request(e){this.cancel(),this.currentId=pl.request(()=>{this.currentId=fl,e()})}cancel=()=>{this.currentId!==fl&&(pl.cancel(this.currentId),this.currentId=fl)};disposeEffect=()=>this.cancel};function hl(){let e=Pt(ml.create).current;return cl(e.disposeEffect),e}function gl(e){return e==null?e:`current`in e?e.current:e}var _l=function(e){return e.startingStyle=`data-starting-style`,e.endingStyle=`data-ending-style`,e}({}),vl={[_l.startingStyle]:``},yl={[_l.endingStyle]:``},bl={transitionStatus(e){return e===`starting`?vl:e===`ending`?yl:null}},xl=n(dt());function Sl(e,t=!1,n=!0){let r=hl();return dn((i,a=null)=>{r.cancel();let o=gl(e);if(o==null)return;let s=o,c=()=>{xl.flushSync(i)};if(typeof s.getAnimations!=`function`||globalThis.BASE_UI_ANIMATIONS_DISABLED){i();return}function l(){Promise.all(s.getAnimations().map(e=>e.finished)).then(()=>{a?.aborted||c()}).catch(()=>{if(n){a?.aborted||c();return}let e=s.getAnimations();!a?.aborted&&e.length>0&&e.some(e=>e.pending||e.playState!==`finished`)&&l()})}if(t){let e=_l.startingStyle;if(!s.hasAttribute(e)){r.request(l);return}let t=new MutationObserver(()=>{s.hasAttribute(e)||(t.disconnect(),l())});t.observe(s,{attributes:!0,attributeFilter:[e]}),a?.addEventListener(`abort`,()=>t.disconnect(),{once:!0});return}r.request(l)})}function Cl(e){let{enabled:t=!0,open:n,ref:r,onComplete:i}=e,a=dn(i),o=Sl(r,n,!1);U.useEffect(()=>{if(!t)return;let e=new AbortController;return o(a,e.signal),()=>{e.abort()}},[t,n,a,o])}function wl(e,t=!1,n=!1){let[r,i]=U.useState(e&&t?`idle`:void 0),[a,o]=U.useState(e);return e&&!a&&(o(!0),i(`starting`)),!e&&a&&r!==`ending`&&!n&&i(`ending`),!e&&!a&&r===`ending`&&i(void 0),B(()=>{if(!e&&a&&r!==`ending`&&n){let e=ml.request(()=>{i(`ending`)});return()=>{ml.cancel(e)}}},[e,a,r,n]),B(()=>{if(!e||t)return;let n=ml.request(()=>{i(void 0)});return()=>{ml.cancel(n)}},[t,e]),B(()=>{if(!e||!t)return;e&&a&&r!==`idle`&&i(`starting`);let n=ml.request(()=>{i(`idle`)});return()=>{ml.cancel(n)}},[t,e,a,r]),{mounted:a,setMounted:o,transitionStatus:r}}function Tl({controlled:e,default:t,name:n,state:r=`value`}){let{current:i}=U.useRef(e!==void 0),[a,o]=U.useState(t);return[i?e:a,U.useCallback(e=>{i||o(e)},[])]}function El(e,t,n,r,i=!0){let{registerFieldControl:a}=ms(),o=U.useRef(null);o.current||=Symbol(),B(()=>{let s=o.current;if(!(!s||!i))return a(s,{controlRef:e,getValue:r,id:t,value:n}),()=>{a(s,void 0)}},[e,i,r,t,a,n])}var Dl=U.forwardRef(function(e,t){let{render:n,className:r,id:i,name:a,value:o,disabled:s=!1,onValueChange:c,defaultValue:l,autoFocus:u=!1,style:d,...f}=e,{state:p,name:m,disabled:h,setTouched:g,setDirty:_,validityData:v,setFocused:y,setFilled:b,validationMode:x,validation:S}=ms(),C=h||s,w=m??a,T={...p,disabled:C},{labelId:E}=gs(),D=_s({id:i});B(()=>{let e=o!=null;S.inputRef.current?.value||e&&o!==``?b(!0):e&&o===``&&b(!1)},[S.inputRef,b,o]);let O=U.useRef(null);B(()=>{u&&O.current===jn(Mn(O.current))&&y(!0)},[u,y]);let[k]=Tl({controlled:o,default:l,name:`FieldControl`,state:`value`}),A=o!==void 0,j=A?k:void 0,M=dn(()=>S.inputRef.current?.value);return El(S.inputRef,D,j,M),Ft(`input`,e,{ref:[t,O],state:T,props:[{id:D,disabled:C,name:w,ref:S.inputRef,"aria-labelledby":E,autoFocus:u,...A?{value:j}:{defaultValue:l},onChange(e){let t=e.currentTarget.value;c?.(t,An(xn,e.nativeEvent)),_(t!==v.initialValue),b(t!==``)},onFocus(){y(!0)},onBlur(e){g(!0),y(!1),x===`onBlur`&&S.commit(e.currentTarget.value)},onKeyDown(e){e.currentTarget.tagName===`INPUT`&&e.key===`Enter`&&(g(!0),S.commit(e.currentTarget.value))}},S.getInputValidationProps(),f],stateAttributesMapping:ds})}),Ol=U.forwardRef(function(e,t){return(0,W.jsx)(Dl,{ref:t,...e})});function kl(e){let t=(0,go.c)(10),n,r,i;t[0]===e?(n=t[1],r=t[2],i=t[3]):({className:n,type:i,...r}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i);let a;t[4]===n?a=t[5]:(a=qt(`h-7 w-full min-w-0 rounded-md border border-input bg-input/20 px-2 py-0.5 text-sm transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-xs/relaxed file:font-medium file:text-foreground placeholder:text-muted-foreground autofill:[-webkit-text-fill-color:var(--color-foreground)] autofill:[transition:background-color_9999s_ease-in-out_0s] focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-xs/relaxed dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40`,n),t[4]=n,t[5]=a);let o;return t[6]!==r||t[7]!==a||t[8]!==i?(o=(0,W.jsx)(Ol,{className:a,"data-slot":`input`,type:i,...r}),t[6]=r,t[7]=a,t[8]=i,t[9]=o):o=t[9],o}var Al=U.forwardRef(function(e,t){let{className:n,render:r,orientation:i=`horizontal`,style:a,...o}=e;return Ft(`div`,e,{state:{orientation:i},ref:t,props:[{role:`separator`,"aria-orientation":i},o]})});function jl(e){let t=(0,go.c)(10),n,r,i;t[0]===e?(n=t[1],r=t[2],i=t[3]):({className:n,orientation:i,...r}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i);let a=i===void 0?`horizontal`:i,o;t[4]===n?o=t[5]:(o=qt(`shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch`,n),t[4]=n,t[5]=o);let s;return t[6]!==a||t[7]!==r||t[8]!==o?(s=(0,W.jsx)(Al,{className:o,"data-slot":`separator`,orientation:a,...r}),t[6]=a,t[7]=r,t[8]=o,t[9]=s):s=t[9],s}var Ml=U.createContext(!1),Nl=U.createContext(void 0);function Pl(e){let t=U.useContext(Nl);if(e===!1&&t===void 0)throw Error(zt(27));return t}var Fl=function(e){return e.open=`data-open`,e.closed=`data-closed`,e[e.startingStyle=_l.startingStyle]=`startingStyle`,e[e.endingStyle=_l.endingStyle]=`endingStyle`,e.anchorHidden=`data-anchor-hidden`,e.side=`data-side`,e.align=`data-align`,e}({}),Il=function(e){return e.popupOpen=`data-popup-open`,e.pressed=`data-pressed`,e}({}),Ll={[Il.popupOpen]:``},Y={[Il.popupOpen]:``,[Il.pressed]:``},Rl={[Fl.open]:``},zl={[Fl.closed]:``},Bl={[Fl.anchorHidden]:``},Vl={open(e){return e?Ll:null}},Hl={open(e){return e?Y:null}},Ul={open(e){return e?Rl:zl},anchorHidden(e){return e?Bl:null}},Wl={...Ul,...bl},Gl=U.forwardRef(function(e,t){let{render:n,className:r,style:i,forceRender:a=!1,...o}=e,{store:s}=Pl(),c=s.useState(`open`),l=s.useState(`nested`),u=s.useState(`mounted`);return Ft(`div`,e,{state:{open:c,transitionStatus:s.useState(`transitionStatus`)},ref:[s.context.backdropRef,t],stateAttributesMapping:Wl,props:[{role:`presentation`,hidden:!u,style:{userSelect:`none`,WebkitUserSelect:`none`}},o],enabled:a||!l})}),Kl=U.forwardRef(function(e,t){let{render:n,className:r,style:i,disabled:a=!1,nativeButton:o=!0,...s}=e,{store:c}=Pl(),l=c.useState(`open`),{getButtonProps:u,buttonRef:d}=un({disabled:a,native:o}),f={disabled:a};function p(e){l&&c.setOpen(!1,An(On,e.nativeEvent))}return Ft(`button`,e,{state:f,ref:[t,d],props:[{onClick:p},s,u]})}),ql=U.forwardRef(function(e,t){let{render:n,className:r,style:i,id:a,...o}=e,{store:s}=Pl(),c=vn(a);return s.useSyncedValueWithCleanup(`descriptionElementId`,c),Ft(`p`,e,{ref:t,props:[{id:c},o]})});function Jl(e,t){return t!=null&&!Zs(t)?0:typeof e==`function`?e():e}function Yl(e,t,n){let r=Jl(e,n);return typeof r==`number`?r:r?.[t]}function Xl(e){return typeof e==`function`?e():e}function Zl(e,t){return t||e===`click`||e===`mousedown`}function Ql(e){return e?.includes(`mouse`)&&e!==`mousedown`}var $l=U.createContext({hasProvider:!1,timeoutMs:0,delayRef:{current:0},initialDelayRef:{current:0},timeout:new ul,currentIdRef:{current:null},currentContextRef:{current:null}});function eu(e){let{children:t,delay:n,timeoutMs:r=0}=e,i=U.useRef(n),a=U.useRef(n),o=U.useRef(null),s=U.useRef(null),c=dl();return(0,W.jsx)($l.Provider,{value:U.useMemo(()=>({hasProvider:!0,delayRef:i,initialDelayRef:a,currentIdRef:o,timeoutMs:r,currentContextRef:s,timeout:c}),[r,c]),children:t})}function tu(e,t={open:!1}){let{open:n}=t,r=`rootStore`in e?e.rootStore:e,i=r.useState(`floatingId`),{currentIdRef:a,delayRef:o,timeoutMs:s,initialDelayRef:c,currentContextRef:l,hasProvider:u,timeout:d}=U.useContext($l),[f,p]=U.useState(!1);return B(()=>{function e(){p(!1),l.current?.setIsInstantPhase(!1),a.current=null,l.current=null,o.current=c.current}if(a.current&&!n&&a.current===i){if(p(!1),s){let t=i;return d.start(s,()=>{r.select(`open`)||a.current&&a.current!==t||e()}),()=>{d.clear()}}e()}},[n,i,a,o,s,c,l,d,r]),B(()=>{if(!n)return;let e=l.current,t=a.current;d.clear(),l.current={onOpenChange:r.setOpen,setIsInstantPhase:p},a.current=i,o.current={open:0,close:Yl(c.current,`close`)},t!==null&&t!==i?(p(!0),e?.setIsInstantPhase(!0),e?.onOpenChange(!1,An(xn))):(p(!1),e?.setIsInstantPhase(!1))},[n,i,r,a,o,c,l,d]),B(()=>()=>{l.current=null},[l]),U.useMemo(()=>({hasProvider:u,delayRef:o,isInstantPhase:f}),[u,o,f])}function X(e,t,n,r){return e.addEventListener(t,n,r),()=>{e.removeEventListener(t,n,r)}}function nu(...e){return()=>{for(let t=0;t{t.current=t.next}};return t}var au={clipPath:`inset(50%)`,overflow:`hidden`,whiteSpace:`nowrap`,border:0,padding:0,width:1,height:1,margin:-1},ou={...au,position:`fixed`,top:0,left:0},su={...au,position:`absolute`},cu=U.forwardRef(function(e,t){let[n,r]=U.useState();B(()=>{ws&&r(`button`)},[]);let i={tabIndex:0,role:n};return(0,W.jsx)(`span`,{...e,ref:t,style:ou,"aria-hidden":n?void 0:!0,...i,"data-base-ui-focus-guard":``})});function lu(e){return`data-base-ui-${e}`}var uu=0;function du(e,t={}){let{preventScroll:n=!1,sync:r=!1,shouldFocus:i}=t;cancelAnimationFrame(uu);function a(){i&&!i()||e?.focus({preventScroll:n})}if(r)return a(),It;let o=requestAnimationFrame(a);return uu=o,()=>{uu===o&&(cancelAnimationFrame(o),uu=0)}}var fu={inert:new WeakMap,"aria-hidden":new WeakMap},pu=`data-base-ui-inert`,mu={inert:new WeakSet,"aria-hidden":new WeakSet},hu=new WeakMap,gu=0;function _u(e){return mu[e]}function vu(e){return e?hn(e)?e.host:vu(e.parentNode):null}var yu=(e,t)=>t.map(t=>{if(e.contains(t))return t;let n=vu(t);return e.contains(n)?n:null}).filter(e=>e!=null),bu=e=>{let t=new Set;return e.forEach(e=>{let n=e;for(;n&&!t.has(n);)t.add(n),n=n.parentNode}),t},xu=(e,t,n)=>{let r=[],i=e=>{!e||n.has(e)||Array.from(e.children).forEach(e=>{ln(e)!==`script`&&(t.has(e)?i(e):r.push(e))})};return i(e),r};function Su(e,t,n,r,{mark:i=!0,markerIgnoreElements:a=[]}){let o=r?`inert`:n?`aria-hidden`:null,s=null,c=null,l=yu(t,e),u=i?yu(t,a):[],d=new Set(u),f=i?xu(t,bu(l),new Set(l)).filter(e=>!d.has(e)):[],p=[],m=[];if(o){let e=fu[o],n=_u(o);c=n,s=e;let r=yu(t,Array.from(t.querySelectorAll(`[aria-live]`))),i=l.concat(r);xu(t,bu(i),new Set(i)).forEach(t=>{let r=t.getAttribute(o),i=r!==null&&r!==`false`,a=(e.get(t)||0)+1;e.set(t,a),p.push(t),a===1&&i&&n.add(t),i||t.setAttribute(o,o===`inert`?``:`true`)})}return i&&f.forEach(e=>{let t=(hu.get(e)||0)+1;hu.set(e,t),m.push(e),t===1&&e.setAttribute(pu,``)}),gu+=1,()=>{s&&p.forEach(e=>{let t=(s.get(e)||0)-1;s.set(e,t),t||(!c?.has(e)&&o&&e.removeAttribute(o),c?.delete(e))}),i&&m.forEach(e=>{let t=(hu.get(e)||0)-1;hu.set(e,t),t||e.removeAttribute(pu)}),--gu,gu||(fu.inert=new WeakMap,fu[`aria-hidden`]=new WeakMap,mu.inert=new WeakSet,mu[`aria-hidden`]=new WeakSet,hu=new WeakMap)}}function Cu(e,t={}){let{ariaHidden:n=!1,inert:r=!1,mark:i=!0,markerIgnoreElements:a=[]}=t,o=Mn(e[0]).body;return Su(e,o,n,r,{mark:i,markerIgnoreElements:a})}var wu={style:{transition:`none`}},Tu={fallbackAxisSide:`none`},Eu={fallbackAxisSide:`end`},Du={clipPath:`inset(50%)`,position:`fixed`,top:0,left:0},Ou=U.createContext(null),ku=()=>U.useContext(Ou),Au=lu(`portal`);function ju(e={}){let{ref:t,container:n,componentProps:r=jt,elementProps:i}=e,a=_n(),o=ku()?.portalNode,[s,c]=U.useState(null),[l,u]=U.useState(null),d=dn(e=>{e!==null&&u(e)}),f=U.useRef(null);B(()=>{if(n===null){f.current&&(f.current=null,u(null),c(null));return}if(a==null)return;let e=(n&&(Zt(n)?n:n.current))??o??document.body;if(e==null){f.current&&(f.current=null,u(null),c(null));return}f.current!==e&&(f.current=e,u(null),c(e))},[n,o,a]);let p=Ft(`div`,r,{ref:[t,d],props:[{id:a,[Au]:``},i]});return{portalNode:l,portalSubtree:s&&p?xl.createPortal(p,s):null}}var Mu=U.forwardRef(function(e,t){let{render:n,className:r,style:i,children:a,container:o,renderGuards:s,...c}=e,{portalNode:l,portalSubtree:u}=ju({container:o,ref:t,componentProps:e,elementProps:c}),d=U.useRef(null),f=U.useRef(null),p=U.useRef(null),m=U.useRef(null),[h,g]=U.useState(null),_=U.useRef(!1),v=h?.modal,y=h?.open,b=typeof s==`boolean`?s:!!h&&!h.modal&&h.open&&!!l;U.useEffect(()=>{if(!l||v)return;function e(e){l&&e.relatedTarget&&il(e)&&(e.type===`focusin`?_.current&&=(ol(l),!1):(al(l),_.current=!0))}return nu(X(l,`focusin`,e,!0),X(l,`focusout`,e,!0))},[l,v]),U.useEffect(()=>{!l||y!==!1||(ol(l),_.current=!1)},[y,l]);let x=U.useMemo(()=>({beforeOutsideRef:d,afterOutsideRef:f,beforeInsideRef:p,afterInsideRef:m,portalNode:l,setFocusManagerState:g}),[l]);return(0,W.jsxs)(U.Fragment,{children:[u,(0,W.jsxs)(Ou.Provider,{value:x,children:[b&&l&&(0,W.jsx)(cu,{"data-type":`outside`,ref:d,onFocus:e=>{il(e,l)?p.current?.focus():rl(h?h.domReference:null)?.focus()}}),b&&l&&(0,W.jsx)(`span`,{"aria-owns":l.id,style:Du}),l&&xl.createPortal(a,l),b&&l&&(0,W.jsx)(cu,{"data-type":`outside`,ref:f,onFocus:e=>{il(e,l)?m.current?.focus():(nl(h?h.domReference:null)?.focus(),h?.closeOnFocusOut&&h?.onOpenChange(!1,An(`focus-out`,e.nativeEvent)))}})]})]})});function Nu(){let e=new Map;return{emit(t,n){e.get(t)?.forEach(e=>e(n))},on(t,n){e.has(t)||e.set(t,new Set),e.get(t).add(n)},off(t,n){e.get(t)?.delete(n)}}}var Pu=U.createContext(null),Fu=U.createContext(null),Iu=()=>U.useContext(Pu)?.id||null,Lu=e=>{let t=U.useContext(Fu);return e??t};function Ru(e,t){let n=$t(bn(e));return e instanceof n.KeyboardEvent?`keyboard`:e instanceof n.FocusEvent?t||`keyboard`:`pointerType`in e?e.pointerType||`keyboard`:`touches`in e?`touch`:e instanceof n.MouseEvent?t||(e.detail===0?`keyboard`:`mouse`):``}var zu=20,Bu=[];function Vu(){Bu=Bu.filter(e=>e.deref()?.isConnected)}function Hu(e){Vu(),e&&ln(e)!==`body`&&(Bu.push(new WeakRef(e)),Bu.length>zu&&(Bu=Bu.slice(-20)))}function Uu(){return Vu(),Bu[Bu.length-1]?.deref()}function Wu(e){return e?Qc(e)?e:el(e)[0]||e:null}function Gu(e,t){if(e.hasAttribute(`tabindex`)&&!e.hasAttribute(`data-tabindex`)||!t.current.includes(`floating`)&&!e.getAttribute(`role`)?.includes(`dialog`))return;let n=$c(e).filter(e=>{let t=e.getAttribute(`data-tabindex`)||``;return Qc(e)||e.hasAttribute(`data-tabindex`)&&!t.startsWith(`-`)}),r=e.getAttribute(`tabindex`);t.current.includes(`floating`)||n.length===0?r!==`0`&&e.setAttribute(`tabindex`,`0`):(r!==`-1`||e.hasAttribute(`data-tabindex`)&&e.getAttribute(`data-tabindex`)!==`-1`)&&(e.setAttribute(`tabindex`,`-1`),e.setAttribute(`data-tabindex`,`-1`))}function Ku(e){let{context:t,children:n,disabled:r=!1,initialFocus:i=!0,returnFocus:a=!0,restoreFocus:o=!1,modal:s=!0,closeOnFocusOut:c=!0,openInteractionType:l=``,nextFocusableElement:u,previousFocusableElement:d,beforeContentFocusGuardRef:f,externalTree:p,getInsideElements:m}=e,h=`rootStore`in t?t.rootStore:t,g=h.useState(`open`),_=h.useState(`domReferenceElement`),v=h.useState(`floatingElement`),{events:y,dataRef:b}=h.context,x=dn(()=>b.current.floatingContext?.nodeId),S=i===!1,C=Hs(_)&&S,w=U.useRef([`content`]),T=ru(i),E=ru(a),D=ru(l),O=Lu(p),k=ku(),A=U.useRef(!1),j=U.useRef(!1),M=U.useRef(!1),ee=U.useRef(null),N=U.useRef(``),P=U.useRef(``),te=U.useRef(null),ne=U.useRef(null),re=Mt(te,f,k?.beforeInsideRef),ie=Mt(ne,k?.afterInsideRef),ae=dl(),F=dl(),oe=hl(),se=k!=null,I=Ws(v),ce=dn((e=I)=>e?el(e):[]),le=dn(()=>m?.().filter(e=>e!=null)??[]);U.useEffect(()=>{if(r||!s)return;function e(e){e.key===`Tab`&&H(I,jn(Mn(I)))&&ce().length===0&&!C&&qs(e)}return X(Mn(I),`keydown`,e)},[r,I,s,C,ce]),U.useEffect(()=>{if(r||!g)return;let e=Mn(I);function t(){M.current=!1}function n(e){let t=bn(e),n=le();M.current=!(H(v,t)||H(_,t)||H(k?.portalNode,t)||n.some(e=>e===t||H(e,t))),P.current=e.pointerType||`keyboard`,t?.closest(`[data-base-ui-click-trigger]`)&&(j.current=!0)}function i(){P.current=`keyboard`}return nu(X(e,`pointerdown`,n,!0),X(e,`pointerup`,t,!0),X(e,`pointercancel`,t,!0),X(e,`keydown`,i,!0))},[r,v,_,I,g,k,le]),U.useEffect(()=>{if(r||!c)return;let e=Mn(I);function t(){j.current=!0,F.start(0,()=>{j.current=!1})}function n(e){let t=bn(e);Qc(t)&&(ee.current=t)}function i(t){let n=t.relatedTarget,r=t.currentTarget,i=bn(t);queueMicrotask(()=>{let a=x(),c=h.context.triggerElements,l=le(),f=n?.hasAttribute(lu(`focus-guard`))&&[te.current,ne.current,k?.beforeInsideRef.current,k?.afterInsideRef.current,k?.beforeOutsideRef.current,k?.afterOutsideRef.current,gl(d),gl(u)].includes(n),p=!(H(_,n)||H(v,n)||H(n,v)||H(k?.portalNode,n)||l.some(e=>e===n||H(e,n))||n!=null&&c.hasElement(n)||c.hasMatchingElement(e=>H(e,n))||f||O&&(Gs(O.nodesRef.current,a).find(e=>H(e.context?.elements.floating,n)||H(e.context?.elements.domReference,n))||Ks(O.nodesRef.current,a).find(e=>[e.context?.elements.floating,Ws(e.context?.elements.floating)].includes(n)||e.context?.elements.domReference===n)));if(r===_&&I&&Gu(I,w),o&&r!==_&&!Rc(i)&&jn(e)===e.body){if(en(I)&&(I.focus(),o===`popup`)){oe.request(()=>{I.focus()});return}let e=ce(),t=ee.current,n=(t&&e.includes(t)?t:null)||e[e.length-1]||I;en(n)&&n.focus()}if(b.current.insideReactTree){b.current.insideReactTree=!1;return}(C||!s)&&n&&p&&!j.current&&(C||n!==Uu())&&(A.current=!0,h.setOpen(!1,An(En,t)))})}function a(){M.current||(b.current.insideReactTree=!0,ae.start(0,()=>{b.current.insideReactTree=!1}))}let l=en(_)?_:null;if(!(!v&&!l))return nu(l&&X(l,`focusout`,i),l&&X(l,`pointerdown`,t),v&&X(v,`focusin`,n),v&&X(v,`focusout`,i),v&&k&&X(v,`focusout`,a,!0))},[r,_,v,I,s,O,k,h,c,o,ce,C,x,w,b,ae,F,oe,u,d,le]),U.useEffect(()=>{if(r||!v||!g)return;let e=Array.from(k?.portalNode?.querySelectorAll(`[${lu(`portal`)}]`)||[]),t=(O?Ks(O.nodesRef.current,x()):[]).find(e=>Hs(e.context?.elements.domReference||null))?.context?.elements.domReference,n=Cu([v,...e,te.current,ne.current,k?.beforeOutsideRef.current,k?.afterOutsideRef.current,...le(),t,gl(d),gl(u),C?_:null].filter(e=>e!=null),{ariaHidden:s||C,mark:!1}),i=Cu([v,...e].filter(e=>e!=null));return()=>{i(),n()}},[g,r,_,v,s,k,C,O,x,u,d,le]),B(()=>{if(!g||r||!en(I))return;let e=Mn(I),t=jn(e);queueMicrotask(()=>{let n=T.current,r=typeof n==`function`?n(D.current||``):n;if(r===void 0||r===!1||H(I,t))return;let i=null,a=()=>(i??=ce(I),i[0]||I),o;o=r===!0||r===null?a():gl(r),o||=a();let s=H(I,jn(e));du(o,{preventScroll:o===I,shouldFocus(){if(s)return!0;let t=jn(e);return!(t!==o&&H(I,t))}})})},[r,g,I,ce,T,D]),B(()=>{if(r||!I)return;let e=Mn(I);Hu(jn(e));function t(e){if(e.open||(N.current=Ru(e.nativeEvent,P.current)),e.reason===`trigger-hover`&&e.nativeEvent.type===`mouseleave`&&(A.current=!0),e.reason===`outside-press`)if(e.nested)A.current=!1;else if(Ys(e.nativeEvent)||Xs(e.nativeEvent))A.current=!1;else{let e=!1;Mn(I).createElement(`div`).focus({get preventScroll(){return e=!0,!1}}),e?A.current=!1:A.current=!0}}y.on(`openchange`,t);function n(){let e=E.current,t=typeof e==`function`?e(N.current):e;if(t===void 0||t===!1)return null;if(t===null&&(t=!0),typeof t==`boolean`)return _?.isConnected?_:Uu()||null;let n=_?.isConnected?_:Uu();return gl(t)||n||null}return()=>{y.off(`openchange`,t);let r=jn(e),i=le(),a=H(v,r)||i.some(e=>e===r||H(e,r))||O&&Gs(O.nodesRef.current,x(),!1).some(e=>H(e.context?.elements.floating,r)),o=E.current,s=n();queueMicrotask(()=>{let t=Wu(s),n=typeof o!=`boolean`;o&&!A.current&&en(t)&&(!(!n&&t!==r&&r!==e.body)||a)&&t.focus({preventScroll:!0}),A.current=!1})}},[r,v,I,E,y,O,_,x,le]),B(()=>{if(!Ss||g||!v)return;let e=jn(Mn(v));!en(e)||!Bs(e)||H(v,e)&&e.blur()},[g,v]),B(()=>{if(!(r||!k))return k.setFocusManagerState({modal:s,closeOnFocusOut:c,open:g,onOpenChange:h.setOpen,domReference:_}),()=>{k.setFocusManagerState(null)}},[r,k,s,g,h,c,_]),B(()=>{if(!(r||!I))return Gu(I,w),()=>{queueMicrotask(Vu)}},[r,I,w]);let ue=!r&&(s?!C:!0)&&(se||s);return(0,W.jsxs)(U.Fragment,{children:[ue&&(0,W.jsx)(cu,{"data-type":`inside`,ref:re,onFocus:e=>{if(s){let e=ce();du(e[e.length-1])}else k?.portalNode&&(A.current=!1,il(e,k.portalNode)?nl(_)?.focus():gl(d??k.beforeOutsideRef)?.focus())}}),n,ue&&(0,W.jsx)(cu,{"data-type":`inside`,ref:ie,onFocus:e=>{s?du(ce()[0]):k?.portalNode&&(c&&(A.current=!0),il(e,k.portalNode)?rl(_)?.focus():gl(u??k.afterOutsideRef)?.focus())}})]})}function qu(e,t){let n=null,r=null,i=!1;return{contextElement:e||void 0,getBoundingClientRect(){let a=e?.getBoundingClientRect()||{width:0,height:0,x:0,y:0},o=t.axis===`x`||t.axis===`both`,s=t.axis===`y`||t.axis===`both`,c=[`mouseenter`,`mousemove`].includes(t.dataRef.current.openEvent?.type||``)&&t.pointerType!==`touch`,l=a.width,u=a.height,d=a.x,f=a.y;return n==null&&t.x&&o&&(n=a.x-t.x),r==null&&t.y&&s&&(r=a.y-t.y),d-=n||0,f-=r||0,l=0,u=0,!i||c?(l=t.axis===`y`?a.width:0,u=t.axis===`x`?a.height:0,d=o&&t.x!=null?t.x:d,f=s&&t.y!=null?t.y:f):i&&!c&&(u=t.axis===`x`?a.height:u,l=t.axis===`y`?a.width:l),i=!0,{width:l,height:u,x:d,y:f,top:f,right:d+l,bottom:f+u,left:d}}}}function Ju(e){return e!=null&&e.clientX!=null}function Yu(e,t={}){let{enabled:n=!0,axis:r=`both`}=t,i=`rootStore`in e?e.rootStore:e,a=i.useState(`open`),o=i.useState(`floatingElement`),s=i.useState(`domReferenceElement`),c=i.context.dataRef,l=U.useRef(!1),u=U.useRef(null),[d,f]=U.useState(),[p,m]=U.useState([]),h=dn(e=>{i.set(`positionReference`,e)}),g=dn((e,t,n)=>{l.current||c.current.openEvent&&!Ju(c.current.openEvent)||i.set(`positionReference`,qu(n??s,{x:e,y:t,axis:r,dataRef:c,pointerType:d}))}),_=dn(e=>{a?u.current||(g(e.clientX,e.clientY,e.currentTarget),m([])):g(e.clientX,e.clientY,e.currentTarget)}),v=Zs(d)?o:a;U.useEffect(()=>{if(!n){h(s);return}if(!v)return;function e(){u.current?.(),u.current=null}let t=$t(o);function r(t){H(o,bn(t))?e():g(t.clientX,t.clientY)}return!c.current.openEvent||Ju(c.current.openEvent)?u.current=X(t,`mousemove`,r):h(s),e},[v,n,o,c,s,i,g,h,p]),U.useEffect(()=>()=>{i.set(`positionReference`,null)},[i]),U.useEffect(()=>{n&&!o&&(l.current=!1)},[n,o]),U.useEffect(()=>{!n&&a&&(l.current=!0)},[n,a]);let y=U.useMemo(()=>{function e(e){f(e.pointerType)}return{onPointerDown:e,onPointerEnter:e,onMouseMove:_,onMouseEnter:_}},[_]);return U.useMemo(()=>n?{reference:y,trigger:y}:{},[n,y])}var Xu={intentional:`onClick`,sloppy:`onPointerDown`};function Zu(){return!1}function Qu(e){return{escapeKey:typeof e==`boolean`?e:e?.escapeKey??!1,outsidePress:typeof e==`boolean`?e:e?.outsidePress??!0}}function $u(e,t={}){let{enabled:n=!0,escapeKey:r=!0,outsidePress:i=!0,outsidePressEvent:a=`sloppy`,referencePress:o=Zu,referencePressEvent:s=`sloppy`,bubbles:c,externalTree:l}=t,u=`rootStore`in e?e.rootStore:e,d=u.useState(`open`),f=u.useState(`floatingElement`),{dataRef:p}=u.context,m=Lu(l),h=dn(typeof i==`function`?i:()=>!1),g=typeof i==`function`?h:i,_=g!==!1,v=dn(()=>a),{escapeKey:y,outsidePress:b}=Qu(c),x=U.useRef(!1),S=U.useRef(!1),C=U.useRef(!1),w=U.useRef(!1),T=U.useRef(``),E=U.useRef(null),D=dl(),O=dl(),k=dn(()=>{O.clear(),p.current.insideReactTree=!1}),A=dn(e=>{let t=p.current.floatingContext?.nodeId;return(m?Gs(m.nodesRef.current,t):[]).some(t=>t.context?.open&&!t.context.dataRef.current[e])}),j=dn(e=>Rs(e,u.select(`floatingElement`))||Rs(e,u.select(`domReferenceElement`))),M=dn(e=>{o()&&u.setOpen(!1,An(Sn,e.nativeEvent))}),ee=dn(e=>{if(!d||!n||!r||e.key!==`Escape`||w.current||!y&&A(`__escapeKeyBubbles`))return;let t=An(yn,Js(e)?e.nativeEvent:e);u.setOpen(!1,t),t.isCanceled||e.preventDefault(),!y&&!t.isPropagationAllowed&&e.stopPropagation()}),N=dn(()=>{p.current.insideReactTree=!0,O.start(0,k)}),P=dn(e=>{if(!d||!n||e.button!==0)return;let t=bn(e.nativeEvent);H(u.select(`floatingElement`),t)&&(x.current||(x.current=!0,S.current=!1))}),te=dn(e=>{!d||!n||(e.defaultPrevented||e.nativeEvent.defaultPrevented)&&x.current&&(S.current=!0)});U.useEffect(()=>{if(!d||!n)return;p.current.__escapeKeyBubbles=y,p.current.__outsidePressBubbles=b;let e=new ul,t=new ul;function i(){e.clear(),w.current=!0}function a(){e.start(Yt()?5:0,()=>{w.current=!1})}function o(){C.current=!0,t.start(0,()=>{C.current=!1})}function s(){x.current=!1,S.current=!1}function c(){let e=T.current,t=e===`pen`||!e?`mouse`:e,n=v(),r=typeof n==`function`?n():n;return typeof r==`string`?r:r[t]}function l(e){let t=c();return t===`intentional`&&e.type!==`click`||t===`sloppy`&&e.type===`click`}function h(e){let t=p.current.floatingContext?.nodeId,n=m&&Gs(m.nodesRef.current,t).some(t=>Rs(e,t.context?.elements.floating));return j(e)||n}function O(e){if(l(e)){e.type!==`click`&&!j(e)&&(t.clear(),C.current=!1),k();return}if(p.current.insideReactTree){k();return}let n=bn(e),r=`[${lu(`inert`)}]`,i=V(n)?n.getRootNode():null,a=Array.from((hn(i)?i:Mn(u.select(`floatingElement`))).querySelectorAll(r)),o=u.context.triggerElements;if(n&&(o.hasElement(n)||o.hasMatchingElement(e=>H(e,n))))return;let s=V(n)?n:null;for(;s&&!gn(s);){let e=rn(s);if(gn(e)||!V(e))break;s=e}if(!(a.length&&V(n)&&!zs(n)&&!H(n,u.select(`floatingElement`))&&a.every(e=>!H(s,e)))){if(en(n)&&!(`touches`in e)){let t=gn(n),r=on(n),i=/auto|scroll/,a=t||i.test(r.overflowX),o=t||i.test(r.overflowY),s=a&&n.clientWidth>0&&n.scrollWidth>n.clientWidth,c=o&&n.clientHeight>0&&n.scrollHeight>n.clientHeight,l=r.direction===`rtl`,u=c&&(l?e.offsetX<=n.offsetWidth-n.clientWidth:e.offsetX>n.clientWidth),d=s&&e.offsetY>n.clientHeight;if(u||d)return}if(!h(e)){if(c()===`intentional`&&C.current){t.clear(),C.current=!1;return}typeof g==`function`&&!g(e)||A(`__outsidePressBubbles`)||(u.setOpen(!1,An(Dn,e)),k())}}}function M(e){c()!==`sloppy`||e.pointerType===`touch`||!u.select(`open`)||!n||j(e)||O(e)}function N(e){if(c()!==`sloppy`||!u.select(`open`)||!n||j(e))return;let t=e.touches[0];t&&(E.current={startTime:Date.now(),startX:t.clientX,startY:t.clientY,dismissOnTouchEnd:!1,dismissOnMouseDown:!0},D.start(1e3,()=>{E.current&&(E.current.dismissOnTouchEnd=!1,E.current.dismissOnMouseDown=!1)}))}function P(e,t){let n=bn(e);if(!n)return;let r=X(n,e.type,()=>{t(e),r()})}function te(e){T.current=`touch`,P(e,N)}function ne(e){D.clear(),e.type===`pointerdown`&&(T.current=e.pointerType),!(e.type===`mousedown`&&E.current&&!E.current.dismissOnMouseDown)&&P(e,e=>{e.type===`pointerdown`?M(e):O(e)})}function re(e){if(!x.current)return;let n=S.current;if(s(),c()===`intentional`){if(e.type===`pointercancel`){n&&o();return}if(!h(e)){if(n){o();return}typeof g==`function`&&!g(e)||(t.clear(),C.current=!0,k())}}}function ie(e){if(c()!==`sloppy`||!E.current||j(e))return;let t=e.touches[0];if(!t)return;let n=Math.abs(t.clientX-E.current.startX),r=Math.abs(t.clientY-E.current.startY),i=Math.sqrt(n*n+r*r);i>5&&(E.current.dismissOnTouchEnd=!0),i>10&&(O(e),D.clear(),E.current=null)}function ae(e){P(e,ie)}function F(e){c()!==`sloppy`||!E.current||j(e)||(E.current.dismissOnTouchEnd&&O(e),D.clear(),E.current=null)}function oe(e){P(e,F)}let se=Mn(f),I=nu(r&&nu(X(se,`keydown`,ee),X(se,`compositionstart`,i),X(se,`compositionend`,a)),_&&nu(X(se,`click`,ne,!0),X(se,`pointerdown`,ne,!0),X(se,`pointerup`,re,!0),X(se,`pointercancel`,re,!0),X(se,`mousedown`,ne,!0),X(se,`mouseup`,re,!0),X(se,`touchstart`,te,!0),X(se,`touchmove`,ae,!0),X(se,`touchend`,oe,!0)));return()=>{I(),e.clear(),t.clear(),s(),C.current=!1}},[p,f,r,_,g,d,n,y,b,ee,k,v,A,j,m,u,D]),U.useEffect(k,[g,k]);let ne=U.useMemo(()=>({onKeyDown:ee,[Xu[s]]:M,...s!==`intentional`&&{onClick:M}}),[ee,M,s]),re=U.useMemo(()=>({onKeyDown:ee,onPointerDown:te,onMouseDown:te,onClickCapture:N,onMouseDownCapture(e){N(),P(e)},onPointerDownCapture(e){N(),P(e)},onMouseUpCapture:N,onTouchEndCapture:N,onTouchMoveCapture:N}),[ee,N,P,te]);return U.useMemo(()=>n?{reference:ne,floating:re,trigger:ne}:{},[n,ne,re])}function ed(e,t,n){let{reference:r,floating:i}=e,a=fc(t),o=pc(t),s=dc(o),c=cc(t),l=a===`y`,u=r.x+r.width/2-i.width/2,d=r.y+r.height/2-i.height/2,f=r[s]/2-i[s]/2,p;switch(c){case`top`:p={x:u,y:r.y-i.height};break;case`bottom`:p={x:u,y:r.y+r.height};break;case`right`:p={x:r.x+r.width,y:d};break;case`left`:p={x:r.x-i.width,y:d};break;default:p={x:r.x,y:r.y}}switch(lc(t)){case`start`:p[o]-=f*(n&&l?-1:1);break;case`end`:p[o]+=f*(n&&l?-1:1);break}return p}async function td(e,t){t===void 0&&(t={});let{x:n,y:r,platform:i,rects:a,elements:o,strategy:s}=e,{boundary:c=`clippingAncestors`,rootBoundary:l=`viewport`,elementContext:u=`floating`,altBoundary:d=!1,padding:f=0}=sc(t,e),p=Tc(f),m=o[d?u===`floating`?`reference`:`floating`:u],h=Ec(await i.getClippingRect({element:await(i.isElement==null?void 0:i.isElement(m))??!0?m:m.contextElement||await(i.getDocumentElement==null?void 0:i.getDocumentElement(o.floating)),boundary:c,rootBoundary:l,strategy:s})),g=u===`floating`?{x:n,y:r,width:a.floating.width,height:a.floating.height}:a.reference,_=await(i.getOffsetParent==null?void 0:i.getOffsetParent(o.floating)),v=await(i.isElement==null?void 0:i.isElement(_))&&await(i.getScale==null?void 0:i.getScale(_))||{x:1,y:1},y=Ec(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:o,rect:g,offsetParent:_,strategy:s}):g);return{top:(h.top-y.top+p.top)/v.y,bottom:(y.bottom-h.bottom+p.bottom)/v.y,left:(h.left-y.left+p.left)/v.x,right:(y.right-h.right+p.right)/v.x}}var nd=50,rd=async(e,t,n)=>{let{placement:r=`bottom`,strategy:i=`absolute`,middleware:a=[],platform:o}=n,s=o.detectOverflow?o:{...o,detectOverflow:td},c=await(o.isRTL==null?void 0:o.isRTL(t)),l=await o.getElementRects({reference:e,floating:t,strategy:i}),{x:u,y:d}=ed(l,r,c),f=r,p=0,m={};for(let n=0;ne<=0)){let e=(i.flip?.index||0)+1,t=S[e];if(t&&(!(u===`alignment`&&_!==fc(t))||T.every(e=>fc(e.placement)===_?e.overflows[0]>0:!0)))return{data:{index:e,overflows:T},reset:{placement:t}};let n=T.filter(e=>e.overflows[0]<=0).sort((e,t)=>e.overflows[1]-t.overflows[1])[0]?.placement;if(!n)switch(f){case`bestFit`:{let e=T.filter(e=>{if(x){let t=fc(e.placement);return t===_||t===`y`}return!0}).map(e=>[e.placement,e.overflows.filter(e=>e>0).reduce((e,t)=>e+t,0)]).sort((e,t)=>e[1]-t[1])[0]?.[0];e&&(n=e);break}case`initialPlacement`:n=o;break}if(r!==n)return{reset:{placement:n}}}return{}}}};function ad(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function od(e){return $s.some(t=>e[t]>=0)}var sd=function(e){return e===void 0&&(e={}),{name:`hide`,options:e,async fn(t){let{rects:n,platform:r}=t,{strategy:i=`referenceHidden`,...a}=sc(e,t);switch(i){case`referenceHidden`:{let e=ad(await r.detectOverflow(t,{...a,elementContext:`reference`}),n.reference);return{data:{referenceHiddenOffsets:e,referenceHidden:od(e)}}}case`escaped`:{let e=ad(await r.detectOverflow(t,{...a,altBoundary:!0}),n.floating);return{data:{escapedOffsets:e,escaped:od(e)}}}default:return{}}}}},cd=new Set([`left`,`top`]);async function ld(e,t){let{placement:n,platform:r,elements:i}=e,a=await(r.isRTL==null?void 0:r.isRTL(i.floating)),o=cc(n),s=lc(n),c=fc(n)===`y`,l=cd.has(o)?-1:1,u=a&&c?-1:1,d=sc(t,e),{mainAxis:f,crossAxis:p,alignmentAxis:m}=typeof d==`number`?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:d.mainAxis||0,crossAxis:d.crossAxis||0,alignmentAxis:d.alignmentAxis};return s&&typeof m==`number`&&(p=s===`end`?m*-1:m),c?{x:p*u,y:f*l}:{x:f*l,y:p*u}}var ud=function(e){return e===void 0&&(e=0),{name:`offset`,options:e,async fn(t){var n;let{x:r,y:i,placement:a,middlewareData:o}=t,s=await ld(t,e);return a===o.offset?.placement&&(n=o.arrow)!=null&&n.alignmentOffset?{}:{x:r+s.x,y:i+s.y,data:{...s,placement:a}}}}},dd=function(e){return e===void 0&&(e={}),{name:`shift`,options:e,async fn(t){let{x:n,y:r,placement:i,platform:a}=t,{mainAxis:o=!0,crossAxis:s=!1,limiter:c={fn:e=>{let{x:t,y:n}=e;return{x:t,y:n}}},...l}=sc(e,t),u={x:n,y:r},d=await a.detectOverflow(t,l),f=fc(cc(i)),p=uc(f),m=u[p],h=u[f];if(o){let e=p===`y`?`top`:`left`,t=p===`y`?`bottom`:`right`,n=m+d[e],r=m-d[t];m=oc(n,m,r)}if(s){let e=f===`y`?`top`:`left`,t=f===`y`?`bottom`:`right`,n=h+d[e],r=h-d[t];h=oc(n,h,r)}let g=c.fn({...t,[p]:m,[f]:h});return{...g,data:{x:g.x-n,y:g.y-r,enabled:{[p]:o,[f]:s}}}}}},fd=function(e){return e===void 0&&(e={}),{options:e,fn(t){let{x:n,y:r,placement:i,rects:a,middlewareData:o}=t,{offset:s=0,mainAxis:c=!0,crossAxis:l=!0}=sc(e,t),u={x:n,y:r},d=fc(i),f=uc(d),p=u[f],m=u[d],h=sc(s,t),g=typeof h==`number`?{mainAxis:h,crossAxis:0}:{mainAxis:0,crossAxis:0,...h};if(c){let e=f===`y`?`height`:`width`,t=a.reference[f]-a.floating[e]+g.mainAxis,n=a.reference[f]+a.reference[e]-g.mainAxis;pn&&(p=n)}if(l){let e=f===`y`?`width`:`height`,t=cd.has(cc(i)),n=a.reference[d]-a.floating[e]+(t&&o.offset?.[d]||0)+(t?0:g.crossAxis),r=a.reference[d]+a.reference[e]+(t?0:o.offset?.[d]||0)-(t?g.crossAxis:0);mr&&(m=r)}return{[f]:p,[d]:m}}}},pd=function(e){return e===void 0&&(e={}),{name:`size`,options:e,async fn(t){var n,r;let{placement:i,rects:a,platform:o,elements:s}=t,{apply:c=()=>{},...l}=sc(e,t),u=await o.detectOverflow(t,l),d=cc(i),f=lc(i),p=fc(i)===`y`,{width:m,height:h}=a.floating,g,_;d===`top`||d===`bottom`?(g=d,_=f===(await(o.isRTL==null?void 0:o.isRTL(s.floating))?`start`:`end`)?`left`:`right`):(_=d,g=f===`end`?`top`:`bottom`);let v=h-u.top-u.bottom,y=m-u.left-u.right,b=ec(h-u[g],v),x=ec(m-u[_],y),S=!t.middlewareData.shift,C=b,w=x;if((n=t.middlewareData.shift)!=null&&n.enabled.x&&(w=y),(r=t.middlewareData.shift)!=null&&r.enabled.y&&(C=v),S&&!f){let e=tc(u.left,0),t=tc(u.right,0),n=tc(u.top,0),r=tc(u.bottom,0);p?w=m-2*(e!==0||t!==0?e+t:tc(u.left,u.right)):C=h-2*(n!==0||r!==0?n+r:tc(u.top,u.bottom))}await c({...t,availableWidth:w,availableHeight:C});let T=await o.getDimensions(s.floating);return m!==T.width||h!==T.height?{reset:{rects:!0}}:{}}}};function md(e){let t=on(e),n=parseFloat(t.width)||0,r=parseFloat(t.height)||0,i=en(e),a=i?e.offsetWidth:n,o=i?e.offsetHeight:r,s=nc(n)!==a||nc(r)!==o;return s&&(n=a,r=o),{width:n,height:r,$:s}}function hd(e){return V(e)?e:e.contextElement}function gd(e){let t=hd(e);if(!en(t))return ic(1);let n=t.getBoundingClientRect(),{width:r,height:i,$:a}=md(t),o=(a?nc(n.width):n.width)/r,s=(a?nc(n.height):n.height)/i;return(!o||!Number.isFinite(o))&&(o=1),(!s||!Number.isFinite(s))&&(s=1),{x:o,y:s}}var _d=ic(0);function vd(e){let t=$t(e);return!Yt()||!t.visualViewport?_d:{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}}function yd(e,t,n){return t===void 0&&(t=!1),!n||t&&n!==$t(e)?!1:t}function bd(e,t,n,r){t===void 0&&(t=!1),n===void 0&&(n=!1);let i=e.getBoundingClientRect(),a=hd(e),o=ic(1);t&&(r?V(r)&&(o=gd(r)):o=gd(e));let s=yd(a,n,r)?vd(a):ic(0),c=(i.left+s.x)/o.x,l=(i.top+s.y)/o.y,u=i.width/o.x,d=i.height/o.y;if(a){let e=$t(a),t=r&&V(r)?$t(r):r,n=e,i=nn(n);for(;i&&r&&t!==n;){let e=gd(i),t=i.getBoundingClientRect(),r=on(i),a=t.left+(i.clientLeft+parseFloat(r.paddingLeft))*e.x,o=t.top+(i.clientTop+parseFloat(r.paddingTop))*e.y;c*=e.x,l*=e.y,u*=e.x,d*=e.y,c+=a,l+=o,n=$t(i),i=nn(n)}}return Ec({width:u,height:d,x:c,y:l})}function Z(e,t){let n=sn(e).scrollLeft;return t?t.left+n:bd(tn(e)).left+n}function xd(e,t){let n=e.getBoundingClientRect();return{x:n.left+t.scrollLeft-Z(e,n),y:n.top+t.scrollTop}}function Sd(e){let{elements:t,rect:n,offsetParent:r,strategy:i}=e,a=i===`fixed`,o=tn(r),s=t?Xt(t.floating):!1;if(r===o||s&&a)return n;let c={scrollLeft:0,scrollTop:0},l=ic(1),u=ic(0),d=en(r);if((d||!d&&!a)&&((ln(r)!==`body`||Jt(o))&&(c=sn(r)),d)){let e=bd(r);l=gd(r),u.x=e.x+r.clientLeft,u.y=e.y+r.clientTop}let f=o&&!d&&!a?xd(o,c):ic(0);return{width:n.width*l.x,height:n.height*l.y,x:n.x*l.x-c.scrollLeft*l.x+u.x+f.x,y:n.y*l.y-c.scrollTop*l.y+u.y+f.y}}function Cd(e){return Array.from(e.getClientRects())}function wd(e){let t=tn(e),n=sn(e),r=e.ownerDocument.body,i=tc(t.scrollWidth,t.clientWidth,r.scrollWidth,r.clientWidth),a=tc(t.scrollHeight,t.clientHeight,r.scrollHeight,r.clientHeight),o=-n.scrollLeft+Z(e),s=-n.scrollTop;return on(r).direction===`rtl`&&(o+=tc(t.clientWidth,r.clientWidth)-i),{width:i,height:a,x:o,y:s}}var Td=25;function Ed(e,t){let n=$t(e),r=tn(e),i=n.visualViewport,a=r.clientWidth,o=r.clientHeight,s=0,c=0;if(i){a=i.width,o=i.height;let e=Yt();(!e||e&&t===`fixed`)&&(s=i.offsetLeft,c=i.offsetTop)}let l=Z(r);if(l<=0){let e=r.ownerDocument,t=e.body,n=getComputedStyle(t),i=e.compatMode===`CSS1Compat`&&parseFloat(n.marginLeft)+parseFloat(n.marginRight)||0,o=Math.abs(r.clientWidth-t.clientWidth-i);o<=Td&&(a-=o)}else l<=Td&&(a+=l);return{width:a,height:o,x:s,y:c}}function Dd(e,t){let n=bd(e,!0,t===`fixed`),r=n.top+e.clientTop,i=n.left+e.clientLeft,a=en(e)?gd(e):ic(1);return{width:e.clientWidth*a.x,height:e.clientHeight*a.y,x:i*a.x,y:r*a.y}}function Od(e,t,n){let r;if(t===`viewport`)r=Ed(e,n);else if(t===`document`)r=wd(tn(e));else if(V(t))r=Dd(t,n);else{let n=vd(e);r={x:t.x-n.x,y:t.y-n.y,width:t.width,height:t.height}}return Ec(r)}function kd(e,t){let n=rn(e);return n===t||!V(n)||gn(n)?!1:on(n).position===`fixed`||kd(n,t)}function Ad(e,t){let n=t.get(e);if(n)return n;let r=an(e,[],!1).filter(e=>V(e)&&ln(e)!==`body`),i=null,a=on(e).position===`fixed`,o=a?rn(e):e;for(;V(o)&&!gn(o);){let t=on(o),n=mn(o);!n&&t.position===`fixed`&&(i=null),(a?!n&&!i:!n&&t.position===`static`&&i&&(i.position===`absolute`||i.position===`fixed`)||Jt(o)&&!n&&kd(e,o))?r=r.filter(e=>e!==o):i=t,o=rn(o)}return t.set(e,r),r}function jd(e){let{element:t,boundary:n,rootBoundary:r,strategy:i}=e,a=[...n===`clippingAncestors`?Xt(t)?[]:Ad(t,this._c):[].concat(n),r],o=Od(t,a[0],i),s=o.top,c=o.right,l=o.bottom,u=o.left;for(let e=1;e{o(!1,1e-7)},1e3)}n===1&&!Bd(l,e.getBoundingClientRect())&&o(),y=!1}try{n=new IntersectionObserver(b,{...v,root:i.ownerDocument})}catch{n=new IntersectionObserver(b,v)}n.observe(e)}return o(!0),a}function Hd(e,t,n,r){r===void 0&&(r={});let{ancestorScroll:i=!0,ancestorResize:a=!0,elementResize:o=typeof ResizeObserver==`function`,layoutShift:s=typeof IntersectionObserver==`function`,animationFrame:c=!1}=r,l=hd(e),u=i||a?[...l?an(l):[],...t?an(t):[]]:[];u.forEach(e=>{i&&e.addEventListener(`scroll`,n,{passive:!0}),a&&e.addEventListener(`resize`,n)});let d=l&&s?Vd(l,n):null,f=-1,p=null;o&&(p=new ResizeObserver(e=>{let[r]=e;r&&r.target===l&&p&&t&&(p.unobserve(t),cancelAnimationFrame(f),f=requestAnimationFrame(()=>{var e;(e=p)==null||e.observe(t)})),n()}),l&&!c&&p.observe(l),t&&p.observe(t));let m,h=c?bd(e):null;c&&g();function g(){let t=bd(e);h&&!Bd(h,t)&&n(),h=t,m=requestAnimationFrame(g)}return n(),()=>{var e;u.forEach(e=>{i&&e.removeEventListener(`scroll`,n),a&&e.removeEventListener(`resize`,n)}),d?.(),(e=p)==null||e.disconnect(),p=null,c&&cancelAnimationFrame(m)}}var Ud=ud,Wd=dd,Gd=id,Kd=pd,qd=sd,Jd=fd,Yd=(e,t,n)=>{let r=new Map,i={platform:zd,...n},a={...i.platform,_c:r};return rd(e,t,{...i,platform:a})},Xd=typeof document<`u`?U.useLayoutEffect:function(){};function Zd(e,t){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e==`function`&&e.toString()===t.toString())return!0;let n,r,i;if(e&&t&&typeof e==`object`){if(Array.isArray(e)){if(n=e.length,n!==t.length)return!1;for(r=n;r--!==0;)if(!Zd(e[r],t[r]))return!1;return!0}if(i=Object.keys(e),n=i.length,n!==Object.keys(t).length)return!1;for(r=n;r--!==0;)if(!{}.hasOwnProperty.call(t,i[r]))return!1;for(r=n;r--!==0;){let n=i[r];if(!(n===`_owner`&&e.$$typeof)&&!Zd(e[n],t[n]))return!1}return!0}return e!==e&&t!==t}function Qd(e){return typeof window>`u`?1:(e.ownerDocument.defaultView||window).devicePixelRatio||1}function $d(e,t){let n=Qd(e);return Math.round(t*n)/n}function ef(e){let t=U.useRef(e);return Xd(()=>{t.current=e}),t}function tf(e){e===void 0&&(e={});let{placement:t=`bottom`,strategy:n=`absolute`,middleware:r=[],platform:i,elements:{reference:a,floating:o}={},transform:s=!0,whileElementsMounted:c,open:l}=e,[u,d]=U.useState({x:0,y:0,strategy:n,placement:t,middlewareData:{},isPositioned:!1}),[f,p]=U.useState(r);Zd(f,r)||p(r);let[m,h]=U.useState(null),[g,_]=U.useState(null),v=U.useCallback(e=>{e!==S.current&&(S.current=e,h(e))},[]),y=U.useCallback(e=>{e!==C.current&&(C.current=e,_(e))},[]),b=a||m,x=o||g,S=U.useRef(null),C=U.useRef(null),w=U.useRef(u),T=c!=null,E=ef(c),D=ef(i),O=ef(l),k=U.useCallback(()=>{if(!S.current||!C.current)return;let e={placement:t,strategy:n,middleware:f};D.current&&(e.platform=D.current),Yd(S.current,C.current,e).then(e=>{let t={...e,isPositioned:O.current!==!1};A.current&&!Zd(w.current,t)&&(w.current=t,xl.flushSync(()=>{d(t)}))})},[f,t,n,D,O]);Xd(()=>{l===!1&&w.current.isPositioned&&(w.current.isPositioned=!1,d(e=>({...e,isPositioned:!1})))},[l]);let A=U.useRef(!1);Xd(()=>(A.current=!0,()=>{A.current=!1}),[]),Xd(()=>{if(b&&(S.current=b),x&&(C.current=x),b&&x){if(E.current)return E.current(b,x,k);k()}},[b,x,k,E,T]);let j=U.useMemo(()=>({reference:S,floating:C,setReference:v,setFloating:y}),[v,y]),M=U.useMemo(()=>({reference:b,floating:x}),[b,x]),ee=U.useMemo(()=>{let e={position:n,left:0,top:0};if(!M.floating)return e;let t=$d(M.floating,u.x),r=$d(M.floating,u.y);return s?{...e,transform:`translate(`+t+`px, `+r+`px)`,...Qd(M.floating)>=1.5&&{willChange:`transform`}}:{position:n,left:t,top:r}},[n,s,M.floating,u.x,u.y]);return U.useMemo(()=>({...u,update:k,refs:j,elements:M,floatingStyles:ee}),[u,k,j,M,ee])}var nf=(e,t)=>{let n=Ud(e);return{name:n.name,fn:n.fn,options:[e,t]}},rf=(e,t)=>{let n=Wd(e);return{name:n.name,fn:n.fn,options:[e,t]}},af=(e,t)=>({fn:Jd(e).fn,options:[e,t]}),of=(e,t)=>{let n=Gd(e);return{name:n.name,fn:n.fn,options:[e,t]}},sf=(e,t)=>{let n=Kd(e);return{name:n.name,fn:n.fn,options:[e,t]}},cf=(e,t)=>{let n=qd(e);return{name:n.name,fn:n.fn,options:[e,t]}},Q=(e,t,n,r,i,a,...o)=>{if(o.length>0)throw Error(zt(1));let s;if(e&&t&&n&&r&&i&&a)s=(o,s,c,l)=>a(e(o,s,c,l),t(o,s,c,l),n(o,s,c,l),r(o,s,c,l),i(o,s,c,l),s,c,l);else if(e&&t&&n&&r&&i)s=(a,o,s,c)=>i(e(a,o,s,c),t(a,o,s,c),n(a,o,s,c),r(a,o,s,c),o,s,c);else if(e&&t&&n&&r)s=(i,a,o,s)=>r(e(i,a,o,s),t(i,a,o,s),n(i,a,o,s),a,o,s);else if(e&&t&&n)s=(r,i,a,o)=>n(e(r,i,a,o),t(r,i,a,o),i,a,o);else if(e&&t)s=(n,r,i,a)=>t(e(n,r,i,a),r,i,a);else if(e)s=e;else throw Error(`Missing arguments`);return s},lf=[],uf=void 0;function df(){return uf}function ff(e){lf.push(e)}function pf(e){let t=(t,n)=>{let r=Pt(hf).current,i;try{uf=r;for(let e of lf)e.before(r);i=e(t,n);for(let e of lf)e.after(r);r.didInitialize=!0}finally{uf=void 0}return i};return t.displayName=e.displayName||e.name,t}function mf(e){return U.forwardRef(pf(e))}function hf(){return{didInitialize:!1}}var gf=ie(),_f=ne(),vf=Lt(19)?xf:Sf;function yf(e,t,n,r,i){return vf(e,t,n,r,i)}function bf(e,t,n,r,i){let a=U.useCallback(()=>t(e.getSnapshot(),n,r,i),[e,t,n,r,i]);return(0,gf.useSyncExternalStore)(e.subscribe,a,a)}ff({before(e){e.syncIndex=0,e.didInitialize||(e.syncTick=1,e.syncHooks=[],e.didChangeStore=!0,e.getSnapshot=()=>{let t=!1;for(let n=0;n0&&(e.didChangeStore&&(e.didChangeStore=!1,e.subscribe=t=>{let n=new Set;for(let t of e.syncHooks)n.add(t.store);let r=[];for(let e of n)r.push(e.subscribe(t));return()=>{for(let e of r)e()}}),(0,gf.useSyncExternalStore)(e.subscribe,e.getSnapshot,e.getSnapshot))}});function xf(e,t,n,r,i){let a=df();if(!a)return bf(e,t,n,r,i);let o=a.syncIndex;a.syncIndex+=1;let s;return a.didInitialize?(s=a.syncHooks[o],(s.store!==e||s.selector!==t||!Object.is(s.a1,n)||!Object.is(s.a2,r)||!Object.is(s.a3,i))&&(s.store!==e&&(a.didChangeStore=!0),s.store=e,s.selector=t,s.a1=n,s.a2=r,s.a3=i,s.didChange=!0)):(s={store:e,selector:t,a1:n,a2:r,a3:i,value:t(e.getSnapshot(),n,r,i),didChange:!1},a.syncHooks.push(s)),s.value}function Sf(e,t,n,r,i){return(0,_f.useSyncExternalStoreWithSelector)(e.subscribe,e.getSnapshot,e.getSnapshot,e=>t(e,n,r,i))}var Cf=class{constructor(e){this.state=e,this.listeners=new Set,this.updateTick=0}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});getSnapshot=()=>this.state;setState(e){if(this.state===e)return;this.state=e,this.updateTick+=1;let t=this.updateTick;for(let n of this.listeners){if(t!==this.updateTick)return;n(e)}}update(e){for(let t in e)if(!Object.is(this.state[t],e[t])){this.setState({...this.state,...e});return}}set(e,t){Object.is(this.state[e],t)||this.setState({...this.state,[e]:t})}notifyAll(){let e={...this.state};this.setState(e)}use(e,t,n,r){return yf(this,e,t,n,r)}},wf=class extends Cf{constructor(e,t={},n){super(e),this.context=t,this.selectors=n}useSyncedValue(e,t){U.useDebugValue(e);let n=this;B(()=>{n.state[e]!==t&&n.set(e,t)},[n,e,t])}useSyncedValueWithCleanup(e,t){let n=this;B(()=>(n.state[e]!==t&&n.set(e,t),()=>{n.set(e,void 0)}),[n,e,t])}useSyncedValues(e){let t=this;B(()=>{t.update(e)},[t,...Object.values(e)])}useControlledProp(e,t){U.useDebugValue(e);let n=this,r=t!==void 0;B(()=>{r&&!Object.is(n.state[e],t)&&n.setState({...n.state,[e]:t})},[n,e,t,r])}select(e,t,n,r){let i=this.selectors[e];return i(this.state,t,n,r)}useState(e,t,n,r){return U.useDebugValue(e),yf(this,this.selectors[e],t,n,r)}useContextCallback(e,t){U.useDebugValue(e);let n=dn(t??It);this.context[e]=n}useStateSetter(e){let t=U.useRef(void 0);return t.current===void 0&&(t.current=t=>{this.set(e,t)}),t.current}observe(e,t){let n;n=typeof e==`function`?e:this.selectors[e];let r=n(this.state);return t(r,r,this),this.subscribe(e=>{let i=n(e);if(!Object.is(r,i)){let e=r;r=i,t(i,e,this)}})}},Tf={open:Q(e=>e.open),transitionStatus:Q(e=>e.transitionStatus),domReferenceElement:Q(e=>e.domReferenceElement),referenceElement:Q(e=>e.positionReference??e.referenceElement),floatingElement:Q(e=>e.floatingElement),floatingId:Q(e=>e.floatingId)},Ef=class extends wf{constructor(e){let{syncOnly:t,nested:n,onOpenChange:r,triggerElements:i,...a}=e;super({...a,positionReference:a.referenceElement,domReferenceElement:a.referenceElement},{onOpenChange:r,dataRef:{current:{}},events:Nu(),nested:n,triggerElements:i},Tf),this.syncOnly=t}syncOpenEvent=(e,t)=>{(!e||!this.state.open||t!=null&&Qs(t))&&(this.context.dataRef.current.openEvent=e?t:void 0)};dispatchOpenChange=(e,t)=>{this.syncOpenEvent(e,t.event);let n={open:e,reason:t.reason,nativeEvent:t.event,nested:this.context.nested,triggerElement:t.trigger};this.context.events.emit(`openchange`,n)};setOpen=(e,t)=>{if(this.syncOnly){this.context.onOpenChange?.(e,t);return}this.dispatchOpenChange(e,t),this.context.onOpenChange?.(e,t)}};function Df(e){let{popupStore:t,treatPopupAsFloatingElement:n=!1,floatingRootContext:r,floatingId:i,nested:a,onOpenChange:o}=e,s=t.useState(`open`),c=t.useState(`activeTriggerElement`),l=t.useState(n?`popupElement`:`positionerElement`),u=t.context.triggerElements,d=o,f=U.useRef(null);r===void 0&&f.current===null&&(f.current=new Ef({open:s,transitionStatus:void 0,referenceElement:c,floatingElement:l,triggerElements:u,onOpenChange:d,floatingId:i,syncOnly:!0,nested:a}));let p=r??f.current;return t.useSyncedValue(`floatingId`,i),B(()=>{let e={open:s,floatingId:i,referenceElement:c,floatingElement:l};V(c)&&(e.domReferenceElement=c),p.state.positionReference===p.state.referenceElement&&(e.positionReference=c),p.update(e)},[s,i,c,l,p]),p.context.onOpenChange=d,p.context.nested=a,p}var Of={tabIndex:-1,[js]:``};function kf(e,t,n=!1){let r=_n(),i=Iu()!=null,a=U.useRef(null);e===void 0&&a.current===null&&(a.current=t(r,i));let o=e??a.current;return Df({popupStore:o,treatPopupAsFloatingElement:n,floatingRootContext:o.state.floatingRootContext,floatingId:r,nested:i,onOpenChange:o.setOpen}),{store:o,internalStore:a.current}}function Af(e,t){let n=U.useRef(null),r=U.useRef(null);return U.useCallback(i=>{if(e===void 0)return;let a=!1;if(n.current!==null){let e=n.current,i=r.current,o=t.context.triggerElements.getById(e);i&&o===i&&(t.context.triggerElements.delete(e),a=!0),n.current=null,r.current=null}if(i!==null&&(n.current=e,r.current=i,t.context.triggerElements.add(e,i),a=!0),a){let e=t.context.triggerElements.size;t.select(`open`)&&t.state.triggerCount!==e&&t.set(`triggerCount`,e)}},[t,e])}function jf(e,t,n){let r=n?.id??null;(r||t)&&(e.activeTriggerId=r,e.activeTriggerElement=n??null)}function Mf(e,t,n,r){let i=n.useState(`isMountedByTrigger`,e),a=Af(e,n),o=dn(t=>{if(a(t),!t)return;let i=n.select(`open`),o=n.select(`activeTriggerId`);if(o===e){n.update({activeTriggerElement:t,...i?r:null});return}o==null&&i&&n.update({activeTriggerId:e,activeTriggerElement:t,...r})});return B(()=>{i&&n.update({activeTriggerElement:t.current,...r})},[i,n,t,...Object.values(r)]),{registerTrigger:o,isMountedByThisTrigger:i}}function Nf(e){let t=e.useState(`open`);B(()=>{if(!t){e.state.triggerCount!==0&&e.set(`triggerCount`,0);return}let n=e.context.triggerElements.size,r={};if(e.state.triggerCount!==n&&(r.triggerCount=n),!e.select(`activeTriggerId`)&&n===1){let t=e.context.triggerElements.entries().next();if(!t.done){let[e,n]=t.value;r.activeTriggerId=e,r.activeTriggerElement=n}}(r.triggerCount!==void 0||r.activeTriggerId!==void 0)&&e.update(r)},[t,e,e.useState(`triggerCount`)])}function Pf(e,t,n){let{mounted:r,setMounted:i,transitionStatus:a}=wl(e);t.useSyncedValues({mounted:r,transitionStatus:a});let o=dn(()=>{i(!1),t.update({activeTriggerId:null,activeTriggerElement:null,mounted:!1,preventUnmountingOnClose:!1}),n?.(),t.context.onOpenChangeComplete?.(!1)}),s=t.useState(`preventUnmountingOnClose`);return Cl({enabled:r&&!e&&!s,open:e,ref:t.context.popupRef,onComplete(){e||o()}}),{forceUnmount:o,transitionStatus:a}}function Ff(e,t){e.useSyncedValues(t),B(()=>()=>{e.update({activeTriggerProps:jt,inactiveTriggerProps:jt,popupProps:jt})},[e])}function If(e,t){B(()=>{!t&&e.state.openMethod!==null&&e.set(`openMethod`,null)},[t,e]),B(()=>()=>{e.state.openMethod!==null&&e.set(`openMethod`,null)},[e])}var Lf=class{constructor(){this.elementsSet=new Set,this.idMap=new Map}add(e,t){let n=this.idMap.get(e);n!==t&&(n!==void 0&&this.elementsSet.delete(n),this.elementsSet.add(t),this.idMap.set(e,t))}delete(e){let t=this.idMap.get(e);t&&(this.elementsSet.delete(t),this.idMap.delete(e))}hasElement(e){return this.elementsSet.has(e)}hasMatchingElement(e){for(let t of this.elementsSet)if(e(t))return!0;return!1}getById(e){return this.idMap.get(e)}entries(){return this.idMap.entries()}elements(){return this.elementsSet.values()}get size(){return this.idMap.size}};function Rf(){return new Ef({open:!1,transitionStatus:void 0,floatingElement:null,referenceElement:null,triggerElements:new Lf,floatingId:void 0,syncOnly:!1,nested:!1,onOpenChange:void 0})}function zf(){return{open:!1,openProp:void 0,mounted:!1,transitionStatus:void 0,floatingRootContext:Rf(),floatingId:void 0,triggerCount:0,preventUnmountingOnClose:!1,payload:void 0,activeTriggerId:null,activeTriggerElement:null,triggerIdProp:void 0,popupElement:null,positionerElement:null,activeTriggerProps:jt,inactiveTriggerProps:jt,popupProps:jt}}function Bf(e,t,n=!1){return new Ef({open:!1,transitionStatus:void 0,floatingElement:null,referenceElement:null,triggerElements:e,floatingId:t,syncOnly:!0,nested:n,onOpenChange:void 0})}var Vf=Q(e=>e.triggerIdProp??e.activeTriggerId),Hf=Q(e=>e.openProp??e.open),Uf=Q(e=>(e.popupElement?.id??e.floatingId)||void 0);function Wf(e,t){return t!==void 0&&Hf(e)&&Vf(e)===t}function Gf(e,t){return Wf(e,t)?!0:t!==void 0&&Hf(e)&&Vf(e)==null&&e.triggerCount===1}var Kf={open:Hf,mounted:Q(e=>e.mounted),transitionStatus:Q(e=>e.transitionStatus),floatingRootContext:Q(e=>e.floatingRootContext),triggerCount:Q(e=>e.triggerCount),preventUnmountingOnClose:Q(e=>e.preventUnmountingOnClose),payload:Q(e=>e.payload),activeTriggerId:Vf,activeTriggerElement:Q(e=>e.mounted?e.activeTriggerElement:null),popupId:Uf,isTriggerActive:Q((e,t)=>t!==void 0&&Vf(e)===t),isOpenedByTrigger:Q((e,t)=>Wf(e,t)),isMountedByTrigger:Q((e,t)=>t!==void 0&&Vf(e)===t&&e.mounted),triggerProps:Q((e,t)=>t?e.activeTriggerProps:e.inactiveTriggerProps),triggerPopupId:Q((e,t)=>Gf(e,t)?Uf(e):void 0),popupProps:Q(e=>e.popupProps),popupElement:Q(e=>e.popupElement),positionerElement:Q(e=>e.positionerElement)};function qf(e){let{open:t=!1,onOpenChange:n,elements:r={}}=e,i=_n(),a=Iu()!=null,o=Pt(()=>new Ef({open:t,transitionStatus:void 0,onOpenChange:n,referenceElement:r.reference??null,floatingElement:r.floating??null,triggerElements:new Lf,floatingId:i,syncOnly:!1,nested:a})).current;return B(()=>{let e={open:t,floatingId:i};r.reference!==void 0&&(e.referenceElement=r.reference,e.domReferenceElement=V(r.reference)?r.reference:null),r.floating!==void 0&&(e.floatingElement=r.floating),o.update(e)},[t,i,r.reference,r.floating,o]),o.context.onOpenChange=n,o.context.nested=a,o}function Jf(e={}){let{nodeId:t,externalTree:n}=e,r=qf(e),i=e.rootContext||r,a=i.useState(`referenceElement`),o=i.useState(`floatingElement`),s=i.useState(`domReferenceElement`),c=i.useState(`open`),l=i.useState(`floatingId`),[u,d]=U.useState(null),[f,p]=U.useState(void 0),[m,h]=U.useState(void 0),g=U.useRef(null),_=Lu(n),v=U.useMemo(()=>({reference:a,floating:o,domReference:s}),[a,o,s]),y=tf({...e,elements:{...v,...u&&{reference:u}}}),b=V(f)?f:null,x=m===void 0?i.state.floatingElement:m;i.useSyncedValue(`referenceElement`,f??null),i.useSyncedValue(`domReferenceElement`,f===void 0?s:b),i.useSyncedValue(`floatingElement`,x);let S=U.useCallback(e=>{let t=V(e)?{getBoundingClientRect:()=>e.getBoundingClientRect(),getClientRects:()=>e.getClientRects(),contextElement:e}:e;d(t),y.refs.setReference(t)},[y.refs]),C=U.useCallback(e=>{(V(e)||e===null)&&(g.current=e,p(e)),(V(y.refs.reference.current)||y.refs.reference.current===null||e!==null&&!V(e))&&y.refs.setReference(e)},[y.refs,p]),w=U.useCallback(e=>{h(e),y.refs.setFloating(e)},[y.refs]),T=U.useMemo(()=>({...y.refs,setReference:C,setFloating:w,setPositionReference:S,domReference:g}),[y.refs,C,w,S]),E=U.useMemo(()=>({...y.elements,domReference:s}),[y.elements,s]),D=U.useMemo(()=>({...y,dataRef:i.context.dataRef,open:c,onOpenChange:i.setOpen,events:i.context.events,floatingId:l,refs:T,elements:E,nodeId:t,rootStore:i}),[y,T,E,t,i,c,l]);return B(()=>{s&&(g.current=s)},[s]),B(()=>{i.context.dataRef.current.floatingContext=D;let e=_?.nodesRef.current.find(e=>e.id===t);e&&(e.context=D)}),U.useMemo(()=>({...y,context:D,refs:T,elements:E,rootStore:i}),[y,T,E,D,i])}var Yf=Es&&ws;function Xf(e,t={}){let{enabled:n=!0,delay:r}=t,i=`rootStore`in e?e.rootStore:e,{events:a,dataRef:o}=i.context,s=U.useRef(!1),c=U.useRef(null),l=U.useRef(!0),u=dl();U.useEffect(()=>{let e=i.select(`domReferenceElement`);if(!n)return;let t=$t(e);function r(){let e=i.select(`domReferenceElement`);!i.select(`open`)&&en(e)&&e===jn(Mn(e))&&(s.current=!0)}function a(){l.current=!0}function o(){l.current=!1}return nu(X(t,`blur`,r),Yf&&X(t,`keydown`,a,!0),Yf&&X(t,`pointerdown`,o,!0))},[i,n]),U.useEffect(()=>{if(!n)return;function e(e){if(e.reason===`trigger-press`||e.reason===`escape-key`){let e=i.select(`domReferenceElement`);V(e)&&(c.current=e,s.current=!0)}}return a.on(`openchange`,e),()=>{a.off(`openchange`,e)}},[a,n,i]);let d=U.useMemo(()=>{function e(){s.current=!1,c.current=null}return{onMouseLeave(){e()},onFocus(t){let n=t.currentTarget;if(s.current){if(c.current===n)return;e()}let a=bn(t.nativeEvent);if(V(a)){if(Yf&&!t.relatedTarget){if(!l.current&&!Bs(a))return}else if(!Us(a))return}let o=Ls(t.relatedTarget,i.context.triggerElements),{nativeEvent:d,currentTarget:f}=t,p=typeof r==`function`?r():r;if(i.select(`open`)&&o||p===0||p===void 0){i.setOpen(!0,An(Tn,d,f));return}u.start(p,()=>{s.current||i.setOpen(!0,An(Tn,d,f))})},onBlur(t){e();let n=t.relatedTarget,r=t.nativeEvent,a=V(n)&&n.hasAttribute(lu(`focus-guard`))&&n.getAttribute(`data-type`)===`outside`;u.start(0,()=>{let e=i.select(`domReferenceElement`),t=jn(Mn(e));!n&&t===e||H(o.current.floatingContext?.refs.floating.current,t)||H(e,t)||a||Ls(n??t,i.context.triggerElements)||i.setOpen(!1,An(Tn,r))})}}},[o,r,i,u]);return U.useMemo(()=>n?{reference:d,trigger:d}:{},[n,d])}var Zf=class e{constructor(){this.pointerType=void 0,this.interactedInside=!1,this.handler=void 0,this.blockMouseMove=!0,this.performedPointerEventsMutation=!1,this.pointerEventsScopeElement=null,this.pointerEventsReferenceElement=null,this.pointerEventsFloatingElement=null,this.restTimeoutPending=!1,this.openChangeTimeout=new ul,this.restTimeout=new ul,this.handleCloseOptions=void 0}static create(){return new e}dispose=()=>{this.openChangeTimeout.clear(),this.restTimeout.clear()};disposeEffect=()=>this.dispose},Qf=new WeakMap;function $f(e){if(!e.performedPointerEventsMutation)return;let t=e.pointerEventsScopeElement;t&&Qf.get(t)===e&&(e.pointerEventsScopeElement?.style.removeProperty(`pointer-events`),e.pointerEventsReferenceElement?.style.removeProperty(`pointer-events`),e.pointerEventsFloatingElement?.style.removeProperty(`pointer-events`),Qf.delete(t)),e.performedPointerEventsMutation=!1,e.pointerEventsScopeElement=null,e.pointerEventsReferenceElement=null,e.pointerEventsFloatingElement=null}function ep(e,t){let{scopeElement:n,referenceElement:r,floatingElement:i}=t,a=Qf.get(n);a&&a!==e&&$f(a),$f(e),e.performedPointerEventsMutation=!0,e.pointerEventsScopeElement=n,e.pointerEventsReferenceElement=r,e.pointerEventsFloatingElement=i,Qf.set(n,e),n.style.pointerEvents=`none`,r.style.pointerEvents=`auto`,i.style.pointerEvents=`auto`}function tp(e){let t=e.context.dataRef.current,n=Pt(()=>t.hoverInteractionState??Zf.create()).current;return t.hoverInteractionState||=n,cl(t.hoverInteractionState.disposeEffect),t.hoverInteractionState}function np(e,t={}){let{enabled:n=!0,closeDelay:r=0,nodeId:i}=t,a=`rootStore`in e?e.rootStore:e,o=a.useState(`open`),s=a.useState(`floatingElement`),c=a.useState(`domReferenceElement`),{dataRef:l}=a.context,u=Lu(),d=Iu(),f=tp(a),p=dl(),m=dn(()=>Zl(l.current.openEvent?.type,f.interactedInside)),h=dn(()=>Ql(l.current.openEvent?.type)),g=dn(()=>{$f(f)});B(()=>{o||(f.pointerType=void 0,f.restTimeoutPending=!1,f.interactedInside=!1,g())},[o,f,g]),U.useEffect(()=>g,[g]),B(()=>{if(n&&o&&f.handleCloseOptions?.blockPointerEvents&&h()&&V(c)&&s){let e=c,t=s,n=Mn(s),r=u?.nodesRef.current.find(e=>e.id===d)?.context?.elements.floating;r&&(r.style.pointerEvents=``);let i=f.pointerEventsScopeElement===t?null:f.pointerEventsScopeElement,a=r===t?null:r;return ep(f,{scopeElement:f.handleCloseOptions?.getScope?.()??i??a??e.closest(`[data-rootownerid]`)??n.body,referenceElement:e,floatingElement:t}),()=>{g()}}},[n,o,c,s,f,h,u,d,g]),U.useEffect(()=>{if(!n)return;function e(){return!!(u&&d&&Gs(u.nodesRef.current,d).length>0)}function t(e){let t=Yl(r,`close`,f.pointerType),n=()=>{a.setOpen(!1,An(Cn,e)),u?.events.emit(`floating.closed`,e)};t?f.openChangeTimeout.start(t,n):(f.openChangeTimeout.clear(),n())}function o(e){let t=bn(e);if(!Vs(t)){f.interactedInside=!1;return}f.interactedInside=t?.closest(`[aria-haspopup]`)!=null}function c(){f.openChangeTimeout.clear(),p.clear(),u?.events.off(`floating.closed`,_),g()}function h(n){if(e()&&u){u.events.on(`floating.closed`,_);return}if(Ls(n.relatedTarget,a.context.triggerElements))return;let r=l.current.floatingContext?.nodeId??i,o=n.relatedTarget;if(!(u&&r&&V(o)&&Gs(u.nodesRef.current,r,!1).some(e=>H(e.context?.elements.floating,o)))){if(f.handler){f.handler(n);return}g(),m()||t(n)}}function _(t){!u||!d||e()||p.start(0,()=>{u.events.off(`floating.closed`,_),a.setOpen(!1,An(Cn,t)),u.events.emit(`floating.closed`,t)})}let v=s;return nu(v&&X(v,`mouseenter`,c),v&&X(v,`mouseleave`,h),v&&X(v,`pointerdown`,o,!0),()=>{u?.events.off(`floating.closed`,_)})},[n,s,a,l,r,i,m,g,f,u,d,p])}var rp={current:null};function ip(e,t={}){let{enabled:n=!0,delay:r=0,handleClose:i=null,mouseOnly:a=!1,restMs:o=0,move:s=!0,triggerElementRef:c=rp,externalTree:l,isActiveTrigger:u=!0,getHandleCloseContext:d,isClosing:f,shouldOpen:p}=t,m=`rootStore`in e?e.rootStore:e,{dataRef:h,events:g}=m.context,_=Lu(l),v=tp(m),y=U.useRef(!1),b=ru(i),x=ru(r),S=ru(o),C=ru(n),w=ru(p),T=ru(f),E=dn(()=>Zl(h.current.openEvent?.type,v.interactedInside)),D=dn(()=>w.current?.()!==!1),O=dn((e,t,n)=>{let r=m.context.triggerElements;if(r.hasElement(t))return!e||!H(e,t);if(!V(n))return!1;let i=n;return r.hasMatchingElement(e=>H(e,i))&&(!e||!H(e,i))}),k=dn(()=>{v.handler&&=(Mn(m.select(`domReferenceElement`)).removeEventListener(`mousemove`,v.handler),void 0)}),A=dn(()=>{$f(v)});return u&&(v.handleCloseOptions=b.current?.__options),U.useEffect(()=>k,[k]),U.useEffect(()=>{if(!n)return;function e(e){e.open?y.current=!1:(y.current=e.reason===Cn,k(),v.openChangeTimeout.clear(),v.restTimeout.clear(),v.blockMouseMove=!0,v.restTimeoutPending=!1)}return g.on(`openchange`,e),()=>{g.off(`openchange`,e)}},[n,g,v,k]),U.useEffect(()=>{if(!n)return;function e(e,t=!0){let n=Yl(x.current,`close`,v.pointerType);n?v.openChangeTimeout.start(n,()=>{m.setOpen(!1,An(Cn,e)),_?.events.emit(`floating.closed`,e)}):t&&(v.openChangeTimeout.clear(),m.setOpen(!1,An(Cn,e)),_?.events.emit(`floating.closed`,e))}let t=c.current??(u?m.select(`domReferenceElement`):null);if(!V(t))return;function r(e){if(v.openChangeTimeout.clear(),v.blockMouseMove=!1,a&&!Zs(v.pointerType))return;let t=Xl(S.current),n=Yl(x.current,`open`,v.pointerType),r=bn(e),i=e.currentTarget??null,o=m.select(`domReferenceElement`),s=i;if(V(r)&&!m.context.triggerElements.hasElement(r)){for(let e of m.context.triggerElements.elements())if(H(e,r)){s=e;break}}V(i)&&V(o)&&!m.context.triggerElements.hasElement(i)&&H(i,o)&&(s=o);let c=s==null?!1:O(o,s,r),l=m.select(`open`),u=T.current?.()??m.select(`transitionStatus`)===`ending`,d=!l&&u&&y.current,f=!c&&V(s)&&V(o)&&H(o,s)&&d,p=t>0&&!n,h=c&&(l||d)||f,g=!l||c;if(h){D()&&m.setOpen(!0,An(Cn,e,s));return}p||(n?v.openChangeTimeout.start(n,()=>{g&&D()&&m.setOpen(!0,An(Cn,e,s))}):g&&D()&&m.setOpen(!0,An(Cn,e,s)))}function i(t){if(E()){A();return}k();let n=Mn(m.select(`domReferenceElement`));v.restTimeout.clear(),v.restTimeoutPending=!1;let r=h.current.floatingContext??d?.();if(!Ls(t.relatedTarget,m.context.triggerElements)){if(b.current&&r){m.select(`open`)||v.openChangeTimeout.clear();let i=c.current;v.handler=b.current({...r,tree:_,x:t.clientX,y:t.clientY,onClose(){A(),k(),C.current&&!E()&&i===m.select(`domReferenceElement`)&&e(t,!0)}}),n.addEventListener(`mousemove`,v.handler),v.handler(t);return}(v.pointerType!==`touch`||!H(m.select(`floatingElement`),t.relatedTarget))&&e(t)}}return s?nu(X(t,`mousemove`,r,{once:!0}),X(t,`mouseenter`,r),X(t,`mouseleave`,i)):nu(X(t,`mouseenter`,r),X(t,`mouseleave`,i))},[k,A,h,x,m,n,b,v,u,O,E,a,s,S,c,_,C,d,T,D]),U.useMemo(()=>{if(!n)return;function e(e){v.pointerType=e.pointerType}return{onPointerDown:e,onPointerEnter:e,onMouseMove(e){let{nativeEvent:t}=e,n=e.currentTarget,r=m.select(`domReferenceElement`),i=m.select(`open`),o=O(r,n,e.target);if(a&&!Zs(v.pointerType))return;if(i&&o&&v.handleCloseOptions?.blockPointerEvents){let e=m.select(`floatingElement`);e&&ep(v,{scopeElement:v.handleCloseOptions?.getScope?.()??n.ownerDocument.body,referenceElement:n,floatingElement:e})}let s=Xl(S.current);if(i&&!o||s===0||!o&&v.restTimeoutPending&&e.movementX**2+e.movementY**2<2)return;v.restTimeout.clear();function c(){if(v.restTimeoutPending=!1,E())return;let e=m.select(`open`);!v.blockMouseMove&&(!e||o)&&D()&&m.setOpen(!0,An(Cn,t,n))}v.pointerType===`touch`?xl.flushSync(()=>{c()}):o&&i?c():(v.restTimeoutPending=!0,v.restTimeout.start(s,c))}}},[n,v,E,O,a,m,S,D])}var ap=.1,op=ap*ap,sp=.5;function cp(e,t,n,r,i,a){return r>=t!=a>=t&&e<=(i-n)*(t-r)/(a-r)+n}function lp(e,t,n,r,i,a,o,s,c,l){let u=!1;return cp(e,t,n,r,i,a)&&(u=!u),cp(e,t,i,a,o,s)&&(u=!u),cp(e,t,o,s,c,l)&&(u=!u),cp(e,t,c,l,n,r)&&(u=!u),u}function up(e,t,n){return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}function dp(e,t,n,r,i,a){return e>=Math.min(n,i)&&e<=Math.max(n,i)&&t>=Math.min(r,a)&&t<=Math.max(r,a)}function fp(e={}){let{blockPointerEvents:t=!1}=e,n=new ul,r=({x:e,y:t,placement:r,elements:i,onClose:a,nodeId:o,tree:s})=>{let c=r?.split(`-`)[0],l=!1,u=null,d=null,f=typeof performance<`u`?performance.now():0;function p(e,t){let n=performance.now(),r=n-f;if(u===null||d===null||r===0)return u=e,d=t,f=n,!1;let i=e-u,a=t-d,o=i*i+a*a,s=r*r*op;return u=e,d=t,f=n,o0)}function b(){y()||m()}if(y())return;let x=a.getBoundingClientRect(),S=u.getBoundingClientRect(),C=e>S.right-S.width/2,w=t>S.bottom-S.height/2,T=S.width>x.width,E=S.height>x.height,D=(T?x:S).left,O=(T?x:S).right,k=(E?x:S).top,A=(E?x:S).bottom;if(c===`top`&&t>=x.bottom-1||c===`bottom`&&t<=x.top+1||c===`left`&&e>=x.right-1||c===`right`&&e<=x.left+1){b();return}let j=!1;switch(c){case`top`:j=dp(d,f,D,x.top+1,O,S.bottom-1);break;case`bottom`:j=dp(d,f,D,S.top+1,O,x.bottom-1);break;case`left`:j=dp(d,f,S.right-1,A,x.left+1,k);break;case`right`:j=dp(d,f,x.right-1,A,S.left+1,k);break;default:}if(j)return;if(l&&!up(d,f,x)){b();return}if(!g&&p(d,f)){b();return}let M=!1;switch(c){case`top`:{let n=T?sp/2:sp*4,r=T||C?e+n:e-n,i=T?e-n:C?e+n:e-n,a=t+sp+1,o=C||T?S.bottom-sp:S.top,s=C?T?S.bottom-sp:S.top:S.bottom-sp;M=lp(d,f,r,a,i,a,S.left,o,S.right,s);break}case`bottom`:{let n=T?sp/2:sp*4,r=T||C?e+n:e-n,i=T?e-n:C?e+n:e-n,a=t-sp,o=C||T?S.top+sp:S.bottom,s=C?T?S.top+sp:S.bottom:S.top+sp;M=lp(d,f,r,a,i,a,S.left,o,S.right,s);break}case`left`:{let n=E?sp/2:sp*4,r=E||w?t+n:t-n,i=E?t-n:w?t+n:t-n,a=e+sp+1,o=w||E?S.right-sp:S.left,s=w?E?S.right-sp:S.left:S.right-sp;M=lp(d,f,o,S.top,s,S.bottom,a,r,a,i);break}case`right`:{let n=E?sp/2:sp*4,r=E||w?t+n:t-n,i=E?t-n:w?t+n:t-n,a=e-sp,o=w||E?S.left+sp:S.right,s=w?E?S.left+sp:S.right:S.left+sp;M=lp(d,f,a,r,a,i,o,S.top,s,S.bottom);break}default:}M?l||n.start(40,b):b()}};return r.__options={...e,blockPointerEvents:t},r}var pp=function(e){return e.nestedDialogs=`--nested-dialogs`,e}({}),mp=function(e){return e[e.open=Fl.open]=`open`,e[e.closed=Fl.closed]=`closed`,e[e.startingStyle=Fl.startingStyle]=`startingStyle`,e[e.endingStyle=Fl.endingStyle]=`endingStyle`,e.nested=`data-nested`,e.nestedDialogOpen=`data-nested-dialog-open`,e}({}),hp=U.createContext(void 0);function gp(){let e=U.useContext(hp);if(e===void 0)throw Error(zt(26));return e}var _p=`ArrowUp`,vp=`ArrowDown`,yp=`ArrowLeft`,bp=`ArrowRight`,xp=`Home`,Sp=new Set([yp,bp]),Cp=new Set([yp,bp,xp,`End`]),wp=new Set([_p,vp]),Tp=new Set([_p,vp,xp,`End`]),Ep=new Set([...Sp,...wp]),Dp=new Set([...Ep,xp,`End`]),Op=new Set([`Shift`,`Control`,`Alt`,`Meta`]);function kp(e){return en(e)&&e.tagName===`INPUT`}function Ap(e){return!!(kp(e)&&e.selectionStart!=null||en(e)&&e.tagName===`TEXTAREA`)}function jp(e,t,n,r){if(!e||!t||!t.scrollTo)return;let i=e.scrollLeft,a=e.scrollTop,o=e.clientWidthe.scrollLeft+e.clientWidth-a.scrollPaddingRight?i=r+t.offsetWidth+o.scrollMarginRight-e.clientWidth+a.scrollPaddingRight:r-o.scrollMarginLefte.scrollLeft+e.clientWidth-a.scrollPaddingRight&&(i=r+t.offsetWidth+o.scrollMarginRight-e.clientWidth+a.scrollPaddingRight))}if(s&&r!==`horizontal`){let n=Mp(e,t,`top`),r=Np(e),i=Np(t);n-i.scrollMarginTope.scrollTop+e.clientHeight-r.scrollPaddingBottom&&(a=n+t.offsetHeight+i.scrollMarginBottom-e.clientHeight+r.scrollPaddingBottom)}e.scrollTo({left:i,top:a,behavior:`auto`})}function Mp(e,t,n){let r=n===`left`?`offsetLeft`:`offsetTop`,i=0;for(;t.offsetParent&&(i+=t[r],t.offsetParent!==e);)t=t.offsetParent;return i}function Np(e){let t=getComputedStyle(e);return{scrollMarginTop:parseFloat(t.scrollMarginTop)||0,scrollMarginRight:parseFloat(t.scrollMarginRight)||0,scrollMarginBottom:parseFloat(t.scrollMarginBottom)||0,scrollMarginLeft:parseFloat(t.scrollMarginLeft)||0,scrollPaddingTop:parseFloat(t.scrollPaddingTop)||0,scrollPaddingRight:parseFloat(t.scrollPaddingRight)||0,scrollPaddingBottom:parseFloat(t.scrollPaddingBottom)||0,scrollPaddingLeft:parseFloat(t.scrollPaddingLeft)||0}}var Pp={...Ul,...bl,nestedDialogOpen(e){return e?{[mp.nestedDialogOpen]:``}:null}},Fp=U.forwardRef(function(e,t){let{render:n,className:r,style:i,finalFocus:a,initialFocus:o,...s}=e,{store:c}=Pl(),l=c.useState(`descriptionElementId`),u=c.useState(`disablePointerDismissal`),d=c.useState(`floatingRootContext`),f=c.useState(`popupProps`),p=c.useState(`modal`),m=c.useState(`mounted`),h=c.useState(`nested`),g=c.useState(`nestedOpenDialogCount`),_=c.useState(`open`),v=c.useState(`openMethod`),y=c.useState(`titleElementId`),b=c.useState(`transitionStatus`),x=c.useState(`role`),S=d.useState(`floatingId`),C=s.id??S;gp(),Cl({open:_,ref:c.context.popupRef,onComplete(){_&&c.context.onOpenChangeComplete?.(!0)}});function w(e){return e===`touch`?c.context.popupRef.current:!0}let T=o===void 0?w:o,E=g>0,D=c.useStateSetter(`popupElement`),O=Ft(`div`,e,{state:{open:_,nested:h,transitionStatus:b,nestedDialogOpen:E},props:[f,{id:C,"aria-labelledby":y??void 0,"aria-describedby":l??void 0,role:x,...Of,hidden:!m,onKeyDown(e){Dp.has(e.key)&&e.stopPropagation()},style:{[pp.nestedDialogs]:g}},s],ref:[t,c.context.popupRef,D],stateAttributesMapping:Pp});return(0,W.jsx)(Ku,{context:d,openInteractionType:v,disabled:!m,closeOnFocusOut:!u,initialFocus:T,returnFocus:a,modal:p!==!1,restoreFocus:`popup`,children:O})});function Ip(e){return Lt(19)?e:e?`true`:void 0}var Lp=U.forwardRef(function(e,t){let{cutout:n,...r}=e,i;if(n){let e=n.getBoundingClientRect();i=`polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${e.left}px ${e.top}px,${e.left}px ${e.bottom}px,${e.right}px ${e.bottom}px,${e.right}px ${e.top}px,${e.left}px ${e.top}px)`}return(0,W.jsx)(`div`,{ref:t,role:`presentation`,"data-base-ui-inert":``,...r,style:{position:`fixed`,inset:0,userSelect:`none`,WebkitUserSelect:`none`,clipPath:i}})}),Rp=U.forwardRef(function(e,t){let{keepMounted:n=!1,...r}=e,{store:i}=Pl(),a=i.useState(`mounted`),o=i.useState(`modal`),s=i.useState(`open`);return a||n?(0,W.jsx)(hp.Provider,{value:n,children:(0,W.jsxs)(Mu,{ref:t,...r,children:[a&&o===!0&&(0,W.jsx)(Lp,{ref:i.context.internalBackdropRef,inert:Ip(!s)}),e.children]})}):null});function zp(e){let t=U.useRef(!0);t.current&&(t.current=!1,e())}var Bp={},Vp={},Hp=``;function Up(e){if(typeof document>`u`)return!1;let t=Mn(e);return $t(t).innerWidth-t.documentElement.clientWidth>0}function Wp(e){if(!(typeof CSS<`u`&&CSS.supports&&CSS.supports(`scrollbar-gutter`,`stable`))||typeof document>`u`)return!1;let t=Mn(e),n=t.documentElement,r=t.body,i=Jt(n)?n:r,a=i.style.overflowY,o=n.style.scrollbarGutter;n.style.scrollbarGutter=`stable`,i.style.overflowY=`scroll`;let s=i.offsetWidth;i.style.overflowY=`hidden`;let c=i.offsetWidth;return i.style.overflowY=a,n.style.scrollbarGutter=o,s===c}function Gp(e){let t=Mn(e),n=t.documentElement,r=t.body,i=Jt(n)?n:r,a={overflowY:i.style.overflowY,overflowX:i.style.overflowX};return Object.assign(i.style,{overflowY:`hidden`,overflowX:`hidden`}),()=>{Object.assign(i.style,a)}}function Kp(e){let t=Mn(e),n=t.documentElement,r=t.body,i=$t(n),a=0,o=0,s=!1,c=ml.create();if(Ss&&(i.visualViewport?.scale??1)!==1)return()=>{};function l(){let t=i.getComputedStyle(n),c=i.getComputedStyle(r),l=(t.scrollbarGutter||``).includes(`both-edges`)?`stable both-edges`:`stable`;a=n.scrollTop,o=n.scrollLeft,Bp={scrollbarGutter:n.style.scrollbarGutter,overflowY:n.style.overflowY,overflowX:n.style.overflowX},Hp=n.style.scrollBehavior,Vp={position:r.style.position,height:r.style.height,width:r.style.width,boxSizing:r.style.boxSizing,overflowY:r.style.overflowY,overflowX:r.style.overflowX,scrollBehavior:r.style.scrollBehavior};let u=n.scrollHeight>n.clientHeight,d=n.scrollWidth>n.clientWidth,f=t.overflowY===`scroll`||c.overflowY===`scroll`,p=t.overflowX===`scroll`||c.overflowX===`scroll`,m=Math.max(0,i.innerWidth-r.clientWidth),h=Math.max(0,i.innerHeight-r.clientHeight),g=parseFloat(c.marginTop)+parseFloat(c.marginBottom),_=parseFloat(c.marginLeft)+parseFloat(c.marginRight),v=Jt(n)?n:r;if(s=Wp(e),s){n.style.scrollbarGutter=l,v.style.overflowY=`hidden`,v.style.overflowX=`hidden`;return}Object.assign(n.style,{scrollbarGutter:l,overflowY:`hidden`,overflowX:`hidden`}),(u||f)&&(n.style.overflowY=`scroll`),(d||p)&&(n.style.overflowX=`scroll`),Object.assign(r.style,{position:`relative`,height:g||h?`calc(100dvh - ${g+h}px)`:`100dvh`,width:_||m?`calc(100vw - ${_+m}px)`:`100vw`,boxSizing:`border-box`,overflow:`hidden`,scrollBehavior:`unset`}),r.scrollTop=a,r.scrollLeft=o,n.setAttribute(`data-base-ui-scroll-locked`,``),n.style.scrollBehavior=`unset`}function u(){Object.assign(n.style,Bp),Object.assign(r.style,Vp),s||(n.scrollTop=a,n.scrollLeft=o,n.removeAttribute(`data-base-ui-scroll-locked`),n.style.scrollBehavior=Hp)}function d(){u(),c.request(l)}l();let f=X(i,`resize`,d);return()=>{c.cancel(),u(),typeof i.removeEventListener==`function`&&f()}}var qp=new class{lockCount=0;restore=null;timeoutLock=ul.create();timeoutUnlock=ul.create();acquire(e){return this.lockCount+=1,this.lockCount===1&&this.restore===null&&this.timeoutLock.start(0,()=>this.lock(e)),this.release}release=()=>{--this.lockCount,this.lockCount===0&&this.restore&&this.timeoutUnlock.start(0,this.unlock)};unlock=()=>{this.lockCount===0&&this.restore&&(this.restore?.(),this.restore=null)};lock(e){if(this.lockCount===0||this.restore!==null)return;let t=Mn(e).documentElement,n=$t(t).getComputedStyle(t).overflowY;if(n===`hidden`||n===`clip`){this.restore=It;return}let r=Cs||!Up(e);this.restore=r?Gp(e):Kp(e)}};function Jp(e=!0,t=null){B(()=>{if(e)return qp.acquire(t)},[e,t])}function Yp(e){let{store:t,parentContext:n,actionsRef:r,isDrawer:i}=e,a=t.useState(`open`);If(t,a),Nf(t);let{forceUnmount:o}=Pf(a,t),s=U.useCallback(()=>{t.setOpen(!1,An(kn))},[t]);return U.useImperativeHandle(r,()=>({unmount:o,close:s}),[o,s]),{parentContext:n,isDrawer:i}}function Xp({store:e,dialogRoot:t}){let{parentContext:n,isDrawer:r}=t,i=e.useState(`open`),a=e.useState(`disablePointerDismissal`),o=e.useState(`modal`),s=e.useState(`popupElement`),c=e.useState(`floatingRootContext`),[l,u]=U.useState(0),[d,f]=U.useState(0),p=l===0,m=$u(c,{outsidePressEvent(){return e.context.internalBackdropRef.current||e.context.backdropRef.current?`intentional`:{mouse:o===`trap-focus`?`sloppy`:`intentional`,touch:`sloppy`}},outsidePress(t){if(!e.context.outsidePressEnabledRef.current||`button`in t&&t.button!==0||`touches`in t&&t.touches.length!==1)return!1;let n=bn(t);if(p&&!a){let t=n;return o&&(e.context.internalBackdropRef.current||e.context.backdropRef.current)?e.context.internalBackdropRef.current===t||e.context.backdropRef.current===t||H(t,s)&&!t?.hasAttribute(`data-base-ui-portal`):!0}return!1},escapeKey:p});return Jp(i&&o===!0,s),e.useContextCallback(`onNestedDialogOpen`,(e,t)=>{u(e),f(t)}),e.useContextCallback(`onNestedDialogClose`,()=>{u(0),f(0)}),U.useEffect(()=>(n?.onNestedDialogOpen&&i&&n.onNestedDialogOpen(l+1,d+ +!!r),n?.onNestedDialogClose&&!i&&n.onNestedDialogClose(),()=>{n?.onNestedDialogClose&&i&&n.onNestedDialogClose()}),[r,i,l,d,n]),Ff(e,{activeTriggerProps:m.reference??jt,inactiveTriggerProps:m.trigger??jt,popupProps:U.useMemo(()=>Nt(Of,m.floating),[m.floating]),nestedOpenDialogCount:l,nestedOpenDrawerCount:d}),null}var Zp={...Kf,modal:Q(e=>e.modal),nested:Q(e=>e.nested),nestedOpenDialogCount:Q(e=>e.nestedOpenDialogCount),nestedOpenDrawerCount:Q(e=>e.nestedOpenDrawerCount),disablePointerDismissal:Q(e=>e.disablePointerDismissal),openMethod:Q(e=>e.openMethod),descriptionElementId:Q(e=>e.descriptionElementId),titleElementId:Q(e=>e.titleElementId),viewportElement:Q(e=>e.viewportElement),role:Q(e=>e.role)},Qp=class e extends wf{constructor(e,t,n=!1){let r=new Lf,i=$p(e);i.floatingRootContext=Bf(r,t,n),super(i,{popupRef:U.createRef(),backdropRef:U.createRef(),internalBackdropRef:U.createRef(),outsidePressEnabledRef:{current:!0},triggerElements:r,onOpenChange:void 0,onOpenChangeComplete:void 0},Zp)}setOpen=(e,t)=>{if(t.preventUnmountOnClose=()=>{this.set(`preventUnmountingOnClose`,!0)},!e&&t.trigger==null&&this.state.activeTriggerId!=null&&(t.trigger=this.state.activeTriggerElement??void 0),this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let n={open:e};jf(n,e,t.trigger),this.update(n)};static useStore(t,n){return kf(t,(t,r)=>new e(n,t,r),!0).store}};function $p(e={}){return{...zf(),modal:!0,disablePointerDismissal:!1,popupElement:null,viewportElement:null,descriptionElementId:void 0,titleElementId:void 0,openMethod:null,nested:!1,nestedOpenDialogCount:0,nestedOpenDrawerCount:0,role:`dialog`,...e}}function em(e,t=`dialog`){let{children:n,open:r,defaultOpen:i=!1,onOpenChange:a,onOpenChangeComplete:o,disablePointerDismissal:s=!1,modal:c=!0,actionsRef:l,handle:u,triggerId:d,defaultTriggerId:f=null}=e,p=t===`drawer`,m=t===`alert-dialog`,h=m?!0:c,g=m||s,_=m?`alertdialog`:`dialog`,v=Pl(!0),y={modal:h,disablePointerDismissal:g,nested:!!v,role:_},b=Qp.useStore(u?.store,{open:i,openProp:r,activeTriggerId:f,triggerIdProp:d,...y});zp(()=>{let e=r===void 0&&b.state.open===!1&&i===!0?{open:!0,activeTriggerId:f}:null;m?b.update(e?{...y,...e}:y):e&&b.update(e)}),b.useControlledProp(`openProp`,r),b.useControlledProp(`triggerIdProp`,d),b.useSyncedValues(y),b.useContextCallback(`onOpenChange`,a),b.useContextCallback(`onOpenChangeComplete`,o);let x=b.useState(`open`),S=b.useState(`mounted`),C=b.useState(`payload`),w=Yp({store:b,actionsRef:l,parentContext:v?.store.context,isDrawer:p}),T=x||S,E=U.useMemo(()=>({store:b}),[b]);return(0,W.jsx)(Ml.Provider,{value:!1,children:(0,W.jsxs)(Nl.Provider,{value:E,children:[T&&(0,W.jsx)(Xp,{store:b,dialogRoot:w}),typeof n==`function`?n({payload:C}):n]})})}function tm(e){return em(e,U.useContext(Ml)?`drawer`:`dialog`)}var nm=U.forwardRef(function(e,t){let{render:n,className:r,style:i,id:a,...o}=e,{store:s}=Pl(),c=vn(a);return s.useSyncedValueWithCleanup(`titleElementId`,c),Ft(`h2`,e,{ref:t,props:[{id:c},o]})});function rm(e){let t=(0,go.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,W.jsx)(tm,{"data-slot":`sheet`,...n}),t[2]=n,t[3]=r),r}function im(e){let t=(0,go.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,W.jsx)(Rp,{"data-slot":`sheet-portal`,...n}),t[2]=n,t[3]=r),r}function am(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`data-closed:fade-out-0 data-open:fade-in-0 data-closed:animate-out data-open:animate-in fixed inset-0 z-50 bg-black/80 duration-100 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(Gl,{className:i,"data-slot":`sheet-overlay`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function om(e){let t=(0,go.c)(17),n,r,i,a,o;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5]):({className:r,children:n,side:a,showCloseButton:o,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o);let s=a===void 0?`right`:a,c=o===void 0?!0:o,l;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,W.jsx)(am,{}),t[6]=l):l=t[6];let u;t[7]===r?u=t[8]:(u=qt(`data-[side=right]:data-closed:slide-out-to-right-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=top]:data-closed:slide-out-to-top-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:fade-out-0 data-open:fade-in-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=bottom]:data-open:slide-in-from-bottom-10 data-closed:animate-out data-open:animate-in fixed z-50 flex flex-col bg-background bg-clip-padding text-xs/relaxed shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm`,r),t[7]=r,t[8]=u);let d;t[9]===c?d=t[10]:(d=c&&(0,W.jsxs)(Kl,{"data-slot":`sheet-close`,render:(0,W.jsx)(fn,{className:`absolute top-4 right-4`,size:`icon-sm`,variant:`ghost`}),children:[(0,W.jsx)(os,{}),(0,W.jsx)(`span`,{className:`sr-only`,children:`Close`})]}),t[9]=c,t[10]=d);let f;return t[11]!==n||t[12]!==i||t[13]!==s||t[14]!==u||t[15]!==d?(f=(0,W.jsxs)(im,{children:[l,(0,W.jsxs)(Fp,{className:u,"data-side":s,"data-slot":`sheet-content`,...i,children:[n,d]})]}),t[11]=n,t[12]=i,t[13]=s,t[14]=u,t[15]=d,t[16]=f):f=t[16],f}function sm(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`flex flex-col gap-1.5 p-6`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(`div`,{className:i,"data-slot":`sheet-header`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function cm(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`text-sm font-medium text-foreground`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(nm,{className:i,"data-slot":`sheet-title`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function lm(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`text-xs/relaxed text-muted-foreground`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(ql,{className:i,"data-slot":`sheet-description`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}var um=U.createContext(void 0);function dm(e){let t=U.useContext(um);if(t===void 0&&!e)throw Error(zt(72));return t}var fm={...Kf,disabled:Q(e=>e.disabled),instantType:Q(e=>e.instantType),isInstantPhase:Q(e=>e.isInstantPhase),trackCursorAxis:Q(e=>e.trackCursorAxis),disableHoverablePopup:Q(e=>e.disableHoverablePopup),lastOpenChangeReason:Q(e=>e.openChangeReason),closeOnClick:Q(e=>e.closeOnClick),closeDelay:Q(e=>e.closeDelay),hasViewport:Q(e=>e.hasViewport)},pm=class e extends wf{constructor(e,t,n=!1){let r=new Lf,i={...mm(),...e};i.floatingRootContext=Bf(r,t,n),super(i,{popupRef:U.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerElements:r},fm)}setOpen=(e,t)=>{let n=t.reason,r=n===Cn,i=e&&n===`trigger-focus`,a=!e&&(n===`trigger-press`||n===`escape-key`);if(t.preventUnmountOnClose=()=>{this.set(`preventUnmountingOnClose`,!0)},this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let o=()=>{let r={open:e,openChangeReason:n};i?r.instantType=`focus`:a?r.instantType=`dismiss`:n===`trigger-hover`&&(r.instantType=void 0),jf(r,e,t.trigger),this.update(r)};r?xl.flushSync(o):o()};cancelPendingOpen(e){this.state.floatingRootContext.dispatchOpenChange(!1,An(Sn,e))}static useStore(t,n){return kf(t,(t,r)=>new e(n,t,r)).store}};function mm(){return{...zf(),disabled:!1,instantType:void 0,isInstantPhase:!1,trackCursorAxis:`none`,disableHoverablePopup:!1,openChangeReason:null,closeOnClick:!0,closeDelay:0,hasViewport:!1}}var hm=pf(function(e){let{disabled:t=!1,defaultOpen:n=!1,open:r,disableHoverablePopup:i=!1,trackCursorAxis:a=`none`,actionsRef:o,onOpenChange:s,onOpenChangeComplete:c,handle:l,triggerId:u,defaultTriggerId:d=null,children:f}=e,p=pm.useStore(l?.store,{open:n,openProp:r,activeTriggerId:d,triggerIdProp:u});zp(()=>{r===void 0&&p.state.open===!1&&n===!0&&p.update({open:!0,activeTriggerId:d})}),p.useControlledProp(`openProp`,r),p.useControlledProp(`triggerIdProp`,u),p.useContextCallback(`onOpenChange`,s),p.useContextCallback(`onOpenChangeComplete`,c);let m=p.useState(`open`),h=!t&&m,g=p.useState(`activeTriggerId`),_=p.useState(`mounted`),v=p.useState(`payload`);p.useSyncedValues({trackCursorAxis:a,disableHoverablePopup:i}),p.useSyncedValue(`disabled`,t),Nf(p);let{forceUnmount:y,transitionStatus:b}=Pf(h,p),x=p.useState(`isInstantPhase`),S=p.useState(`instantType`),C=p.useState(`lastOpenChangeReason`),w=U.useRef(null);B(()=>{m&&t&&p.setOpen(!1,An(wn))},[m,t,p]),B(()=>{b===`ending`&&C===`none`||b!==`ending`&&x?(S!==`delay`&&(w.current=S),p.set(`instantType`,`delay`)):w.current!==null&&(p.set(`instantType`,w.current),w.current=null)},[b,x,C,S,p]),B(()=>{h&&(g??p.set(`payload`,void 0))},[p,g,h]);let T=U.useCallback(()=>{p.setOpen(!1,An(kn))},[p]);U.useImperativeHandle(o,()=>({unmount:y,close:T}),[y,T]);let E=h||_||!t&&a!==`none`;return(0,W.jsxs)(um.Provider,{value:p,children:[E&&(0,W.jsx)(gm,{store:p,disabled:t,trackCursorAxis:a}),typeof f==`function`?f({payload:v}):f]})});function gm({store:e,disabled:t,trackCursorAxis:n}){let r=e.useState(`floatingRootContext`),i=$u(r,{enabled:!t,referencePress:()=>e.select(`closeOnClick`)}),a=Yu(r,{enabled:!t&&n!==`none`,axis:n===`none`?void 0:n});return Ff(e,{activeTriggerProps:U.useMemo(()=>Nt(a.reference,i.reference),[a.reference,i.reference]),inactiveTriggerProps:U.useMemo(()=>Nt(a.trigger,i.trigger),[a.trigger,i.trigger]),popupProps:U.useMemo(()=>Nt(Of,a.floating,i.floating),[a.floating,i.floating])}),null}var _m=U.createContext(void 0);function vm(){return U.useContext(_m)}var ym=function(e){return e[e.popupOpen=Il.popupOpen]=`popupOpen`,e.triggerDisabled=`data-trigger-disabled`,e}({}),bm=`data-base-ui-tooltip-trigger`;function xm(e){if(`composedPath`in e){let t=e.composedPath();for(let e=0;em.select(`transitionStatus`)===`ending`,shouldOpen(){return!N.current}}),F=Xf(v,{enabled:!A}).reference,oe=e=>{let t=N.current,n=xm(e),r=ie(n),i=y.current,a=i&&n&&H(i,n);if(r&&m.select(`open`)&&m.select(`lastOpenChangeReason`)===`trigger-hover`){m.setOpen(!1,An(Cn,e));return}if(t&&!r&&a&&!j.current&&!m.select(`open`)&&i&&Zs(te.current)){let t=()=>{!N.current&&!j.current&&!m.select(`open`)&&m.setOpen(!0,An(Cn,e,i))},n=ne();n===0?(P.clear(),t()):P.start(n,t)}},se=m.useState(`triggerProps`,C);return Ft(`button`,e,{state:{open:_},ref:[t,S,y],props:[ae,F,C||M!==`none`?se:void 0,{onMouseOver(e){oe(e.nativeEvent)},onFocus(e){re(xm(e.nativeEvent))&&e.preventBaseUIHandler()},onMouseLeave(){N.current=!1,P.clear(),te.current=void 0},onPointerEnter(e){te.current=e.pointerType},onPointerDown(e){te.current=e.pointerType,m.set(`closeOnClick`,l),l&&!m.select(`open`)&&m.cancelPendingOpen(e.nativeEvent)},onClick(e){l&&!m.select(`open`)&&m.cancelPendingOpen(e.nativeEvent)},id:h,[ym.triggerDisabled]:A?``:void 0,[bm]:A?void 0:``},f],stateAttributesMapping:Vl})}),wm=U.createContext(void 0);function Tm(){let e=U.useContext(wm);if(e===void 0)throw Error(zt(70));return e}var Em=U.forwardRef(function(e,t){let{children:n,container:r,className:i,render:a,style:o,...s}=e,{portalNode:c,portalSubtree:l}=ju({container:r,ref:t,componentProps:e,elementProps:s});return!l&&!c?null:(0,W.jsxs)(U.Fragment,{children:[l,c&&xl.createPortal(n,c)]})}),Dm=U.forwardRef(function(e,t){let{keepMounted:n=!1,...r}=e;return dm().useState(`mounted`)||n?(0,W.jsx)(wm.Provider,{value:n,children:(0,W.jsx)(Em,{ref:t,...r})}):null}),Om=U.createContext(void 0);function km(){let e=U.useContext(Om);if(e===void 0)throw Error(zt(71));return e}var Am=U.createContext(void 0);function jm(){return U.useContext(Am)?.direction??`ltr`}var Mm=e=>({name:`arrow`,options:e,async fn(t){let{x:n,y:r,placement:i,rects:a,platform:o,elements:s,middlewareData:c}=t,{element:l,padding:u=0,offsetParent:d=`real`}=sc(e,t)||{};if(l==null)return{};let f=Tc(u),p={x:n,y:r},m=pc(i),h=dc(m),g=await o.getDimensions(l),_=m===`y`,v=_?`top`:`left`,y=_?`bottom`:`right`,b=_?`clientHeight`:`clientWidth`,x=a.reference[h]+a.reference[m]-p[m]-a.floating[h],S=p[m]-a.reference[m],C=d===`real`?await o.getOffsetParent?.(l):s.floating,w=s.floating[b]||a.floating[h];(!w||!await o.isElement?.(C))&&(w=s.floating[b]||a.floating[h]);let T=x/2-S/2,E=w/2-g[h]/2-1,D=Math.min(f[v],E),O=Math.min(f[y],E),k=D,A=w-g[h]-O,j=w/2-g[h]/2+T,M=oc(k,j,A),ee=!c.arrow&&lc(i)!=null&&j!==M&&a.reference[h]/2-(j({...Mm(e),options:[e,t]}),Pm={name:`hide`,async fn(e){let{width:t,height:n,x:r,y:i}=e.rects.reference,a=t===0&&n===0&&r===0&&i===0;return{data:{referenceHidden:(await cf().fn(e)).data?.referenceHidden||a}}}},Fm={sideX:`left`,sideY:`top`},Im={name:`adaptiveOrigin`,async fn(e){let{x:t,y:n,rects:{floating:r},elements:{floating:i},platform:a,strategy:o,placement:s}=e,c=$t(i),l=c.getComputedStyle(i);if(!(l.transitionDuration!==`0s`&&l.transitionDuration!==``))return{x:t,y:n,data:Fm};let u=await a.getOffsetParent?.(i),d={width:0,height:0};if(o===`fixed`&&c?.visualViewport)d={width:c.visualViewport.width,height:c.visualViewport.height};else if(u===c){let e=Mn(i);d={width:e.documentElement.clientWidth,height:e.documentElement.clientHeight}}else await a.isElement?.(u)&&(d=await a.getDimensions(u));let f=cc(s),p=t,m=n;f===`left`&&(p=d.width-(t+r.width)),f===`top`&&(m=d.height-(n+r.height));let h=f===`left`?`right`:Fm.sideX,g=f===`top`?`bottom`:Fm.sideY;return{x:p,y:m,data:{sideX:h,sideY:g}}}};function Lm(e,t,n){let r=e===`inline-start`||e===`inline-end`;return{top:`top`,right:r?n?`inline-start`:`inline-end`:`right`,bottom:`bottom`,left:r?n?`inline-end`:`inline-start`:`left`}[t]}function Rm(e,t,n){let{rects:r,placement:i}=e;return{side:Lm(t,cc(i),n),align:lc(i)||`center`,anchor:{width:r.reference.width,height:r.reference.height},positioner:{width:r.floating.width,height:r.floating.height}}}function zm(e){let{anchor:t,positionMethod:n=`absolute`,side:r=`bottom`,sideOffset:i=0,align:a=`center`,alignOffset:o=0,collisionBoundary:s,collisionPadding:c=5,sticky:l=!1,arrowPadding:u=5,disableAnchorTracking:d=!1,inline:f,keepMounted:p=!1,floatingRootContext:m,mounted:h,collisionAvoidance:g,shiftCrossAxis:_=!1,nodeId:v,adaptiveOrigin:y,lazyFlip:b=!1,externalTree:x}=e,[S,C]=U.useState(null);!h&&S!==null&&C(null);let w=g.side||`flip`,T=g.align||`flip`,E=g.fallbackAxisSide||`end`,D=typeof t==`function`?t:void 0,O=dn(D),k=D?O:t,A=ru(t),j=ru(h),M=jm()===`rtl`,ee=S||{top:`top`,right:`right`,bottom:`bottom`,left:`left`,"inline-end":M?`left`:`right`,"inline-start":M?`right`:`left`}[r],N=a===`center`?ee:`${ee}-${a}`,P=c,te=+(r===`bottom`),ne=+(r===`top`),re=+(r===`right`),ie=+(r===`left`);typeof P==`number`?P={top:P+te,right:P+ie,bottom:P+ne,left:P+re}:P&&={top:(P.top||0)+te,right:(P.right||0)+ie,bottom:(P.bottom||0)+ne,left:(P.left||0)+re};let ae={boundary:s===`clipping-ancestors`?`clippingAncestors`:s,padding:P},F=U.useRef(null),oe=ru(i),se=ru(o),I=typeof i==`function`?0:i,ce=typeof o==`function`?0:o,le=[];f&&le.push(f),le.push(nf(e=>{let t=Rm(e,r,M),n=typeof oe.current==`function`?oe.current(t):oe.current,i=typeof se.current==`function`?se.current(t):se.current;return{mainAxis:n,crossAxis:i,alignmentAxis:i}},[I,ce,M,r]));let ue=T===`none`&&w!==`shift`,de=!ue&&(l||_||w===`shift`),fe=w===`none`?null:of({...ae,padding:{top:P.top+1,right:P.right+1,bottom:P.bottom+1,left:P.left+1},mainAxis:!_&&w===`flip`,crossAxis:T===`flip`?`alignment`:!1,fallbackAxisSideDirection:E}),pe=ue?null:rf(e=>{let t=Mn(e.elements.floating).documentElement;return{...ae,rootBoundary:_?{x:0,y:0,width:t.clientWidth,height:t.clientHeight}:void 0,mainAxis:T!==`none`,crossAxis:de,limiter:l||_?void 0:af(e=>{if(!F.current)return{};let{width:t,height:n}=F.current.getBoundingClientRect(),r=fc(cc(e.placement)),i=r===`y`?t:n,a=r===`y`?P.left+P.right:P.top+P.bottom;return{offset:i/2+a/2}})}},[ae,l,_,P,T]);w===`shift`||T===`shift`||a===`center`?le.push(pe,fe):le.push(fe,pe),le.push(sf({...ae,apply({elements:{floating:e},availableWidth:t,availableHeight:n,rects:r}){if(!j.current)return;let i=e.style;i.setProperty(`--available-width`,`${t}px`),i.setProperty(`--available-height`,`${n}px`);let a=$t(e).devicePixelRatio||1,{x:o,y:s,width:c,height:l}=r.reference,u=(Math.round((o+c)*a)-Math.round(o*a))/a,d=(Math.round((s+l)*a)-Math.round(s*a))/a;i.setProperty(`--anchor-width`,`${u}px`),i.setProperty(`--anchor-height`,`${d}px`)}}),Nm(e=>({element:F.current||Mn(e.elements.floating).createElement(`div`),padding:u,offsetParent:`floating`}),[u]),{name:`transformOrigin`,fn(e){let{elements:t,middlewareData:n,placement:a,rects:o,y:s}=e,c=cc(a),l=fc(c),u=F.current,d=n.arrow?.x||0,f=n.arrow?.y||0,p=u?.clientWidth||0,m=u?.clientHeight||0,h=d+p/2,g=f+m/2,_=Math.abs(n.shift?.y||0),v=o.reference.height/2,y=typeof i==`function`?i(Rm(e,r,M)):i,b=_>y,x={top:`${h}px calc(100% + ${y}px)`,bottom:`${h}px ${-y}px`,left:`calc(100% + ${y}px) ${g}px`,right:`${-y}px ${g}px`}[c],S=`${h}px ${o.reference.y+v-s}px`;return t.floating.style.setProperty(`--transform-origin`,de&&l===`y`&&b?S:x),{}}},Pm,y),B(()=>{!h&&m&&m.update({referenceElement:null,floatingElement:null,domReferenceElement:null,positionReference:null})},[h,m]);let me=U.useMemo(()=>({elementResize:!d&&typeof ResizeObserver<`u`,layoutShift:!d&&typeof IntersectionObserver<`u`}),[d]),{refs:he,elements:ge,x:_e,y:ve,middlewareData:L,update:ye,placement:be,context:xe,isPositioned:Se,floatingStyles:Ce}=Jf({rootContext:m,open:p?h:void 0,placement:N,middleware:le,strategy:n,whileElementsMounted:p?void 0:(...e)=>Hd(...e,me),nodeId:v,externalTree:x}),{sideX:we,sideY:Te}=L.adaptiveOrigin||Fm,Ee=Se?n:`fixed`,De=U.useMemo(()=>{let e=y?{position:Ee,[we]:_e,[Te]:ve}:{position:Ee,...Ce};return Se||(e.opacity=0),e},[y,Ee,we,_e,Te,ve,Ce,Se]),Oe=U.useRef(null);B(()=>{if(!h)return;let e=A.current,t=typeof e==`function`?e():e,n=(Bm(t)?t.current:t)||null;n!==Oe.current&&(he.setPositionReference(n),Oe.current=n)},[h,he,k,A]),U.useEffect(()=>{if(!h)return;let e=A.current;typeof e!=`function`&&Bm(e)&&e.current!==Oe.current&&(he.setPositionReference(e.current),Oe.current=e.current)},[h,he,k,A]),U.useEffect(()=>{if(p&&h&&ge.domReference&&ge.floating)return Hd(ge.domReference,ge.floating,ye,me)},[p,h,ge,ye,me]);let ke=cc(be),Ae=Lm(r,ke,M),je=lc(be)||`center`,Me=!!L.hide?.referenceHidden;B(()=>{b&&h&&Se&&C(ke)},[b,h,Se,ke]);let Ne=U.useMemo(()=>({position:`absolute`,top:L.arrow?.y,left:L.arrow?.x}),[L.arrow]),Pe=L.arrow?.centerOffset!==0;return U.useMemo(()=>({positionerStyles:De,arrowStyles:Ne,arrowRef:F,arrowUncentered:Pe,side:Ae,align:je,physicalSide:ke,anchorHidden:Me,refs:he,context:xe,isPositioned:Se,update:ye}),[De,Ne,F,Pe,Ae,je,ke,Me,he,xe,Se,ye])}function Bm(e){return e!=null&&`current`in e}function Vm(e){return e===`starting`?wu:jt}function Hm(e,t,{styles:n,transitionStatus:r,props:i,refs:a,hidden:o,inert:s=!1}){let c={...n};return s&&(c.pointerEvents=`none`),Ft(`div`,e,{state:t,ref:a,props:[{role:`presentation`,hidden:o,style:c},Vm(r),i],stateAttributesMapping:Ul})}var Um=U.forwardRef(function(e,t){let{render:n,className:r,anchor:i,positionMethod:a=`absolute`,side:o=`top`,align:s=`center`,sideOffset:c=0,alignOffset:l=0,collisionBoundary:u=`clipping-ancestors`,collisionPadding:d=5,arrowPadding:f=5,sticky:p=!1,disableAnchorTracking:m=!1,collisionAvoidance:h=Eu,style:g,..._}=e,v=dm(),y=Tm(),b=v.useState(`open`),x=v.useState(`mounted`),S=v.useState(`trackCursorAxis`),C=v.useState(`disableHoverablePopup`),w=v.useState(`floatingRootContext`),T=v.useState(`instantType`),E=v.useState(`transitionStatus`),D=zm({anchor:i,positionMethod:a,floatingRootContext:w,mounted:x,side:o,sideOffset:c,align:s,alignOffset:l,collisionBoundary:u,collisionPadding:d,sticky:p,arrowPadding:f,disableAnchorTracking:m,keepMounted:y,collisionAvoidance:h,adaptiveOrigin:v.useState(`hasViewport`)?Im:void 0}),O=Hm(e,U.useMemo(()=>({open:b,side:D.side,align:D.align,anchorHidden:D.anchorHidden,instant:S===`none`?T:`tracking-cursor`}),[b,D.side,D.align,D.anchorHidden,S,T]),{styles:D.positionerStyles,transitionStatus:E,props:_,refs:[t,v.useStateSetter(`positionerElement`)],hidden:!x,inert:!b||S===`both`||C});return(0,W.jsx)(Om.Provider,{value:D,children:O})}),Wm={...Ul,...bl},Gm=U.forwardRef(function(e,t){let{render:n,className:r,style:i,...a}=e,o=dm(),{side:s,align:c}=km(),l=o.useState(`open`),u=o.useState(`instantType`),d=o.useState(`transitionStatus`),f=o.useState(`popupProps`),p=o.useState(`floatingRootContext`),m=o.useState(`disabled`),h=o.useState(`closeDelay`);Cl({open:l,ref:o.context.popupRef,onComplete(){l&&o.context.onOpenChangeComplete?.(!0)}}),np(p,{enabled:!m,closeDelay:h});let g=o.useStateSetter(`popupElement`);return Ft(`div`,e,{state:{open:l,side:s,align:c,instant:u,transitionStatus:d},ref:[t,o.context.popupRef,g],props:[f,Vm(d),a],stateAttributesMapping:Wm})}),Km=U.forwardRef(function(e,t){let{render:n,className:r,style:i,...a}=e,o=dm(),{arrowRef:s,side:c,align:l,arrowUncentered:u,arrowStyles:d}=km();return Ft(`div`,e,{state:{open:o.useState(`open`),side:c,align:l,uncentered:u,instant:o.useState(`instantType`)},ref:[t,s],props:[{style:d,"aria-hidden":!0},a],stateAttributesMapping:Ul})}),qm=function(e){let{delay:t,closeDelay:n,timeout:r=400}=e,i=U.useMemo(()=>({delay:t,closeDelay:n}),[t,n]),a=U.useMemo(()=>({open:t,close:n}),[t,n]);return(0,W.jsx)(_m.Provider,{value:i,children:(0,W.jsx)(eu,{delay:a,timeoutMs:r,children:e.children})})};function Jm(e){let t=(0,go.c)(6),n,r;t[0]===e?(n=t[1],r=t[2]):({delay:r,...n}=e,t[0]=e,t[1]=n,t[2]=r);let i=r===void 0?0:r,a;return t[3]!==i||t[4]!==n?(a=(0,W.jsx)(qm,{"data-slot":`tooltip-provider`,delay:i,...n}),t[3]=i,t[4]=n,t[5]=a):a=t[5],a}function Ym(e){let t=(0,go.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,W.jsx)(Jm,{children:(0,W.jsx)(hm,{"data-slot":`tooltip`,...n})}),t[2]=n,t[3]=r),r}function Xm(e){let t=(0,go.c)(4),n;t[0]===e?n=t[1]:({...n}=e,t[0]=e,t[1]=n);let r;return t[2]===n?r=t[3]:(r=(0,W.jsx)(Cm,{"data-slot":`tooltip-trigger`,...n}),t[2]=n,t[3]=r),r}function Zm(e){let t=(0,go.c)(21),n,r,i,a,o,s,c;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6],c=t[7]):({className:r,side:a,sideOffset:o,align:s,alignOffset:c,children:n,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o,t[6]=s,t[7]=c);let l=a===void 0?`top`:a,u=o===void 0?4:o,d=s===void 0?`center`:s,f=c===void 0?0:c,p;t[8]===r?p=t[9]:(p=qt(`data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:fade-out-0 data-closed:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=delayed-open]:animate-in data-closed:animate-out data-open:animate-in z-50 w-fit max-w-xs origin-(--transform-origin) rounded-md bg-foreground px-3 py-1.5 text-xs text-background **:data-[slot=kbd]:rounded-md`,r),t[8]=r,t[9]=p);let m;t[10]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,W.jsx)(Km,{className:`z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5`}),t[10]=m):m=t[10];let h;t[11]!==n||t[12]!==i||t[13]!==p?(h=(0,W.jsxs)(Gm,{className:p,"data-slot":`tooltip-content`,...i,children:[n,m]}),t[11]=n,t[12]=i,t[13]=p,t[14]=h):h=t[14];let g;return t[15]!==d||t[16]!==f||t[17]!==l||t[18]!==u||t[19]!==h?(g=(0,W.jsx)(Dm,{children:(0,W.jsx)(Um,{align:d,alignOffset:f,className:`isolate z-50`,side:l,sideOffset:u,children:h})}),t[15]=d,t[16]=f,t[17]=l,t[18]=u,t[19]=h,t[20]=g):g=t[20],g}var Qm=window.matchMedia(`(max-width: 767px)`);function $m(e){return Qm.addEventListener(`change`,e),()=>Qm.removeEventListener(`change`,e)}function eh(){return Qm.matches}function th(){return(0,U.useSyncExternalStore)($m,eh)}var nh=`sidebar_state`,rh=3600*24*7,ih=`12rem`,ah=`16rem`,oh=`3rem`,sh=`b`,ch=U.createContext(null);function lh(){let e=U.useContext(ch);if(!e)throw Error(`useSidebar must be used within a SidebarProvider.`);return e}function uh(e){let t=(0,go.c)(36),n,r,i,a,o,s,c;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6],c=t[7]):({defaultOpen:c,open:i,onOpenChange:o,className:r,style:s,children:n,...a}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o,t[6]=s,t[7]=c);let l=c===void 0?!0:c,u=th(),[d,f]=U.useState(!1),[p,m]=U.useState(l),h=i??p,g;t[8]!==h||t[9]!==o?(g=e=>{let t=typeof e==`function`?e(h):e;o?o(t):m(t),document.cookie=`${nh}=${t}; path=/; max-age=${rh}`},t[8]=h,t[9]=o,t[10]=g):g=t[10];let _=g,v;t[11]!==u||t[12]!==_?(v=()=>u?f(fh):_(dh),t[11]=u,t[12]=_,t[13]=v):v=t[13];let y=v,b,x;t[14]===y?(b=t[15],x=t[16]):(b=()=>{let e=e=>{e.key===sh&&(e.metaKey||e.ctrlKey)&&(e.preventDefault(),y())};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},x=[y],t[14]=y,t[15]=b,t[16]=x),U.useEffect(b,x);let S=h?`expanded`:`collapsed`,C;t[17]!==u||t[18]!==h||t[19]!==d||t[20]!==_||t[21]!==S||t[22]!==y?(C={state:S,open:h,setOpen:_,isMobile:u,openMobile:d,setOpenMobile:f,toggleSidebar:y},t[17]=u,t[18]=h,t[19]=d,t[20]=_,t[21]=S,t[22]=y,t[23]=C):C=t[23];let w=C,T;t[24]===r?T=t[25]:(T=qt(`group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar`,r),t[24]=r,t[25]=T);let E;t[26]===s?E=t[27]:(E={"--sidebar-width":ih,"--sidebar-width-icon":oh,...s},t[26]=s,t[27]=E);let D=E,O;t[28]!==n||t[29]!==a||t[30]!==T||t[31]!==D?(O=(0,W.jsx)(`div`,{className:T,"data-slot":`sidebar-wrapper`,style:D,...a,children:n}),t[28]=n,t[29]=a,t[30]=T,t[31]=D,t[32]=O):O=t[32];let k;return t[33]!==w||t[34]!==O?(k=(0,W.jsx)(ch.Provider,{value:w,children:O}),t[33]=w,t[34]=O,t[35]=k):k=t[35],k}function dh(e){return!e}function fh(e){return!e}function ph(e){let t=(0,go.c)(46),n,r,i,a,o,s;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6]):({side:a,variant:o,collapsible:s,className:r,children:n,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o,t[6]=s);let c=a===void 0?`left`:a,l=o===void 0?`sidebar`:o,u=s===void 0?`offExamples`:s,{isMobile:d,state:f,openMobile:p,setOpenMobile:m}=lh();if(u===`none`){let e;t[7]===r?e=t[8]:(e=qt(`flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground`,r),t[7]=r,t[8]=e);let a;return t[9]!==n||t[10]!==i||t[11]!==e?(a=(0,W.jsx)(`div`,{className:e,"data-slot":`sidebar`,...i,children:n}),t[9]=n,t[10]=i,t[11]=e,t[12]=a):a=t[12],a}if(d){let e;t[13]===Symbol.for(`react.memo_cache_sentinel`)?(e={"--sidebar-width":ah},t[13]=e):e=t[13];let r=e,a;t[14]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,W.jsxs)(sm,{className:`sr-only`,children:[(0,W.jsx)(cm,{children:`Sidebar`}),(0,W.jsx)(lm,{children:`Displays the mobile sidebar.`})]}),t[14]=a):a=t[14];let o;t[15]===n?o=t[16]:(o=(0,W.jsx)(`div`,{className:`flex h-full w-full flex-col`,children:n}),t[15]=n,t[16]=o);let s;t[17]!==c||t[18]!==o?(s=(0,W.jsxs)(om,{className:`w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden`,"data-mobile":`true`,"data-sidebar":`sidebar`,"data-slot":`sidebar`,side:c,style:r,children:[a,o]}),t[17]=c,t[18]=o,t[19]=s):s=t[19];let l;return t[20]!==p||t[21]!==i||t[22]!==m||t[23]!==s?(l=(0,W.jsx)(rm,{onOpenChange:m,open:p,...i,children:s}),t[20]=p,t[21]=i,t[22]=m,t[23]=s,t[24]=l):l=t[24],l}let h=f===`collapsed`?u:``,g=l===`floating`||l===`inset`?`group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]`:`group-data-[collapsible=icon]:w-(--sidebar-width-icon)`,_;t[25]===g?_=t[26]:(_=qt(`relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear`,`group-data-[collapsible=offExamples]:w-0`,`group-data-[side=right]:rotate-180`,g),t[25]=g,t[26]=_);let v;t[27]===_?v=t[28]:(v=(0,W.jsx)(`div`,{className:_,"data-slot":`sidebar-gap`}),t[27]=_,t[28]=v);let y=c===`left`?`left-0 group-data-[collapsible=offExamples]:left-[calc(var(--sidebar-width)*-1)]`:`right-0 group-data-[collapsible=offExamples]:right-[calc(var(--sidebar-width)*-1)]`,b=l===`floating`||l===`inset`?`p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]`:`group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l`,x;t[29]!==r||t[30]!==y||t[31]!==b?(x=qt(`fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex`,y,b,r),t[29]=r,t[30]=y,t[31]=b,t[32]=x):x=t[32];let S;t[33]===n?S=t[34]:(S=(0,W.jsx)(`div`,{className:`flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border`,"data-sidebar":`sidebar`,"data-slot":`sidebar-inner`,children:n}),t[33]=n,t[34]=S);let C;t[35]!==i||t[36]!==x||t[37]!==S?(C=(0,W.jsx)(`div`,{className:x,"data-slot":`sidebar-container`,...i,children:S}),t[35]=i,t[36]=x,t[37]=S,t[38]=C):C=t[38];let w;return t[39]!==c||t[40]!==f||t[41]!==C||t[42]!==h||t[43]!==v||t[44]!==l?(w=(0,W.jsxs)(`div`,{className:`group peer hidden text-sidebar-foreground md:block`,"data-collapsible":h,"data-side":c,"data-slot":`sidebar`,"data-state":f,"data-variant":l,children:[v,C]}),t[39]=c,t[40]=f,t[41]=C,t[42]=h,t[43]=v,t[44]=l,t[45]=w):w=t[45],w}function mh(e){let t=(0,go.c)(15),n,r,i;t[0]===e?(n=t[1],r=t[2],i=t[3]):({className:n,onClick:r,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i);let{toggleSidebar:a}=lh(),o;t[4]===n?o=t[5]:(o=qt(n),t[4]=n,t[5]=o);let s;t[6]!==r||t[7]!==a?(s=e=>{r?.(e),a()},t[6]=r,t[7]=a,t[8]=s):s=t[8];let c,l;t[9]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,W.jsx)(Xo,{}),l=(0,W.jsx)(`span`,{className:`sr-only`,children:`Toggle Sidebar`}),t[9]=c,t[10]=l):(c=t[9],l=t[10]);let u;return t[11]!==i||t[12]!==o||t[13]!==s?(u=(0,W.jsxs)(fn,{className:o,"data-sidebar":`trigger`,"data-slot":`sidebar-trigger`,onClick:s,size:`icon-lg`,variant:`ghost`,...i,children:[c,l]}),t[11]=i,t[12]=o,t[13]=s,t[14]=u):u=t[14],u}function hh(e){let t=(0,go.c)(9),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let{toggleSidebar:i}=lh(),a;t[3]===n?a=t[4]:(a=qt(`absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex`,`in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize`,`[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize`,`group-data-[collapsible=offExamples]:translate-x-0 group-data-[collapsible=offExamples]:after:left-full hover:group-data-[collapsible=offExamples]:bg-sidebar`,`[[data-side=left][data-collapsible=offExamples]_&]:-right-2`,`[[data-side=right][data-collapsible=offExamples]_&]:-left-2`,n),t[3]=n,t[4]=a);let o;return t[5]!==r||t[6]!==a||t[7]!==i?(o=(0,W.jsx)(`button`,{"aria-label":`Toggle Sidebar`,className:a,"data-sidebar":`rail`,"data-slot":`sidebar-rail`,onClick:i,tabIndex:-1,title:`Toggle Sidebar`,...r}),t[5]=r,t[6]=a,t[7]=i,t[8]=o):o=t[8],o}function gh(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`flex flex-col gap-2 p-2`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(`div`,{className:i,"data-sidebar":`header`,"data-slot":`sidebar-header`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function _h(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`flex flex-col gap-2 p-2`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(`div`,{className:i,"data-sidebar":`footer`,"data-slot":`sidebar-footer`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function vh(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`mx-2 w-auto bg-sidebar-border`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(jl,{className:i,"data-sidebar":`separator`,"data-slot":`sidebar-separator`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function yh(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`no-scrollbar flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(`div`,{className:i,"data-sidebar":`content`,"data-slot":`sidebar-content`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function bh(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`relative flex w-full min-w-0 flex-col px-2 py-1`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(`div`,{className:i,"data-sidebar":`group`,"data-slot":`sidebar-group`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function xh(e){let t=(0,go.c)(10),n,r,i,a;if(t[0]!==e){let{className:o,render:s,...c}=e;n=s,a=xo,r=`div`,i=Nt({className:qt(`flex h-8 shrink-0 items-center rounded-md px-2 text-xs text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0`,o)},c),t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a}else n=t[1],r=t[2],i=t[3],a=t[4];let o;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(o={slot:`sidebar-group-label`,sidebar:`group-label`},t[5]=o):o=t[5];let s;return t[6]!==n||t[7]!==r||t[8]!==i?(s={defaultTagName:r,props:i,render:n,state:o},t[6]=n,t[7]=r,t[8]=i,t[9]=s):s=t[9],a(s)}function Sh(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`flex w-full min-w-0 flex-col gap-px`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(`ul`,{className:i,"data-sidebar":`menu`,"data-slot":`sidebar-menu`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Ch(e){let t=(0,go.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=qt(`group/menu-item relative`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,W.jsx)(`li`,{className:i,"data-sidebar":`menu-item`,"data-slot":`sidebar-menu-item`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}var wh=Rt(`peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-[calc(var(--radius-sm)+2px)] p-2 text-left text-xs ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate`,{variants:{variant:{default:`hover:bg-sidebar-accent hover:text-sidebar-accent-foreground`,outline:`bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]`},size:{default:`h-8 text-xs`,sm:`h-7 text-xs`,lg:`h-12 text-xs group-data-[collapsible=icon]:p-0!`}},defaultVariants:{variant:`default`,size:`default`}});function Th(e){let t=(0,go.c)(15),{render:n,isActive:r,variant:i,size:a,tooltip:o,className:s,...c}=e,l=r===void 0?!1:r,u=i===void 0?`default`:i,d=a===void 0?`default`:a,{isMobile:f,state:p}=lh(),m=Nt({className:qt(wh({variant:u,size:d}),s)},c),h=o?Xm:n,g;t[0]!==l||t[1]!==d?(g={slot:`sidebar-menu-button`,sidebar:`menu-button`,size:d,active:l},t[0]=l,t[1]=d,t[2]=g):g=t[2];let _;t[3]!==m||t[4]!==h||t[5]!==g?(_={defaultTagName:`button`,props:m,render:h,state:g},t[3]=m,t[4]=h,t[5]=g,t[6]=_):_=t[6];let v=xo(_);if(!o)return v;let y;t[7]===o?y=t[8]:(y=typeof o==`string`?{children:o}:o,t[7]=o,t[8]=y);let b=y,x=p!==`collapsed`||f,S;t[9]!==x||t[10]!==b?(S=(0,W.jsx)(Zm,{align:`center`,hidden:x,side:`right`,...b}),t[9]=x,t[10]=b,t[11]=S):S=t[11];let C;return t[12]!==v||t[13]!==S?(C=(0,W.jsxs)(Ym,{children:[v,S]}),t[12]=v,t[13]=S,t[14]=C):C=t[14],C}var Eh=`ap-southeast-2`,Dh=9999,Oh=class extends Error{status;constructor(e,t,n){super(e),this.name=t,this.status=n}};function kh(e,t){let n=`<${t}>`,r=``,i=e.indexOf(n);if(i===-1)return null;let a=e.indexOf(r,i+n.length);return a===-1?null:e.slice(i+n.length,a)}async function Ah({action:e,credentials:t,service:n=`spinifex`}){let r=window.location.protocol.replace(`:`,``),i=`Action=${e}`,a=new Ut({method:`POST`,protocol:r,hostname:`localhost`,port:Dh,path:`/`,headers:{host:`localhost:${Dh}`,"content-type":`application/x-www-form-urlencoded`},body:i}),o=await new Gt({credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey},region:Eh,service:n,sha256:Ht}).sign(a),s={};for(let[e,t]of Object.entries(o.headers))typeof t==`string`&&(s[e]=t);let c=`${window.location.protocol}//${window.location.host}/proxy/awsgw/`,l=await fetch(c,{method:`POST`,headers:s,body:i});if(!l.ok){let t=await l.text().catch(()=>``),n=kh(t,`Code`),r=kh(t,`Message`)??t;throw new Oh(`${e} failed: ${l.status}${r?` - ${r}`:``}`,n??`SignedFetchError`,l.status)}return await l.json()}var jh=`000000000001`,Mh=(0,U.createContext)(void 0);function Nh({children:e}){let[t,n]=(0,U.useState)({isAdmin:!1,accountId:null,userName:null,version:null,license:null,loading:!0});return(0,U.useEffect)(()=>{async function e(){let e=Kt();if(!e){n(e=>({...e,loading:!1}));return}try{let t=await Ah({action:`GetCallerIdentity`,credentials:e}),r=t.account_id===jh,i=null;if(r)try{i=await Ah({action:`GetVersion`,credentials:e})}catch{}n({isAdmin:r,accountId:t.account_id,userName:t.user_name,version:i,license:i?.license??null,loading:!1})}catch{n(e=>({...e,loading:!1}))}}e()},[]),(0,W.jsx)(Mh,{value:t,children:e})}function Ph(){let e=(0,U.useContext)(Mh);if(e===void 0)throw Error(`useAdmin must be used within an AdminProvider`);return e}var Fh=new Set([`InvalidClientTokenId`,`SignatureDoesNotMatch`,`AuthFailure`,`MissingAuthenticationToken`,`IncompleteSignature`,`UnrecognizedClientException`,`ExpiredToken`,`ExpiredTokenException`,`InvalidAccessKeyId`]);function Ih(e){if(typeof e!=`object`||!e||!(`name`in e))return!1;let{name:t}=e;return typeof t==`string`&&Fh.has(t)}var Lh=class extends R.classBuilder().ep($e).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`CertificateManager`,`ListCertificates`,{}).n(`ACMClient`,`ListCertificatesCommand`).sc(ot).build(){},Rh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeAddresses`,{}).n(`EC2Client`,`DescribeAddressesCommand`).sc(Pe).build(){},zh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeAvailabilityZones`,{}).n(`EC2Client`,`DescribeAvailabilityZonesCommand`).sc(Ie).build(){},Bh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeIamInstanceProfileAssociations`,{}).n(`EC2Client`,`DescribeIamInstanceProfileAssociationsCommand`).sc(ye).build(){},Vh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeImages`,{}).n(`EC2Client`,`DescribeImagesCommand`).sc(Ee).build(){},Hh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeInstances`,{}).n(`EC2Client`,`DescribeInstancesCommand`).sc(Re).build(){},Uh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeInstanceTypes`,{}).n(`EC2Client`,`DescribeInstanceTypesCommand`).sc(Ne).build(){},Wh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeInternetGateways`,{}).n(`EC2Client`,`DescribeInternetGatewaysCommand`).sc(kt).build(){},Gh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeKeyPairs`,{}).n(`EC2Client`,`DescribeKeyPairsCommand`).sc(_e).build(){},Kh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeNatGateways`,{}).n(`EC2Client`,`DescribeNatGatewaysCommand`).sc(Ue).build(){},qh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribePlacementGroups`,{}).n(`EC2Client`,`DescribePlacementGroupsCommand`).sc(we).build(){},Jh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeRegions`,{}).n(`EC2Client`,`DescribeRegionsCommand`).sc(Be).build(){},Yh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeRouteTables`,{}).n(`EC2Client`,`DescribeRouteTablesCommand`).sc(Ke).build(){},Xh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeSecurityGroups`,{}).n(`EC2Client`,`DescribeSecurityGroupsCommand`).sc(xe).build(){},Zh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeSnapshots`,{}).n(`EC2Client`,`DescribeSnapshotsCommand`).sc(je).build(){},Qh=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeSubnets`,{}).n(`EC2Client`,`DescribeSubnetsCommand`).sc(bt).build(){},$h=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeVolumes`,{}).n(`EC2Client`,`DescribeVolumesCommand`).sc(Oe).build(){},eg=class extends R.classBuilder().ep(mt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AmazonEC2`,`DescribeVpcs`,{}).n(`EC2Client`,`DescribeVpcsCommand`).sc(Xe).build(){},tg=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`DescribeAccessEntry`,{}).n(`EKSClient`,`DescribeAccessEntryCommand`).sc(At).build(){},ng=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`DescribeAddon`,{}).n(`EKSClient`,`DescribeAddonCommand`).sc(ve).build(){},rg=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`DescribeAddonVersions`,{}).n(`EKSClient`,`DescribeAddonVersionsCommand`).sc(We).build(){},ig=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`DescribeCluster`,{}).n(`EKSClient`,`DescribeClusterCommand`).sc(Te).build(){},ag=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`DescribeNodegroup`,{}).n(`EKSClient`,`DescribeNodegroupCommand`).sc(Ve).build(){},og=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`ListAccessEntries`,{}).n(`EKSClient`,`ListAccessEntriesCommand`).sc(Se).build(){},sg=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`ListAccessPolicies`,{}).n(`EKSClient`,`ListAccessPoliciesCommand`).sc(Me).build(){},cg=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`ListAddons`,{}).n(`EKSClient`,`ListAddonsCommand`).sc(xt).build(){},lg=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`ListAssociatedAccessPolicies`,{}).n(`EKSClient`,`ListAssociatedAccessPoliciesCommand`).sc(ke).build(){},ug=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`ListClusters`,{}).n(`EKSClient`,`ListClustersCommand`).sc(Ze).build(){},dg=class extends R.classBuilder().ep(it).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSWesleyFrontend`,`ListNodegroups`,{}).n(`EKSClient`,`ListNodegroupsCommand`).sc(Je).build(){},fg=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeListeners`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeListenersCommand`).sc(at).build(){},pg=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeLoadBalancerAttributes`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeLoadBalancerAttributesCommand`).sc(vt).build(){},mg=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeLoadBalancers`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeLoadBalancersCommand`).sc(ht).build(){},hg=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeRules`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeRulesCommand`).sc(lt).build(){},gg=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeSSLPolicies`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeSSLPoliciesCommand`).sc(st).build(){},_g=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeTags`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeTagsCommand`).sc(et).build(){},vg=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeTargetGroupAttributes`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeTargetGroupAttributesCommand`).sc(Et).build(){},yg=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeTargetGroups`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeTargetGroupsCommand`).sc(Ot).build(){},bg=class extends R.classBuilder().ep(ft).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`ElasticLoadBalancing_v10`,`DescribeTargetHealth`,{}).n(`ElasticLoadBalancingV2Client`,`DescribeTargetHealthCommand`).sc(rt).build(){},xg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`GetInstanceProfile`,{}).n(`IAMClient`,`GetInstanceProfileCommand`).sc(ge).build(){},Sg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`GetPolicy`,{}).n(`IAMClient`,`GetPolicyCommand`).sc(He).build(){},Cg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`GetPolicyVersion`,{}).n(`IAMClient`,`GetPolicyVersionCommand`).sc(Ce).build(){},wg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`GetRole`,{}).n(`IAMClient`,`GetRoleCommand`).sc(ze).build(){},Tg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`GetUser`,{}).n(`IAMClient`,`GetUserCommand`).sc(Ge).build(){},Eg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListAccessKeys`,{}).n(`IAMClient`,`ListAccessKeysCommand`).sc(be).build(){},Dg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListAttachedRolePolicies`,{}).n(`IAMClient`,`ListAttachedRolePoliciesCommand`).sc(Ae).build(){},Og=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListAttachedUserPolicies`,{}).n(`IAMClient`,`ListAttachedUserPoliciesCommand`).sc(yt).build(){},kg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListInstanceProfiles`,{}).n(`IAMClient`,`ListInstanceProfilesCommand`).sc(De).build(){},Ag=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListInstanceProfilesForRole`,{}).n(`IAMClient`,`ListInstanceProfilesForRoleCommand`).sc(Ye).build(){},jg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListPolicies`,{}).n(`IAMClient`,`ListPoliciesCommand`).sc(qe).build(){},Mg=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListRoles`,{}).n(`IAMClient`,`ListRolesCommand`).sc(Qe).build(){},Ng=class extends R.classBuilder().ep(Tt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions())]}).s(`AWSIdentityManagementV20100508`,`ListUsers`,{}).n(`IAMClient`,`ListUsersCommand`).sc(Le).build(){},Pg=class extends R.classBuilder().ep(Dt).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions()),nt(n)]}).s(`AmazonS3`,`ListBuckets`,{}).n(`S3Client`,`ListBucketsCommand`).sc(pt).build(){},Fg=class extends R.classBuilder().ep({...Dt,Bucket:{type:`contextParams`,name:`Bucket`},Prefix:{type:`contextParams`,name:`Prefix`}}).m(function(e,t,n,r){return[L(n,e.getEndpointParameterInstructions()),nt(n)]}).s(`AmazonS3`,`ListObjectsV2`,{}).n(`S3Client`,`ListObjectsV2Command`).sc(ct).build(){},Ig=!1;function Lg(e,t){Ig||!Ih(e)||(Ig=!0,Wt(),wt(),t.clear(),window.location.pathname!==`/login`&&(window.location.href=`/login?reason=expired`))}function Rg(){let e=new Nr({queryCache:new Mr({onError:t=>Lg(t,e)}),mutationCache:new Ar({onError:t=>Lg(t,e)}),defaultOptions:{queries:{staleTime:5e3,refetchIntervalInBackground:!1}}});return e}function zg(){let e=(0,go.c)(8),{theme:t,setTheme:n}=bo(),r;e[0]!==n||e[1]!==t?(r=()=>{n(t===`light`?`dark`:t===`dark`?`light`:`dark`)},e[0]=n,e[1]=t,e[2]=r):r=e[2];let i=r,a,o,s;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,W.jsx)(ns,{className:`size-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90`}),o=(0,W.jsx)(Jo,{className:`absolute size-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0`}),s=(0,W.jsx)(`span`,{className:`sr-only`,children:`Toggle theme`}),e[3]=a,e[4]=o,e[5]=s):(a=e[3],o=e[4],s=e[5]);let c;return e[6]===i?c=e[7]:(c=(0,W.jsxs)(fn,{className:`rounded-full`,onClick:i,size:`icon-sm`,variant:`ghost`,children:[a,o,s]}),e[6]=i,e[7]=c),c}function Bg(){let e=(0,go.c)(7),{isAdmin:t,license:n}=Ph(),r=t&&n===`open-source`,i,a;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(i=qt(`z-50 flex h-10 shrink-0 items-center gap-2 border-b border-sidebar-border bg-sidebar px-4 text-sidebar-foreground`,`sticky top-0`),a=(0,W.jsx)(mh,{className:`-ml-1`}),e[0]=i,e[1]=a):(i=e[0],a=e[1]);let o;e[2]===r?o=e[3]:(o=r&&(0,W.jsx)(`a`,{href:`https://mulgadc.com/purchase`,target:`_blank`,rel:`noopener noreferrer`,className:qt(cn({variant:`outline`,size:`sm`})),children:`Upgrade`}),e[2]=r,e[3]=o);let s;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,W.jsx)(zg,{}),e[4]=s):s=e[4];let c;return e[5]===o?c=e[6]:(c=(0,W.jsxs)(`header`,{className:i,children:[a,(0,W.jsxs)(`div`,{className:`flex w-full items-center justify-end gap-2`,children:[o,s]})]}),e[5]=o,e[6]=c),c}function Vg(){let e=(0,go.c)(3),t,n;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,W.jsx)(`h1`,{className:`text-4xl font-bold`,children:`404`}),n=(0,W.jsx)(`p`,{className:`text-muted-foreground`,children:`Page not found`}),e[0]=t,e[1]=n):(t=e[0],n=e[1]);let r;return e[2]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,W.jsxs)(`div`,{className:`flex flex-1 flex-col items-center justify-center gap-4`,children:[t,n,(0,W.jsx)(Fe,{to:`/`,children:(0,W.jsx)(fn,{variant:`outline`,children:`Go back home`})})]}),e[2]=r):r=e[2],r}function Hg(){let e=(0,go.c)(194),t;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t={select:Ug},e[0]=t):t=e[0];let n=ao(t),r=Fa(),i=Fr(),{isAdmin:a}=Ph(),o;e[1]!==r||e[2]!==i?(o=function(){Wt(),wt(),i.clear(),r({to:`/login`})},e[1]=r,e[2]=i,e[3]=o):o=e[3];let s=o,c;e[4]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,W.jsxs)(gh,{className:`flex flex-row items-center gap-2 px-4 py-3`,children:[(0,W.jsx)(`img`,{src:`/mulga-logo.svg`,alt:`Mulga`,className:`size-6 shrink-0 dark:invert`}),(0,W.jsx)(`span`,{className:`truncate text-sm font-semibold group-data-[collapsible=icon]:hidden`,children:`Spinifex`})]}),e[4]=c):c=e[4];let l;e[5]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,W.jsx)(xh,{children:`General`}),e[5]=l):l=e[5];let u=n===`/`,d,f;e[6]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,W.jsx)(Bo,{className:`size-4`}),f=(0,W.jsx)(`span`,{children:`Dashboard`}),e[6]=d,e[7]=f):(d=e[6],f=e[7]);let p;e[8]===u?p=e[9]:(p=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/`,children:(0,W.jsxs)(Th,{isActive:u,tooltip:`Dashboard`,children:[d,f]})})}),e[8]=u,e[9]=p);let m;e[10]!==a||e[11]!==n?(m=a&&(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/nodes`,children:(0,W.jsxs)(Th,{isActive:n.startsWith(`/nodes`),tooltip:`Nodes`,children:[(0,W.jsx)($o,{className:`size-4`}),(0,W.jsx)(`span`,{children:`Nodes`})]})})}),e[10]=a,e[11]=n,e[12]=m):m=e[12];let h;e[13]!==p||e[14]!==m?(h=(0,W.jsxs)(bh,{children:[l,(0,W.jsxs)(Sh,{children:[p,m]})]}),e[13]=p,e[14]=m,e[15]=h):h=e[15];let g;e[16]===Symbol.for(`react.memo_cache_sentinel`)?(g=(0,W.jsx)(xh,{children:`EC2`}),e[16]=g):g=e[16];let _;e[17]===n?_=e[18]:(_=n.startsWith(`/ec2/describe-instances`)||n.startsWith(`/ec2/run-instances`),e[17]=n,e[18]=_);let v,y;e[19]===Symbol.for(`react.memo_cache_sentinel`)?(v=(0,W.jsx)($o,{className:`size-4`}),y=(0,W.jsx)(`span`,{children:`Instances`}),e[19]=v,e[20]=y):(v=e[19],y=e[20]);let b;e[21]===_?b=e[22]:(b=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-instances`,children:(0,W.jsxs)(Th,{isActive:_,tooltip:`Instances`,children:[v,y]})})}),e[21]=_,e[22]=b);let x;e[23]===n?x=e[24]:(x=n.startsWith(`/ec2/describe-images`),e[23]=n,e[24]=x);let S,C;e[25]===Symbol.for(`react.memo_cache_sentinel`)?(S=(0,W.jsx)(Ho,{className:`size-4`}),C=(0,W.jsx)(`span`,{children:`Images`}),e[25]=S,e[26]=C):(S=e[25],C=e[26]);let w;e[27]===x?w=e[28]:(w=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-images`,children:(0,W.jsxs)(Th,{isActive:x,tooltip:`Images`,children:[S,C]})})}),e[27]=x,e[28]=w);let T;e[29]===n?T=e[30]:(T=n.startsWith(`/ec2/describe-key-pairs`),e[29]=n,e[30]=T);let E,D;e[31]===Symbol.for(`react.memo_cache_sentinel`)?(E=(0,W.jsx)(Uo,{className:`size-4`}),D=(0,W.jsx)(`span`,{children:`Key Pairs`}),e[31]=E,e[32]=D):(E=e[31],D=e[32]);let O;e[33]===T?O=e[34]:(O=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-key-pairs`,children:(0,W.jsxs)(Th,{isActive:T,tooltip:`Key Pairs`,children:[E,D]})})}),e[33]=T,e[34]=O);let k;e[35]===n?k=e[36]:(k=n.startsWith(`/ec2/describe-addresses`)||n.startsWith(`/ec2/allocate-address`),e[35]=n,e[36]=k);let A,j;e[37]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,W.jsx)(qo,{className:`size-4`}),j=(0,W.jsx)(`span`,{children:`Elastic IPs`}),e[37]=A,e[38]=j):(A=e[37],j=e[38]);let M;e[39]===k?M=e[40]:(M=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-addresses`,children:(0,W.jsxs)(Th,{isActive:k,tooltip:`Elastic IPs`,children:[A,j]})})}),e[39]=k,e[40]=M);let ee;e[41]===n?ee=e[42]:(ee=n.startsWith(`/ec2/describe-volumes`)||n.startsWith(`/ec2/create-volume`)||n.startsWith(`/ec2/modify-volume`),e[41]=n,e[42]=ee);let N,P;e[43]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,W.jsx)(zo,{className:`size-4`}),P=(0,W.jsx)(`span`,{children:`Volumes`}),e[43]=N,e[44]=P):(N=e[43],P=e[44]);let te;e[45]===ee?te=e[46]:(te=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-volumes`,children:(0,W.jsxs)(Th,{isActive:ee,tooltip:`Volumes`,children:[N,P]})})}),e[45]=ee,e[46]=te);let ne;e[47]===n?ne=e[48]:(ne=n.startsWith(`/ec2/describe-snapshots`)||n.startsWith(`/ec2/create-snapshot`),e[47]=n,e[48]=ne);let re,ie;e[49]===Symbol.for(`react.memo_cache_sentinel`)?(re=(0,W.jsx)(Fo,{className:`size-4`}),ie=(0,W.jsx)(`span`,{children:`Snapshots`}),e[49]=re,e[50]=ie):(re=e[49],ie=e[50]);let ae;e[51]===ne?ae=e[52]:(ae=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-snapshots`,children:(0,W.jsxs)(Th,{isActive:ne,tooltip:`Snapshots`,children:[re,ie]})})}),e[51]=ne,e[52]=ae);let F;e[53]===n?F=e[54]:(F=n.startsWith(`/ec2/describe-placement-groups`)||n.startsWith(`/ec2/create-placement-group`),e[53]=n,e[54]=F);let oe,se;e[55]===Symbol.for(`react.memo_cache_sentinel`)?(oe=(0,W.jsx)(Wo,{className:`size-4`}),se=(0,W.jsx)(`span`,{children:`Placement Groups`}),e[55]=oe,e[56]=se):(oe=e[55],se=e[56]);let I;e[57]===F?I=e[58]:(I=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-placement-groups`,children:(0,W.jsxs)(Th,{isActive:F,tooltip:`Placement Groups`,children:[oe,se]})})}),e[57]=F,e[58]=I);let ce;e[59]===n?ce=e[60]:(ce=n.startsWith(`/ec2/describe-load-balancers`)||n.startsWith(`/ec2/create-load-balancer`),e[59]=n,e[60]=ce);let le,ue;e[61]===Symbol.for(`react.memo_cache_sentinel`)?(le=(0,W.jsx)(as,{className:`size-4`}),ue=(0,W.jsx)(`span`,{children:`Load Balancers`}),e[61]=le,e[62]=ue):(le=e[61],ue=e[62]);let de;e[63]===ce?de=e[64]:(de=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-load-balancers`,children:(0,W.jsxs)(Th,{isActive:ce,tooltip:`Load Balancers`,children:[le,ue]})})}),e[63]=ce,e[64]=de);let fe;e[65]===n?fe=e[66]:(fe=n.startsWith(`/ec2/describe-target-groups`)||n.startsWith(`/ec2/create-target-group`),e[65]=n,e[66]=fe);let pe,me;e[67]===Symbol.for(`react.memo_cache_sentinel`)?(pe=(0,W.jsx)(Lo,{className:`size-4`}),me=(0,W.jsx)(`span`,{children:`Target Groups`}),e[67]=pe,e[68]=me):(pe=e[67],me=e[68]);let he;e[69]===fe?he=e[70]:(he=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-target-groups`,children:(0,W.jsxs)(Th,{isActive:fe,tooltip:`Target Groups`,children:[pe,me]})})}),e[69]=fe,e[70]=he);let ge;e[71]!==b||e[72]!==w||e[73]!==O||e[74]!==M||e[75]!==te||e[76]!==ae||e[77]!==I||e[78]!==de||e[79]!==he?(ge=(0,W.jsxs)(bh,{children:[g,(0,W.jsxs)(Sh,{children:[b,w,O,M,te,ae,I,de,he]})]}),e[71]=b,e[72]=w,e[73]=O,e[74]=M,e[75]=te,e[76]=ae,e[77]=I,e[78]=de,e[79]=he,e[80]=ge):ge=e[80];let _e;e[81]===Symbol.for(`react.memo_cache_sentinel`)?(_e=(0,W.jsx)(xh,{children:`Networking`}),e[81]=_e):_e=e[81];let ve;e[82]===n?ve=e[83]:(ve=n.startsWith(`/ec2/describe-vpcs`)||n.startsWith(`/ec2/create-vpc`),e[82]=n,e[83]=ve);let L,ye;e[84]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,W.jsx)(Yo,{className:`size-4`}),ye=(0,W.jsx)(`span`,{children:`VPCs`}),e[84]=L,e[85]=ye):(L=e[84],ye=e[85]);let be;e[86]===ve?be=e[87]:(be=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-vpcs`,children:(0,W.jsxs)(Th,{isActive:ve,tooltip:`VPCs`,children:[L,ye]})})}),e[86]=ve,e[87]=be);let xe;e[88]===n?xe=e[89]:(xe=n.startsWith(`/ec2/describe-subnets`)||n.startsWith(`/ec2/create-subnet`),e[88]=n,e[89]=xe);let Se,Ce;e[90]===Symbol.for(`react.memo_cache_sentinel`)?(Se=(0,W.jsx)(Go,{className:`size-4`}),Ce=(0,W.jsx)(`span`,{children:`Subnets`}),e[90]=Se,e[91]=Ce):(Se=e[90],Ce=e[91]);let we;e[92]===xe?we=e[93]:(we=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-subnets`,children:(0,W.jsxs)(Th,{isActive:xe,tooltip:`Subnets`,children:[Se,Ce]})})}),e[92]=xe,e[93]=we);let Te;e[94]===n?Te=e[95]:(Te=n.startsWith(`/ec2/describe-security-groups`)||n.startsWith(`/ec2/create-security-group`),e[94]=n,e[95]=Te);let Ee,De;e[96]===Symbol.for(`react.memo_cache_sentinel`)?(Ee=(0,W.jsx)(es,{className:`size-4`}),De=(0,W.jsx)(`span`,{children:`Security Groups`}),e[96]=Ee,e[97]=De):(Ee=e[96],De=e[97]);let Oe;e[98]===Te?Oe=e[99]:(Oe=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-security-groups`,children:(0,W.jsxs)(Th,{isActive:Te,tooltip:`Security Groups`,children:[Ee,De]})})}),e[98]=Te,e[99]=Oe);let ke;e[100]===n?ke=e[101]:(ke=n.startsWith(`/ec2/describe-route-tables`)||n.startsWith(`/ec2/create-route-table`),e[100]=n,e[101]=ke);let Ae,je;e[102]===Symbol.for(`react.memo_cache_sentinel`)?(Ae=(0,W.jsx)(Qo,{className:`size-4`}),je=(0,W.jsx)(`span`,{children:`Route Tables`}),e[102]=Ae,e[103]=je):(Ae=e[102],je=e[103]);let Me;e[104]===ke?Me=e[105]:(Me=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-route-tables`,children:(0,W.jsxs)(Th,{isActive:ke,tooltip:`Route Tables`,children:[Ae,je]})})}),e[104]=ke,e[105]=Me);let Ne;e[106]===n?Ne=e[107]:(Ne=n.startsWith(`/ec2/describe-internet-gateways`),e[106]=n,e[107]=Ne);let Pe,Ie;e[108]===Symbol.for(`react.memo_cache_sentinel`)?(Pe=(0,W.jsx)(Ro,{className:`size-4`}),Ie=(0,W.jsx)(`span`,{children:`Internet Gateways`}),e[108]=Pe,e[109]=Ie):(Pe=e[108],Ie=e[109]);let Le;e[110]===Ne?Le=e[111]:(Le=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-internet-gateways`,children:(0,W.jsxs)(Th,{isActive:Ne,tooltip:`Internet Gateways`,children:[Pe,Ie]})})}),e[110]=Ne,e[111]=Le);let Re;e[112]===n?Re=e[113]:(Re=n.startsWith(`/ec2/describe-nat-gateways`)||n.startsWith(`/ec2/create-nat-gateway`),e[112]=n,e[113]=Re);let R,ze;e[114]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,W.jsx)(Zo,{className:`size-4`}),ze=(0,W.jsx)(`span`,{children:`NAT Gateways`}),e[114]=R,e[115]=ze):(R=e[114],ze=e[115]);let Be;e[116]===Re?Be=e[117]:(Be=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/ec2/describe-nat-gateways`,children:(0,W.jsxs)(Th,{isActive:Re,tooltip:`NAT Gateways`,children:[R,ze]})})}),e[116]=Re,e[117]=Be);let Ve;e[118]!==be||e[119]!==we||e[120]!==Oe||e[121]!==Me||e[122]!==Le||e[123]!==Be?(Ve=(0,W.jsxs)(bh,{children:[_e,(0,W.jsxs)(Sh,{children:[be,we,Oe,Me,Le,Be]})]}),e[118]=be,e[119]=we,e[120]=Oe,e[121]=Me,e[122]=Le,e[123]=Be,e[124]=Ve):Ve=e[124];let He;e[125]===Symbol.for(`react.memo_cache_sentinel`)?(He=(0,W.jsx)(xh,{children:`EKS`}),e[125]=He):He=e[125];let Ue;e[126]===n?Ue=e[127]:(Ue=n.startsWith(`/eks/list-clusters`)||n.startsWith(`/eks/create-cluster`),e[126]=n,e[127]=Ue);let We,Ge;e[128]===Symbol.for(`react.memo_cache_sentinel`)?(We=(0,W.jsx)(Po,{className:`size-4`}),Ge=(0,W.jsx)(`span`,{children:`Clusters`}),e[128]=We,e[129]=Ge):(We=e[128],Ge=e[129]);let Ke;e[130]===Ue?Ke=e[131]:(Ke=(0,W.jsxs)(bh,{children:[He,(0,W.jsx)(Sh,{children:(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/eks/list-clusters`,children:(0,W.jsxs)(Th,{isActive:Ue,tooltip:`Clusters`,children:[We,Ge]})})})})]}),e[130]=Ue,e[131]=Ke);let qe;e[132]===Symbol.for(`react.memo_cache_sentinel`)?(qe=(0,W.jsx)(xh,{children:`IAM`}),e[132]=qe):qe=e[132];let Je;e[133]===n?Je=e[134]:(Je=n.startsWith(`/iam/list-users`)||n.startsWith(`/iam/create-user`),e[133]=n,e[134]=Je);let Ye,Xe;e[135]===Symbol.for(`react.memo_cache_sentinel`)?(Ye=(0,W.jsx)(is,{className:`size-4`}),Xe=(0,W.jsx)(`span`,{children:`Users`}),e[135]=Ye,e[136]=Xe):(Ye=e[135],Xe=e[136]);let Ze;e[137]===Je?Ze=e[138]:(Ze=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/iam/list-users`,children:(0,W.jsxs)(Th,{isActive:Je,tooltip:`Users`,children:[Ye,Xe]})})}),e[137]=Je,e[138]=Ze);let Qe;e[139]===n?Qe=e[140]:(Qe=n.startsWith(`/iam/list-policies`)||n.startsWith(`/iam/create-policy`),e[139]=n,e[140]=Qe);let $e,et;e[141]===Symbol.for(`react.memo_cache_sentinel`)?($e=(0,W.jsx)(ts,{className:`size-4`}),et=(0,W.jsx)(`span`,{children:`Policies`}),e[141]=$e,e[142]=et):($e=e[141],et=e[142]);let tt;e[143]===Qe?tt=e[144]:(tt=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/iam/list-policies`,children:(0,W.jsxs)(Th,{isActive:Qe,tooltip:`Policies`,children:[$e,et]})})}),e[143]=Qe,e[144]=tt);let nt;e[145]===n?nt=e[146]:(nt=n.startsWith(`/iam/list-roles`)||n.startsWith(`/iam/create-role`),e[145]=n,e[146]=nt);let z,rt;e[147]===Symbol.for(`react.memo_cache_sentinel`)?(z=(0,W.jsx)(rs,{className:`size-4`}),rt=(0,W.jsx)(`span`,{children:`Roles`}),e[147]=z,e[148]=rt):(z=e[147],rt=e[148]);let it;e[149]===nt?it=e[150]:(it=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/iam/list-roles`,children:(0,W.jsxs)(Th,{isActive:nt,tooltip:`Roles`,children:[z,rt]})})}),e[149]=nt,e[150]=it);let at;e[151]===n?at=e[152]:(at=n.startsWith(`/iam/list-instance-profiles`)||n.startsWith(`/iam/create-instance-profile`),e[151]=n,e[152]=at);let ot,st;e[153]===Symbol.for(`react.memo_cache_sentinel`)?(ot=(0,W.jsx)(Vo,{className:`size-4`}),st=(0,W.jsx)(`span`,{children:`Instance Profiles`}),e[153]=ot,e[154]=st):(ot=e[153],st=e[154]);let ct;e[155]===at?ct=e[156]:(ct=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/iam/list-instance-profiles`,children:(0,W.jsxs)(Th,{isActive:at,tooltip:`Instance Profiles`,children:[ot,st]})})}),e[155]=at,e[156]=ct);let lt;e[157]!==Ze||e[158]!==tt||e[159]!==it||e[160]!==ct?(lt=(0,W.jsxs)(bh,{children:[qe,(0,W.jsxs)(Sh,{children:[Ze,tt,it,ct]})]}),e[157]=Ze,e[158]=tt,e[159]=it,e[160]=ct,e[161]=lt):lt=e[161];let ut;e[162]===Symbol.for(`react.memo_cache_sentinel`)?(ut=(0,W.jsx)(xh,{children:`S3`}),e[162]=ut):ut=e[162];let dt;e[163]===n?dt=e[164]:(dt=n.startsWith(`/s3/ls`),e[163]=n,e[164]=dt);let ft,pt;e[165]===Symbol.for(`react.memo_cache_sentinel`)?(pt=(0,W.jsx)($o,{className:`size-4`}),ft=(0,W.jsx)(`span`,{children:`Buckets`}),e[165]=ft,e[166]=pt):(ft=e[165],pt=e[166]);let mt;e[167]===dt?mt=e[168]:(mt=(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/s3/ls`,children:(0,W.jsxs)(Th,{isActive:dt,tooltip:`Buckets`,children:[pt,ft]})})}),e[167]=dt,e[168]=mt);let ht;e[169]!==a||e[170]!==n?(ht=a&&(0,W.jsx)(Ch,{children:(0,W.jsx)(Fe,{to:`/s3/service-metrics`,children:(0,W.jsxs)(Th,{isActive:n.startsWith(`/s3/service-metrics`),tooltip:`Service Metrics`,children:[(0,W.jsx)(Mo,{className:`size-4`}),(0,W.jsx)(`span`,{children:`Service Metrics`})]})})}),e[169]=a,e[170]=n,e[171]=ht):ht=e[171];let gt;e[172]!==mt||e[173]!==ht?(gt=(0,W.jsxs)(bh,{children:[ut,(0,W.jsxs)(Sh,{children:[mt,ht]})]}),e[172]=mt,e[173]=ht,e[174]=gt):gt=e[174];let _t;e[175]===a?_t=e[176]:(_t=a&&(0,W.jsxs)(bh,{children:[(0,W.jsx)(xh,{children:`Documentation`}),(0,W.jsx)(Sh,{children:(0,W.jsx)(Ch,{children:(0,W.jsx)(`a`,{href:`https://docs.mulgadc.com`,target:`_blank`,rel:`noopener noreferrer`,children:(0,W.jsxs)(Th,{tooltip:`Documentation`,children:[(0,W.jsx)(No,{className:`size-4`}),(0,W.jsx)(`span`,{children:`Docs`})]})})})})]}),e[175]=a,e[176]=_t);let vt;e[177]!==gt||e[178]!==_t||e[179]!==ge||e[180]!==Ve||e[181]!==Ke||e[182]!==h||e[183]!==lt?(vt=(0,W.jsxs)(yh,{children:[h,ge,Ve,Ke,lt,gt,_t]}),e[177]=gt,e[178]=_t,e[179]=ge,e[180]=Ve,e[181]=Ke,e[182]=h,e[183]=lt,e[184]=vt):vt=e[184];let yt;e[185]===Symbol.for(`react.memo_cache_sentinel`)?(yt=(0,W.jsx)(vh,{className:`mx-0 w-full`}),e[185]=yt):yt=e[185];let bt,xt;e[186]===Symbol.for(`react.memo_cache_sentinel`)?(bt=(0,W.jsx)(Ko,{className:`size-4`}),xt=(0,W.jsx)(`span`,{children:`Sign out`}),e[186]=bt,e[187]=xt):(bt=e[186],xt=e[187]);let St;e[188]===s?St=e[189]:(St=(0,W.jsxs)(_h,{children:[yt,(0,W.jsx)(Sh,{children:(0,W.jsx)(Ch,{children:(0,W.jsxs)(Th,{onClick:s,tooltip:`Sign out`,children:[bt,xt]})})})]}),e[188]=s,e[189]=St);let Ct;e[190]===Symbol.for(`react.memo_cache_sentinel`)?(Ct=(0,W.jsx)(hh,{}),e[190]=Ct):Ct=e[190];let Tt;return e[191]!==vt||e[192]!==St?(Tt=(0,W.jsxs)(ph,{collapsible:`icon`,children:[c,vt,St,Ct]}),e[191]=vt,e[192]=St,e[193]=Tt):Tt=e[193],Tt}function Ug(e){return e.pathname}function Wg(e){let t=(0,go.c)(18),{error:n,reset:r}=e,i=M(),a=Fr(),o;t[0]!==a||t[1]!==r||t[2]!==i?(o=function(){a.invalidateQueries(),r(),i.invalidate()},t[0]=a,t[1]=r,t[2]=i,t[3]=o):o=t[3];let s=o,c;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,W.jsx)(Hg,{}),t[4]=c):c=t[4];let l;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,W.jsx)(Bg,{}),t[5]=l):l=t[5];let u,d;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(u=(0,W.jsx)(`div`,{className:`flex justify-center`,children:(0,W.jsx)(Io,{className:`size-12 text-destructive`})}),d=(0,W.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Something went wrong`}),t[6]=u,t[7]=d):(u=t[6],d=t[7]);let f=n.message||`An unexpected error occurred`,p;t[8]===f?p=t[9]:(p=(0,W.jsx)(`p`,{className:`text-muted-foreground`,children:f}),t[8]=f,t[9]=p);let m;t[10]===s?m=t[11]:(m=(0,W.jsx)(fn,{onClick:s,variant:`default`,children:`Try Again`}),t[10]=s,t[11]=m);let h;t[12]===Symbol.for(`react.memo_cache_sentinel`)?(h=(0,W.jsx)(Fe,{to:`/`,children:(0,W.jsx)(fn,{variant:`outline`,children:`Go Home`})}),t[12]=h):h=t[12];let g;t[13]===m?g=t[14]:(g=(0,W.jsxs)(`div`,{className:`flex justify-center gap-2`,children:[m,h]}),t[13]=m,t[14]=g);let _;return t[15]!==g||t[16]!==p?(_=(0,W.jsxs)(W.Fragment,{children:[c,(0,W.jsxs)(`main`,{className:`flex flex-1 flex-col`,children:[l,(0,W.jsx)(`div`,{className:`flex flex-1 items-center justify-center p-6 pt-0`,children:(0,W.jsxs)(`div`,{className:`max-w-md space-y-4 text-center`,children:[u,d,p,g]})})]})]}),t[15]=g,t[16]=p,t[17]=_):_=t[17],_}var Gg=za()({head:()=>({meta:[{title:`Mulga`}]}),component:()=>(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(uo,{}),(0,W.jsx)(Hg,{}),(0,W.jsxs)(`main`,{className:`flex flex-1 flex-col`,children:[(0,W.jsx)(Bg,{}),(0,W.jsx)(`div`,{className:`flex flex-1 flex-col p-8`,children:(0,W.jsx)(Za,{})})]})]}),errorComponent:Wg,notFoundComponent:Vg}),Kg=K(`/login`)({validateSearch:e=>({reason:e.reason===`expired`?`expired`:void 0}),beforeLoad:()=>{if(Kt())throw me({to:`/`})},component:q(()=>z(()=>import(`./login.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13])),`component`)}),qg=K(`/_auth`)({beforeLoad:()=>{if(!Kt())throw me({to:`/login`})},component:q(()=>z(()=>import(`./route.js`),__vite__mapDeps([14,1])),`component`)}),Jg=a({queryKey:[`ec2`,`availabilityZones`],queryFn:async()=>{let e=new zh({});return await St().send(e)},staleTime:3e5}),Yg=a({queryKey:[`ec2`,`instances`],queryFn:async()=>{let e=new Hh({});return await St().send(e)},refetchInterval:5e3}),Xg=e=>a({queryKey:[`ec2`,`instances`,e],queryFn:async()=>{let t=new Hh({InstanceIds:[e]});return await St().send(t)},refetchInterval:5e3}),Zg=e=>a({queryKey:[`ec2`,`iam-instance-profile-associations`,e],queryFn:async()=>{let t=new Bh({Filters:[{Name:`instance-id`,Values:[e]}]});return await St().send(t)},refetchInterval:5e3}),Qg=a({queryKey:[`ec2`,`instances`,`types`],queryFn:async()=>{let e=new Uh({Filters:[{Name:`capacity`,Values:[`true`]}]});return await St().send(e)},refetchInterval:5e3}),$g=a({queryKey:[`ec2`,`images`],queryFn:async()=>{let e=new Vh({});return await St().send(e)}}),e_=e=>a({queryKey:[`ec2`,`images`,e??`none`],queryFn:async()=>{if(!e)return{Images:[],$metadata:{}};let t=new Vh({ImageIds:[e]});return await St().send(t)}}),t_=a({queryKey:[`ec2`,`keypairs`],queryFn:async()=>{let e=new Gh({});return await St().send(e)}}),n_=e=>a({queryKey:[`ec2`,`keypairs`,e],queryFn:async()=>{let t=new Gh({KeyPairIds:[e]});return await St().send(t)}}),r_=a({queryKey:[`ec2`,`regions`],queryFn:async()=>{let e=new Jh({});return await St().send(e)},staleTime:3e5}),i_=a({queryKey:[`ec2`,`subnets`],queryFn:async()=>{let e=new Qh({});return await St().send(e)}}),a_=e=>a({queryKey:[`ec2`,`subnets`,e],queryFn:async()=>{let t=new Qh({SubnetIds:[e]});return await St().send(t)}}),o_=a({queryKey:[`ec2`,`snapshots`],queryFn:async()=>{let e=new Zh({});return await St().send(e)},refetchInterval:5e3}),s_=e=>a({queryKey:[`ec2`,`snapshots`,e],queryFn:async()=>{let t=new Zh({SnapshotIds:[e]});return await St().send(t)},refetchInterval:5e3}),c_=a({queryKey:[`ec2`,`internetGateways`],queryFn:async()=>{let e=new Wh({});return await St().send(e)},refetchInterval:5e3}),l_=e=>a({queryKey:[`ec2`,`internetGateways`,e],queryFn:async()=>{let t=new Wh({InternetGatewayIds:[e]});return await St().send(t)},refetchInterval:5e3}),u_=a({queryKey:[`ec2`,`routeTables`],queryFn:async()=>{let e=new Yh({});return await St().send(e)},refetchInterval:5e3}),d_=e=>a({queryKey:[`ec2`,`routeTables`,e],queryFn:async()=>{let t=new Yh({RouteTableIds:[e]});return await St().send(t)},refetchInterval:5e3}),f_=a({queryKey:[`ec2`,`vpcs`],queryFn:async()=>{let e=new eg({});return await St().send(e)}}),p_=e=>a({queryKey:[`ec2`,`vpcs`,e],queryFn:async()=>{let t=new eg({VpcIds:[e]});return await St().send(t)}}),m_=a({queryKey:[`ec2`,`placementGroups`],queryFn:async()=>{let e=new qh({});return await St().send(e)}}),h_=e=>a({queryKey:[`ec2`,`placementGroups`,e],queryFn:async()=>{let t=new qh({GroupIds:[e]});return await St().send(t)}}),g_=a({queryKey:[`ec2`,`securityGroups`],queryFn:async()=>{let e=new Xh({});return await St().send(e)}}),__=e=>a({queryKey:[`ec2`,`securityGroups`,e],queryFn:async()=>{let t=new Xh({GroupIds:[e]});return await St().send(t)}}),v_=a({queryKey:[`ec2`,`addresses`],queryFn:async()=>{let e=new Rh({});return await St().send(e)},refetchInterval:5e3}),y_=e=>a({queryKey:[`ec2`,`addresses`,e],queryFn:async()=>{let t=new Rh({AllocationIds:[e]});return await St().send(t)},refetchInterval:5e3}),b_=a({queryKey:[`ec2`,`natGateways`],queryFn:async()=>{let e=new Kh({});return await St().send(e)},refetchInterval:5e3}),x_=e=>a({queryKey:[`ec2`,`natGateways`,e],queryFn:async()=>{let t=new Kh({NatGatewayIds:[e]});return await St().send(t)},refetchInterval:5e3}),S_=a({queryKey:[`ec2`,`volumes`],queryFn:async()=>{let e=new $h({});return await St().send(e)},refetchInterval:5e3}),C_=e=>a({queryKey:[`ec2`,`volumes`,e],queryFn:async()=>{let t=new $h({VolumeIds:[e]});return await St().send(t)},refetchInterval:5e3}),w_=a({queryKey:[`s3`,`buckets`],queryFn:async()=>{let e=new Pg({});return await Ct().send(e)}}),T_=(e,t)=>a({queryKey:[`s3`,`buckets`,e,`objects`,t??``],queryFn:async()=>{let n=new Fg({Bucket:e,Prefix:t,Delimiter:`/`});return await Ct().send(n)}}),E_=K(`/_auth/`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData(Yg),e.queryClient.ensureQueryData(S_),e.queryClient.ensureQueryData(f_),e.queryClient.ensureQueryData(t_),e.queryClient.ensureQueryData(w_),e.queryClient.ensureQueryData(g_),e.queryClient.ensureQueryData(b_),e.queryClient.ensureQueryData(v_),e.queryClient.ensureQueryData(c_)])},head:()=>({meta:[{title:`Dashboard | Mulga`}]}),component:q(()=>z(()=>import(`./_auth.js`),__vite__mapDeps([15,1,2,3,4,5,6,16,17,18,19,20,21,22,8,12,23,24])),`component`)}),D_=K(`/_auth/nodes`)({head:()=>({meta:[{title:`Nodes | Mulga`}]}),component:q(()=>z(()=>import(`./nodes.js`),__vite__mapDeps([25,1,5,4,16,17,26,20,22,8,12,23,24])),`component`)}),O_=K(`/_auth/s3/service-metrics`)({head:()=>({meta:[{title:`Service Metrics | S3 | Mulga`}]}),component:q(()=>z(()=>import(`./service-metrics.js`),__vite__mapDeps([27,1,16,17,26,20,22,5,4,8,12,23,24])),`component`)}),k_=K(`/_auth/s3/ls/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(w_)},head:()=>({meta:[{title:`Buckets | S3 | Mulga`}]}),component:q(()=>z(()=>import(`./ls.js`),__vite__mapDeps([28,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),A_=K(`/_auth/s3/(buckets)/create-bucket`)({head:()=>({meta:[{title:`Create Bucket | S3 | Mulga`}]}),component:q(()=>z(()=>import(`./create-bucket.js`),__vite__mapDeps([31,1,32,2,3,4,5,6,33,11,34,35,7,8,36,18,37,38,39,20,13])),`component`)}),j_=K(`/_auth/iam/(users)/create-user`)({head:()=>({meta:[{title:`Create User | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./create-user.js`),__vite__mapDeps([40,1,41,2,3,4,5,6,33,11,34,35,7,8,36,18,37,38,39,20,13,42])),`component`)}),M_=K(`/_auth/iam/(roles)/create-role`)({head:()=>({meta:[{title:`Create Role | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./create-role.js`),__vite__mapDeps([43,1,41,2,3,4,5,6,33,11,34,35,7,8,36,18,37,38,39,20,13,44,42])),`component`)}),N_=K(`/_auth/iam/(policies)/create-policy`)({head:()=>({meta:[{title:`Create Policy | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./create-policy.js`),__vite__mapDeps([45,1,41,2,3,4,5,6,33,11,34,35,7,8,36,18,37,38,39,20,13,44,42])),`component`)}),P_=K(`/_auth/iam/(instance-profiles)/create-instance-profile`)({head:()=>({meta:[{title:`Create Instance Profile | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./create-instance-profile.js`),__vite__mapDeps([46,1,41,2,3,4,5,6,33,11,34,35,7,8,36,18,37,38,39,20,13,42])),`component`)}),F_=a({queryKey:[`iam`,`users`],queryFn:async()=>{let e=new Ng({});return await _t().send(e)},staleTime:3e5}),I_=e=>a({queryKey:[`iam`,`users`,e],queryFn:async()=>{let t=new Tg({UserName:e});return await _t().send(t)},staleTime:3e5}),L_=e=>a({queryKey:[`iam`,`access-keys`,e],queryFn:async()=>{let t=new Eg({UserName:e});return await _t().send(t)},staleTime:3e5}),R_=a({queryKey:[`iam`,`policies`],queryFn:async()=>{let e=new jg({Scope:`Local`});return await _t().send(e)},staleTime:3e5}),z_=e=>a({queryKey:[`iam`,`policies`,e],queryFn:async()=>{let t=new Sg({PolicyArn:e});return await _t().send(t)},staleTime:3e5}),B_=(e,t)=>a({queryKey:[`iam`,`policy-versions`,e,t],queryFn:async()=>{let n=new Cg({PolicyArn:e,VersionId:t});return await _t().send(n)},staleTime:3e5}),V_=e=>a({queryKey:[`iam`,`attached-user-policies`,e],queryFn:async()=>{let t=new Og({UserName:e});return await _t().send(t)},staleTime:3e5}),H_=a({queryKey:[`iam`,`roles`],queryFn:async()=>{let e=new Mg({});return await _t().send(e)},staleTime:3e5}),U_=e=>a({queryKey:[`iam`,`roles`,e],queryFn:async()=>{let t=new wg({RoleName:e});return await _t().send(t)},staleTime:3e5}),W_=e=>a({queryKey:[`iam`,`attached-role-policies`,e],queryFn:async()=>{let t=new Dg({RoleName:e});return await _t().send(t)},staleTime:3e5}),G_=a({queryKey:[`iam`,`instance-profiles`],queryFn:async()=>{let e=new kg({});return await _t().send(e)},staleTime:3e5}),K_=e=>a({queryKey:[`iam`,`instance-profiles`,e],queryFn:async()=>{let t=new xg({InstanceProfileName:e});return await _t().send(t)},staleTime:3e5}),q_=e=>a({queryKey:[`iam`,`instance-profiles-for-role`,e],queryFn:async()=>{let t=new Ag({RoleName:e});return await _t().send(t)},staleTime:3e5}),J_=K(`/_auth/eks/(clusters)/create-cluster`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData(f_),e.queryClient.ensureQueryData(i_),e.queryClient.ensureQueryData(H_),e.queryClient.ensureQueryData($g)])},head:()=>({meta:[{title:`Create Cluster | EKS | Mulga`}]}),component:q(()=>z(()=>import(`./create-cluster.js`),__vite__mapDeps([47,1,5,4,48,2,3,6,33,49,7,8,50,51,10,36,18,11,29,17,34,37,52,53,39,20,13,38,54])),`component`)}),Y_=K(`/_auth/ec2/(vpc)/create-vpc`)({head:()=>({meta:[{title:`Create VPC | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-vpc.js`),__vite__mapDeps([55,1,56,2,3,4,5,6,33,49,7,8,50,51,10,36,18,11,34,19,35,37,38,39,20,13,57])),`component`)}),X_=K(`/_auth/ec2/(volumes)/create-volume`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(Jg)},head:()=>({meta:[{title:`Create Volume | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-volume.js`),__vite__mapDeps([58,1,56,2,3,4,5,6,33,49,50,7,8,51,10,36,18,11,29,17,34,35,37,38,39,20,13,57])),`component`)}),Z_=K(`/_auth/ec2/(target-groups)/create-target-group`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(f_)},head:()=>({meta:[{title:`Create Target Group | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-target-group.js`),__vite__mapDeps([59,1,60,2,3,4,5,6,33,11,29,17,34,35,7,8,36,18,37,38,61,9,10,50,51,62,63,13,39,20,64])),`component`)}),Q_=K(`/_auth/ec2/(subnet)/create-subnet`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData(f_),e.queryClient.ensureQueryData(Jg)])},head:()=>({meta:[{title:`Create Subnet | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-subnet.js`),__vite__mapDeps([65,1,5,4,56,2,3,6,33,49,50,7,8,51,10,36,18,11,29,17,34,35,37,38,39,20,13,57])),`component`)}),$_=K(`/_auth/ec2/(snapshots)/create-snapshot`)({validateSearch:e=>({volumeId:typeof e.volumeId==`string`?e.volumeId:void 0}),loader:async({context:e})=>{await e.queryClient.ensureQueryData(S_)},head:()=>({meta:[{title:`Create Snapshot | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-snapshot.js`),__vite__mapDeps([66,1,56,2,3,4,5,6,33,49,50,7,8,51,10,36,18,11,29,17,34,35,37,38,39,20,13,44,57])),`component`)}),ev=K(`/_auth/ec2/(security-groups)/create-security-group`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(f_)},head:()=>({meta:[{title:`Create Security Group | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-security-group.js`),__vite__mapDeps([67,1,5,4,56,2,3,6,33,49,50,7,8,51,10,36,18,11,29,17,34,35,37,38,39,20,13,57])),`component`)}),tv=K(`/_auth/ec2/(route-tables)/create-route-table`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(f_)},head:()=>({meta:[{title:`Create Route Table | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-route-table.js`),__vite__mapDeps([68,1,5,4,56,2,3,6,33,49,50,7,8,51,10,36,18,11,29,17,34,39,20,13,57])),`component`)}),nv=K(`/_auth/ec2/(placement-groups)/create-placement-group`)({head:()=>({meta:[{title:`Create Placement Group | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-placement-group.js`),__vite__mapDeps([69,1,56,2,3,4,5,6,33,49,50,7,8,51,10,36,18,11,34,35,37,38,39,20,13,57])),`component`)}),rv=K(`/_auth/ec2/(nat-gateways)/create-nat-gateway`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData(i_),e.queryClient.ensureQueryData(v_)])},head:()=>({meta:[{title:`Create NAT Gateway | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-nat-gateway.js`),__vite__mapDeps([70,1,2,3,4,5,6,56,33,49,50,7,8,51,10,36,18,11,29,17,34,35,37,38,39,20,13,57])),`component`)}),iv=a({queryKey:[`acm`,`certificates`],queryFn:async()=>{let e=new Lh({});return await gt().send(e)}}),av=5e3,ov=a({queryKey:[`elbv2`,`loadBalancers`],queryFn:async()=>{let e=new mg({});return await tt().send(e)},refetchInterval:e=>(e.state.data?.LoadBalancers??[]).some(e=>e.State?.Code===`provisioning`)?av:!1}),sv=e=>a({queryKey:[`elbv2`,`loadBalancers`,e],queryFn:async()=>{let t=new mg({LoadBalancerArns:[e]});return await tt().send(t)},refetchInterval:e=>(e.state.data?.LoadBalancers?.[0])?.State?.Code===`provisioning`?av:!1}),cv=e=>a({queryKey:[`elbv2`,`loadBalancers`,e,`attributes`],queryFn:async()=>{let t=new pg({LoadBalancerArn:e});return await tt().send(t)}}),lv=a({queryKey:[`elbv2`,`targetGroups`],queryFn:async()=>{let e=new yg({});return await tt().send(e)}}),uv=e=>a({queryKey:[`elbv2`,`targetGroups`,e],queryFn:async()=>{let t=new yg({TargetGroupArns:[e]});return await tt().send(t)}}),dv=e=>a({queryKey:[`elbv2`,`targetGroups`,e,`attributes`],queryFn:async()=>{let t=new vg({TargetGroupArn:e});return await tt().send(t)}}),fv=e=>a({queryKey:[`elbv2`,`listeners`,e],queryFn:async()=>{let t=new fg({LoadBalancerArn:e});return await tt().send(t)}}),pv=e=>a({queryKey:[`elbv2`,`listeners`,e,`rules`],queryFn:async()=>{let t=new hg({ListenerArn:e});return await tt().send(t)}}),mv=e=>a({queryKey:[`elbv2`,`targetGroups`,e,`health`],queryFn:async()=>{let t=new bg({TargetGroupArn:e});return await tt().send(t)}}),hv=a({queryKey:[`elbv2`,`sslPolicies`],queryFn:async()=>{let e=new gg({});return await tt().send(e)},staleTime:1/0}),gv=e=>a({queryKey:[`elbv2`,`tags`,...e],queryFn:async()=>{let t=new _g({ResourceArns:e});return await tt().send(t)},enabled:e.length>0}),_v=K(`/_auth/ec2/(load-balancers)/create-load-balancer`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData(f_),e.queryClient.ensureQueryData(i_),e.queryClient.ensureQueryData(g_),e.queryClient.ensureQueryData(lv),e.queryClient.ensureQueryData(iv),e.queryClient.ensureQueryData(hv)])},head:()=>({meta:[{title:`Create Load Balancer | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-load-balancer.js`),__vite__mapDeps([71,1,5,4,72,2,3,6,73,7,8,33,13,44,60,50,51,10,36,18,11,29,17,34,62,35,37,38,63,61,9,39,20,64])),`component`)}),vv=K(`/_auth/ec2/(key)/import-key-pair`)({head:()=>({meta:[{title:`Import Key Pair | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./import-key-pair.js`),__vite__mapDeps([74,1,56,2,3,4,5,6,33,49,11,34,35,7,8,36,18,37,38,39,20,13,44,57])),`component`)}),yv=K(`/_auth/ec2/(key)/create-key-pair`)({head:()=>({meta:[{title:`Create Key Pair | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-key-pair.js`),__vite__mapDeps([75,1,56,2,3,4,5,6,33,49,73,7,8,11,34,36,76,35,18,37,38,53,39,20,13,57])),`component`)}),bv=K(`/_auth/ec2/(internet-gateways)/create-internet-gateway`)({head:()=>({meta:[{title:`Create Internet Gateway | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./create-internet-gateway.js`),__vite__mapDeps([77,1,56,2,3,4,5,6,33,49,11,34,35,7,8,36,18,37,38,39,20,13,57])),`component`)}),xv=K(`/_auth/ec2/(instances)/run-instances`)({loader:async({context:e})=>{await Promise.all([e.queryClient.ensureQueryData($g),e.queryClient.ensureQueryData(t_),e.queryClient.ensureQueryData(Qg),e.queryClient.ensureQueryData(i_),e.queryClient.ensureQueryData(m_),e.queryClient.ensureQueryData(f_),e.queryClient.ensureQueryData(g_)])},head:()=>({meta:[{title:`Run Instances | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./run-instances.js`),__vite__mapDeps([78,1,5,4,56,2,3,6,33,49,7,8,9,10,50,51,36,18,11,29,17,34,35,37,38,20,13,57])),`component`)}),Sv=K(`/_auth/ec2/(elastic-ips)/allocate-address`)({head:()=>({meta:[{title:`Allocate Elastic IP | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./allocate-address.js`),__vite__mapDeps([79,1,56,2,3,4,5,6,33,49,11,34,35,7,8,36,18,37,38,39,20,13,57])),`component`)}),Cv=K(`/_auth/s3/ls/$bucket`)({component:q(()=>z(()=>import(`./route2.js`),__vite__mapDeps([80,1])),`component`)}),wv=K(`/_auth/s3/ls/$bucket/`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(T_(t.bucket))},head:({params:e})=>({meta:[{title:`${e.bucket} | S3 | Mulga`}]}),component:q(()=>z(()=>import(`./_bucket.js`),__vite__mapDeps([81,1,32,2,3,4,5,6,33,82,7,8,76,63,83,73,29,17,34,20])),`component`)}),Tv=K(`/_auth/iam/(users)/list-users/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(F_)},head:()=>({meta:[{title:`Users | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./list-users.js`),__vite__mapDeps([84,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),Ev=K(`/_auth/iam/(roles)/list-roles/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(H_)},head:()=>({meta:[{title:`Roles | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./list-roles.js`),__vite__mapDeps([85,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),Dv=K(`/_auth/iam/(policies)/list-policies/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(R_)},head:()=>({meta:[{title:`Policies | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./list-policies.js`),__vite__mapDeps([86,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),Ov=K(`/_auth/iam/(instance-profiles)/list-instance-profiles/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(G_)},head:()=>({meta:[{title:`Instance Profiles | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./list-instance-profiles.js`),__vite__mapDeps([87,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),kv=3e4,Av=3e5,jv=a({queryKey:[`eks`,`clusters`],queryFn:async()=>{let e=new ug({});return await ut().send(e)},staleTime:kv}),Mv=e=>a({queryKey:[`eks`,`clusters`,e],queryFn:async()=>{let t=new ig({name:e});return await ut().send(t)},staleTime:kv}),Nv=e=>a({queryKey:[`eks`,`clusters`,e,`nodegroups`],queryFn:async()=>{let t=new dg({clusterName:e});return await ut().send(t)},staleTime:kv}),Pv=(e,t)=>a({queryKey:[`eks`,`clusters`,e,`nodegroups`,t],queryFn:async()=>{let n=new ag({clusterName:e,nodegroupName:t});return await ut().send(n)},staleTime:kv}),Fv=e=>a({queryKey:[`eks`,`clusters`,e,`access-entries`],queryFn:async()=>{let t=new og({clusterName:e});return await ut().send(t)},staleTime:kv}),Iv=(e,t)=>a({queryKey:[`eks`,`clusters`,e,`access-entries`,t],queryFn:async()=>{let n=new tg({clusterName:e,principalArn:t});return await ut().send(n)},staleTime:kv}),Lv=(e,t)=>a({queryKey:[`eks`,`clusters`,e,`access-entries`,t,`policies`],queryFn:async()=>{let n=new lg({clusterName:e,principalArn:t});return await ut().send(n)},staleTime:kv});a({queryKey:[`eks`,`access-policies`],queryFn:async()=>{let e=new sg({});return await ut().send(e)},staleTime:Av});var Rv=a({queryKey:[`eks`,`addon-versions`],queryFn:async()=>{let e=new rg({});return await ut().send(e)},staleTime:Av}),zv=e=>a({queryKey:[`eks`,`clusters`,e,`addons`],queryFn:async()=>{let t=new cg({clusterName:e});return await ut().send(t)},staleTime:kv}),Bv=(e,t)=>a({queryKey:[`eks`,`clusters`,e,`addons`,t],queryFn:async()=>{let n=new ng({clusterName:e,addonName:t});return await ut().send(n)},staleTime:kv}),Vv=K(`/_auth/eks/(clusters)/list-clusters/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(jv)},head:()=>({meta:[{title:`Clusters | EKS | Mulga`}]}),component:q(()=>z(()=>import(`./list-clusters.js`),__vite__mapDeps([88,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),Hv=K(`/_auth/ec2/(vpc)/describe-vpcs/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(f_)},head:()=>({meta:[{title:`VPCs | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-vpcs.js`),__vite__mapDeps([89,1,2,3,4,5,6,7,8,29,17,30,20,21])),`component`)}),Uv=K(`/_auth/ec2/(volumes)/describe-volumes/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(S_)},head:()=>({meta:[{title:`Volumes | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-volumes.js`),__vite__mapDeps([90,1,2,3,4,5,6,7,8,29,17,30,20,21])),`component`)}),Wv=K(`/_auth/ec2/(target-groups)/describe-target-groups/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(lv)},head:()=>({meta:[{title:`Target Groups | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-target-groups.js`),__vite__mapDeps([91,1,2,3,4,5,6,7,8,16,17,29,20])),`component`)}),Gv=K(`/_auth/ec2/(subnet)/describe-subnets/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(i_)},head:()=>({meta:[{title:`Subnets | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-subnets.js`),__vite__mapDeps([92,1,2,3,4,5,6,7,8,29,17,30,20,21])),`component`)}),Kv=K(`/_auth/ec2/(snapshots)/describe-snapshots/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(o_)},head:()=>({meta:[{title:`Snapshots | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-snapshots.js`),__vite__mapDeps([93,1,2,3,4,5,6,7,8,29,17,30,20,21])),`component`)}),qv=K(`/_auth/ec2/(security-groups)/describe-security-groups/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(g_)},head:()=>({meta:[{title:`Security Groups | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-security-groups.js`),__vite__mapDeps([94,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),Jv=K(`/_auth/ec2/(route-tables)/describe-route-tables/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(u_)},head:()=>({meta:[{title:`Route Tables | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-route-tables.js`),__vite__mapDeps([95,1,2,3,4,5,6,7,8,29,17,30,20,22])),`component`)}),Yv=K(`/_auth/ec2/(placement-groups)/describe-placement-groups/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(m_)},head:()=>({meta:[{title:`Placement Groups | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-placement-groups.js`),__vite__mapDeps([96,1,2,3,4,5,6,7,8,29,17,30,20,21])),`component`)}),Xv=K(`/_auth/ec2/(nat-gateways)/describe-nat-gateways/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(b_)},head:()=>({meta:[{title:`NAT Gateways | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-nat-gateways.js`),__vite__mapDeps([97,1,2,3,4,5,6,7,8,29,17,30,20,21])),`component`)}),Zv=K(`/_auth/ec2/(load-balancers)/describe-load-balancers/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(ov)},head:()=>({meta:[{title:`Load Balancers | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-load-balancers.js`),__vite__mapDeps([98,1,2,3,4,5,6,7,8,29,17,20,21])),`component`)}),Qv=K(`/_auth/ec2/(key)/describe-key-pairs/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(t_)},head:()=>({meta:[{title:`Key Pairs | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-key-pairs.js`),__vite__mapDeps([99,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),$v=K(`/_auth/ec2/(internet-gateways)/describe-internet-gateways/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(c_)},head:()=>({meta:[{title:`Internet Gateways | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-internet-gateways.js`),__vite__mapDeps([100,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),ey=K(`/_auth/ec2/(instances)/describe-instances/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(Yg)},head:()=>({meta:[{title:`Instances | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-instances.js`),__vite__mapDeps([101,1,2,3,4,5,6,7,8,29,17,30,20,21])),`component`)}),ty=K(`/_auth/ec2/(images)/describe-images/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData($g)},head:()=>({meta:[{title:`Images | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-images.js`),__vite__mapDeps([102,1,29,17,30,2,3,4,5,6,20,21,54])),`component`)}),ny=K(`/_auth/ec2/(elastic-ips)/describe-addresses/`)({loader:async({context:e})=>{await e.queryClient.ensureQueryData(v_)},head:()=>({meta:[{title:`Elastic IPs | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./describe-addresses.js`),__vite__mapDeps([103,1,2,3,4,5,6,7,8,29,17,30,20])),`component`)}),ry=K(`/_auth/s3/ls/$bucket/$`)({loader:async({context:e,params:t})=>{let n=Vt(t._splat??``);await e.queryClient.ensureQueryData(T_(t.bucket,n))},head:({params:e})=>({meta:[{title:`${Bt(e._splat??``,`${e.bucket}/`)} | S3 | Mulga`}]}),component:q(()=>z(()=>import(`./_.js`),__vite__mapDeps([104,1,2,3,4,5,6,32,33,82,7,8,76,63,83,73,29,17,34])),`component`)}),iy=K(`/_auth/iam/(users)/list-users/$userName`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(I_(t.userName)),e.queryClient.ensureQueryData(L_(t.userName)),e.queryClient.ensureQueryData(V_(t.userName)),e.queryClient.ensureQueryData(R_)])},head:({params:e})=>({meta:[{title:`${e.userName} | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./_userName.js`),__vite__mapDeps([105,1,5,4,41,2,3,6,33,73,7,8,29,17,34,36,63,53,83,106,20,21,38])),`component`)}),ay=K(`/_auth/iam/(roles)/list-roles/$roleName`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(U_(t.roleName)),e.queryClient.ensureQueryData(W_(t.roleName)),e.queryClient.ensureQueryData(q_(t.roleName)),e.queryClient.ensureQueryData(R_)])},head:({params:e})=>({meta:[{title:`${e.roleName} | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./_roleName.js`),__vite__mapDeps([107,1,2,3,4,5,6,41,33,7,8,29,17,34,63,83,73,106,20,108])),`component`)}),oy=K(`/_auth/iam/(policies)/list-policies/$policyArn`)({loader:async({context:e,params:t})=>{let n=decodeURIComponent(t.policyArn),r=await e.queryClient.ensureQueryData(z_(n)),i=r.Policy?.DefaultVersionId;return i&&await e.queryClient.ensureQueryData(B_(n,i)),r},head:({loaderData:e})=>({meta:[{title:`${e?.Policy?.PolicyName??`Policy`} | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./_policyArn.js`),__vite__mapDeps([109,1,5,4,41,2,3,6,33,7,8,29,17,34,63,83,73,106,20,108])),`component`)}),sy=K(`/_auth/iam/(instance-profiles)/list-instance-profiles/$instanceProfileName`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(K_(t.instanceProfileName)),e.queryClient.ensureQueryData(H_)])},head:({params:e})=>({meta:[{title:`${e.instanceProfileName} | IAM | Mulga`}]}),component:q(()=>z(()=>import(`./_instanceProfileName.js`),__vite__mapDeps([110,1,2,3,4,5,6,41,33,7,8,29,17,34,63,83,73,106,20])),`component`)}),cy=K(`/_auth/eks/(clusters)/list-clusters/$clusterName`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(Mv(t.clusterName)),e.queryClient.ensureQueryData(Nv(t.clusterName)),e.queryClient.ensureQueryData(Fv(t.clusterName))])},head:({params:e})=>({meta:[{title:`${e.clusterName} | EKS | Mulga`}]}),component:q(()=>z(()=>import(`./_clusterName.js`),__vite__mapDeps([111,1,2,3,4,5,6,48,33,49,73,7,8,112,51,10,11,16,17,29,34,35,36,18,37,38,63,83,106,20,22,13,44])),`component`)}),ly=K(`/_auth/ec2/(vpc)/describe-vpcs/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(p_(t.id)),e.queryClient.ensureQueryData(i_)])},head:({params:e})=>({meta:[{title:`${e.id} | VPC | Mulga`}]}),component:q(()=>z(()=>import(`./_id16.js`),__vite__mapDeps([113,1,2,3,4,5,6,56,33,49,7,8,29,17,34,63,83,73,106,20,21])),`component`)}),uy=K(`/_auth/ec2/(volumes)/modify-volume/$id`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(C_(t.id))},head:({params:e})=>({meta:[{title:`Resize ${e.id} | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./_id15.js`),__vite__mapDeps([114,1,56,2,3,4,5,6,33,49,11,29,17,34,39,7,8,20,13,57])),`component`)}),dy=K(`/_auth/ec2/(volumes)/describe-volumes/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(C_(t.id)),e.queryClient.ensureQueryData(Yg)])},head:({params:e})=>({meta:[{title:`${e.id} | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./_id14.js`),__vite__mapDeps([115,1,2,3,4,5,6,56,33,49,73,7,8,50,51,10,36,18,29,17,34,63,83,106,20,21,13])),`component`)}),fy=K(`/_auth/ec2/(target-groups)/describe-target-groups/$id`)({loader:async({context:e,params:t})=>{let n=decodeURIComponent(t.id);await Promise.all([e.queryClient.ensureQueryData(uv(n)),e.queryClient.ensureQueryData(dv(n)),e.queryClient.ensureQueryData(gv([n])),e.queryClient.ensureQueryData(Yg)])},head:({params:e})=>({meta:[{title:`${decodeURIComponent(e.id)} | Target Group | Mulga`}]}),component:q(()=>z(()=>import(`./_id13.js`),__vite__mapDeps([116,1,5,4,60,2,3,6,33,73,7,8,112,51,10,16,17,29,34,63,83,106,117,50,36,18,62,13,20])),`component`)}),py=K(`/_auth/ec2/(subnet)/describe-subnets/$id`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(a_(t.id))},head:({params:e})=>({meta:[{title:`${e.id} | Subnet | Mulga`}]}),component:q(()=>z(()=>import(`./_id12.js`),__vite__mapDeps([118,1,2,3,4,5,6,56,33,49,7,8,29,17,34,63,83,73,106,20,21])),`component`)}),my=K(`/_auth/ec2/(snapshots)/describe-snapshots/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(s_(t.id)),e.queryClient.ensureQueryData(r_)])},head:({params:e})=>({meta:[{title:`${e.id} | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./_id11.js`),__vite__mapDeps([119,1,2,3,4,5,6,56,33,49,73,7,8,29,17,34,37,63,83,106,20,21,13])),`component`)}),hy=K(`/_auth/ec2/(security-groups)/describe-security-groups/$id`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(__(t.id))},head:({params:e})=>({meta:[{title:`${e.id} | Security Group | Mulga`}]}),component:q(()=>z(()=>import(`./_id10.js`),__vite__mapDeps([120,1,2,3,4,5,6,56,33,49,73,7,8,50,51,10,36,18,11,29,17,34,62,63,83,106,20,13,57])),`component`)}),gy=K(`/_auth/ec2/(route-tables)/describe-route-tables/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(d_(t.id)),e.queryClient.ensureQueryData(c_),e.queryClient.ensureQueryData(b_),e.queryClient.ensureQueryData(i_)])},head:({params:e})=>({meta:[{title:`${e.id} | Route Table | Mulga`}]}),component:q(()=>z(()=>import(`./_id9.js`),__vite__mapDeps([121,1,2,3,4,5,6,56,33,49,7,8,50,51,10,36,18,11,29,17,34,63,83,73,106,20,22,13,57])),`component`)}),_y=K(`/_auth/ec2/(placement-groups)/describe-placement-groups/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(h_(t.id)),e.queryClient.ensureQueryData(Yg)])},head:({params:e})=>({meta:[{title:`${e.id} | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./_id8.js`),__vite__mapDeps([122,1,2,3,4,5,6,56,33,49,7,8,29,17,34,63,83,73,106,20,21])),`component`)}),vy=K(`/_auth/ec2/(nat-gateways)/describe-nat-gateways/$id`)({loader:async({context:e,params:t})=>{await e.queryClient.ensureQueryData(x_(t.id))},head:({params:e})=>({meta:[{title:`${e.id} | NAT Gateway | Mulga`}]}),component:q(()=>z(()=>import(`./_id7.js`),__vite__mapDeps([123,1,2,3,4,5,6,56,33,49,7,8,29,17,34,63,83,73,106,20,21])),`component`)}),yy=K(`/_auth/ec2/(load-balancers)/describe-load-balancers/$id`)({loader:async({context:e,params:t})=>{let n=decodeURIComponent(t.id);await Promise.all([e.queryClient.ensureQueryData(sv(n)),e.queryClient.ensureQueryData(fv(n)),e.queryClient.ensureQueryData(cv(n)),e.queryClient.ensureQueryData(gv([n])),e.queryClient.ensureQueryData(lv),e.queryClient.ensureQueryData(i_)])},head:({params:e})=>({meta:[{title:`${decodeURIComponent(e.id)} | Load Balancer | Mulga`}]}),component:q(()=>z(()=>import(`./_id6.js`),__vite__mapDeps([124,1,5,4,72,2,3,6,73,7,8,33,13,44,60,50,51,10,36,18,112,11,29,17,34,63,83,106,117,62,20,21,64])),`component`)}),by=K(`/_auth/ec2/(key)/describe-key-pairs/$id`)({loader:async({context:e,params:t})=>await e.queryClient.ensureQueryData(n_(t.id)),head:({loaderData:e})=>({meta:[{title:`${e?.KeyPairs?.[0]?.KeyName??`Key Pair`} | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./_id5.js`),__vite__mapDeps([125,1,5,4,56,2,3,6,33,49,7,8,29,17,34,63,83,73,106,20])),`component`)}),xy=K(`/_auth/ec2/(internet-gateways)/describe-internet-gateways/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(l_(t.id)),e.queryClient.ensureQueryData(f_)])},head:({params:e})=>({meta:[{title:`${e.id} | Internet Gateway | Mulga`}]}),component:q(()=>z(()=>import(`./_id4.js`),__vite__mapDeps([126,1,2,3,4,5,6,56,33,49,7,8,50,51,10,36,18,29,17,34,63,83,73,106,20])),`component`)}),Sy=K(`/_auth/ec2/(instances)/describe-instances/$id`)({loader:async({context:e,params:t})=>{let[n]=await Promise.all([e.queryClient.ensureQueryData(Xg(t.id)),e.queryClient.ensureQueryData(Qg)]),r=n.Reservations?.[0]?.Instances?.[0]?.ImageId;return await e.queryClient.ensureQueryData(e_(r)),n},head:({loaderData:e})=>({meta:[{title:`${e?.Reservations?.[0]?.Instances?.[0]?.InstanceId??`Instance`} | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./_id3.js`),__vite__mapDeps([127,1,2,3,4,5,6,56,33,49,73,7,8,50,51,10,36,18,16,17,29,34,52,63,83,106,20,21,13,44,23,24,128])),`component`)}),Cy=K(`/_auth/ec2/(images)/describe-images/$id`)({loader:async({context:e,params:t})=>await e.queryClient.ensureQueryData(e_(t.id)),head:({loaderData:e})=>({meta:[{title:`${e?.Images?.[0]?.Name??`Image`} | EC2 | Mulga`}]}),component:q(()=>z(()=>import(`./_id2.js`),__vite__mapDeps([129,1,29,17,34,2,3,4,5,6,20,21,54,128,106])),`component`)}),wy=K(`/_auth/ec2/(elastic-ips)/describe-addresses/$id`)({loader:async({context:e,params:t})=>{await Promise.all([e.queryClient.ensureQueryData(y_(t.id)),e.queryClient.ensureQueryData(Yg)])},head:({params:e})=>({meta:[{title:`${e.id} | Elastic IP | Mulga`}]}),component:q(()=>z(()=>import(`./_id.js`),__vite__mapDeps([130,1,5,4,56,2,3,6,33,49,7,8,50,51,10,36,18,29,17,34,63,83,73,106,20])),`component`)}),Ty=Kg.update({id:`/login`,path:`/login`,getParentRoute:()=>Gg}),$=qg.update({id:`/_auth`,getParentRoute:()=>Gg}),Ey=E_.update({id:`/`,path:`/`,getParentRoute:()=>$}),Dy=D_.update({id:`/nodes`,path:`/nodes`,getParentRoute:()=>$}),Oy=O_.update({id:`/s3/service-metrics`,path:`/s3/service-metrics`,getParentRoute:()=>$}),ky=k_.update({id:`/s3/ls/`,path:`/s3/ls/`,getParentRoute:()=>$}),Ay=A_.update({id:`/s3/(buckets)/create-bucket`,path:`/s3/create-bucket`,getParentRoute:()=>$}),jy=j_.update({id:`/iam/(users)/create-user`,path:`/iam/create-user`,getParentRoute:()=>$}),My=M_.update({id:`/iam/(roles)/create-role`,path:`/iam/create-role`,getParentRoute:()=>$}),Ny=N_.update({id:`/iam/(policies)/create-policy`,path:`/iam/create-policy`,getParentRoute:()=>$}),Py=P_.update({id:`/iam/(instance-profiles)/create-instance-profile`,path:`/iam/create-instance-profile`,getParentRoute:()=>$}),Fy=J_.update({id:`/eks/(clusters)/create-cluster`,path:`/eks/create-cluster`,getParentRoute:()=>$}),Iy=Y_.update({id:`/ec2/(vpc)/create-vpc`,path:`/ec2/create-vpc`,getParentRoute:()=>$}),Ly=X_.update({id:`/ec2/(volumes)/create-volume`,path:`/ec2/create-volume`,getParentRoute:()=>$}),Ry=Z_.update({id:`/ec2/(target-groups)/create-target-group`,path:`/ec2/create-target-group`,getParentRoute:()=>$}),zy=Q_.update({id:`/ec2/(subnet)/create-subnet`,path:`/ec2/create-subnet`,getParentRoute:()=>$}),By=$_.update({id:`/ec2/(snapshots)/create-snapshot`,path:`/ec2/create-snapshot`,getParentRoute:()=>$}),Vy=ev.update({id:`/ec2/(security-groups)/create-security-group`,path:`/ec2/create-security-group`,getParentRoute:()=>$}),Hy=tv.update({id:`/ec2/(route-tables)/create-route-table`,path:`/ec2/create-route-table`,getParentRoute:()=>$}),Uy=nv.update({id:`/ec2/(placement-groups)/create-placement-group`,path:`/ec2/create-placement-group`,getParentRoute:()=>$}),Wy=rv.update({id:`/ec2/(nat-gateways)/create-nat-gateway`,path:`/ec2/create-nat-gateway`,getParentRoute:()=>$}),Gy=_v.update({id:`/ec2/(load-balancers)/create-load-balancer`,path:`/ec2/create-load-balancer`,getParentRoute:()=>$}),Ky=vv.update({id:`/ec2/(key)/import-key-pair`,path:`/ec2/import-key-pair`,getParentRoute:()=>$}),qy=yv.update({id:`/ec2/(key)/create-key-pair`,path:`/ec2/create-key-pair`,getParentRoute:()=>$}),Jy=bv.update({id:`/ec2/(internet-gateways)/create-internet-gateway`,path:`/ec2/create-internet-gateway`,getParentRoute:()=>$}),Yy=xv.update({id:`/ec2/(instances)/run-instances`,path:`/ec2/run-instances`,getParentRoute:()=>$}),Xy=Sv.update({id:`/ec2/(elastic-ips)/allocate-address`,path:`/ec2/allocate-address`,getParentRoute:()=>$}),Zy=Cv.update({id:`/s3/ls/$bucket`,path:`/s3/ls/$bucket`,getParentRoute:()=>$}),Qy=wv.update({id:`/`,path:`/`,getParentRoute:()=>Zy}),$y=Tv.update({id:`/iam/(users)/list-users/`,path:`/iam/list-users/`,getParentRoute:()=>$}),eb=Ev.update({id:`/iam/(roles)/list-roles/`,path:`/iam/list-roles/`,getParentRoute:()=>$}),tb=Dv.update({id:`/iam/(policies)/list-policies/`,path:`/iam/list-policies/`,getParentRoute:()=>$}),nb=Ov.update({id:`/iam/(instance-profiles)/list-instance-profiles/`,path:`/iam/list-instance-profiles/`,getParentRoute:()=>$}),rb=Vv.update({id:`/eks/(clusters)/list-clusters/`,path:`/eks/list-clusters/`,getParentRoute:()=>$}),ib=Hv.update({id:`/ec2/(vpc)/describe-vpcs/`,path:`/ec2/describe-vpcs/`,getParentRoute:()=>$}),ab=Uv.update({id:`/ec2/(volumes)/describe-volumes/`,path:`/ec2/describe-volumes/`,getParentRoute:()=>$}),ob=Wv.update({id:`/ec2/(target-groups)/describe-target-groups/`,path:`/ec2/describe-target-groups/`,getParentRoute:()=>$}),sb=Gv.update({id:`/ec2/(subnet)/describe-subnets/`,path:`/ec2/describe-subnets/`,getParentRoute:()=>$}),cb=Kv.update({id:`/ec2/(snapshots)/describe-snapshots/`,path:`/ec2/describe-snapshots/`,getParentRoute:()=>$}),lb=qv.update({id:`/ec2/(security-groups)/describe-security-groups/`,path:`/ec2/describe-security-groups/`,getParentRoute:()=>$}),ub=Jv.update({id:`/ec2/(route-tables)/describe-route-tables/`,path:`/ec2/describe-route-tables/`,getParentRoute:()=>$}),db=Yv.update({id:`/ec2/(placement-groups)/describe-placement-groups/`,path:`/ec2/describe-placement-groups/`,getParentRoute:()=>$}),fb=Xv.update({id:`/ec2/(nat-gateways)/describe-nat-gateways/`,path:`/ec2/describe-nat-gateways/`,getParentRoute:()=>$}),pb=Zv.update({id:`/ec2/(load-balancers)/describe-load-balancers/`,path:`/ec2/describe-load-balancers/`,getParentRoute:()=>$}),mb=Qv.update({id:`/ec2/(key)/describe-key-pairs/`,path:`/ec2/describe-key-pairs/`,getParentRoute:()=>$}),hb=$v.update({id:`/ec2/(internet-gateways)/describe-internet-gateways/`,path:`/ec2/describe-internet-gateways/`,getParentRoute:()=>$}),gb=ey.update({id:`/ec2/(instances)/describe-instances/`,path:`/ec2/describe-instances/`,getParentRoute:()=>$}),_b=ty.update({id:`/ec2/(images)/describe-images/`,path:`/ec2/describe-images/`,getParentRoute:()=>$}),vb=ny.update({id:`/ec2/(elastic-ips)/describe-addresses/`,path:`/ec2/describe-addresses/`,getParentRoute:()=>$}),yb=ry.update({id:`/$`,path:`/$`,getParentRoute:()=>Zy}),bb=iy.update({id:`/iam/(users)/list-users/$userName`,path:`/iam/list-users/$userName`,getParentRoute:()=>$}),xb=ay.update({id:`/iam/(roles)/list-roles/$roleName`,path:`/iam/list-roles/$roleName`,getParentRoute:()=>$}),Sb=oy.update({id:`/iam/(policies)/list-policies/$policyArn`,path:`/iam/list-policies/$policyArn`,getParentRoute:()=>$}),Cb=sy.update({id:`/iam/(instance-profiles)/list-instance-profiles/$instanceProfileName`,path:`/iam/list-instance-profiles/$instanceProfileName`,getParentRoute:()=>$}),wb=cy.update({id:`/eks/(clusters)/list-clusters/$clusterName`,path:`/eks/list-clusters/$clusterName`,getParentRoute:()=>$}),Tb=ly.update({id:`/ec2/(vpc)/describe-vpcs/$id`,path:`/ec2/describe-vpcs/$id`,getParentRoute:()=>$}),Eb=uy.update({id:`/ec2/(volumes)/modify-volume/$id`,path:`/ec2/modify-volume/$id`,getParentRoute:()=>$}),Db=dy.update({id:`/ec2/(volumes)/describe-volumes/$id`,path:`/ec2/describe-volumes/$id`,getParentRoute:()=>$}),Ob=fy.update({id:`/ec2/(target-groups)/describe-target-groups/$id`,path:`/ec2/describe-target-groups/$id`,getParentRoute:()=>$}),kb=py.update({id:`/ec2/(subnet)/describe-subnets/$id`,path:`/ec2/describe-subnets/$id`,getParentRoute:()=>$}),Ab=my.update({id:`/ec2/(snapshots)/describe-snapshots/$id`,path:`/ec2/describe-snapshots/$id`,getParentRoute:()=>$}),jb=hy.update({id:`/ec2/(security-groups)/describe-security-groups/$id`,path:`/ec2/describe-security-groups/$id`,getParentRoute:()=>$}),Mb=gy.update({id:`/ec2/(route-tables)/describe-route-tables/$id`,path:`/ec2/describe-route-tables/$id`,getParentRoute:()=>$}),Nb=_y.update({id:`/ec2/(placement-groups)/describe-placement-groups/$id`,path:`/ec2/describe-placement-groups/$id`,getParentRoute:()=>$}),Pb=vy.update({id:`/ec2/(nat-gateways)/describe-nat-gateways/$id`,path:`/ec2/describe-nat-gateways/$id`,getParentRoute:()=>$}),Fb=yy.update({id:`/ec2/(load-balancers)/describe-load-balancers/$id`,path:`/ec2/describe-load-balancers/$id`,getParentRoute:()=>$}),Ib=by.update({id:`/ec2/(key)/describe-key-pairs/$id`,path:`/ec2/describe-key-pairs/$id`,getParentRoute:()=>$}),Lb=xy.update({id:`/ec2/(internet-gateways)/describe-internet-gateways/$id`,path:`/ec2/describe-internet-gateways/$id`,getParentRoute:()=>$}),Rb=Sy.update({id:`/ec2/(instances)/describe-instances/$id`,path:`/ec2/describe-instances/$id`,getParentRoute:()=>$}),zb=Cy.update({id:`/ec2/(images)/describe-images/$id`,path:`/ec2/describe-images/$id`,getParentRoute:()=>$}),Bb=wy.update({id:`/ec2/(elastic-ips)/describe-addresses/$id`,path:`/ec2/describe-addresses/$id`,getParentRoute:()=>$}),Vb={AuthS3LsBucketSplatRoute:yb,AuthS3LsBucketIndexRoute:Qy},Hb={AuthNodesRoute:Dy,AuthIndexRoute:Ey,AuthS3ServiceMetricsRoute:Oy,AuthS3LsBucketRouteRoute:Zy._addFileChildren(Vb),AuthEc2elasticIpsAllocateAddressRoute:Xy,AuthEc2instancesRunInstancesRoute:Yy,AuthEc2internetGatewaysCreateInternetGatewayRoute:Jy,AuthEc2keyCreateKeyPairRoute:qy,AuthEc2keyImportKeyPairRoute:Ky,AuthEc2loadBalancersCreateLoadBalancerRoute:Gy,AuthEc2natGatewaysCreateNatGatewayRoute:Wy,AuthEc2placementGroupsCreatePlacementGroupRoute:Uy,AuthEc2routeTablesCreateRouteTableRoute:Hy,AuthEc2securityGroupsCreateSecurityGroupRoute:Vy,AuthEc2snapshotsCreateSnapshotRoute:By,AuthEc2subnetCreateSubnetRoute:zy,AuthEc2targetGroupsCreateTargetGroupRoute:Ry,AuthEc2volumesCreateVolumeRoute:Ly,AuthEc2vpcCreateVpcRoute:Iy,AuthEksclustersCreateClusterRoute:Fy,AuthIaminstanceProfilesCreateInstanceProfileRoute:Py,AuthIampoliciesCreatePolicyRoute:Ny,AuthIamrolesCreateRoleRoute:My,AuthIamusersCreateUserRoute:jy,AuthS3bucketsCreateBucketRoute:Ay,AuthS3LsIndexRoute:ky,AuthEc2elasticIpsDescribeAddressesIdRoute:Bb,AuthEc2imagesDescribeImagesIdRoute:zb,AuthEc2instancesDescribeInstancesIdRoute:Rb,AuthEc2internetGatewaysDescribeInternetGatewaysIdRoute:Lb,AuthEc2keyDescribeKeyPairsIdRoute:Ib,AuthEc2loadBalancersDescribeLoadBalancersIdRoute:Fb,AuthEc2natGatewaysDescribeNatGatewaysIdRoute:Pb,AuthEc2placementGroupsDescribePlacementGroupsIdRoute:Nb,AuthEc2routeTablesDescribeRouteTablesIdRoute:Mb,AuthEc2securityGroupsDescribeSecurityGroupsIdRoute:jb,AuthEc2snapshotsDescribeSnapshotsIdRoute:Ab,AuthEc2subnetDescribeSubnetsIdRoute:kb,AuthEc2targetGroupsDescribeTargetGroupsIdRoute:Ob,AuthEc2volumesDescribeVolumesIdRoute:Db,AuthEc2volumesModifyVolumeIdRoute:Eb,AuthEc2vpcDescribeVpcsIdRoute:Tb,AuthEksclustersListClustersClusterNameRoute:wb,AuthIaminstanceProfilesListInstanceProfilesInstanceProfileNameRoute:Cb,AuthIampoliciesListPoliciesPolicyArnRoute:Sb,AuthIamrolesListRolesRoleNameRoute:xb,AuthIamusersListUsersUserNameRoute:bb,AuthEc2elasticIpsDescribeAddressesIndexRoute:vb,AuthEc2imagesDescribeImagesIndexRoute:_b,AuthEc2instancesDescribeInstancesIndexRoute:gb,AuthEc2internetGatewaysDescribeInternetGatewaysIndexRoute:hb,AuthEc2keyDescribeKeyPairsIndexRoute:mb,AuthEc2loadBalancersDescribeLoadBalancersIndexRoute:pb,AuthEc2natGatewaysDescribeNatGatewaysIndexRoute:fb,AuthEc2placementGroupsDescribePlacementGroupsIndexRoute:db,AuthEc2routeTablesDescribeRouteTablesIndexRoute:ub,AuthEc2securityGroupsDescribeSecurityGroupsIndexRoute:lb,AuthEc2snapshotsDescribeSnapshotsIndexRoute:cb,AuthEc2subnetDescribeSubnetsIndexRoute:sb,AuthEc2targetGroupsDescribeTargetGroupsIndexRoute:ob,AuthEc2volumesDescribeVolumesIndexRoute:ab,AuthEc2vpcDescribeVpcsIndexRoute:ib,AuthEksclustersListClustersIndexRoute:rb,AuthIaminstanceProfilesListInstanceProfilesIndexRoute:nb,AuthIampoliciesListPoliciesIndexRoute:tb,AuthIamrolesListRolesIndexRoute:eb,AuthIamusersListUsersIndexRoute:$y},Ub={AuthRouteRoute:$._addFileChildren(Hb),LoginRoute:Ty},Wb=Gg._addFileChildren(Ub)._addFileTypes(),Gb=Rg(),Kb=no({routeTree:Wb,context:{queryClient:Gb},defaultPreload:`intent`,scrollRestoration:!0,defaultStructuralSharing:!0,defaultPreloadStaleTime:0}),qb=document.querySelector(`#app`);qb&&!qb.innerHTML&&ho.createRoot(qb).render((0,W.jsx)(U.StrictMode,{children:(0,W.jsx)(yo,{defaultTheme:`dark`,storageKey:`spinifex-ui-theme`,children:(0,W.jsx)(Ir,{client:Gb,children:(0,W.jsx)(Nh,{children:(0,W.jsx)(uh,{children:(0,W.jsx)(io,{router:Kb})})})})})}));export{R_ as $,Mc as $n,Pn as $r,Fp as $t,jv as A,X as An,zo as Ar,s_ as At,gv as B,Tl as Bn,mr as Br,Ph as Bt,Fv as C,Lu as Cn,os as Cr,h_ as Ct,zv as D,ou as Dn,Zo as Dr,u_ as Dt,Rv as E,du as En,$o as Er,d_ as Et,fv as F,Hl as Fn,Za as Fr,S_ as Ft,iv as G,bl as Gn,zn as Gr,jm as Gt,uv as H,Cl as Hn,ur as Hr,Hm as Ht,cv as I,Vl as In,Fa as Ir,p_ as It,W_ as J,dl as Jn,Un as Jr,Jp as Jt,$_ as K,ml as Kn,rr as Kr,nm as Kt,sv as L,Al as Ln,Fr as Lr,f_ as Lt,Nv as M,Kl as Mn,Fo as Mr,a_ as Mt,wv as N,Gl as Nn,jo as Nr,i_ as Nt,Lv as O,su as On,Yo as Or,__ as Ot,pv as P,Ul as Pn,xo as Pr,C_ as Pt,G_ as Q,Fc as Qn,In as Qr,Ip as Qt,ov as R,kl as Rn,kr as Rr,Kg as Rt,ry as S,Iu as Sn,ds as Sr,b_ as St,Bv as T,Tu as Tn,ts as Tr,r_ as Tt,lv as U,Sl as Un,Jn as Ur,Vm as Ut,dv as V,wl as Vn,dr as Vr,Ah as Vt,mv as W,_l as Wn,Vn as Wr,zm as Wt,K_ as X,jc as Xn,cr as Xr,Rp as Xt,V_ as Y,Nc as Yn,Qn as Yr,zp as Yt,q_ as Z,Pc as Zn,Hn as Zr,Lp as Zt,cy as _,yf as _n,Cs as _r,l_ as _t,by as a,Dp as an,Ec as ar,F_ as at,ay as b,$u as bn,gs as br,t_ as bt,_y as c,Cp as cn,Xs as cr,y_ as ct,my as d,Tp as dn,Hs as dr,Zg as dt,Nn as ei,vp as en,Ac as er,z_ as et,py as f,Ap as fn,Bs as fr,e_ as ft,ly as g,Cf as gn,Fs as gr,Yg as gt,uy as h,Of as hn,Ps as hr,Qg as ht,xy as i,_p as in,Ic as ir,I_ as it,Pv as j,ql as jn,Ro as jr,o_ as jt,Mv as k,ru as kn,Uo as kr,g_ as kt,gy as l,Op as ln,qs as lr,v_ as lt,dy as m,qf as mn,Ns as mr,Xg as mt,Cy as n,yp as nn,Rc as nr,U_ as nt,yy as o,xp as on,Zs as or,T_ as ot,fy as p,jp as pn,Is as pr,$g as pt,L_ as q,hl as qn,Wn as qr,em as qt,Sy as r,bp as rn,Oc as rr,H_ as rt,vy as s,Sp as sn,Ys as sr,w_ as st,wy as t,Ep as tn,kc as tr,B_ as tt,hy as u,wp as un,Ws as ur,Jg as ut,sy as v,Q as vn,Ss as vr,c_ as vt,Iv as w,Mu as wn,as as wr,m_ as wt,iy as x,Ku as xn,ms as xr,x_ as xt,oy as y,zd as yn,_s as yr,n_ as yt,hv as z,El as zn,Tr as zr,Hh as zt}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/index2.browser.js b/spinifex/services/spinifexui/frontend/dist/assets/index2.browser.js new file mode 100644 index 000000000..704a2957b --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/index2.browser.js @@ -0,0 +1 @@ +import{a as e}from"./compiler-runtime.js";import{A as t,D as n,F as r,M as i,N as a,P as o,T as s,d as c,f as l,j as u,k as d}from"./index.browser.js";var f=function(){function e(){this.crc32=new p}return e.prototype.update=function(e){o(e)||this.crc32.update(r(e))},e.prototype.digest=function(){return d(this,void 0,void 0,function(){return t(this,function(e){return[2,a(this.crc32.digest())]})})},e.prototype.reset=function(){this.crc32=new p},e}(),p=function(){function e(){this.checksum=4294967295}return e.prototype.update=function(e){var t,n;try{for(var r=u(e),i=r.next();!i.done;i=r.next()){var a=i.value;this.checksum=this.checksum>>>8^m[(this.checksum^a)&255]}}catch(e){t={error:e}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return this},e.prototype.digest=function(){return(this.checksum^4294967295)>>>0},e}(),m=i([0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117]),h=class e{bytes;constructor(e){if(this.bytes=e,e.byteLength!==8)throw Error(`Int64 buffers must be exactly 8 bytes`)}static fromNumber(t){if(t>0x8000000000000000||t<-0x8000000000000000)throw Error(`${t} is too large (or, if negative, too small) to represent as an Int64`);let n=new Uint8Array(8);for(let e=7,r=Math.abs(Math.round(t));e>-1&&r>0;e--,r/=256)n[e]=r;return t<0&&g(n),new e(n)}valueOf(){let e=this.bytes.slice(0),t=e[0]&128;return t&&g(e),parseInt(l(e),16)*(t?-1:1)}toString(){return String(this.valueOf())}};function g(e){for(let t=0;t<8;t++)e[t]^=255;for(let t=7;t>-1&&(e[t]++,e[t]===0);t--);}var _=class{toUtf8;fromUtf8;constructor(e,t){this.toUtf8=e,this.fromUtf8=t}format(e){let t=[];for(let n of Object.keys(e)){let r=this.fromUtf8(n);t.push(Uint8Array.from([r.byteLength]),r,this.formatHeaderValue(e[n]))}let n=new Uint8Array(t.reduce((e,t)=>e+t.byteLength,0)),r=0;for(let e of t)n.set(e,r),r+=e.byteLength;return n}formatHeaderValue(e){switch(e.type){case`boolean`:return Uint8Array.from([+!e.value]);case`byte`:return Uint8Array.from([2,e.value]);case`short`:let t=new DataView(new ArrayBuffer(3));return t.setUint8(0,3),t.setInt16(1,e.value,!1),new Uint8Array(t.buffer);case`integer`:let n=new DataView(new ArrayBuffer(5));return n.setUint8(0,4),n.setInt32(1,e.value,!1),new Uint8Array(n.buffer);case`long`:let r=new Uint8Array(9);return r[0]=5,r.set(e.value.bytes,1),r;case`binary`:let i=new DataView(new ArrayBuffer(3+e.value.byteLength));i.setUint8(0,6),i.setUint16(1,e.value.byteLength,!1);let a=new Uint8Array(i.buffer);return a.set(e.value,3),a;case`string`:let o=this.fromUtf8(e.value),s=new DataView(new ArrayBuffer(3+o.byteLength));s.setUint8(0,7),s.setUint16(1,o.byteLength,!1);let l=new Uint8Array(s.buffer);return l.set(o,3),l;case`timestamp`:let u=new Uint8Array(9);return u[0]=8,u.set(h.fromNumber(e.value.valueOf()).bytes,1),u;case`uuid`:if(!O.test(e.value))throw Error(`Invalid UUID received: ${e.value}`);let d=new Uint8Array(17);return d[0]=9,d.set(c(e.value.replace(/\-/g,``)),1),d}}parse(e){let t={},n=0;for(;n{if(typeof e!=`number`)throw Error(`Attempted to allocate an event message where size was not a number: `+e);t=e,n=4,r=new Uint8Array(e),new DataView(r.buffer).setUint32(0,e,!1)},o=async function*(){let o=e[Symbol.asyncIterator]();for(;;){let{value:e,done:s}=await o.next();if(s){if(!t)return;if(t===n)yield r;else throw Error(`Truncated event message received.`);return}let c=e.length,l=0;for(;l({[Symbol.asyncIterator]:async function*(){let t=e.getReader();try{for(;;){let{done:e,value:n}=await t.read();if(e)return;yield n}}finally{t.releaseLock()}}}),U=e=>{let t=e[Symbol.asyncIterator]();return new ReadableStream({async pull(e){let{done:n,value:r}=await t.next();if(n)return e.close();e.enqueue(r)}})},W=class{universalMarshaller;constructor({utf8Encoder:e,utf8Decoder:t}){this.universalMarshaller=new V({utf8Decoder:t,utf8Encoder:e})}deserialize(e,t){let n=G(e)?H(e):e;return this.universalMarshaller.deserialize(n,t)}serialize(e,t){let n=this.universalMarshaller.serialize(e,t);return typeof ReadableStream==`function`?U(n):n}},G=e=>typeof ReadableStream==`function`&&e instanceof ReadableStream,K=e=>new W(e),q=e=>Object.assign(e,{eventStreamMarshaller:e.eventStreamSerdeProvider(e)}),J=class{marshaller;serializer;deserializer;serdeContext;defaultContentType;constructor({marshaller:e,serializer:t,deserializer:n,serdeContext:r,defaultContentType:i}){this.marshaller=e,this.serializer=t,this.deserializer=n,this.serdeContext=r,this.defaultContentType=i}async serializeEventStream({eventStream:e,requestSchema:t,initialRequest:n}){let r=this.marshaller,i=t.getEventStreamMember(),a=t.getMemberSchema(i),o=this.serializer,s=this.defaultContentType,c=Symbol(`initialRequestMarker`),l={async*[Symbol.asyncIterator](){if(n){let e={":event-type":{type:`string`,value:`initial-request`},":message-type":{type:`string`,value:`event`},":content-type":{type:`string`,value:s}};o.write(t,n);let r=o.flush();yield{[c]:!0,headers:e,body:r}}for await(let t of e)yield t}};return r.serialize(l,e=>{if(e[c])return{headers:e.headers,body:e.body};let t=``;for(let n in e)if(n!==`__type`){t=n;break}let{additionalHeaders:n,body:r,eventType:i,explicitPayloadContentType:o}=this.writeEventBody(t,a,e);return{headers:{":event-type":{type:`string`,value:i},":message-type":{type:`string`,value:`event`},":content-type":{type:`string`,value:o??s},...n},body:r}})}async deserializeEventStream({response:e,responseSchema:t,initialResponseContainer:n}){let r=this.marshaller,i=t.getEventStreamMember(),a=t.getMemberSchema(i).getMemberSchemas(),o=Symbol(`initialResponseMarker`),c=r.deserialize(e.body,async e=>{let n=``;for(let t in e)if(t!==`__type`){n=t;break}let r=e[n].body;if(n===`initial-response`){let e=await this.deserializer.read(t,r);return delete e[i],{[o]:!0,...e}}else if(n in a){let t=a[n];if(t.isStructSchema()){let i={},a=!1;for(let[o,c]of t.structIterator()){let{eventHeader:t,eventPayload:l}=c.getMergedTraits();if(a||=!!(t||l),l)c.isBlobSchema()?i[o]=r:c.isStringSchema()?i[o]=(this.serdeContext?.utf8Encoder??s)(r):c.isStructSchema()&&(i[o]=await this.deserializer.read(c,r));else if(t){let t=e[n].headers[o]?.value;t!=null&&(c.isNumericSchema()?t&&typeof t==`object`&&`bytes`in t?i[o]=BigInt(t.toString()):i[o]=Number(t):i[o]=t)}}if(a)return{[n]:i};if(r.byteLength===0)return{[n]:{}}}return{[n]:await this.deserializer.read(t,r)}}else return{$unknown:e}}),l=c[Symbol.asyncIterator](),u=await l.next();if(u.done)return c;if(u.value?.[o]){if(!t)throw Error(`@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.`);for(let e in u.value)n[e]=u.value[e]}return{async*[Symbol.asyncIterator](){for(u?.value?.[o]||(yield u.value);;){let{done:e,value:t}=await l.next();if(e)break;yield t}}}}writeEventBody(e,t,r){let i=this.serializer,a=e,o=null,s,c=t.getSchema()[4].includes(e),l={};if(c){let n=t.getMemberSchema(e);if(n.isStructSchema()){for(let[t,i]of n.structIterator()){let{eventHeader:n,eventPayload:a}=i.getMergedTraits();if(a)o=t;else if(n){let n=r[e][t],a=`binary`;i.isNumericSchema()?a=(-2)**31<=n&&n<=2**31-1?`integer`:`long`:i.isTimestampSchema()?a=`timestamp`:i.isStringSchema()?a=`string`:i.isBooleanSchema()&&(a=`boolean`),n!=null&&(l[t]={type:a,value:n},delete r[e][t])}}if(o!==null){let t=n.getMemberSchema(o);t.isBlobSchema()?s=`application/octet-stream`:t.isStringSchema()&&(s=`text/plain`),i.write(t,r[e][o])}else i.write(n,r[e])}else if(n.isUnitSchema())i.write(n,{});else throw Error(`@smithy/core/event-streams - non-struct member not supported in event stream union.`)}else{let[t,n]=r[e];a=t,i.write(15,n)}let u=i.flush()??new Uint8Array;return{body:typeof u==`string`?(this.serdeContext?.utf8Decoder??n)(u):u,eventType:a,explicitPayloadContentType:s,additionalHeaders:l}}},Y=e({EventStreamSerde:()=>J});export{f as i,q as n,K as r,Y as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/list-card.js b/spinifex/services/spinifexui/frontend/dist/assets/list-card.js index 6d472da8c..85039f0be 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/list-card.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/list-card.js @@ -1 +1 @@ -import{Fo as e,Ho as t,Lo as n}from"./index.js";var r=e(),i=t();function a(e){let t=(0,r.c)(22),a,o,s,c,l;t[0]===e?(a=t[1],o=t[2],s=t[3],c=t[4],l=t[5]):({title:l,subtitle:c,badge:a,children:o,...s}=e,t[0]=e,t[1]=a,t[2]=o,t[3]=s,t[4]=c,t[5]=l);let u;t[6]===l?u=t[7]:(u=(0,i.jsx)(`h3`,{className:`font-semibold`,children:l}),t[6]=l,t[7]=u);let d;t[8]===c?d=t[9]:(d=c&&(0,i.jsx)(`p`,{className:`mt-1 text-sm font-medium text-muted-foreground`,children:c}),t[8]=c,t[9]=d);let f;t[10]!==o||t[11]!==u||t[12]!==d?(f=(0,i.jsxs)(`div`,{className:`flex-1`,children:[u,d,o]}),t[10]=o,t[11]=u,t[12]=d,t[13]=f):f=t[13];let p;t[14]===a?p=t[15]:(p=a&&(0,i.jsx)(`div`,{className:`ml-4`,children:a}),t[14]=a,t[15]=p);let m;t[16]!==f||t[17]!==p?(m=(0,i.jsxs)(`div`,{className:`flex items-start justify-between`,children:[f,p]}),t[16]=f,t[17]=p,t[18]=m):m=t[18];let h;return t[19]!==s||t[20]!==m?(h=(0,i.jsx)(n,{className:`block rounded-lg border bg-card p-4 transition-colors hover:bg-accent`,...s,children:m}),t[19]=s,t[20]=m,t[21]=h):h=t[21],h}export{a as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{Or as n}from"./awsClient.js";var r=t(),i=e();function a(e){let t=(0,r.c)(22),a,o,s,c,l;t[0]===e?(a=t[1],o=t[2],s=t[3],c=t[4],l=t[5]):({title:l,subtitle:c,badge:a,children:o,...s}=e,t[0]=e,t[1]=a,t[2]=o,t[3]=s,t[4]=c,t[5]=l);let u;t[6]===l?u=t[7]:(u=(0,i.jsx)(`h3`,{className:`font-semibold`,children:l}),t[6]=l,t[7]=u);let d;t[8]===c?d=t[9]:(d=c&&(0,i.jsx)(`p`,{className:`mt-1 text-sm font-medium text-muted-foreground`,children:c}),t[8]=c,t[9]=d);let f;t[10]!==o||t[11]!==u||t[12]!==d?(f=(0,i.jsxs)(`div`,{className:`flex-1`,children:[u,d,o]}),t[10]=o,t[11]=u,t[12]=d,t[13]=f):f=t[13];let p;t[14]===a?p=t[15]:(p=a&&(0,i.jsx)(`div`,{className:`ml-4`,children:a}),t[14]=a,t[15]=p);let m;t[16]!==f||t[17]!==p?(m=(0,i.jsxs)(`div`,{className:`flex items-start justify-between`,children:[f,p]}),t[16]=f,t[17]=p,t[18]=m):m=t[18];let h;return t[19]!==s||t[20]!==m?(h=(0,i.jsx)(n,{className:`block rounded-lg border bg-card p-4 transition-colors hover:bg-accent`,...s,children:m}),t[19]=s,t[20]=m,t[21]=h):h=t[21],h}export{a as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/list-clusters.js b/spinifex/services/spinifexui/frontend/dist/assets/list-clusters.js new file mode 100644 index 000000000..49c23b5a0 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/list-clusters.js @@ -0,0 +1 @@ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{A as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=e();function u(){let e=(0,c.c)(3),{data:t}=n(a),s;if(e[0]!==t.clusters){let n=(t.clusters??[]).toSorted(f),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,l.jsx)(o,{actions:(0,l.jsx)(r,{to:`/eks/create-cluster`,children:(0,l.jsx)(i,{children:`Create Cluster`})}),title:`Clusters`}),e[2]=a):a=e[2],s=(0,l.jsxs)(l.Fragment,{children:[a,n.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:n.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No EKS clusters found.`})]}),e[0]=t.clusters,e[1]=s}else s=e[1];return s}function d(e){return(0,l.jsx)(s,{params:{clusterName:e},title:e,to:`/eks/list-clusters/$clusterName`},e)}function f(e,t){return e.toLowerCase().localeCompare(t.toLowerCase())}var p=u;export{p as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/list-instance-profiles.js b/spinifex/services/spinifexui/frontend/dist/assets/list-instance-profiles.js new file mode 100644 index 000000000..e1420a2f4 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/list-instance-profiles.js @@ -0,0 +1 @@ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{Q as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=e();function u(){let e=(0,c.c)(3),{data:t}=n(a),s;if(e[0]!==t.InstanceProfiles){let n=(t.InstanceProfiles??[]).toSorted(f),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,l.jsx)(o,{actions:(0,l.jsx)(r,{to:`/iam/create-instance-profile`,children:(0,l.jsx)(i,{children:`Create Instance Profile`})}),title:`Instance Profiles`}),e[2]=a):a=e[2],s=(0,l.jsxs)(l.Fragment,{children:[a,n.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:n.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No instance profiles found.`})]}),e[0]=t.InstanceProfiles,e[1]=s}else s=e[1];return s}function d(e){return e.InstanceProfileName?(0,l.jsx)(s,{params:{instanceProfileName:e.InstanceProfileName},subtitle:e.Arn??``,title:e.InstanceProfileName,to:`/iam/list-instance-profiles/$instanceProfileName`},e.InstanceProfileName):null}function f(e,t){let n=e.InstanceProfileName?.toLowerCase()??``,r=t.InstanceProfileName?.toLowerCase()??``;return n.localeCompare(r)}export{u as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/list-policies.js b/spinifex/services/spinifexui/frontend/dist/assets/list-policies.js index 2988a27c9..ced494a2e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/list-policies.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/list-policies.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{Fo as t,Ho as n,Lo as r,S as i,Xa as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=n();function u(){let t=(0,c.c)(3),{data:n}=e(i),s;if(t[0]!==n.Policies){let e=(n.Policies??[]).toSorted(f),i;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(i=(0,l.jsx)(o,{actions:(0,l.jsx)(r,{to:`/iam/create-policy`,children:(0,l.jsx)(a,{children:`Create Policy`})}),title:`Policies`}),t[2]=i):i=t[2],s=(0,l.jsxs)(l.Fragment,{children:[i,e.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:e.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No IAM policies found.`})]}),t[0]=n.Policies,t[1]=s}else s=t[1];return s}function d(e){return e.Arn?(0,l.jsx)(s,{params:{policyArn:encodeURIComponent(e.Arn)},subtitle:e.Arn??``,title:e.PolicyName??``,to:`/iam/list-policies/$policyArn`},e.Arn):null}function f(e,t){let n=e.PolicyName?.toLowerCase()??``,r=t.PolicyName?.toLowerCase()??``;return n.localeCompare(r)}export{u as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{$ as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=e();function u(){let e=(0,c.c)(3),{data:t}=n(a),s;if(e[0]!==t.Policies){let n=(t.Policies??[]).toSorted(f),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,l.jsx)(o,{actions:(0,l.jsx)(r,{to:`/iam/create-policy`,children:(0,l.jsx)(i,{children:`Create Policy`})}),title:`Policies`}),e[2]=a):a=e[2],s=(0,l.jsxs)(l.Fragment,{children:[a,n.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:n.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No IAM policies found.`})]}),e[0]=t.Policies,e[1]=s}else s=e[1];return s}function d(e){return e.Arn?(0,l.jsx)(s,{params:{policyArn:encodeURIComponent(e.Arn)},subtitle:e.Arn??``,title:e.PolicyName??``,to:`/iam/list-policies/$policyArn`},e.Arn):null}function f(e,t){let n=e.PolicyName?.toLowerCase()??``,r=t.PolicyName?.toLowerCase()??``;return n.localeCompare(r)}export{u as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/list-roles.js b/spinifex/services/spinifexui/frontend/dist/assets/list-roles.js new file mode 100644 index 000000000..7d2e3b148 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/list-roles.js @@ -0,0 +1 @@ +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{rt as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=e();function u(){let e=(0,c.c)(3),{data:t}=n(a),s;if(e[0]!==t.Roles){let n=(t.Roles??[]).toSorted(f),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,l.jsx)(o,{actions:(0,l.jsx)(r,{to:`/iam/create-role`,children:(0,l.jsx)(i,{children:`Create Role`})}),title:`Roles`}),e[2]=a):a=e[2],s=(0,l.jsxs)(l.Fragment,{children:[a,n.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:n.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No IAM roles found.`})]}),e[0]=t.Roles,e[1]=s}else s=e[1];return s}function d(e){return e.RoleName?(0,l.jsx)(s,{params:{roleName:e.RoleName},subtitle:e.Arn??``,title:e.RoleName,to:`/iam/list-roles/$roleName`},e.RoleName):null}function f(e,t){let n=e.RoleName?.toLowerCase()??``,r=t.RoleName?.toLowerCase()??``;return n.localeCompare(r)}export{u as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/list-users.js b/spinifex/services/spinifexui/frontend/dist/assets/list-users.js index dce405247..285c02311 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/list-users.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/list-users.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{E as t,Fo as n,Ho as r,Lo as i,Xa as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=n(),l=r();function u(){let n=(0,c.c)(3),{data:r}=e(t),s;if(n[0]!==r.Users){let e=(r.Users??[]).toSorted(f),t;n[2]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,l.jsx)(o,{actions:(0,l.jsx)(i,{to:`/iam/create-user`,children:(0,l.jsx)(a,{children:`Create User`})}),title:`Users`}),n[2]=t):t=n[2],s=(0,l.jsxs)(l.Fragment,{children:[t,e.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:e.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No IAM users found.`})]}),n[0]=r.Users,n[1]=s}else s=n[1];return s}function d(e){return e.UserName?(0,l.jsx)(s,{params:{userName:e.UserName},subtitle:e.UserId??``,title:e.UserName,to:`/iam/list-users/$userName`},e.UserName):null}function f(e,t){let n=e.UserName?.toLowerCase()??``,r=t.UserName?.toLowerCase()??``;return n.localeCompare(r)}export{u as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{t as i}from"./button.js";import{at as a}from"./index.js";import{t as o}from"./page-heading.js";import{t as s}from"./list-card.js";var c=t(),l=e();function u(){let e=(0,c.c)(3),{data:t}=n(a),s;if(e[0]!==t.Users){let n=(t.Users??[]).toSorted(f),a;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,l.jsx)(o,{actions:(0,l.jsx)(r,{to:`/iam/create-user`,children:(0,l.jsx)(i,{children:`Create User`})}),title:`Users`}),e[2]=a):a=e[2],s=(0,l.jsxs)(l.Fragment,{children:[a,n.length>0?(0,l.jsx)(`div`,{className:`space-y-4`,children:n.map(d)}):(0,l.jsx)(`p`,{className:`text-muted-foreground`,children:`No IAM users found.`})]}),e[0]=t.Users,e[1]=s}else s=e[1];return s}function d(e){return e.UserName?(0,l.jsx)(s,{params:{userName:e.UserName},subtitle:e.UserId??``,title:e.UserName,to:`/iam/list-users/$userName`},e.UserName):null}function f(e,t){let n=e.UserName?.toLowerCase()??``,r=t.UserName?.toLowerCase()??``;return n.localeCompare(r)}export{u as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/login.js b/spinifex/services/spinifexui/frontend/dist/assets/login.js index f1402e9ed..51fde4887 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/login.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/login.js @@ -1 +1 @@ -import{Fo as e,Fr as t,Ho as n,Mr as r,Nr as i,Uo as a,Xa as o,Xi as s,_t as c,cs as l,gt as u,ln as d,vt as f}from"./index.js";import{i as p,t as m}from"./zod.js";import{a as h,i as g,r as _,t as v}from"./field.js";import{n as y,r as b,t as x}from"./collapsible.js";import{a as S,o as C,r as w,t as T}from"./card.js";var E=e(),D=l(a()),O=n();function k(){let{reason:e}=u.useSearch(),[n,a]=(0,D.useState)(null),{register:l,handleSubmit:y,formState:{errors:b,isSubmitting:x}}=p({resolver:m(r)});async function E(e){a(null),c(),t(e);try{await f().send(new d({})),window.location.href=`/`}catch{i(),c(),a(`Invalid credentials. Please check your Access Key ID and Secret Access Key.`)}}return(0,O.jsx)(`div`,{className:`flex flex-1 items-center justify-center`,children:(0,O.jsxs)(`div`,{className:`flex w-full max-w-sm flex-col gap-4`,children:[(0,O.jsxs)(T,{children:[(0,O.jsx)(S,{children:(0,O.jsx)(C,{children:`AWS Credentials`})}),(0,O.jsxs)(w,{children:[e===`expired`&&!n&&(0,O.jsx)(`p`,{className:`mb-4 rounded-md bg-muted p-3 text-sm text-muted-foreground`,children:`Your session is no longer valid — please sign in again.`}),n&&(0,O.jsx)(`p`,{className:`mb-4 rounded-md bg-destructive/10 p-3 text-sm text-destructive`,children:n}),(0,O.jsx)(`form`,{onSubmit:y(E),children:(0,O.jsxs)(g,{children:[(0,O.jsxs)(v,{children:[(0,O.jsx)(h,{children:(0,O.jsx)(`label`,{htmlFor:`accessKeyId`,children:`Access Key ID`})}),(0,O.jsx)(s,{"aria-invalid":!!b.accessKeyId,autoComplete:`username`,id:`accessKeyId`,placeholder:`AKIAIOSFODNN7EXAMPLE`,...l(`accessKeyId`)}),(0,O.jsx)(_,{errors:[b.accessKeyId]})]}),(0,O.jsxs)(v,{children:[(0,O.jsx)(h,{children:(0,O.jsx)(`label`,{htmlFor:`secretAccessKey`,children:`Secret Access Key`})}),(0,O.jsx)(s,{"aria-invalid":!!b.secretAccessKey,autoComplete:`current-password`,id:`secretAccessKey`,placeholder:`wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,type:`password`,...l(`secretAccessKey`)}),(0,O.jsx)(_,{errors:[b.secretAccessKey]})]}),(0,O.jsx)(o,{className:`w-full`,disabled:x,type:`submit`,children:x?`Signing in...`:`Sign In`})]})})]})]}),(0,O.jsx)(A,{})]})})}function A(){let e=(0,E.c)(4),t,n;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`p`,{className:`text-sm font-medium`,children:`Install Local Certificate Authority`}),(0,O.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`To avoid browser certificate warnings, download and install the Spinifex CA as a trusted root on your OS.`})]}),n=(0,O.jsx)(`a`,{className:`inline-flex h-8 w-full items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground`,download:`spinifex-ca.pem`,href:`/api/ca.pem`,children:`Download CA Certificate`}),e[0]=t,e[1]=n):(t=e[0],n=e[1]);let r;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,O.jsx)(b,{className:`cursor-pointer text-xs text-muted-foreground underline-offset-4 hover:underline`,children:`Installation instructions`}),e[2]=r):r=e[2];let i;return e[3]===Symbol.for(`react.memo_cache_sentinel`)?(i=(0,O.jsx)(T,{className:`border-muted bg-muted/30`,children:(0,O.jsx)(w,{className:`pt-4 pb-4`,children:(0,O.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[t,n,(0,O.jsxs)(x,{children:[r,(0,O.jsx)(y,{children:(0,O.jsxs)(`div`,{className:`mt-2 flex flex-col gap-2 text-xs text-muted-foreground`,children:[(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`p`,{className:`font-medium text-foreground`,children:`macOS`}),(0,O.jsx)(`code`,{className:`mt-1 block rounded bg-muted p-2 text-[11px] break-all`,children:`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain spinifex-ca.pem`})]}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`p`,{className:`font-medium text-foreground`,children:`Windows`}),(0,O.jsx)(`p`,{children:`Double-click the file → Install Certificate → Local Machine → Trusted Root Certification Authorities`})]}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`p`,{className:`font-medium text-foreground`,children:`Linux`}),(0,O.jsx)(`code`,{className:`mt-1 block rounded bg-muted p-2 text-[11px] break-all`,children:`sudo cp spinifex-ca.pem /usr/local/share/ca-certificates/spinifex-ca.crt && sudo update-ca-certificates`})]})]})})]})]})})}),e[3]=i):i=e[3],i}export{k as component}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{r as i,t as a}from"./awsClient.js";import{c as o,d as s,l as c}from"./SignatureV4.js";import{t as l}from"./button.js";import{Rn as u,Rt as d,zt as f}from"./index.js";import{i as p,t as m}from"./zod.js";import{a as h,i as g,r as _,t as v}from"./field.js";import{n as y,r as b,t as x}from"./collapsible.js";import{a as S,o as C,r as w,t as T}from"./card.js";var E=r(),D=t(n()),O=e();function k(){let{reason:e}=d.useSearch(),[t,n]=(0,D.useState)(null),{register:r,handleSubmit:y,formState:{errors:b,isSubmitting:x}}=p({resolver:m(o)});async function E(e){n(null),a(),s(e);try{await i().send(new f({})),window.location.href=`/`}catch{c(),a(),n(`Invalid credentials. Please check your Access Key ID and Secret Access Key.`)}}return(0,O.jsx)(`div`,{className:`flex flex-1 items-center justify-center`,children:(0,O.jsxs)(`div`,{className:`flex w-full max-w-sm flex-col gap-4`,children:[(0,O.jsxs)(T,{children:[(0,O.jsx)(S,{children:(0,O.jsx)(C,{children:`AWS Credentials`})}),(0,O.jsxs)(w,{children:[e===`expired`&&!t&&(0,O.jsx)(`p`,{className:`mb-4 rounded-md bg-muted p-3 text-sm text-muted-foreground`,children:`Your session is no longer valid — please sign in again.`}),t&&(0,O.jsx)(`p`,{className:`mb-4 rounded-md bg-destructive/10 p-3 text-sm text-destructive`,children:t}),(0,O.jsx)(`form`,{onSubmit:y(E),children:(0,O.jsxs)(g,{children:[(0,O.jsxs)(v,{children:[(0,O.jsx)(h,{children:(0,O.jsx)(`label`,{htmlFor:`accessKeyId`,children:`Access Key ID`})}),(0,O.jsx)(u,{"aria-invalid":!!b.accessKeyId,autoComplete:`username`,id:`accessKeyId`,placeholder:`AKIAIOSFODNN7EXAMPLE`,...r(`accessKeyId`)}),(0,O.jsx)(_,{errors:[b.accessKeyId]})]}),(0,O.jsxs)(v,{children:[(0,O.jsx)(h,{children:(0,O.jsx)(`label`,{htmlFor:`secretAccessKey`,children:`Secret Access Key`})}),(0,O.jsx)(u,{"aria-invalid":!!b.secretAccessKey,autoComplete:`current-password`,id:`secretAccessKey`,placeholder:`wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,type:`password`,...r(`secretAccessKey`)}),(0,O.jsx)(_,{errors:[b.secretAccessKey]})]}),(0,O.jsx)(l,{className:`w-full`,disabled:x,type:`submit`,children:x?`Signing in...`:`Sign In`})]})})]})]}),(0,O.jsx)(A,{})]})})}function A(){let e=(0,E.c)(4),t,n;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`p`,{className:`text-sm font-medium`,children:`Install Local Certificate Authority`}),(0,O.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`To avoid browser certificate warnings, download and install the Spinifex CA as a trusted root on your OS.`})]}),n=(0,O.jsx)(`a`,{className:`inline-flex h-8 w-full items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground`,download:`spinifex-ca.pem`,href:`/api/ca.pem`,children:`Download CA Certificate`}),e[0]=t,e[1]=n):(t=e[0],n=e[1]);let r;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,O.jsx)(b,{className:`cursor-pointer text-xs text-muted-foreground underline-offset-4 hover:underline`,children:`Installation instructions`}),e[2]=r):r=e[2];let i;return e[3]===Symbol.for(`react.memo_cache_sentinel`)?(i=(0,O.jsx)(T,{className:`border-muted bg-muted/30`,children:(0,O.jsx)(w,{className:`pt-4 pb-4`,children:(0,O.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[t,n,(0,O.jsxs)(x,{children:[r,(0,O.jsx)(y,{children:(0,O.jsxs)(`div`,{className:`mt-2 flex flex-col gap-2 text-xs text-muted-foreground`,children:[(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`p`,{className:`font-medium text-foreground`,children:`macOS`}),(0,O.jsx)(`code`,{className:`mt-1 block rounded bg-muted p-2 text-[11px] break-all`,children:`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain spinifex-ca.pem`})]}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`p`,{className:`font-medium text-foreground`,children:`Windows`}),(0,O.jsx)(`p`,{children:`Double-click the file → Install Certificate → Local Machine → Trusted Root Certification Authorities`})]}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`p`,{className:`font-medium text-foreground`,children:`Linux`}),(0,O.jsx)(`code`,{className:`mt-1 block rounded bg-muted p-2 text-[11px] break-all`,children:`sudo cp spinifex-ca.pem /usr/local/share/ca-certificates/spinifex-ca.crt && sudo update-ca-certificates`})]})]})})]})]})})}),e[3]=i):i=e[3],i}export{k as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/ls.js b/spinifex/services/spinifexui/frontend/dist/assets/ls.js index 43417a575..52abc4e23 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/ls.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/ls.js @@ -1 +1 @@ -import{t as e}from"./useSuspenseQuery.js";import{B as t,Fo as n,Ho as r,Lo as i,Xa as a,no as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";var l=n(),u=r();function d(){let n=(0,l.c)(5),{data:r}=e(t),o;n[0]===r.Buckets?o=n[1]:(o=r.Buckets??[],n[0]=r.Buckets,n[1]=o);let c=o,d;n[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(s,{actions:(0,u.jsx)(i,{to:`/s3/create-bucket`,children:(0,u.jsx)(a,{children:`Create Bucket`})}),title:`Buckets`}),n[2]=d):d=n[2];let p;return n[3]===c?p=n[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No buckets found.`})]}),n[3]=c,n[4]=p),p}function f(e){return e.Name?(0,u.jsx)(c,{params:{bucket:e.Name},subtitle:e.CreationDate?`Created: ${o(e.CreationDate)}`:void 0,title:e.Name,to:`/s3/ls/$bucket`},e.Name):null}export{d as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useSuspenseQuery.js";import{Or as r}from"./awsClient.js";import{D as i}from"./SignatureV4.js";import{t as a}from"./button.js";import{st as o}from"./index.js";import{t as s}from"./page-heading.js";import{t as c}from"./list-card.js";var l=t(),u=e();function d(){let e=(0,l.c)(5),{data:t}=n(o),i;e[0]===t.Buckets?i=e[1]:(i=t.Buckets??[],e[0]=t.Buckets,e[1]=i);let c=i,d;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,u.jsx)(s,{actions:(0,u.jsx)(r,{to:`/s3/create-bucket`,children:(0,u.jsx)(a,{children:`Create Bucket`})}),title:`Buckets`}),e[2]=d):d=e[2];let p;return e[3]===c?p=e[4]:(p=(0,u.jsxs)(u.Fragment,{children:[d,c.length>0?(0,u.jsx)(`div`,{className:`space-y-4`,children:c.map(f)}):(0,u.jsx)(`p`,{className:`text-muted-foreground`,children:`No buckets found.`})]}),e[3]=c,e[4]=p),p}function f(e){return e.Name?(0,u.jsx)(c,{params:{bucket:e.Name},subtitle:e.CreationDate?`Created: ${i(e.CreationDate)}`:void 0,title:e.Name,to:`/s3/ls/$bucket`},e.Name):null}export{d as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/matchContext.js b/spinifex/services/spinifexui/frontend/dist/assets/matchContext.js new file mode 100644 index 000000000..f7bb2fd7e --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/matchContext.js @@ -0,0 +1 @@ +import{o as e,r as t}from"./compiler-runtime.js";function n(e){return e?.isNotFound===!0}var r=`__root__`,i=e(t(),1),a=i.createContext(void 0),o=i.createContext(void 0);export{n as i,a as n,r,o as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/nodes.js b/spinifex/services/spinifexui/frontend/dist/assets/nodes.js index 622648dd7..53a30d72c 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/nodes.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/nodes.js @@ -1 +1 @@ -import{t as e}from"./useQuery.js";import{Fo as t,Ho as n,gr as r,io as i,zo as a}from"./index.js";import{t as o}from"./page-heading.js";import{r as s,t as c}from"./admin.js";import{t as l}from"./badge.js";import{a as u,o as d,r as f,t as p}from"./card.js";var m=t(),h=n();function g(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600);if(t>0)return`${t}d ${n}h`;let r=Math.floor(e%3600/60);return n>0?`${n}h ${r}m`:`${r}m`}function _(e){return e>=1?`${e.toFixed(1)}Gi`:`${Math.round(e*1024)}Mi`}function v(e){let t=Math.floor(Date.now()/1e3-e);if(t<60)return`${t}s`;let n=Math.floor(t/60);if(n<60)return`${n}m`;let r=Math.floor(n/60);return r<24?`${r}h ${n%60}m`:`${Math.floor(r/24)}d ${r%24}h`}function y(){let t=(0,m.c)(31),{isAdmin:n}=r(),i;t[0]===n?i=t[1]:(i={...c,enabled:n,refetchInterval:5e3},t[0]=n,t[1]=i);let{data:l}=e(i),g;t[2]===n?g=t[3]:(g={...s,enabled:n,refetchInterval:5e3},t[2]=n,t[3]=g);let{data:_}=e(g);if(!n)throw a({to:`/`});let v,y,T;if(t[4]!==l?.nodes||t[5]!==_?.vms){v=l?.nodes??[];let e;t[9]===_?.vms?e=t[10]:(e=_?.vms??[],t[9]=_?.vms,t[10]=e);let n;t[11]===e?n=t[12]:(n=e.filter(C),t[11]=e,t[12]=n),T=n;let r=new Map;for(let e of v)for(let t of e.instance_types??[]){let e=r.get(t.name);e?e.available+=t.available:r.set(t.name,{vcpu:t.vcpu,memory_gb:t.memory_gb,available:t.available})}y=[...r.entries()].map(S).toSorted(x),t[4]=l?.nodes,t[5]=_?.vms,t[6]=v,t[7]=y,t[8]=T}else v=t[6],y=t[7],T=t[8];let E=y,O;t[13]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,h.jsx)(o,{title:`Nodes`}),t[13]=O):O=t[13];let A;t[14]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,h.jsx)(u,{children:(0,h.jsx)(d,{className:`text-sm`,children:`Cluster Nodes`})}),t[14]=A):A=t[14];let M;t[15]===v?M=t[16]:(M=(0,h.jsxs)(p,{children:[A,(0,h.jsx)(f,{children:v.length===0?(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No nodes responding`}):(0,h.jsx)(w,{nodes:v})})]}),t[15]=v,t[16]=M);let N;t[17]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,h.jsx)(u,{children:(0,h.jsx)(d,{className:`text-sm`,children:`Elastic Compute Cloud (EC2)`})}),t[17]=N):N=t[17];let P;t[18]===T?P=t[19]:(P=(0,h.jsxs)(p,{children:[N,(0,h.jsx)(f,{children:T.length===0?(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No instances running`}):(0,h.jsx)(D,{vms:T})})]}),t[18]=T,t[19]=P);let I;t[20]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,h.jsx)(u,{children:(0,h.jsx)(d,{className:`text-sm`,children:`Resource Usage`})}),t[20]=I):I=t[20];let L;t[21]!==E||t[22]!==v?(L=(0,h.jsxs)(p,{children:[I,(0,h.jsx)(f,{className:`space-y-4`,children:v.length===0?(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No data`}):(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(k,{nodes:v}),E.length>0&&(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`h4`,{className:`mt-4 text-xs font-medium text-muted-foreground`,children:`Instance Type Capacity`}),(0,h.jsx)(j,{instanceTypes:E})]})]})})]}),t[21]=E,t[22]=v,t[23]=L):L=t[23];let R;t[24]===v?R=t[25]:(R=v.some(b)&&(0,h.jsxs)(p,{children:[(0,h.jsx)(u,{children:(0,h.jsx)(d,{className:`text-sm`,children:`GPU Inventory`})}),(0,h.jsx)(f,{children:(0,h.jsx)(F,{nodes:v})})]}),t[24]=v,t[25]=R);let z;return t[26]!==R||t[27]!==M||t[28]!==P||t[29]!==L?(z=(0,h.jsxs)(h.Fragment,{children:[O,(0,h.jsxs)(`div`,{className:`space-y-6`,children:[M,P,L,R]})]}),t[26]=R,t[27]=M,t[28]=P,t[29]=L,t[30]=z):z=t[30],z}function b(e){return(e.gpus?.length??0)>0}function x(e,t){return e.name.localeCompare(t.name)}function S(e){let[t,n]=e;return{name:t,...n}}function C(e){return!e.managed_by}function w(e){let t=(0,m.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Name`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Status`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Roles`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`IP`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Region`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`AZ`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Uptime`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`EC2`}),(0,h.jsx)(`th`,{className:`pb-1 font-medium`,children:`Services`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(T),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,h.jsx)(`div`,{className:`overflow-x-auto`,children:(0,h.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,h.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function T(e){let t=[e.nats_role?`nats:${e.nats_role}`:null,e.predastore_role?`predastore:${e.predastore_role}`:null].filter(Boolean).join(`,`);return(0,h.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.node}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,h.jsx)(l,{variant:e.status===`Ready`?`default`:`secondary`,className:`text-[0.625rem]`,children:e.status})}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono text-muted-foreground`,children:t||`-`}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.host}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.region}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.az}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:g(e.uptime)}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.vm_count}),(0,h.jsx)(`td`,{className:`py-1.5`,children:(0,h.jsx)(`span`,{className:`text-muted-foreground`,children:e.services?.join(`,`)??`-`})})]},e.node)}function E(e){return e?e.profile?`${i(e.vram_mib)} (${e.profile})`:`${e.model} ${i(e.vram_mib)}`:`-`}function D(e){let t=(0,m.c)(5),{vms:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Instance`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Status`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Type`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`vCPU`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Memory`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`GPU`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Node`}),(0,h.jsx)(`th`,{className:`pb-1 font-medium`,children:`Age`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(O),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,h.jsx)(`div`,{className:`overflow-x-auto`,children:(0,h.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,h.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function O(e){return(0,h.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.instance_id}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,h.jsx)(l,{variant:e.status===`running`?`default`:`secondary`,className:`text-[0.625rem]`,children:e.status})}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.instance_type}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.vcpu}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:_(e.memory_gb)}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:E(e.gpu)}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.node}),(0,h.jsx)(`td`,{className:`py-1.5`,children:e.launch_time>0?v(e.launch_time):`-`})]},e.instance_id)}function k(e){let t=(0,m.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Name`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`CPU (used/total)`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`MEM (used/total)`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`GPU (used/total)`}),(0,h.jsx)(`th`,{className:`pb-1 font-medium`,children:`EC2`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(A),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,h.jsx)(`div`,{className:`overflow-x-auto`,children:(0,h.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,h.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function A(e){return(0,h.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.node}),(0,h.jsxs)(`td`,{className:`py-1.5 pr-4 font-mono`,children:[e.alloc_vcpu,`/`,e.total_vcpu]}),(0,h.jsxs)(`td`,{className:`py-1.5 pr-4 font-mono`,children:[_(e.alloc_mem_gb),`/`,_(e.total_mem_gb)]}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.total_gpus>0?`${e.alloc_gpus}/${e.total_gpus}`:`-`}),(0,h.jsx)(`td`,{className:`py-1.5`,children:e.vm_count})]},e.node)}function j(e){let t=(0,m.c)(5),{instanceTypes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Instance Type`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Available`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`vCPU`}),(0,h.jsx)(`th`,{className:`pb-1 font-medium`,children:`Memory`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(M),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,h.jsx)(`div`,{className:`overflow-x-auto`,children:(0,h.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,h.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function M(e){return(0,h.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.name}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.available}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.vcpu}),(0,h.jsx)(`td`,{className:`py-1.5`,children:_(e.memory_gb)})]},e.name)}function N(e){let t=(0,m.c)(22),{gpu:n}=e,r;t[0]===n.model?r=t[1]:(r=(0,h.jsx)(`span`,{className:`font-medium`,children:n.model}),t[0]=n.model,t[1]=r);let a;t[2]===n.vram_mib?a=t[3]:(a=i(n.vram_mib),t[2]=n.vram_mib,t[3]=a);let o;t[4]===a?o=t[5]:(o=(0,h.jsx)(`span`,{className:`text-muted-foreground`,children:a}),t[4]=a,t[5]=o);let s;t[6]===n.pci_address?s=t[7]:(s=(0,h.jsx)(`span`,{className:`text-muted-foreground`,children:n.pci_address}),t[6]=n.pci_address,t[7]=s);let c;t[8]!==n.instance_id||t[9]!==n.mig_enabled?(c=n.mig_enabled?(0,h.jsx)(l,{className:`text-[0.625rem]`,variant:`secondary`,children:`MIG`}):(0,h.jsx)(`span`,{className:n.instance_id?`text-amber-500`:`text-green-600`,children:n.instance_id?`in-use: ${n.instance_id}`:`free`}),t[8]=n.instance_id,t[9]=n.mig_enabled,t[10]=c):c=t[10];let u;t[11]!==r||t[12]!==o||t[13]!==s||t[14]!==c?(u=(0,h.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3 font-mono`,children:[r,o,s,c]}),t[11]=r,t[12]=o,t[13]=s,t[14]=c,t[15]=u):u=t[15];let d;t[16]!==n.mig_enabled||t[17]!==n.slices?(d=n.mig_enabled&&n.slices&&n.slices.length>0&&(0,h.jsxs)(`table`,{className:`mt-2 w-full`,children:[(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-0.5 font-normal`,children:`GI`}),(0,h.jsx)(`th`,{className:`pr-4 pb-0.5 font-normal`,children:`Profile`}),(0,h.jsx)(`th`,{className:`pr-4 pb-0.5 font-normal`,children:`VRAM`}),(0,h.jsx)(`th`,{className:`pb-0.5 font-normal`,children:`Instance`})]})}),(0,h.jsx)(`tbody`,{children:n.slices.map(P)})]}),t[16]=n.mig_enabled,t[17]=n.slices,t[18]=d):d=t[18];let f;return t[19]!==u||t[20]!==d?(f=(0,h.jsxs)(`div`,{className:`rounded border p-2 text-xs`,children:[u,d]}),t[19]=u,t[20]=d,t[21]=f):f=t[21],f}function P(e){return(0,h.jsxs)(`tr`,{children:[(0,h.jsx)(`td`,{className:`py-0.5 pr-4 font-mono`,children:e.gi_id}),(0,h.jsx)(`td`,{className:`py-0.5 pr-4 font-mono`,children:e.profile}),(0,h.jsx)(`td`,{className:`py-0.5 pr-4`,children:i(e.vram_mib)}),(0,h.jsx)(`td`,{className:e.instance_id?`py-0.5 font-mono text-amber-500`:`py-0.5 text-green-600`,children:e.instance_id??`free`})]},e.gi_id)}function F(e){let t=(0,m.c)(6),{nodes:n}=e,r,i;if(t[0]!==n){let e=n.filter(R);r=`space-y-4`,i=e.map(I),t[0]=n,t[1]=r,t[2]=i}else r=t[1],i=t[2];let a;return t[3]!==r||t[4]!==i?(a=(0,h.jsx)(`div`,{className:r,children:i}),t[3]=r,t[4]=i,t[5]=a):a=t[5],a}function I(e){return(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h4`,{className:`mb-1.5 text-xs font-medium text-muted-foreground`,children:e.node}),(0,h.jsx)(`div`,{className:`space-y-2`,children:e.gpus?.map(L)})]},e.node)}function L(e){return(0,h.jsx)(N,{gpu:e},e.pci_address)}function R(e){return(e.gpus?.length??0)>0}export{y as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useQuery.js";import{n as r}from"./redirect.js";import{k as i}from"./SignatureV4.js";import{Bt as a}from"./index.js";import{t as o}from"./page-heading.js";import{r as s,t as c}from"./admin.js";import{t as l}from"./badge.js";import{a as u,o as d,r as f,t as p}from"./card.js";var m=t(),h=e();function g(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600);if(t>0)return`${t}d ${n}h`;let r=Math.floor(e%3600/60);return n>0?`${n}h ${r}m`:`${r}m`}function _(e){return e>=1?`${e.toFixed(1)}Gi`:`${Math.round(e*1024)}Mi`}function v(e){let t=Math.floor(Date.now()/1e3-e);if(t<60)return`${t}s`;let n=Math.floor(t/60);if(n<60)return`${n}m`;let r=Math.floor(n/60);return r<24?`${r}h ${n%60}m`:`${Math.floor(r/24)}d ${r%24}h`}function y(){let e=(0,m.c)(31),{isAdmin:t}=a(),i;e[0]===t?i=e[1]:(i={...c,enabled:t,refetchInterval:5e3},e[0]=t,e[1]=i);let{data:l}=n(i),g;e[2]===t?g=e[3]:(g={...s,enabled:t,refetchInterval:5e3},e[2]=t,e[3]=g);let{data:_}=n(g);if(!t)throw r({to:`/`});let v,y,T;if(e[4]!==l?.nodes||e[5]!==_?.vms){v=l?.nodes??[];let t;e[9]===_?.vms?t=e[10]:(t=_?.vms??[],e[9]=_?.vms,e[10]=t);let n;e[11]===t?n=e[12]:(n=t.filter(C),e[11]=t,e[12]=n),T=n;let r=new Map;for(let e of v)for(let t of e.instance_types??[]){let e=r.get(t.name);e?e.available+=t.available:r.set(t.name,{vcpu:t.vcpu,memory_gb:t.memory_gb,available:t.available})}y=[...r.entries()].map(S).toSorted(x),e[4]=l?.nodes,e[5]=_?.vms,e[6]=v,e[7]=y,e[8]=T}else v=e[6],y=e[7],T=e[8];let E=y,O;e[13]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,h.jsx)(o,{title:`Nodes`}),e[13]=O):O=e[13];let A;e[14]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,h.jsx)(u,{children:(0,h.jsx)(d,{className:`text-sm`,children:`Cluster Nodes`})}),e[14]=A):A=e[14];let M;e[15]===v?M=e[16]:(M=(0,h.jsxs)(p,{children:[A,(0,h.jsx)(f,{children:v.length===0?(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No nodes responding`}):(0,h.jsx)(w,{nodes:v})})]}),e[15]=v,e[16]=M);let N;e[17]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,h.jsx)(u,{children:(0,h.jsx)(d,{className:`text-sm`,children:`Elastic Compute Cloud (EC2)`})}),e[17]=N):N=e[17];let P;e[18]===T?P=e[19]:(P=(0,h.jsxs)(p,{children:[N,(0,h.jsx)(f,{children:T.length===0?(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No instances running`}):(0,h.jsx)(D,{vms:T})})]}),e[18]=T,e[19]=P);let I;e[20]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,h.jsx)(u,{children:(0,h.jsx)(d,{className:`text-sm`,children:`Resource Usage`})}),e[20]=I):I=e[20];let L;e[21]!==E||e[22]!==v?(L=(0,h.jsxs)(p,{children:[I,(0,h.jsx)(f,{className:`space-y-4`,children:v.length===0?(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No data`}):(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(k,{nodes:v}),E.length>0&&(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`h4`,{className:`mt-4 text-xs font-medium text-muted-foreground`,children:`Instance Type Capacity`}),(0,h.jsx)(j,{instanceTypes:E})]})]})})]}),e[21]=E,e[22]=v,e[23]=L):L=e[23];let R;e[24]===v?R=e[25]:(R=v.some(b)&&(0,h.jsxs)(p,{children:[(0,h.jsx)(u,{children:(0,h.jsx)(d,{className:`text-sm`,children:`GPU Inventory`})}),(0,h.jsx)(f,{children:(0,h.jsx)(F,{nodes:v})})]}),e[24]=v,e[25]=R);let z;return e[26]!==R||e[27]!==M||e[28]!==P||e[29]!==L?(z=(0,h.jsxs)(h.Fragment,{children:[O,(0,h.jsxs)(`div`,{className:`space-y-6`,children:[M,P,L,R]})]}),e[26]=R,e[27]=M,e[28]=P,e[29]=L,e[30]=z):z=e[30],z}function b(e){return(e.gpus?.length??0)>0}function x(e,t){return e.name.localeCompare(t.name)}function S(e){let[t,n]=e;return{name:t,...n}}function C(e){return!e.managed_by}function w(e){let t=(0,m.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Name`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Status`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Roles`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`IP`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Region`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`AZ`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Uptime`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`EC2`}),(0,h.jsx)(`th`,{className:`pb-1 font-medium`,children:`Services`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(T),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,h.jsx)(`div`,{className:`overflow-x-auto`,children:(0,h.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,h.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function T(e){let t=[e.nats_role?`nats:${e.nats_role}`:null,e.predastore_role?`predastore:${e.predastore_role}`:null].filter(Boolean).join(`,`);return(0,h.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.node}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,h.jsx)(l,{variant:e.status===`Ready`?`default`:`secondary`,className:`text-[0.625rem]`,children:e.status})}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono text-muted-foreground`,children:t||`-`}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.host}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.region}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.az}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:g(e.uptime)}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.vm_count}),(0,h.jsx)(`td`,{className:`py-1.5`,children:(0,h.jsx)(`span`,{className:`text-muted-foreground`,children:e.services?.join(`,`)??`-`})})]},e.node)}function E(e){return e?e.profile?`${i(e.vram_mib)} (${e.profile})`:`${e.model} ${i(e.vram_mib)}`:`-`}function D(e){let t=(0,m.c)(5),{vms:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Instance`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Status`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Type`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`vCPU`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Memory`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`GPU`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Node`}),(0,h.jsx)(`th`,{className:`pb-1 font-medium`,children:`Age`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(O),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,h.jsx)(`div`,{className:`overflow-x-auto`,children:(0,h.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,h.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function O(e){return(0,h.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.instance_id}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,h.jsx)(l,{variant:e.status===`running`?`default`:`secondary`,className:`text-[0.625rem]`,children:e.status})}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.instance_type}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.vcpu}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:_(e.memory_gb)}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:E(e.gpu)}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.node}),(0,h.jsx)(`td`,{className:`py-1.5`,children:e.launch_time>0?v(e.launch_time):`-`})]},e.instance_id)}function k(e){let t=(0,m.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Name`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`CPU (used/total)`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`MEM (used/total)`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`GPU (used/total)`}),(0,h.jsx)(`th`,{className:`pb-1 font-medium`,children:`EC2`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(A),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,h.jsx)(`div`,{className:`overflow-x-auto`,children:(0,h.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,h.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function A(e){return(0,h.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.node}),(0,h.jsxs)(`td`,{className:`py-1.5 pr-4 font-mono`,children:[e.alloc_vcpu,`/`,e.total_vcpu]}),(0,h.jsxs)(`td`,{className:`py-1.5 pr-4 font-mono`,children:[_(e.alloc_mem_gb),`/`,_(e.total_mem_gb)]}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.total_gpus>0?`${e.alloc_gpus}/${e.total_gpus}`:`-`}),(0,h.jsx)(`td`,{className:`py-1.5`,children:e.vm_count})]},e.node)}function j(e){let t=(0,m.c)(5),{instanceTypes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Instance Type`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Available`}),(0,h.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`vCPU`}),(0,h.jsx)(`th`,{className:`pb-1 font-medium`,children:`Memory`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(M),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,h.jsx)(`div`,{className:`overflow-x-auto`,children:(0,h.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,h.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function M(e){return(0,h.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,h.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.name}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.available}),(0,h.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.vcpu}),(0,h.jsx)(`td`,{className:`py-1.5`,children:_(e.memory_gb)})]},e.name)}function N(e){let t=(0,m.c)(22),{gpu:n}=e,r;t[0]===n.model?r=t[1]:(r=(0,h.jsx)(`span`,{className:`font-medium`,children:n.model}),t[0]=n.model,t[1]=r);let a;t[2]===n.vram_mib?a=t[3]:(a=i(n.vram_mib),t[2]=n.vram_mib,t[3]=a);let o;t[4]===a?o=t[5]:(o=(0,h.jsx)(`span`,{className:`text-muted-foreground`,children:a}),t[4]=a,t[5]=o);let s;t[6]===n.pci_address?s=t[7]:(s=(0,h.jsx)(`span`,{className:`text-muted-foreground`,children:n.pci_address}),t[6]=n.pci_address,t[7]=s);let c;t[8]!==n.instance_id||t[9]!==n.mig_enabled?(c=n.mig_enabled?(0,h.jsx)(l,{className:`text-[0.625rem]`,variant:`secondary`,children:`MIG`}):(0,h.jsx)(`span`,{className:n.instance_id?`text-amber-500`:`text-green-600`,children:n.instance_id?`in-use: ${n.instance_id}`:`free`}),t[8]=n.instance_id,t[9]=n.mig_enabled,t[10]=c):c=t[10];let u;t[11]!==r||t[12]!==o||t[13]!==s||t[14]!==c?(u=(0,h.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3 font-mono`,children:[r,o,s,c]}),t[11]=r,t[12]=o,t[13]=s,t[14]=c,t[15]=u):u=t[15];let d;t[16]!==n.mig_enabled||t[17]!==n.slices?(d=n.mig_enabled&&n.slices&&n.slices.length>0&&(0,h.jsxs)(`table`,{className:`mt-2 w-full`,children:[(0,h.jsx)(`thead`,{children:(0,h.jsxs)(`tr`,{className:`text-left text-muted-foreground`,children:[(0,h.jsx)(`th`,{className:`pr-4 pb-0.5 font-normal`,children:`GI`}),(0,h.jsx)(`th`,{className:`pr-4 pb-0.5 font-normal`,children:`Profile`}),(0,h.jsx)(`th`,{className:`pr-4 pb-0.5 font-normal`,children:`VRAM`}),(0,h.jsx)(`th`,{className:`pb-0.5 font-normal`,children:`Instance`})]})}),(0,h.jsx)(`tbody`,{children:n.slices.map(P)})]}),t[16]=n.mig_enabled,t[17]=n.slices,t[18]=d):d=t[18];let f;return t[19]!==u||t[20]!==d?(f=(0,h.jsxs)(`div`,{className:`rounded border p-2 text-xs`,children:[u,d]}),t[19]=u,t[20]=d,t[21]=f):f=t[21],f}function P(e){return(0,h.jsxs)(`tr`,{children:[(0,h.jsx)(`td`,{className:`py-0.5 pr-4 font-mono`,children:e.gi_id}),(0,h.jsx)(`td`,{className:`py-0.5 pr-4 font-mono`,children:e.profile}),(0,h.jsx)(`td`,{className:`py-0.5 pr-4`,children:i(e.vram_mib)}),(0,h.jsx)(`td`,{className:e.instance_id?`py-0.5 font-mono text-amber-500`:`py-0.5 text-green-600`,children:e.instance_id??`free`})]},e.gi_id)}function F(e){let t=(0,m.c)(6),{nodes:n}=e,r,i;if(t[0]!==n){let e=n.filter(R);r=`space-y-4`,i=e.map(I),t[0]=n,t[1]=r,t[2]=i}else r=t[1],i=t[2];let a;return t[3]!==r||t[4]!==i?(a=(0,h.jsx)(`div`,{className:r,children:i}),t[3]=r,t[4]=i,t[5]=a):a=t[5],a}function I(e){return(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h4`,{className:`mb-1.5 text-xs font-medium text-muted-foreground`,children:e.node}),(0,h.jsx)(`div`,{className:`space-y-2`,children:e.gpus?.map(L)})]},e.node)}function L(e){return(0,h.jsx)(N,{gpu:e},e.pci_address)}function R(e){return(e.gpus?.length??0)>0}export{y as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/page-heading.js b/spinifex/services/spinifexui/frontend/dist/assets/page-heading.js index e2f16b5e9..228291c31 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/page-heading.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/page-heading.js @@ -1 +1 @@ -import{Fo as e,Ho as t}from"./index.js";var n=e(),r=t();function i(e){let t=(0,n.c)(12),{title:i,subtitle:a,actions:o}=e,s;t[0]===i?s=t[1]:(s=(0,r.jsx)(`h1`,{className:`text-3xl`,children:i}),t[0]=i,t[1]=s);let c;t[2]===a?c=t[3]:(c=a&&(0,r.jsx)(`p`,{className:`mt-1 text-muted-foreground`,children:a}),t[2]=a,t[3]=c);let l;t[4]!==s||t[5]!==c?(l=(0,r.jsxs)(`div`,{children:[s,c]}),t[4]=s,t[5]=c,t[6]=l):l=t[6];let u;t[7]===o?u=t[8]:(u=o&&(0,r.jsx)(`div`,{children:o}),t[7]=o,t[8]=u);let d;return t[9]!==l||t[10]!==u?(d=(0,r.jsxs)(`div`,{className:`mb-6 flex items-start justify-between`,children:[l,u]}),t[9]=l,t[10]=u,t[11]=d):d=t[11],d}export{i as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";var n=t(),r=e();function i(e){let t=(0,n.c)(12),{title:i,subtitle:a,actions:o}=e,s;t[0]===i?s=t[1]:(s=(0,r.jsx)(`h1`,{className:`text-3xl`,children:i}),t[0]=i,t[1]=s);let c;t[2]===a?c=t[3]:(c=a&&(0,r.jsx)(`p`,{className:`mt-1 text-muted-foreground`,children:a}),t[2]=a,t[3]=c);let l;t[4]!==s||t[5]!==c?(l=(0,r.jsxs)(`div`,{children:[s,c]}),t[4]=s,t[5]=c,t[6]=l):l=t[6];let u;t[7]===o?u=t[8]:(u=o&&(0,r.jsx)(`div`,{children:o}),t[7]=o,t[8]=u);let d;return t[9]!==l||t[10]!==u?(d=(0,r.jsxs)(`div`,{className:`mb-6 flex items-start justify-between`,children:[l,u]}),t[9]=l,t[10]=u,t[11]=d):d=t[11],d}export{i as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/plus.js b/spinifex/services/spinifexui/frontend/dist/assets/plus.js index 9cba1b0a0..1aa37d59f 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/plus.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/plus.js @@ -1 +1 @@ -import{wo as e}from"./index.js";var t=e(`plus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]);export{t}; \ No newline at end of file +import{Nr as e}from"./index.js";var t=e(`plus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/policy-document-viewer.js b/spinifex/services/spinifexui/frontend/dist/assets/policy-document-viewer.js new file mode 100644 index 000000000..12264bbf7 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/policy-document-viewer.js @@ -0,0 +1 @@ +import{n as e,t}from"./compiler-runtime.js";var n=t(),r=e();function i(e){let t=(0,n.c)(4),{document:i}=e,a;try{let e;t[0]===i?e=t[1]:(e=JSON.stringify(JSON.parse(i),null,2),t[0]=i,t[1]=e),a=e}catch{a=i}let o;return t[2]===a?o=t[3]:(o=(0,r.jsx)(`pre`,{className:`overflow-auto rounded-md border bg-muted p-4 font-mono text-sm`,children:a}),t[2]=a,t[3]=o),o}export{i as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/queryOptions.js b/spinifex/services/spinifexui/frontend/dist/assets/queryOptions.js new file mode 100644 index 000000000..a81db320d --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/queryOptions.js @@ -0,0 +1 @@ +function e(e){return e}export{e as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/redirect.js b/spinifex/services/spinifexui/frontend/dist/assets/redirect.js new file mode 100644 index 000000000..dbe39e3b1 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/redirect.js @@ -0,0 +1 @@ +function e(e){if(e.statusCode=e.statusCode||e.code||307,!e._builtLocation&&!e.reloadDocument&&typeof e.href==`string`)try{new URL(e.href),e.reloadDocument=!0}catch{}let t=new Headers(e.headers);e.href&&t.get(`Location`)===null&&t.set(`Location`,e.href);let n=new Response(null,{status:e.statusCode,headers:t});if(n.options=e,e.throw)throw n;return n}function t(e){return e instanceof Response&&!!e.options}export{e as n,t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/rotate-cw.js b/spinifex/services/spinifexui/frontend/dist/assets/rotate-cw.js new file mode 100644 index 000000000..7dbb5ace0 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/rotate-cw.js @@ -0,0 +1 @@ +import{Nr as e}from"./index.js";var t=e(`rotate-cw`,[[`path`,{d:`M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8`,key:`1p45f6`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/route.js b/spinifex/services/spinifexui/frontend/dist/assets/route.js index 781036335..b0364dc26 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/route.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/route.js @@ -1 +1 @@ -import{Fo as e,Ho as t,Io as n}from"./index.js";var r=e(),i=t(),a=()=>{let e=(0,r.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,i.jsx)(n,{}),e[0]=t):t=e[0],t};export{a as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{Fr as n}from"./index.js";var r=t(),i=e(),a=()=>{let e=(0,r.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,i.jsx)(n,{}),e[0]=t):t=e[0],t};export{a as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/route2.js b/spinifex/services/spinifexui/frontend/dist/assets/route2.js index 781036335..b0364dc26 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/route2.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/route2.js @@ -1 +1 @@ -import{Fo as e,Ho as t,Io as n}from"./index.js";var r=e(),i=t(),a=()=>{let e=(0,r.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,i.jsx)(n,{}),e[0]=t):t=e[0],t};export{a as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{Fr as n}from"./index.js";var r=t(),i=e(),a=()=>{let e=(0,r.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,i.jsx)(n,{}),e[0]=t):t=e[0],t};export{a as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/run-instances.js b/spinifex/services/spinifexui/frontend/dist/assets/run-instances.js index 8f5fdbabe..bcc630b97 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/run-instances.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/run-instances.js @@ -1,16 +1,16 @@ -import{t as e}from"./error-banner.js";import{t}from"./useSuspenseQuery.js";import{t as n}from"./back-link.js";import{t as r}from"./chevron-down.js";import{a as i,c as a,i as o,n as s,o as c,r as l,s as u,t as d}from"./select.js";import{t as f}from"./cli-command-panel.js";import{u as ee}from"./ec2.js";import{G as p,Ho as m,Q as h,Ro as g,Uo as _,Xa as v,Xi as y,cs as b,dt as x,ht as S,io as C,nt as w,q as te,st as T}from"./index.js";import{i as ne,n as E,t as re}from"./zod.js";import{t as D}from"./page-heading.js";import{a as O,n as k,r as A,t as j}from"./field.js";import{i as ie,t as ae,v as oe}from"./ec22.js";import{n as se,r as ce,t as le}from"./collapsible.js";var ue=b(_()),M=m(),N=`/dev/sda1`,de=`gp3`,fe=1,pe=16384;function me(){let m=g(),{data:_}=t(p),{data:b}=t(h),{data:N}=t(te),{data:me}=t(x),{data:ge}=t(w),{data:_e}=t(S),{data:ve}=t(T),F=ee(),I=_.Images??[],ye=b.KeyPairs??[],be=me.Subnets??[],xe=ge.PlacementGroups??[],Se=_e.Vpcs??[],L=ve.SecurityGroups??[],Ce=(Se.find(e=>e.IsDefault)??Se[0])?.VpcId,we=oe(L.map(e=>e.GroupName??``)),R={},z=new Map;for(let e of N.InstanceTypes??[]){let t=e.InstanceType;if(t&&(R[t]=(R[t]??0)+1,!z.has(t))){let n=e.GpuInfo?.Gpus;n&&n.length>0&&z.set(t,{totalMiB:e.GpuInfo?.TotalGpuMemoryInMiB??0,gpuName:n[0]?.Name??``})}}let B=Object.keys(R).toSorted(),V=B.filter(e=>z.has(e)),H=B.filter(e=>!z.has(e)),Te=I[0]?.ImageId,Ee=ye[0]?.KeyName,De=B.find(e=>e.endsWith(`.nano`))??B[0],Oe=P(I.find(e=>e.ImageId===Te)),{control:U,handleSubmit:ke,register:W,setValue:G,getValues:Ae,watch:K,formState:{errors:q,isSubmitting:J}}=ne({resolver:re(ie.refine(e=>e.count<=(R[e.instanceType]??1),{message:`Cannot exceed available capacity`,path:[`count`]}).superRefine((e,t)=>{let n=P(I.find(t=>t.ImageId===e.imageId)).snapshotSize;e.rootVolumeSize!==void 0&&n!==void 0&&e.rootVolumeSizee.ImageId===Z)),Me=K(`subnetId`),Ne=K(`securityGroupMode`),Pe=K(`ruleSource`),Fe=K(`securityGroupIds`)??[],$=be.find(e=>e.SubnetId===Me)?.VpcId??Ce,Ie=$?L.filter(e=>e.VpcId===$):L,Le=e=>{let t=Ae(`securityGroupIds`)??[];G(`securityGroupIds`,t.includes(e)?t.filter(t=>t!==e):[...t,e],{shouldValidate:!0})};return(0,ue.useEffect)(()=>{G(`rootDeviceName`,Q.deviceName)},[Z,Q.deviceName,G]),(0,M.jsxs)(M.Fragment,{children:[(0,M.jsx)(n,{to:`/ec2/describe-instances`,children:`Back to instances`}),(0,M.jsx)(D,{title:`Run Instances`}),B.length===0&&(0,M.jsx)(e,{msg:`No compute available. No new instances can be created until compute is available.`}),F.error&&(0,M.jsx)(e,{error:F.error,msg:`Failed to create instance`}),(0,M.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:ke(async e=>{await F.mutateAsync({...e,resolvedVpcId:$}),m({to:`/ec2/describe-instances`})}),children:[(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`imageId`,children:`Image`})}),(0,M.jsx)(E,{control:U,name:`imageId`,render:({field:e})=>{let t=I.find(t=>t.ImageId===e.value);return(0,M.jsxs)(d,{onValueChange:t=>e.onChange(t),value:e.value??``,children:[(0,M.jsx)(u,{"aria-invalid":!!q.imageId,className:`w-full`,id:`imageId`,children:(0,M.jsx)(a,{children:t?`${t.Name??`Unnamed`} (${t.Architecture})`:``})}),(0,M.jsx)(s,{children:I.map(e=>(0,M.jsxs)(o,{value:e.ImageId??``,children:[e.Name??`Unnamed`,` (`,e.Architecture,`)`]},e.ImageId))})]})}}),(0,M.jsx)(A,{errors:[q.imageId]})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`instanceType`,children:`Instance Type`})}),(0,M.jsx)(E,{control:U,name:`instanceType`,render:({field:e})=>(0,M.jsxs)(d,{onValueChange:t=>e.onChange(t),value:e.value||``,children:[(0,M.jsx)(u,{"aria-invalid":!!q.instanceType,className:`w-full`,id:`instanceType`,children:(0,M.jsx)(a,{})}),(0,M.jsxs)(s,{children:[H.length>0&&(0,M.jsxs)(l,{children:[V.length>0&&(0,M.jsx)(i,{children:`Standard`}),H.map(e=>(0,M.jsxs)(o,{value:e,children:[e,` (`,R[e],` available)`]},e))]}),V.length>0&&H.length>0&&(0,M.jsx)(c,{}),V.length>0&&(0,M.jsxs)(l,{children:[(0,M.jsx)(i,{children:`GPU-Accelerated`}),V.map(e=>{let t=z.get(e);return(0,M.jsxs)(o,{value:e,children:[e,t?` — ${C(t.totalMiB)}`:``,` (`,R[e],` available)`]},e)})]})]})]})}),X&&(0,M.jsxs)(k,{children:[`GPU: `,X.gpuName,` ·`,` `,C(X.totalMiB),` VRAM`]}),(0,M.jsx)(A,{errors:[q.instanceType]})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`keyName`,children:`Key Pair`})}),(0,M.jsx)(E,{control:U,name:`keyName`,render:({field:e})=>(0,M.jsxs)(d,{onValueChange:t=>e.onChange(t),value:e.value||``,children:[(0,M.jsx)(u,{"aria-invalid":!!q.keyName,className:`w-full`,id:`keyName`,children:(0,M.jsx)(a,{})}),(0,M.jsx)(s,{children:ye.map(e=>(0,M.jsx)(o,{value:e.KeyName??``,children:e.KeyName},e.KeyPairId))})]})}),(0,M.jsx)(A,{errors:[q.keyName]})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`subnetId`,children:`Subnet`})}),(0,M.jsx)(E,{control:U,name:`subnetId`,render:({field:e})=>(0,M.jsxs)(d,{onValueChange:t=>e.onChange(t===`none`?void 0:t),value:e.value??`none`,children:[(0,M.jsx)(u,{className:`w-full`,id:`subnetId`,children:(0,M.jsx)(a,{})}),(0,M.jsxs)(s,{children:[(0,M.jsx)(o,{value:`none`,children:`none`}),be.map(e=>(0,M.jsx)(o,{value:e.SubnetId??``,children:e.SubnetId},e.SubnetId))]})]})})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:`Security Group`}),(0,M.jsx)(k,{children:$?`Applies to VPC ${$}`:`No VPC resolved — select a subnet`}),(0,M.jsx)(E,{control:U,name:`securityGroupMode`,render:({field:e})=>(0,M.jsxs)(`div`,{className:`flex gap-4 text-sm`,children:[(0,M.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,M.jsx)(`input`,{"aria-label":`Create new security group`,checked:e.value===`create`,onChange:()=>e.onChange(`create`),type:`radio`}),`Create new`]}),(0,M.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,M.jsx)(`input`,{"aria-label":`Select existing security group`,checked:e.value===`existing`,onChange:()=>e.onChange(`existing`),type:`radio`}),`Select existing`]})]})}),Ne===`create`?(0,M.jsxs)(`div`,{className:`mt-3 space-y-4 rounded-md border border-border p-4`,children:[(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`newSgName`,children:`Name`})}),(0,M.jsx)(y,{"aria-invalid":!!q.newSgName,id:`newSgName`,type:`text`,...W(`newSgName`)}),(0,M.jsx)(A,{errors:[q.newSgName]})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`newSgDescription`,children:`Description`})}),(0,M.jsx)(y,{id:`newSgDescription`,type:`text`,...W(`newSgDescription`)})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:`Inbound rules`}),(0,M.jsxs)(`div`,{className:`space-y-1`,children:[(0,M.jsx)(E,{control:U,name:`allowSsh`,render:({field:e})=>(0,M.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,M.jsx)(`input`,{"aria-label":`Allow SSH`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Allow SSH (tcp/22)`]})}),(0,M.jsx)(E,{control:U,name:`allowHttp`,render:({field:e})=>(0,M.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,M.jsx)(`input`,{"aria-label":`Allow HTTP`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Allow HTTP (tcp/80)`]})}),(0,M.jsx)(E,{control:U,name:`allowHttps`,render:({field:e})=>(0,M.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,M.jsx)(`input`,{"aria-label":`Allow HTTPS`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Allow HTTPS (tcp/443)`]})})]})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:`Source`}),(0,M.jsx)(E,{control:U,name:`ruleSource`,render:({field:e})=>(0,M.jsxs)(`div`,{className:`flex gap-4 text-sm`,children:[(0,M.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,M.jsx)(`input`,{"aria-label":`Source anywhere`,checked:e.value===`anywhere`,onChange:()=>e.onChange(`anywhere`),type:`radio`}),`Anywhere (0.0.0.0/0)`]}),(0,M.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,M.jsx)(`input`,{"aria-label":`Source custom CIDR`,checked:e.value===`custom`,onChange:()=>e.onChange(`custom`),type:`radio`}),`Custom CIDR`]})]})}),Pe===`custom`&&(0,M.jsxs)(`div`,{className:`mt-2`,children:[(0,M.jsx)(y,{"aria-invalid":!!q.customCidr,placeholder:`203.0.113.0/24`,type:`text`,...W(`customCidr`)}),(0,M.jsx)(A,{errors:[q.customCidr]})]})]})]}):(0,M.jsxs)(`div`,{className:`mt-3`,children:[Ie.length===0?(0,M.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No security groups in the resolved VPC.`}):(0,M.jsx)(`div`,{className:`space-y-1`,children:Ie.map(e=>(0,M.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,M.jsx)(`input`,{"aria-label":`Security group ${e.GroupId} (${e.GroupName})`,checked:Fe.includes(e.GroupId??``),onChange:()=>Le(e.GroupId??``),type:`checkbox`}),(0,M.jsxs)(`span`,{className:`font-mono`,children:[e.GroupId,` (`,e.GroupName,`)`]})]},e.GroupId))}),(0,M.jsx)(A,{errors:[q.securityGroupIds]})]})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`placementGroupName`,children:`Placement Group`})}),(0,M.jsx)(E,{control:U,name:`placementGroupName`,render:({field:e})=>(0,M.jsxs)(d,{onValueChange:t=>e.onChange(t===`none`?void 0:t),value:e.value??`none`,children:[(0,M.jsx)(u,{className:`w-full`,id:`placementGroupName`,children:(0,M.jsx)(a,{})}),(0,M.jsxs)(s,{children:[(0,M.jsx)(o,{value:`none`,children:`none`}),xe.map(e=>(0,M.jsxs)(o,{value:e.GroupName??``,children:[e.GroupName,` (`,e.Strategy,`)`]},e.GroupId))]})]})})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`count`,children:`Number of Instances`})}),(0,M.jsx)(y,{"aria-describedby":`count-description`,"aria-invalid":!!q.count,id:`count`,type:`number`,...W(`count`,{valueAsNumber:!0})}),(0,M.jsx)(`p`,{className:`text-xs text-muted-foreground`,id:`count-description`,children:Y&&`Available capacity for ${Y}: ${je}`}),(0,M.jsx)(A,{errors:[q.count]})]}),(0,M.jsxs)(le,{children:[(0,M.jsxs)(ce,{className:`group flex h-7 w-full items-center justify-between rounded-md border border-input bg-input/20 px-2 py-0.5 text-sm transition-colors outline-none hover:bg-input/40 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 md:text-xs/relaxed dark:bg-input/30 dark:hover:bg-input/50`,render:(0,M.jsx)(`button`,{"aria-label":`Block Device Mappings (root volume)`,type:`button`}),children:[(0,M.jsx)(`span`,{children:`Block Device Mappings (root volume)`}),(0,M.jsx)(r,{className:`size-3.5 text-muted-foreground transition-transform group-data-[panel-open]:rotate-180`})]}),(0,M.jsxs)(se,{className:`mt-3 space-y-4 rounded-md border border-border p-4`,children:[(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`rootDeviceName`,children:`Device Name`})}),(0,M.jsx)(y,{"aria-invalid":!!q.rootDeviceName,id:`rootDeviceName`,type:`text`,...W(`rootDeviceName`)}),(0,M.jsx)(A,{errors:[q.rootDeviceName]})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`rootVolumeSize`,children:`Volume Size (GiB)`})}),(0,M.jsx)(y,{"aria-invalid":!!q.rootVolumeSize,id:`rootVolumeSize`,max:pe,min:Q.snapshotSize??fe,placeholder:Q.snapshotSize?`${Q.snapshotSize} (AMI default)`:`use AMI / backend default`,type:`number`,...W(`rootVolumeSize`,{setValueAs:e=>e===``||e==null?void 0:Number(e)})}),Q.snapshotSize!==void 0&&(0,M.jsxs)(k,{children:[`AMI snapshot size: `,Q.snapshotSize,` GiB`]}),(0,M.jsx)(A,{errors:[q.rootVolumeSize]})]}),(0,M.jsxs)(j,{children:[(0,M.jsx)(O,{children:(0,M.jsx)(`label`,{htmlFor:`rootVolumeType`,children:`Volume Type`})}),(0,M.jsx)(E,{control:U,name:`rootVolumeType`,render:({field:e})=>(0,M.jsxs)(d,{onValueChange:t=>e.onChange(t),value:e.value??de,children:[(0,M.jsx)(u,{"aria-invalid":!!q.rootVolumeType,className:`w-full`,id:`rootVolumeType`,children:(0,M.jsx)(a,{})}),(0,M.jsx)(s,{children:ae.map(e=>(0,M.jsx)(o,{value:e,children:e},e))})]})}),(0,M.jsx)(A,{errors:[q.rootVolumeType]})]}),(0,M.jsx)(j,{children:(0,M.jsx)(E,{control:U,name:`rootDeleteOnTermination`,render:({field:e})=>(0,M.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,M.jsx)(`input`,{"aria-label":`Delete on termination`,checked:e.value??!0,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Delete on termination`]})})})]})]}),(0,M.jsx)(f,{commands:he(K,$)}),(0,M.jsxs)(`div`,{className:`flex gap-2`,children:[(0,M.jsx)(v,{disabled:J||F.isPending,onClick:async()=>await m({to:`/ec2/describe-instances`}),type:`button`,variant:`outline`,children:`Cancel`}),(0,M.jsx)(v,{disabled:J||F.isPending||B.length===0,type:`submit`,children:J||F.isPending?`Creating…`:`Create Instance`})]})]})]})}function P(e){let t=e?.RootDeviceName??N,n=e?.BlockDeviceMappings??[];return{deviceName:t,snapshotSize:(n.find(e=>e.DeviceName===t)??n[0])?.Ebs?.VolumeSize}}function he(e,t){let n=e(`imageId`),r=typeof n==`string`?n:``,i=e(`instanceType`),a=typeof i==`string`?i:``,o=e(`keyName`),s=typeof o==`string`?o:``,c=e(`subnetId`),l=typeof c==`string`?c:``,u=e(`placementGroupName`),d=typeof u==`string`?u:``,f=e(`count`),ee=typeof f==`number`?f:0,p=e(`rootDeviceName`),m=typeof p==`string`?p:``,h=e(`rootVolumeSize`),g=typeof h==`number`?h:void 0,_=e(`rootVolumeType`),v=typeof _==`string`?_:``,y=e(`rootDeleteOnTermination`),b=typeof y==`boolean`?y:!0,x=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 run-instances`},{type:`flag`,value:` \\ +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{t as r}from"./error-banner.js";import{t as i}from"./useSuspenseQuery.js";import{k as a}from"./SignatureV4.js";import{t as o}from"./back-link.js";import{t as ee}from"./chevron-down.js";import{a as s,c,i as l,n as u,o as d,r as f,s as p,t as m}from"./select.js";import{t as h}from"./cli-command-panel.js";import{t as g}from"./button.js";import{d as _}from"./ec2.js";import{Ir as v,Lt as y,Nt as b,Rn as x,bt as S,ht as te,kt as C,pt as ne,wt as w}from"./index.js";import{i as re,n as T,t as ie}from"./zod.js";import{t as E}from"./page-heading.js";import{a as D,n as O,r as k,t as A}from"./field.js";import{i as ae,t as oe,v as se}from"./ec22.js";import{n as ce,r as le,t as ue}from"./collapsible.js";var de=t(n()),j=e(),M=`/dev/sda1`,fe=`gp3`,pe=1,me=16384;function he(){let e=v(),{data:t}=i(ne),{data:n}=i(S),{data:M}=i(te),{data:he}=i(b),{data:_e}=i(w),{data:ve}=i(y),{data:ye}=i(C),P=_(),F=t.Images??[],be=n.KeyPairs??[],I=he.Subnets??[],xe=_e.PlacementGroups??[],Se=ve.Vpcs??[],L=ye.SecurityGroups??[],Ce=(Se.find(e=>e.IsDefault)??Se[0])?.VpcId,we=se(L.map(e=>e.GroupName??``)),R={},z=new Map;for(let e of M.InstanceTypes??[]){let t=e.InstanceType;if(t&&(R[t]=(R[t]??0)+1,!z.has(t))){let n=e.GpuInfo?.Gpus;n&&n.length>0&&z.set(t,{totalMiB:e.GpuInfo?.TotalGpuMemoryInMiB??0,gpuName:n[0]?.Name??``})}}let B=Object.keys(R).toSorted(),V=B.filter(e=>z.has(e)),H=B.filter(e=>!z.has(e)),Te=F[0]?.ImageId,Ee=be[0]?.KeyName,De=B.find(e=>e.endsWith(`.nano`))??B[0],Oe=N(F.find(e=>e.ImageId===Te)),{control:U,handleSubmit:ke,register:W,setValue:G,getValues:Ae,watch:K,formState:{errors:q,isSubmitting:J}}=re({resolver:ie(ae.refine(e=>e.count<=(R[e.instanceType]??1),{message:`Cannot exceed available capacity`,path:[`count`]}).superRefine((e,t)=>{let n=N(F.find(t=>t.ImageId===e.imageId)).snapshotSize;e.rootVolumeSize!==void 0&&n!==void 0&&e.rootVolumeSizee.ImageId===Me)),Ne=K(`subnetId`),Pe=K(`securityGroupMode`),Fe=K(`ruleSource`),Ie=K(`securityGroupIds`)??[],Q=I.find(e=>e.SubnetId===Ne)?.VpcId??Ce,$=Q?L.filter(e=>e.VpcId===Q):L,Le=e=>{let t=Ae(`securityGroupIds`)??[];G(`securityGroupIds`,t.includes(e)?t.filter(t=>t!==e):[...t,e],{shouldValidate:!0})};return(0,de.useEffect)(()=>{G(`rootDeviceName`,Z.deviceName)},[Me,Z.deviceName,G]),(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(o,{to:`/ec2/describe-instances`,children:`Back to instances`}),(0,j.jsx)(E,{title:`Run Instances`}),B.length===0&&(0,j.jsx)(r,{msg:`No compute available. No new instances can be created until compute is available.`}),P.error&&(0,j.jsx)(r,{error:P.error,msg:`Failed to create instance`}),(0,j.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:ke(async t=>{await P.mutateAsync({...t,resolvedVpcId:Q}),e({to:`/ec2/describe-instances`})}),children:[(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`imageId`,children:`Image`})}),(0,j.jsx)(T,{control:U,name:`imageId`,render:({field:e})=>{let t=F.find(t=>t.ImageId===e.value);return(0,j.jsxs)(m,{onValueChange:t=>e.onChange(t),value:e.value??``,children:[(0,j.jsx)(p,{"aria-invalid":!!q.imageId,className:`w-full`,id:`imageId`,children:(0,j.jsx)(c,{children:t?`${t.Name??`Unnamed`} (${t.Architecture})`:``})}),(0,j.jsx)(u,{children:F.map(e=>(0,j.jsxs)(l,{value:e.ImageId??``,children:[e.Name??`Unnamed`,` (`,e.Architecture,`)`]},e.ImageId))})]})}}),(0,j.jsx)(k,{errors:[q.imageId]})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`instanceType`,children:`Instance Type`})}),(0,j.jsx)(T,{control:U,name:`instanceType`,render:({field:e})=>(0,j.jsxs)(m,{onValueChange:t=>e.onChange(t),value:e.value||``,children:[(0,j.jsx)(p,{"aria-invalid":!!q.instanceType,className:`w-full`,id:`instanceType`,children:(0,j.jsx)(c,{})}),(0,j.jsxs)(u,{children:[H.length>0&&(0,j.jsxs)(f,{children:[V.length>0&&(0,j.jsx)(s,{children:`Standard`}),H.map(e=>(0,j.jsxs)(l,{value:e,children:[e,` (`,R[e],` available)`]},e))]}),V.length>0&&H.length>0&&(0,j.jsx)(d,{}),V.length>0&&(0,j.jsxs)(f,{children:[(0,j.jsx)(s,{children:`GPU-Accelerated`}),V.map(e=>{let t=z.get(e);return(0,j.jsxs)(l,{value:e,children:[e,t?` — ${a(t.totalMiB)}`:``,` (`,R[e],` available)`]},e)})]})]})]})}),X&&(0,j.jsxs)(O,{children:[`GPU: `,X.gpuName,` ·`,` `,a(X.totalMiB),` VRAM`]}),(0,j.jsx)(k,{errors:[q.instanceType]})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`keyName`,children:`Key Pair`})}),(0,j.jsx)(T,{control:U,name:`keyName`,render:({field:e})=>(0,j.jsxs)(m,{onValueChange:t=>e.onChange(t),value:e.value||``,children:[(0,j.jsx)(p,{"aria-invalid":!!q.keyName,className:`w-full`,id:`keyName`,children:(0,j.jsx)(c,{})}),(0,j.jsx)(u,{children:be.map(e=>(0,j.jsx)(l,{value:e.KeyName??``,children:e.KeyName},e.KeyPairId))})]})}),(0,j.jsx)(k,{errors:[q.keyName]})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`subnetId`,children:`Subnet`})}),(0,j.jsx)(T,{control:U,name:`subnetId`,render:({field:e})=>(0,j.jsxs)(m,{onValueChange:t=>e.onChange(t===`none`?void 0:t),value:e.value??`none`,children:[(0,j.jsx)(p,{className:`w-full`,id:`subnetId`,children:(0,j.jsx)(c,{})}),(0,j.jsxs)(u,{children:[(0,j.jsx)(l,{value:`none`,children:`none`}),I.map(e=>(0,j.jsx)(l,{value:e.SubnetId??``,children:e.SubnetId},e.SubnetId))]})]})})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:`Security Group`}),(0,j.jsx)(O,{children:Q?`Applies to VPC ${Q}`:`No VPC resolved — select a subnet`}),(0,j.jsx)(T,{control:U,name:`securityGroupMode`,render:({field:e})=>(0,j.jsxs)(`div`,{className:`flex gap-4 text-sm`,children:[(0,j.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,j.jsx)(`input`,{"aria-label":`Create new security group`,checked:e.value===`create`,onChange:()=>e.onChange(`create`),type:`radio`}),`Create new`]}),(0,j.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,j.jsx)(`input`,{"aria-label":`Select existing security group`,checked:e.value===`existing`,onChange:()=>e.onChange(`existing`),type:`radio`}),`Select existing`]})]})}),Pe===`create`?(0,j.jsxs)(`div`,{className:`mt-3 space-y-4 rounded-md border border-border p-4`,children:[(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`newSgName`,children:`Name`})}),(0,j.jsx)(x,{"aria-invalid":!!q.newSgName,id:`newSgName`,type:`text`,...W(`newSgName`)}),(0,j.jsx)(k,{errors:[q.newSgName]})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`newSgDescription`,children:`Description`})}),(0,j.jsx)(x,{id:`newSgDescription`,type:`text`,...W(`newSgDescription`)})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:`Inbound rules`}),(0,j.jsxs)(`div`,{className:`space-y-1`,children:[(0,j.jsx)(T,{control:U,name:`allowSsh`,render:({field:e})=>(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,j.jsx)(`input`,{"aria-label":`Allow SSH`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Allow SSH (tcp/22)`]})}),(0,j.jsx)(T,{control:U,name:`allowHttp`,render:({field:e})=>(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,j.jsx)(`input`,{"aria-label":`Allow HTTP`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Allow HTTP (tcp/80)`]})}),(0,j.jsx)(T,{control:U,name:`allowHttps`,render:({field:e})=>(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,j.jsx)(`input`,{"aria-label":`Allow HTTPS`,checked:e.value,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Allow HTTPS (tcp/443)`]})})]})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:`Source`}),(0,j.jsx)(T,{control:U,name:`ruleSource`,render:({field:e})=>(0,j.jsxs)(`div`,{className:`flex gap-4 text-sm`,children:[(0,j.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,j.jsx)(`input`,{"aria-label":`Source anywhere`,checked:e.value===`anywhere`,onChange:()=>e.onChange(`anywhere`),type:`radio`}),`Anywhere (0.0.0.0/0)`]}),(0,j.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,j.jsx)(`input`,{"aria-label":`Source custom CIDR`,checked:e.value===`custom`,onChange:()=>e.onChange(`custom`),type:`radio`}),`Custom CIDR`]})]})}),Fe===`custom`&&(0,j.jsxs)(`div`,{className:`mt-2`,children:[(0,j.jsx)(x,{"aria-invalid":!!q.customCidr,placeholder:`203.0.113.0/24`,type:`text`,...W(`customCidr`)}),(0,j.jsx)(k,{errors:[q.customCidr]})]})]})]}):(0,j.jsxs)(`div`,{className:`mt-3`,children:[$.length===0?(0,j.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No security groups in the resolved VPC.`}):(0,j.jsx)(`div`,{className:`space-y-1`,children:$.map(e=>(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,j.jsx)(`input`,{"aria-label":`Security group ${e.GroupId} (${e.GroupName})`,checked:Ie.includes(e.GroupId??``),onChange:()=>Le(e.GroupId??``),type:`checkbox`}),(0,j.jsxs)(`span`,{className:`font-mono`,children:[e.GroupId,` (`,e.GroupName,`)`]})]},e.GroupId))}),(0,j.jsx)(k,{errors:[q.securityGroupIds]})]})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`placementGroupName`,children:`Placement Group`})}),(0,j.jsx)(T,{control:U,name:`placementGroupName`,render:({field:e})=>(0,j.jsxs)(m,{onValueChange:t=>e.onChange(t===`none`?void 0:t),value:e.value??`none`,children:[(0,j.jsx)(p,{className:`w-full`,id:`placementGroupName`,children:(0,j.jsx)(c,{})}),(0,j.jsxs)(u,{children:[(0,j.jsx)(l,{value:`none`,children:`none`}),xe.map(e=>(0,j.jsxs)(l,{value:e.GroupName??``,children:[e.GroupName,` (`,e.Strategy,`)`]},e.GroupId))]})]})})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`count`,children:`Number of Instances`})}),(0,j.jsx)(x,{"aria-describedby":`count-description`,"aria-invalid":!!q.count,id:`count`,type:`number`,...W(`count`,{valueAsNumber:!0})}),(0,j.jsx)(`p`,{className:`text-xs text-muted-foreground`,id:`count-description`,children:Y&&`Available capacity for ${Y}: ${je}`}),(0,j.jsx)(k,{errors:[q.count]})]}),(0,j.jsxs)(ue,{children:[(0,j.jsxs)(le,{className:`group flex h-7 w-full items-center justify-between rounded-md border border-input bg-input/20 px-2 py-0.5 text-sm transition-colors outline-none hover:bg-input/40 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 md:text-xs/relaxed dark:bg-input/30 dark:hover:bg-input/50`,render:(0,j.jsx)(`button`,{"aria-label":`Block Device Mappings (root volume)`,type:`button`}),children:[(0,j.jsx)(`span`,{children:`Block Device Mappings (root volume)`}),(0,j.jsx)(ee,{className:`size-3.5 text-muted-foreground transition-transform group-data-[panel-open]:rotate-180`})]}),(0,j.jsxs)(ce,{className:`mt-3 space-y-4 rounded-md border border-border p-4`,children:[(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`rootDeviceName`,children:`Device Name`})}),(0,j.jsx)(x,{"aria-invalid":!!q.rootDeviceName,id:`rootDeviceName`,type:`text`,...W(`rootDeviceName`)}),(0,j.jsx)(k,{errors:[q.rootDeviceName]})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`rootVolumeSize`,children:`Volume Size (GiB)`})}),(0,j.jsx)(x,{"aria-invalid":!!q.rootVolumeSize,id:`rootVolumeSize`,max:me,min:Z.snapshotSize??pe,placeholder:Z.snapshotSize?`${Z.snapshotSize} (AMI default)`:`use AMI / backend default`,type:`number`,...W(`rootVolumeSize`,{setValueAs:e=>e===``||e==null?void 0:Number(e)})}),Z.snapshotSize!==void 0&&(0,j.jsxs)(O,{children:[`AMI snapshot size: `,Z.snapshotSize,` GiB`]}),(0,j.jsx)(k,{errors:[q.rootVolumeSize]})]}),(0,j.jsxs)(A,{children:[(0,j.jsx)(D,{children:(0,j.jsx)(`label`,{htmlFor:`rootVolumeType`,children:`Volume Type`})}),(0,j.jsx)(T,{control:U,name:`rootVolumeType`,render:({field:e})=>(0,j.jsxs)(m,{onValueChange:t=>e.onChange(t),value:e.value??fe,children:[(0,j.jsx)(p,{"aria-invalid":!!q.rootVolumeType,className:`w-full`,id:`rootVolumeType`,children:(0,j.jsx)(c,{})}),(0,j.jsx)(u,{children:oe.map(e=>(0,j.jsx)(l,{value:e,children:e},e))})]})}),(0,j.jsx)(k,{errors:[q.rootVolumeType]})]}),(0,j.jsx)(A,{children:(0,j.jsx)(T,{control:U,name:`rootDeleteOnTermination`,render:({field:e})=>(0,j.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,j.jsx)(`input`,{"aria-label":`Delete on termination`,checked:e.value??!0,onChange:t=>e.onChange(t.target.checked),type:`checkbox`}),`Delete on termination`]})})})]})]}),(0,j.jsx)(h,{commands:ge(K,Q)}),(0,j.jsxs)(`div`,{className:`flex gap-2`,children:[(0,j.jsx)(g,{disabled:J||P.isPending,onClick:async()=>await e({to:`/ec2/describe-instances`}),type:`button`,variant:`outline`,children:`Cancel`}),(0,j.jsx)(g,{disabled:J||P.isPending||B.length===0,type:`submit`,children:J||P.isPending?`Creating…`:`Create Instance`})]})]})]})}function N(e){let t=e?.RootDeviceName??M,n=e?.BlockDeviceMappings??[];return{deviceName:t,snapshotSize:(n.find(e=>e.DeviceName===t)??n[0])?.Ebs?.VolumeSize}}function ge(e,t){let n=e(`imageId`),r=typeof n==`string`?n:``,i=e(`instanceType`),a=typeof i==`string`?i:``,o=e(`keyName`),ee=typeof o==`string`?o:``,s=e(`subnetId`),c=typeof s==`string`?s:``,l=e(`placementGroupName`),u=typeof l==`string`?l:``,d=e(`count`),f=typeof d==`number`?d:0,p=e(`rootDeviceName`),m=typeof p==`string`?p:``,h=e(`rootVolumeSize`),g=typeof h==`number`?h:void 0,_=e(`rootVolumeType`),v=typeof _==`string`?_:``,y=e(`rootDeleteOnTermination`),b=typeof y==`boolean`?y:!0,x=[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 run-instances`},{type:`flag`,value:` \\ --image-id`},{type:`value`,value:` ${r||``}`},{type:`flag`,value:` \\ --instance-type`},{type:`value`,value:` ${a||``}`},{type:`flag`,value:` \\ - --key-name`},{type:`value`,value:` ${s||``}`}];l&&x.push({type:`flag`,value:` \\ - --subnet-id`},{type:`value`,value:` ${l}`}),d&&x.push({type:`flag`,value:` \\ - --placement`},{type:`value`,value:` GroupName=${d}`}),x.push({type:`flag`,value:` \\ - --count`},{type:`value`,value:` ${ee||1}`});let S=e(`securityGroupMode`),C=typeof S==`string`?S:`create`,w=e(`newSgName`),te=typeof w==`string`?w:``,T=e(`newSgDescription`),ne=typeof T==`string`?T:``,E=e(`ruleSource`),re=typeof E==`string`?E:`anywhere`,D=e(`customCidr`),O=typeof D==`string`?D:``,k=[];if(C===`create`){k.push({label:`Create security group`,parts:[{type:`bin`,value:`SG_ID=$(AWS_PROFILE=spinifex aws ec2 create-security-group`},{type:`flag`,value:` \\ - --group-name`},{type:`value`,value:` ${te||``}`},{type:`flag`,value:` \\ - --description`},{type:`value`,value:` '${ne||`Created by launch wizard`}'`},{type:`flag`,value:` \\ + --key-name`},{type:`value`,value:` ${ee||``}`}];c&&x.push({type:`flag`,value:` \\ + --subnet-id`},{type:`value`,value:` ${c}`}),u&&x.push({type:`flag`,value:` \\ + --placement`},{type:`value`,value:` GroupName=${u}`}),x.push({type:`flag`,value:` \\ + --count`},{type:`value`,value:` ${f||1}`});let S=e(`securityGroupMode`),te=typeof S==`string`?S:`create`,C=e(`newSgName`),ne=typeof C==`string`?C:``,w=e(`newSgDescription`),re=typeof w==`string`?w:``,T=e(`ruleSource`),ie=typeof T==`string`?T:`anywhere`,E=e(`customCidr`),D=typeof E==`string`?E:``,O=[];if(te===`create`){O.push({label:`Create security group`,parts:[{type:`bin`,value:`SG_ID=$(AWS_PROFILE=spinifex aws ec2 create-security-group`},{type:`flag`,value:` \\ + --group-name`},{type:`value`,value:` ${ne||``}`},{type:`flag`,value:` \\ + --description`},{type:`value`,value:` '${re||`Created by launch wizard`}'`},{type:`flag`,value:` \\ --vpc-id`},{type:`value`,value:` ${t??``}`},{type:`flag`,value:` \\ - --query`},{type:`value`,value:` 'GroupId' --output text)`}]});let n=re===`custom`&&O?O:`0.0.0.0/0`,r=[...e(`allowSsh`)===!0?[22]:[],...e(`allowHttp`)===!0?[80]:[],...e(`allowHttps`)===!0?[443]:[]];if(r.length>0){let e=JSON.stringify(r.map(e=>({IpProtocol:`tcp`,FromPort:e,ToPort:e,IpRanges:[{CidrIp:n}]})));k.push({label:`Authorize inbound rules`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 authorize-security-group-ingress`},{type:`flag`,value:` \\ + --query`},{type:`value`,value:` 'GroupId' --output text)`}]});let n=ie===`custom`&&D?D:`0.0.0.0/0`,r=[...e(`allowSsh`)===!0?[22]:[],...e(`allowHttp`)===!0?[80]:[],...e(`allowHttps`)===!0?[443]:[]];if(r.length>0){let e=JSON.stringify(r.map(e=>({IpProtocol:`tcp`,FromPort:e,ToPort:e,IpRanges:[{CidrIp:n}]})));O.push({label:`Authorize inbound rules`,parts:[{type:`bin`,value:`AWS_PROFILE=spinifex aws ec2 authorize-security-group-ingress`},{type:`flag`,value:` \\ --group-id`},{type:`value`,value:` $SG_ID`},{type:`flag`,value:` \\ --ip-permissions`},{type:`value`,value:` '${e}'`}]})}x.push({type:`flag`,value:` \\ --security-group-ids`},{type:`value`,value:` $SG_ID`})}else{let t=e(`securityGroupIds`),n=Array.isArray(t)?t.filter(e=>typeof e==`string`):[];n.length>0&&x.push({type:`flag`,value:` \\ - --security-group-ids`},{type:`value`,value:` ${n.join(` `)}`})}if(g!==void 0||_!==void 0||y!==void 0){let e={};g!==void 0&&(e.VolumeSize=g),v&&(e.VolumeType=v),y!==void 0&&(e.DeleteOnTermination=b);let t=JSON.stringify([{DeviceName:m||N,Ebs:e}]);x.push({type:`flag`,value:` \\ - --block-device-mappings`},{type:`value`,value:` '${t}'`})}return[...k,{label:`Run Instances`,parts:x}]}export{me as component}; \ No newline at end of file + --security-group-ids`},{type:`value`,value:` ${n.join(` `)}`})}if(g!==void 0||_!==void 0||y!==void 0){let e={};g!==void 0&&(e.VolumeSize=g),v&&(e.VolumeType=v),y!==void 0&&(e.DeleteOnTermination=b);let t=JSON.stringify([{DeviceName:m||M,Ebs:e}]);x.push({type:`flag`,value:` \\ + --block-device-mappings`},{type:`value`,value:` '${t}'`})}return[...O,{label:`Run Instances`,parts:x}]}export{he as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/s3.js b/spinifex/services/spinifexui/frontend/dist/assets/s3.js index 114193736..a83f2c20e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/s3.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/s3.js @@ -1,6 +1,6 @@ -import{n as e}from"./error-banner.js";import{Ar as t,At as n,Ct as r,Dr as i,Er as a,Et as o,Fo as s,Ft as c,It as l,Lt as u,Mt as d,Nt as f,Or as p,Ot as m,Pt as h,St as g,Tr as _,Tt as v,Vo as y,ar as b,cr as x,fr as S,hr as C,jr as w,jt as T,kr as E,kt as ee,lr as D,mr as te,or as O,sr as k,ss as A,ur as j,wr as M,wt as N,xr as P,xt as F,yr as I}from"./index.js";var L=typeof ReadableStream==`function`?ReadableStream:function(){},R=class extends L{},z=({expectedChecksum:e,checksum:t,source:n,checksumSourceLocation:r,base64Encoder:i})=>{if(!te(n))throw Error(`@smithy/util-stream: unsupported source type ${n?.constructor?.name??n} in ChecksumStream.`);let a=i??C;if(typeof TransformStream!=`function`)throw Error(`@smithy/util-stream: unable to instantiate ChecksumStream because API unavailable: ReadableStream/TransformStream.`);let o=new TransformStream({start(){},async transform(e,n){t.update(e),n.enqueue(e)},async flush(n){let i=a(await t.digest());if(e!==i){let t=Error(`Checksum mismatch: expected "${e}" but received "${i}" in response header "${r}".`);n.error(t)}else n.terminate()}});n.pipeThrough(o);let s=o.readable;return Object.setPrototypeOf(s,R.prototype),s},ne=class{allocByteArray;byteLength=0;byteArrays=[];constructor(e){this.allocByteArray=e}push(e){this.byteArrays.push(e),this.byteLength+=e.byteLength}flush(){if(this.byteArrays.length===1){let e=this.byteArrays[0];return this.reset(),e}let e=this.allocByteArray(this.byteLength),t=0;for(let n=0;nnew Uint8Array(e))],s=-1,c=async e=>{let{value:l,done:u}=await r.read(),d=l;if(u){if(s!==-1){let t=H(o,s);U(t)>0&&e.enqueue(t)}e.close()}else{let r=ie(d,!1);if(s!==r&&(s>=0&&e.enqueue(H(o,s)),s=r),s===-1){e.enqueue(d);return}let l=U(d);a+=l;let u=U(o[s]);if(l>=t&&u===0)e.enqueue(d);else{let r=re(o,s,d);!i&&a>t*2&&(i=!0,n?.warn(`@smithy/util-stream - stream chunk size ${l} is below threshold of ${t}, automatically buffering.`)),r>=t?e.enqueue(H(o,s)):await c(e)}}};return new ReadableStream({pull:c})}var V=B;function re(e,t,n){switch(t){case 0:return e[0]+=n,U(e[0]);case 1:case 2:return e[t].push(n),U(e[t])}}function H(e,t){switch(t){case 0:let n=e[0];return e[0]=``,n;case 1:case 2:return e[t].flush()}throw Error(`@smithy/util-stream - invalid index ${t} given to flush()`)}function U(e){return e?.byteLength??e?.length??0}function ie(e,t=!0){return t&&typeof Buffer<`u`&&e instanceof Buffer?2:e instanceof Uint8Array?1:typeof e==`string`?0:-1}var W=(e,{requestChecksumRequired:t,requestAlgorithmMember:n,requestChecksumCalculation:r})=>{if(!n)return r===l.WHEN_SUPPORTED||t?c:void 0;if(e[n])return e[n]},G=e=>e===h.MD5?`content-md5`:`x-amz-checksum-${e.toLowerCase()}`,ae=(e,t)=>{let n=e.toLowerCase();for(let e of Object.keys(t))if(n===e.toLowerCase())return!0;return!1},oe=(e,t)=>{let n=e.toLowerCase();for(let e of Object.keys(t))if(e.toLowerCase().startsWith(n))return!0;return!1},se=e=>e!==void 0&&typeof e!=`string`&&!ArrayBuffer.isView(e)&&!I(e),ce=function(){function e(){this.crc32c=new le}return e.prototype.update=function(e){t(e)||this.crc32c.update(w(e))},e.prototype.digest=function(){return _(this,void 0,void 0,function(){return a(this,function(e){return[2,E(this.crc32c.digest())]})})},e.prototype.reset=function(){this.crc32c=new le},e}(),le=function(){function e(){this.checksum=4294967295}return e.prototype.update=function(e){var t,n;try{for(var r=i(e),a=r.next();!a.done;a=r.next()){var o=a.value;this.checksum=this.checksum>>>8^ue[(this.checksum^o)&255]}}catch(e){t={error:e}}finally{try{a&&!a.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return this},e.prototype.digest=function(){return(this.checksum^4294967295)>>>0},e}(),ue=p([0,4067132163,3778769143,324072436,3348797215,904991772,648144872,3570033899,2329499855,2024987596,1809983544,2575936315,1296289744,3207089363,2893594407,1578318884,274646895,3795141740,4049975192,51262619,3619967088,632279923,922689671,3298075524,2592579488,1760304291,2075979607,2312596564,1562183871,2943781820,3156637768,1313733451,549293790,3537243613,3246849577,871202090,3878099393,357341890,102525238,4101499445,2858735121,1477399826,1264559846,3107202533,1845379342,2677391885,2361733625,2125378298,820201905,3263744690,3520608582,598981189,4151959214,85089709,373468761,3827903834,3124367742,1213305469,1526817161,2842354314,2107672161,2412447074,2627466902,1861252501,1098587580,3004210879,2688576843,1378610760,2262928035,1955203488,1742404180,2511436119,3416409459,969524848,714683780,3639785095,205050476,4266873199,3976438427,526918040,1361435347,2739821008,2954799652,1114974503,2529119692,1691668175,2005155131,2247081528,3690758684,697762079,986182379,3366744552,476452099,3993867776,4250756596,255256311,1640403810,2477592673,2164122517,1922457750,2791048317,1412925310,1197962378,3037525897,3944729517,427051182,170179418,4165941337,746937522,3740196785,3451792453,1070968646,1905808397,2213795598,2426610938,1657317369,3053634322,1147748369,1463399397,2773627110,4215344322,153784257,444234805,3893493558,1021025245,3467647198,3722505002,797665321,2197175160,1889384571,1674398607,2443626636,1164749927,3070701412,2757221520,1446797203,137323447,4198817972,3910406976,461344835,3484808360,1037989803,781091935,3705997148,2460548119,1623424788,1939049696,2180517859,1429367560,2807687179,3020495871,1180866812,410100952,3927582683,4182430767,186734380,3756733383,763408580,1053836080,3434856499,2722870694,1344288421,1131464017,2971354706,1708204729,2545590714,2229949006,1988219213,680717673,3673779818,3383336350,1002577565,4010310262,493091189,238226049,4233660802,2987750089,1082061258,1395524158,2705686845,1972364758,2279892693,2494862625,1725896226,952904198,3399985413,3656866545,731699698,4283874585,222117402,510512622,3959836397,3280807620,837199303,582374963,3504198960,68661723,4135334616,3844915500,390545967,1230274059,3141532936,2825850620,1510247935,2395924756,2091215383,1878366691,2644384480,3553878443,565732008,854102364,3229815391,340358836,3861050807,4117890627,119113024,1493875044,2875275879,3090270611,1247431312,2660249211,1828433272,2141937292,2378227087,3811616794,291187481,34330861,4032846830,615137029,3603020806,3314634738,939183345,1776939221,2609017814,2295496738,2058945313,2926798794,1545135305,1330124605,3173225534,4084100981,17165430,307568514,3762199681,888469610,3332340585,3587147933,665062302,2042050490,2346497209,2559330125,1793573966,3190661285,1279665062,1595330642,2910671697]),de=()=>{let e=Array(8);for(let t=0;t<8;t++){let n=Array(512);for(let e=0;e<256;e++){let r=BigInt(e);for(let e=0;e<8*(t+1);e++)r&1n?r=r>>1n^11127430586519243189n:r>>=1n;n[e*2]=Number(r>>32n&4294967295n),n[e*2+1]=Number(r&4294967295n)}e[t]=new Uint32Array(n)}return e},fe,K,pe,me,he,ge,_e,ve,ye,be=()=>{fe||(fe=de(),[K,pe,me,he,ge,_e,ve,ye]=fe)},xe=class{c1=0;c2=0;constructor(){be(),this.reset()}update(e){let t=e.length,n=0,r=this.c1,i=this.c2;for(;n+8<=t;){let t=((i^e[n++])&255)<<1,a=((i>>>8^e[n++])&255)<<1,o=((i>>>16^e[n++])&255)<<1,s=((i>>>24^e[n++])&255)<<1,c=((r^e[n++])&255)<<1,l=((r>>>8^e[n++])&255)<<1,u=((r>>>16^e[n++])&255)<<1,d=((r>>>24^e[n++])&255)<<1;r=ye[t]^ve[a]^_e[o]^ge[s]^he[c]^me[l]^pe[u]^K[d],i=ye[t+1]^ve[a+1]^_e[o+1]^ge[s+1]^he[c+1]^me[l+1]^pe[u+1]^K[d+1]}for(;n>>8|(r&255)<<24)>>>0,r=r>>>8^K[t],i^=K[t+1],n++}this.c1=r,this.c2=i}async digest(){let e=this.c1^4294967295,t=this.c2^4294967295;return new Uint8Array([e>>>24,e>>>16&255,e>>>8&255,e&255,t>>>24,t>>>16&255,t>>>8&255,t&255])}reset(){this.c1=4294967295,this.c2=4294967295}},Se={CrtCrc64Nvme:null},Ce=()=>f,we=[h.CRC32,h.CRC32C,h.CRC64NVME,h.SHA1,h.SHA256],Te=[h.SHA256,h.SHA1,h.CRC32,h.CRC32C,h.CRC64NVME],Ee=(e,t)=>{let{checksumAlgorithms:n={}}=t;switch(e){case h.MD5:return n?.MD5??t.md5;case h.CRC32:return n?.CRC32??Ce();case h.CRC32C:return n?.CRC32C??ce;case h.CRC64NVME:return typeof Se.CrtCrc64Nvme==`function`?n?.CRC64NVME??Se.CrtCrc64Nvme:n?.CRC64NVME??xe;case h.SHA1:return n?.SHA1??t.sha1;case h.SHA256:return n?.SHA256??t.sha256;default:if(n?.[e])return n[e];throw Error(`The checksum algorithm "${e}" is not supported by the client. Select one of ${we}, or provide an implementation to the client constructor checksums field.`)}},De=(e,t)=>{let n=new e;return n.update(P(t||``)),n.digest()},Oe={name:`flexibleChecksumsMiddleware`,step:`build`,tags:[`BODY_CHECKSUM`],override:!0},ke=(e,t)=>(n,r)=>async i=>{if(!M.isInstance(i.request)||oe(`x-amz-checksum-`,i.request.headers))return n(i);let{request:a,input:o}=i,{body:s,headers:u}=a,{base64Encoder:d,streamHasher:f}=e,{requestChecksumRequired:p,requestAlgorithmMember:m}=t,g=await e.requestChecksumCalculation(),_=m?.name,v=m?.httpHeader;_&&!o[_]&&(g===l.WHEN_SUPPORTED||p)&&(o[_]=c,v&&(u[v]=c));let y=W(o,{requestChecksumRequired:p,requestAlgorithmMember:m?.name,requestChecksumCalculation:g}),b=s,x=u;if(y){switch(y){case h.CRC32:j(r,`FLEXIBLE_CHECKSUMS_REQ_CRC32`,`U`);break;case h.CRC32C:j(r,`FLEXIBLE_CHECKSUMS_REQ_CRC32C`,`V`);break;case h.CRC64NVME:j(r,`FLEXIBLE_CHECKSUMS_REQ_CRC64`,`W`);break;case h.SHA1:j(r,`FLEXIBLE_CHECKSUMS_REQ_SHA1`,`X`);break;case h.SHA256:j(r,`FLEXIBLE_CHECKSUMS_REQ_SHA256`,`Y`);break}let t=G(y),n=Ee(y,e);if(se(s)){let{getAwsChunkedEncodingStream:i,bodyLengthChecker:a}=e;b=i(typeof e.requestStreamBufferSize==`number`&&e.requestStreamBufferSize>=8*1024?V(s,e.requestStreamBufferSize,r.logger):s,{base64Encoder:d,bodyLengthChecker:a,checksumLocationName:t,checksumAlgorithmFn:n,streamHasher:f}),x={...u,"content-encoding":u[`content-encoding`]?`${u[`content-encoding`]},aws-chunked`:`aws-chunked`,"transfer-encoding":`chunked`,"x-amz-decoded-content-length":u[`content-length`],"x-amz-content-sha256":`STREAMING-UNSIGNED-PAYLOAD-TRAILER`,"x-amz-trailer":t},delete x[`content-length`]}else if(!ae(t,u)){let e=await De(n,s);x={...u,[t]:d(e)}}}try{return await n({...i,request:{...a,headers:x,body:b}})}catch(e){if(e instanceof Error&&e.name===`InvalidChunkSizeError`)try{e.message.endsWith(`.`)||(e.message+=`.`),e.message+=` Set [requestStreamBufferSize=number e.g. 65_536] in client constructor to instruct AWS SDK to buffer your input stream.`}catch{}throw e}},Ae={name:`flexibleChecksumsInputMiddleware`,toMiddleware:`serializerMiddleware`,relation:`before`,tags:[`BODY_CHECKSUM`],override:!0},je=(e,t)=>(n,r)=>async i=>{let a=i.input,{requestValidationModeMember:o}=t,s=await e.requestChecksumCalculation(),c=await e.responseChecksumValidation();switch(s){case l.WHEN_REQUIRED:j(r,`FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED`,`a`);break;case l.WHEN_SUPPORTED:j(r,`FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED`,`Z`);break}switch(c){case u.WHEN_REQUIRED:j(r,`FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED`,`c`);break;case u.WHEN_SUPPORTED:j(r,`FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED`,`b`);break}return o&&!a[o]&&c===u.WHEN_SUPPORTED&&(a[o]=`ENABLED`),n(i)},Me=(e=[])=>{let t=[],n=Te.length;for(let r of e){let e=Te.indexOf(r);e===-1?t[n++]=r:t[e]=r}return t.filter(Boolean)},Ne=e=>{let t=e.lastIndexOf(`-`);if(t!==-1){let n=e.slice(t+1);if(!n.startsWith(`0`)){let e=parseInt(n,10);if(!isNaN(e)&&e>=1&&e<=1e4)return!0}}return!1},Pe=async(e,{checksumAlgorithmFn:t,base64Encoder:n})=>n(await De(t,e)),Fe=async(e,{config:t,responseAlgorithms:n,logger:r})=>{let i=Me(n),{body:a,headers:o}=e;for(let n of i){let i=G(n),s=o[i];if(s){let o;try{o=Ee(n,t)}catch(e){if(n===h.CRC64NVME){r?.warn(`Skipping ${h.CRC64NVME} checksum validation: ${e.message}`);continue}throw e}let{base64Encoder:c}=t;if(se(a)){e.body=z({expectedChecksum:s,checksumSourceLocation:i,checksum:new o,source:a,base64Encoder:c});return}let l=await Pe(a,{checksumAlgorithmFn:o,base64Encoder:c});if(l===s)break;throw Error(`Checksum mismatch: expected "${l}" but received "${s}" in response header "${i}".`)}}},Ie={name:`flexibleChecksumsResponseMiddleware`,toMiddleware:`deserializerMiddleware`,relation:`after`,tags:[`BODY_CHECKSUM`],override:!0},Le=(e,t)=>(n,r)=>async i=>{if(!M.isInstance(i.request))return n(i);let a=i.input,o=await n(i),s=o.response,{requestValidationModeMember:c,responseAlgorithms:l}=t;if(c&&a[c]===`ENABLED`){let{clientName:t,commandName:n}=r,i=Object.keys(e.checksumAlgorithms??{}).filter(e=>{let t=G(e);return s.headers[t]!==void 0}),a=Me([...l??[],...i]);if(t===`S3Client`&&n===`GetObjectCommand`&&a.every(e=>{let t=G(e),n=s.headers[t];return!n||Ne(n)}))return o;await Fe(s,{config:e,responseAlgorithms:a,logger:r.logger})}return o},Re=(e,t)=>({applyToStack:n=>{n.add(ke(e,t),Oe),n.addRelativeTo(je(e,t),Ae),n.addRelativeTo(Le(e,t),Ie)}}),ze=`content-length`,Be=`x-amz-decoded-content-length`;function Ve(){return(e,t)=>async n=>{let{request:r}=n;if(M.isInstance(r)&&!(ze in r.headers)&&!(Be in r.headers)){let e=`Are you using a Stream of unknown length as the Body of a PutObject request? Consider using Upload instead from @aws-sdk/lib-storage.`;typeof t?.logger?.warn==`function`&&!(t.logger instanceof b)?t.logger.warn(e):console.warn(e)}return e({...n})}}var He={step:`finalizeRequest`,tags:[`CHECK_CONTENT_LENGTH_HEADER`],name:`getCheckContentLengthHeaderPlugin`,override:!0},Ue=e=>({applyToStack:e=>{e.add(Ve(),He)}}),We=class extends O.classBuilder().ep({...T,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[k(n,e.getEndpointParameterInstructions()),d(n)]}).s(`AmazonS3`,`AbortMultipartUpload`,{}).n(`S3Client`,`AbortMultipartUploadCommand`).sc(g).build(){};function Ge(e){return t=>async n=>{let r={...n.input};for(let t of[{target:`SSECustomerKey`,hash:`SSECustomerKeyMD5`},{target:`CopySourceSSECustomerKey`,hash:`CopySourceSSECustomerKeyMD5`}]){let n=r[t.target];if(n){let i;typeof n==`string`?qe(n,e)?i=e.base64Decoder(n):(i=e.utf8Decoder(n),r[t.target]=e.base64Encoder(i)):(i=ArrayBuffer.isView(n)?new Uint8Array(n.buffer,n.byteOffset,n.byteLength):new Uint8Array(n),r[t.target]=e.base64Encoder(i));let a=new e.md5;a.update(i),r[t.hash]=e.base64Encoder(await a.digest())}}return t({...n,input:r})}}var Ke={name:`ssecMiddleware`,step:`initialize`,tags:[`SSE`],override:!0},q=e=>({applyToStack:t=>{t.add(Ge(e),Ke)}});function qe(e,t){if(!/^(?:[A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e))return!1;try{return t.base64Decoder(e).length===32}catch{return!1}}var Je=class extends O.classBuilder().ep({...T,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[k(n,e.getEndpointParameterInstructions()),d(n),q(n)]}).s(`AmazonS3`,`CompleteMultipartUpload`,{}).n(`S3Client`,`CompleteMultipartUploadCommand`).sc(r).build(){};function Ye(e){return t=>async n=>{let{CreateBucketConfiguration:r}=n.input,i=await e.region();return!r?.LocationConstraint&&!r?.Location&&i!==`us-east-1`&&(n.input.CreateBucketConfiguration=n.input.CreateBucketConfiguration??{},n.input.CreateBucketConfiguration.LocationConstraint=i),t(n)}}var Xe={step:`initialize`,tags:[`LOCATION_CONSTRAINT`,`CREATE_BUCKET_CONFIGURATION`],name:`locationConstraintMiddleware`,override:!0},Ze=e=>({applyToStack:t=>{t.add(Ye(e),Xe)}}),Qe=class extends O.classBuilder().ep({...T,UseS3ExpressControlEndpoint:{type:`staticContextParams`,value:!0},DisableAccessPoints:{type:`staticContextParams`,value:!0},Bucket:{type:`contextParams`,name:`Bucket`}}).m(function(e,t,n,r){return[k(n,e.getEndpointParameterInstructions()),d(n),Ze(n)]}).s(`AmazonS3`,`CreateBucket`,{}).n(`S3Client`,`CreateBucketCommand`).sc(N).build(){},$e=class extends O.classBuilder().ep({...T,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[k(n,e.getEndpointParameterInstructions()),d(n),q(n)]}).s(`AmazonS3`,`CreateMultipartUpload`,{}).n(`S3Client`,`CreateMultipartUploadCommand`).sc(v).build(){},et=class extends O.classBuilder().ep({...T,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[k(n,e.getEndpointParameterInstructions()),d(n)]}).s(`AmazonS3`,`DeleteObject`,{}).n(`S3Client`,`DeleteObjectCommand`).sc(o).build(){},tt=class extends O.classBuilder().ep({...T,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[k(n,e.getEndpointParameterInstructions()),Re(n,{requestAlgorithmMember:{httpHeader:`x-amz-sdk-checksum-algorithm`,name:`ChecksumAlgorithm`},requestChecksumRequired:!1}),Ue(n),d(n),q(n)]}).s(`AmazonS3`,`PutObject`,{}).n(`S3Client`,`PutObjectCommand`).sc(m).build(){},nt=class extends O.classBuilder().ep({...T,Bucket:{type:`contextParams`,name:`Bucket`}}).m(function(e,t,n,r){return[k(n,e.getEndpointParameterInstructions()),Re(n,{requestAlgorithmMember:{httpHeader:`x-amz-sdk-checksum-algorithm`,name:`ChecksumAlgorithm`},requestChecksumRequired:!0}),d(n)]}).s(`AmazonS3`,`PutObjectTagging`,{}).n(`S3Client`,`PutObjectTaggingCommand`).sc(ee).build(){},rt=class extends O.classBuilder().ep({...T,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[k(n,e.getEndpointParameterInstructions()),Re(n,{requestAlgorithmMember:{httpHeader:`x-amz-sdk-checksum-algorithm`,name:`ChecksumAlgorithm`},requestChecksumRequired:!1}),d(n),q(n)]}).s(`AmazonS3`,`UploadPart`,{}).n(`S3Client`,`UploadPartCommand`).sc(n).build(){},it={CRC32:`CRC32`,CRC32C:`CRC32C`,CRC64NVME:`CRC64NVME`,MD5:`MD5`,SHA1:`SHA1`,SHA256:`SHA256`,SHA512:`SHA512`,XXHASH128:`XXHASH128`,XXHASH3:`XXHASH3`,XXHASH64:`XXHASH64`},at=A(((e,t)=>{var n=typeof Reflect==`object`?Reflect:null,r=n&&typeof n.apply==`function`?n.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)},i=n&&typeof n.ownKeys==`function`?n.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};function a(e){console&&console.warn&&console.warn(e)}var o=Number.isNaN||function(e){return e!==e};function s(){s.init.call(this)}t.exports=s,t.exports.once=y,s.EventEmitter=s,s.prototype._events=void 0,s.prototype._eventsCount=0,s.prototype._maxListeners=void 0;var c=10;function l(e){if(typeof e!=`function`)throw TypeError(`The "listener" argument must be of type Function. Received type `+typeof e)}Object.defineProperty(s,`defaultMaxListeners`,{enumerable:!0,get:function(){return c},set:function(e){if(typeof e!=`number`||e<0||o(e))throw RangeError(`The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received `+e+`.`);c=e}}),s.init=function(){(this._events===void 0||this._events===Object.getPrototypeOf(this)._events)&&(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},s.prototype.setMaxListeners=function(e){if(typeof e!=`number`||e<0||o(e))throw RangeError(`The value of "n" is out of range. It must be a non-negative number. Received `+e+`.`);return this._maxListeners=e,this};function u(e){return e._maxListeners===void 0?s.defaultMaxListeners:e._maxListeners}s.prototype.getMaxListeners=function(){return u(this)},s.prototype.emit=function(e){for(var t=[],n=1;n0&&(o=t[0]),o instanceof Error)throw o;var s=Error(`Unhandled error.`+(o?` (`+o.message+`)`:``));throw s.context=o,s}var c=a[e];if(c===void 0)return!1;if(typeof c==`function`)r(c,this,t);else for(var l=c.length,u=g(c,l),n=0;n0&&s.length>i&&!s.warned){s.warned=!0;var c=Error(`Possible EventEmitter memory leak detected. `+s.length+` `+String(t)+` listeners added. Use emitter.setMaxListeners() to increase limit`);c.name=`MaxListenersExceededWarning`,c.emitter=e,c.type=t,c.count=s.length,a(c)}return e}s.prototype.addListener=function(e,t){return d(this,e,t,!1)},s.prototype.on=s.prototype.addListener,s.prototype.prependListener=function(e,t){return d(this,e,t,!0)};function f(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,arguments.length===0?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function p(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},i=f.bind(r);return i.listener=n,r.wrapFn=i,i}s.prototype.once=function(e,t){return l(t),this.on(e,p(this,e,t)),this},s.prototype.prependOnceListener=function(e,t){return l(t),this.prependListener(e,p(this,e,t)),this},s.prototype.removeListener=function(e,t){var n,r,i,a,o;if(l(t),r=this._events,r===void 0||(n=r[e],n===void 0))return this;if(n===t||n.listener===t)--this._eventsCount===0?this._events=Object.create(null):(delete r[e],r.removeListener&&this.emit(`removeListener`,e,n.listener||t));else if(typeof n!=`function`){for(i=-1,a=n.length-1;a>=0;a--)if(n[a]===t||n[a].listener===t){o=n[a].listener,i=a;break}if(i<0)return this;i===0?n.shift():_(n,i),n.length===1&&(r[e]=n[0]),r.removeListener!==void 0&&this.emit(`removeListener`,e,o||t)}return this},s.prototype.off=s.prototype.removeListener,s.prototype.removeAllListeners=function(e){var t,n=this._events,r;if(n===void 0)return this;if(n.removeListener===void 0)return arguments.length===0?(this._events=Object.create(null),this._eventsCount=0):n[e]!==void 0&&(--this._eventsCount===0?this._events=Object.create(null):delete n[e]),this;if(arguments.length===0){var i=Object.keys(n),a;for(r=0;r=0;r--)this.removeListener(e,t[r]);return this};function m(e,t,n){var r=e._events;if(r===void 0)return[];var i=r[t];return i===void 0?[]:typeof i==`function`?n?[i.listener||i]:[i]:n?v(i):g(i,i.length)}s.prototype.listeners=function(e){return m(this,e,!0)},s.prototype.rawListeners=function(e){return m(this,e,!1)},s.listenerCount=function(e,t){return typeof e.listenerCount==`function`?e.listenerCount(t):h.call(e,t)},s.prototype.listenerCount=h;function h(e){var t=this._events;if(t!==void 0){var n=t[e];if(typeof n==`function`)return 1;if(n!==void 0)return n.length}return 0}s.prototype.eventNames=function(){return this._eventsCount>0?i(this._events):[]};function g(e,t){for(var n=Array(t),r=0;r{e.byteLength=c,e.toByteArray=u,e.fromByteArray=p;for(var t=[],n=[],r=typeof Uint8Array<`u`?Uint8Array:Array,i=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`,a=0,o=i.length;a0)throw Error(`Invalid string. Length must be a multiple of 4`);var n=e.indexOf(`=`);n===-1&&(n=t);var r=n===t?0:4-n%4;return[n,r]}function c(e){var t=s(e),n=t[0],r=t[1];return(n+r)*3/4-r}function l(e,t,n){return(t+n)*3/4-n}function u(e){var t,i=s(e),a=i[0],o=i[1],c=new r(l(e,a,o)),u=0,d=o>0?a-4:a,f;for(f=0;f>16&255,c[u++]=t>>8&255,c[u++]=t&255;return o===2&&(t=n[e.charCodeAt(f)]<<2|n[e.charCodeAt(f+1)]>>4,c[u++]=t&255),o===1&&(t=n[e.charCodeAt(f)]<<10|n[e.charCodeAt(f+1)]<<4|n[e.charCodeAt(f+2)]>>2,c[u++]=t>>8&255,c[u++]=t&255),c}function d(e){return t[e>>18&63]+t[e>>12&63]+t[e>>6&63]+t[e&63]}function f(e,t,n){for(var r,i=[],a=t;ac?c:s+o));return i===1?(n=e[r-1],a.push(t[n>>2]+t[n<<4&63]+`==`)):i===2&&(n=(e[r-2]<<8)+e[r-1],a.push(t[n>>10]+t[n>>4&63]+t[n<<2&63]+`=`)),a.join(``)}})),st=A((e=>{e.read=function(e,t,n,r,i){var a,o,s=i*8-r-1,c=(1<>1,u=-7,d=n?i-1:0,f=n?-1:1,p=e[t+d];for(d+=f,a=p&(1<<-u)-1,p>>=-u,u+=s;u>0;a=a*256+e[t+d],d+=f,u-=8);for(o=a&(1<<-u)-1,a>>=-u,u+=r;u>0;o=o*256+e[t+d],d+=f,u-=8);if(a===0)a=1-l;else if(a===c)return o?NaN:(p?-1:1)*(1/0);else o+=2**r,a-=l;return(p?-1:1)*o*2**(a-r)},e.write=function(e,t,n,r,i,a){var o,s,c,l=a*8-i-1,u=(1<>1,f=i===23?2**-24-2**-77:0,p=r?0:a-1,m=r?1:-1,h=+(t<0||t===0&&1/t<0);for(t=Math.abs(t),isNaN(t)||t===1/0?(s=+!!isNaN(t),o=u):(o=Math.floor(Math.log(t)/Math.LN2),t*(c=2**-o)<1&&(o--,c*=2),o+d>=1?t+=f/c:t+=f*2**(1-d),t*c>=2&&(o++,c/=2),o+d>=u?(s=0,o=u):o+d>=1?(s=(t*c-1)*2**i,o+=d):(s=t*2**(d-1)*2**i,o=0));i>=8;e[n+p]=s&255,p+=m,s/=256,i-=8);for(o=o<0;e[n+p]=o&255,p+=m,o/=256,l-=8);e[n+p-m]|=h*128}})),J=A((e=>{var t=ot(),n=st(),r=typeof Symbol==`function`&&typeof Symbol.for==`function`?Symbol.for(`nodejs.util.inspect.custom`):null;e.Buffer=s,e.SlowBuffer=_,e.INSPECT_MAX_BYTES=50;var i=2147483647;e.kMaxLength=i,s.TYPED_ARRAY_SUPPORT=a(),!s.TYPED_ARRAY_SUPPORT&&typeof console<`u`&&typeof console.error==`function`&&console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support.");function a(){try{var e=new Uint8Array(1),t={foo:function(){return 42}};return Object.setPrototypeOf(t,Uint8Array.prototype),Object.setPrototypeOf(e,t),e.foo()===42}catch{return!1}}Object.defineProperty(s.prototype,`parent`,{enumerable:!0,get:function(){if(s.isBuffer(this))return this.buffer}}),Object.defineProperty(s.prototype,`offset`,{enumerable:!0,get:function(){if(s.isBuffer(this))return this.byteOffset}});function o(e){if(e>i)throw RangeError(`The value "`+e+`" is invalid for option "size"`);var t=new Uint8Array(e);return Object.setPrototypeOf(t,s.prototype),t}function s(e,t,n){if(typeof e==`number`){if(typeof t==`string`)throw TypeError(`The "string" argument must be of type string. Received type number`);return d(e)}return c(e,t,n)}s.poolSize=8192;function c(e,t,n){if(typeof e==`string`)return f(e,t);if(ArrayBuffer.isView(e))return p(e);if(e==null)throw TypeError(`The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type `+typeof e);if(W(e,ArrayBuffer)||e&&W(e.buffer,ArrayBuffer)||typeof SharedArrayBuffer<`u`&&(W(e,SharedArrayBuffer)||e&&W(e.buffer,SharedArrayBuffer)))return m(e,t,n);if(typeof e==`number`)throw TypeError(`The "value" argument must not be of type number. Received type number`);var r=e.valueOf&&e.valueOf();if(r!=null&&r!==e)return s.from(r,t,n);var i=h(e);if(i)return i;if(typeof Symbol<`u`&&Symbol.toPrimitive!=null&&typeof e[Symbol.toPrimitive]==`function`)return s.from(e[Symbol.toPrimitive](`string`),t,n);throw TypeError(`The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type `+typeof e)}s.from=function(e,t,n){return c(e,t,n)},Object.setPrototypeOf(s.prototype,Uint8Array.prototype),Object.setPrototypeOf(s,Uint8Array);function l(e){if(typeof e!=`number`)throw TypeError(`"size" argument must be of type number`);if(e<0)throw RangeError(`The value "`+e+`" is invalid for option "size"`)}function u(e,t,n){return l(e),e<=0||t===void 0?o(e):typeof n==`string`?o(e).fill(t,n):o(e).fill(t)}s.alloc=function(e,t,n){return u(e,t,n)};function d(e){return l(e),o(e<0?0:g(e)|0)}s.allocUnsafe=function(e){return d(e)},s.allocUnsafeSlow=function(e){return d(e)};function f(e,t){if((typeof t!=`string`||t===``)&&(t=`utf8`),!s.isEncoding(t))throw TypeError(`Unknown encoding: `+t);var n=v(e,t)|0,r=o(n),i=r.write(e,t);return i!==n&&(r=r.slice(0,i)),r}function p(e){for(var t=e.length<0?0:g(e.length)|0,n=o(t),r=0;r=i)throw RangeError(`Attempt to allocate Buffer larger than maximum size: 0x`+i.toString(16)+` bytes`);return e|0}function _(e){return+e!=e&&(e=0),s.alloc(+e)}s.isBuffer=function(e){return e!=null&&e._isBuffer===!0&&e!==s.prototype},s.compare=function(e,t){if(W(e,Uint8Array)&&(e=s.from(e,e.offset,e.byteLength)),W(t,Uint8Array)&&(t=s.from(t,t.offset,t.byteLength)),!s.isBuffer(e)||!s.isBuffer(t))throw TypeError(`The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array`);if(e===t)return 0;for(var n=e.length,r=t.length,i=0,a=Math.min(n,r);i2&&arguments[2]===!0;if(!r&&n===0)return 0;for(var i=!1;;)switch(t){case`ascii`:case`latin1`:case`binary`:return n;case`utf8`:case`utf-8`:return V(e).length;case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:return n*2;case`hex`:return n>>>1;case`base64`:return U(e).length;default:if(i)return r?-1:V(e).length;t=(``+t).toLowerCase(),i=!0}}s.byteLength=v;function y(e,t,n){var r=!1;if((t===void 0||t<0)&&(t=0),t>this.length||((n===void 0||n>this.length)&&(n=this.length),n<=0)||(n>>>=0,t>>>=0,n<=t))return``;for(e||=`utf8`;;)switch(e){case`hex`:return N(this,t,n);case`utf8`:case`utf-8`:return O(this,t,n);case`ascii`:return j(this,t,n);case`latin1`:case`binary`:return M(this,t,n);case`base64`:return te(this,t,n);case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:return P(this,t,n);default:if(r)throw TypeError(`Unknown encoding: `+e);e=(e+``).toLowerCase(),r=!0}}s.prototype._isBuffer=!0;function b(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}s.prototype.swap16=function(){var e=this.length;if(e%2!=0)throw RangeError(`Buffer size must be a multiple of 16-bits`);for(var t=0;tn&&(t+=` ... `),``},r&&(s.prototype[r]=s.prototype.inspect),s.prototype.compare=function(e,t,n,r,i){if(W(e,Uint8Array)&&(e=s.from(e,e.offset,e.byteLength)),!s.isBuffer(e))throw TypeError(`The "target" argument must be one of type Buffer or Uint8Array. Received type `+typeof e);if(t===void 0&&(t=0),n===void 0&&(n=e?e.length:0),r===void 0&&(r=0),i===void 0&&(i=this.length),t<0||n>e.length||r<0||i>this.length)throw RangeError(`out of range index`);if(r>=i&&t>=n)return 0;if(r>=i)return-1;if(t>=n)return 1;if(t>>>=0,n>>>=0,r>>>=0,i>>>=0,this===e)return 0;for(var a=i-r,o=n-t,c=Math.min(a,o),l=this.slice(r,i),u=e.slice(t,n),d=0;d2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,G(n)&&(n=i?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(i)return-1;n=e.length-1}else if(n<0)if(i)n=0;else return-1;if(typeof t==`string`&&(t=s.from(t,r)),s.isBuffer(t))return t.length===0?-1:S(e,t,n,r,i);if(typeof t==`number`)return t&=255,typeof Uint8Array.prototype.indexOf==`function`?i?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):S(e,[t],n,r,i);throw TypeError(`val must be string, number or Buffer`)}function S(e,t,n,r,i){var a=1,o=e.length,s=t.length;if(r!==void 0&&(r=String(r).toLowerCase(),r===`ucs2`||r===`ucs-2`||r===`utf16le`||r===`utf-16le`)){if(e.length<2||t.length<2)return-1;a=2,o/=2,s/=2,n/=2}function c(e,t){return a===1?e[t]:e.readUInt16BE(t*a)}var l;if(i){var u=-1;for(l=n;lo&&(n=o-s),l=n;l>=0;l--){for(var d=!0,f=0;fi&&(r=i)):r=i;var a=t.length;r>a/2&&(r=a/2);for(var o=0;o>>=0,isFinite(n)?(n>>>=0,r===void 0&&(r=`utf8`)):(r=n,n=void 0);else throw Error(`Buffer.write(string, encoding, offset[, length]) is no longer supported`);var i=this.length-t;if((n===void 0||n>i)&&(n=i),e.length>0&&(n<0||t<0)||t>this.length)throw RangeError(`Attempt to write outside buffer bounds`);r||=`utf8`;for(var a=!1;;)switch(r){case`hex`:return C(this,e,t,n);case`utf8`:case`utf-8`:return w(this,e,t,n);case`ascii`:return T(this,e,t,n);case`latin1`:case`binary`:return E(this,e,t,n);case`base64`:return ee(this,e,t,n);case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:return D(this,e,t,n);default:if(a)throw TypeError(`Unknown encoding: `+r);r=(``+r).toLowerCase(),a=!0}},s.prototype.toJSON=function(){return{type:`Buffer`,data:Array.prototype.slice.call(this._arr||this,0)}};function te(e,n,r){return n===0&&r===e.length?t.fromByteArray(e):t.fromByteArray(e.slice(n,r))}function O(e,t,n){n=Math.min(e.length,n);for(var r=[],i=t;i239?4:a>223?3:a>191?2:1;if(i+s<=n){var c,l,u,d;switch(s){case 1:a<128&&(o=a);break;case 2:c=e[i+1],(c&192)==128&&(d=(a&31)<<6|c&63,d>127&&(o=d));break;case 3:c=e[i+1],l=e[i+2],(c&192)==128&&(l&192)==128&&(d=(a&15)<<12|(c&63)<<6|l&63,d>2047&&(d<55296||d>57343)&&(o=d));break;case 4:c=e[i+1],l=e[i+2],u=e[i+3],(c&192)==128&&(l&192)==128&&(u&192)==128&&(d=(a&15)<<18|(c&63)<<12|(l&63)<<6|u&63,d>65535&&d<1114112&&(o=d))}}o===null?(o=65533,s=1):o>65535&&(o-=65536,r.push(o>>>10&1023|55296),o=56320|o&1023),r.push(o),i+=s}return A(r)}var k=4096;function A(e){var t=e.length;if(t<=k)return String.fromCharCode.apply(String,e);for(var n=``,r=0;rr)&&(n=r);for(var i=``,a=t;an&&(e=n),t<0?(t+=n,t<0&&(t=0)):t>n&&(t=n),tn)throw RangeError(`Trying to access beyond buffer length`)}s.prototype.readUIntLE=function(e,t,n){e>>>=0,t>>>=0,n||F(e,t,this.length);for(var r=this[e],i=1,a=0;++a>>=0,t>>>=0,n||F(e,t,this.length);for(var r=this[e+--t],i=1;t>0&&(i*=256);)r+=this[e+--t]*i;return r},s.prototype.readUInt8=function(e,t){return e>>>=0,t||F(e,1,this.length),this[e]},s.prototype.readUInt16LE=function(e,t){return e>>>=0,t||F(e,2,this.length),this[e]|this[e+1]<<8},s.prototype.readUInt16BE=function(e,t){return e>>>=0,t||F(e,2,this.length),this[e]<<8|this[e+1]},s.prototype.readUInt32LE=function(e,t){return e>>>=0,t||F(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+this[e+3]*16777216},s.prototype.readUInt32BE=function(e,t){return e>>>=0,t||F(e,4,this.length),this[e]*16777216+(this[e+1]<<16|this[e+2]<<8|this[e+3])},s.prototype.readIntLE=function(e,t,n){e>>>=0,t>>>=0,n||F(e,t,this.length);for(var r=this[e],i=1,a=0;++a=i&&(r-=2**(8*t)),r},s.prototype.readIntBE=function(e,t,n){e>>>=0,t>>>=0,n||F(e,t,this.length);for(var r=t,i=1,a=this[e+--r];r>0&&(i*=256);)a+=this[e+--r]*i;return i*=128,a>=i&&(a-=2**(8*t)),a},s.prototype.readInt8=function(e,t){return e>>>=0,t||F(e,1,this.length),this[e]&128?(255-this[e]+1)*-1:this[e]},s.prototype.readInt16LE=function(e,t){e>>>=0,t||F(e,2,this.length);var n=this[e]|this[e+1]<<8;return n&32768?n|4294901760:n},s.prototype.readInt16BE=function(e,t){e>>>=0,t||F(e,2,this.length);var n=this[e+1]|this[e]<<8;return n&32768?n|4294901760:n},s.prototype.readInt32LE=function(e,t){return e>>>=0,t||F(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},s.prototype.readInt32BE=function(e,t){return e>>>=0,t||F(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},s.prototype.readFloatLE=function(e,t){return e>>>=0,t||F(e,4,this.length),n.read(this,e,!0,23,4)},s.prototype.readFloatBE=function(e,t){return e>>>=0,t||F(e,4,this.length),n.read(this,e,!1,23,4)},s.prototype.readDoubleLE=function(e,t){return e>>>=0,t||F(e,8,this.length),n.read(this,e,!0,52,8)},s.prototype.readDoubleBE=function(e,t){return e>>>=0,t||F(e,8,this.length),n.read(this,e,!1,52,8)};function I(e,t,n,r,i,a){if(!s.isBuffer(e))throw TypeError(`"buffer" argument must be a Buffer instance`);if(t>i||te.length)throw RangeError(`Index out of range`)}s.prototype.writeUIntLE=function(e,t,n,r){if(e=+e,t>>>=0,n>>>=0,!r){var i=2**(8*n)-1;I(this,e,t,n,i,0)}var a=1,o=0;for(this[t]=e&255;++o>>=0,n>>>=0,!r){var i=2**(8*n)-1;I(this,e,t,n,i,0)}var a=n-1,o=1;for(this[t+a]=e&255;--a>=0&&(o*=256);)this[t+a]=e/o&255;return t+n},s.prototype.writeUInt8=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,1,255,0),this[t]=e&255,t+1},s.prototype.writeUInt16LE=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,2,65535,0),this[t]=e&255,this[t+1]=e>>>8,t+2},s.prototype.writeUInt16BE=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=e&255,t+2},s.prototype.writeUInt32LE=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=e&255,t+4},s.prototype.writeUInt32BE=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e&255,t+4},s.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t>>>=0,!r){var i=2**(8*n-1);I(this,e,t,n,i-1,-i)}var a=0,o=1,s=0;for(this[t]=e&255;++a>0)-s&255;return t+n},s.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t>>>=0,!r){var i=2**(8*n-1);I(this,e,t,n,i-1,-i)}var a=n-1,o=1,s=0;for(this[t+a]=e&255;--a>=0&&(o*=256);)e<0&&s===0&&this[t+a+1]!==0&&(s=1),this[t+a]=(e/o>>0)-s&255;return t+n},s.prototype.writeInt8=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=e&255,t+1},s.prototype.writeInt16LE=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,2,32767,-32768),this[t]=e&255,this[t+1]=e>>>8,t+2},s.prototype.writeInt16BE=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=e&255,t+2},s.prototype.writeInt32LE=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,4,2147483647,-2147483648),this[t]=e&255,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},s.prototype.writeInt32BE=function(e,t,n){return e=+e,t>>>=0,n||I(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e&255,t+4};function L(e,t,n,r,i,a){if(n+r>e.length||n<0)throw RangeError(`Index out of range`)}function R(e,t,r,i,a){return t=+t,r>>>=0,a||L(e,t,r,4,34028234663852886e22,-34028234663852886e22),n.write(e,t,r,i,23,4),r+4}s.prototype.writeFloatLE=function(e,t,n){return R(this,e,t,!0,n)},s.prototype.writeFloatBE=function(e,t,n){return R(this,e,t,!1,n)};function z(e,t,r,i,a){return t=+t,r>>>=0,a||L(e,t,r,8,17976931348623157e292,-17976931348623157e292),n.write(e,t,r,i,52,8),r+8}s.prototype.writeDoubleLE=function(e,t,n){return z(this,e,t,!0,n)},s.prototype.writeDoubleBE=function(e,t,n){return z(this,e,t,!1,n)},s.prototype.copy=function(e,t,n,r){if(!s.isBuffer(e))throw TypeError(`argument should be a Buffer`);if(n||=0,!r&&r!==0&&(r=this.length),t>=e.length&&(t=e.length),t||=0,r>0&&r=this.length)throw RangeError(`Index out of range`);if(r<0)throw RangeError(`sourceEnd out of bounds`);r>this.length&&(r=this.length),e.length-t=0;--a)e[a+t]=this[a+n];else Uint8Array.prototype.set.call(e,this.subarray(n,r),t);return i},s.prototype.fill=function(e,t,n,r){if(typeof e==`string`){if(typeof t==`string`?(r=t,t=0,n=this.length):typeof n==`string`&&(r=n,n=this.length),r!==void 0&&typeof r!=`string`)throw TypeError(`encoding must be a string`);if(typeof r==`string`&&!s.isEncoding(r))throw TypeError(`Unknown encoding: `+r);if(e.length===1){var i=e.charCodeAt(0);(r===`utf8`&&i<128||r===`latin1`)&&(e=i)}}else typeof e==`number`?e&=255:typeof e==`boolean`&&(e=Number(e));if(t<0||this.length>>=0,n=n===void 0?this.length:n>>>0,e||=0;var a;if(typeof e==`number`)for(a=t;a55295&&n<57344){if(!i){if(n>56319){(t-=3)>-1&&a.push(239,191,189);continue}else if(o+1===r){(t-=3)>-1&&a.push(239,191,189);continue}i=n;continue}if(n<56320){(t-=3)>-1&&a.push(239,191,189),i=n;continue}n=(i-55296<<10|n-56320)+65536}else i&&(t-=3)>-1&&a.push(239,191,189);if(i=null,n<128){if(--t<0)break;a.push(n)}else if(n<2048){if((t-=2)<0)break;a.push(n>>6|192,n&63|128)}else if(n<65536){if((t-=3)<0)break;a.push(n>>12|224,n>>6&63|128,n&63|128)}else if(n<1114112){if((t-=4)<0)break;a.push(n>>18|240,n>>12&63|128,n>>6&63|128,n&63|128)}else throw Error(`Invalid code point`)}return a}function re(e){for(var t=[],n=0;n>8,i=n%256,a.push(i),a.push(r);return a}function U(e){return t.toByteArray(B(e))}function ie(e,t,n,r){for(var i=0;i=t.length||i>=e.length);++i)t[i+n]=e[i];return i}function W(e,t){return e instanceof t||e!=null&&e.constructor!=null&&e.constructor.name!=null&&e.constructor.name===t.name}function G(e){return e!==e}var ae=(function(){for(var e=`0123456789abcdef`,t=Array(256),n=0;n<16;++n)for(var r=n*16,i=0;i<16;++i)t[r+i]=e[n]+e[i];return t})()})),ct=s(),lt=at(),Y=J(),ut={lstatSync:()=>{},isFileReadStream(e){return!1},runtime:`browser`},dt=e=>{if(e==null)return 0;if(typeof e==`string`)return Y.Buffer.byteLength(e);if(typeof e.byteLength==`number`)return e.byteLength;if(typeof e.length==`number`)return e.length;if(typeof e.size==`number`)return e.size;if(typeof e.start==`number`&&typeof e.end==`number`)return e.end+1-e.start;if(ut.isFileReadStream(e))try{return ut.lstatSync(e.path).size}catch{return}},X;(function(e){e.EMPTY_INPUT=`a null or undefined Body`,e.CONTENT_LENGTH=`the ContentLength property of the params set by the caller`,e.STRING_LENGTH=`the encoded byte length of the Body string`,e.TYPED_ARRAY=`the byteLength of a typed byte array such as Uint8Array`,e.LENGTH=`the value of Body.length`,e.SIZE=`the value of Body.size`,e.START_END_DIFF=`the numeric difference between Body.start and Body.end`,e.LSTAT=`the size of the file given by Body.path on disk as reported by lstatSync`})(X||={});var ft=(e,t)=>{if(t!=null)return X.CONTENT_LENGTH;if(e==null)return X.EMPTY_INPUT;if(typeof e==`string`)return X.STRING_LENGTH;if(typeof e.byteLength==`number`)return X.TYPED_ARRAY;if(typeof e.length==`number`)return X.LENGTH;if(typeof e.size==`number`)return X.SIZE;if(typeof e.start==`number`&&typeof e.end==`number`)return X.START_END_DIFF;if(ut.isFileReadStream(e))try{return ut.lstatSync(e.path).size,X.LSTAT}catch{return}},Z=A(((e,t)=>{typeof Object.create==`function`?t.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:t.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}})),pt=A(((e,t)=>{t.exports=at().EventEmitter})),mt=A(((e,t)=>{t.exports={}})),ht=A(((e,t)=>{function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function r(e){for(var t=1;t0?this.tail.next=t:this.head=t,this.tail=t,++this.length}},{key:`unshift`,value:function(e){var t={data:e,next:this.head};this.length===0&&(this.tail=t),this.head=t,++this.length}},{key:`shift`,value:function(){if(this.length!==0){var e=this.head.data;return this.length===1?this.head=this.tail=null:this.head=this.head.next,--this.length,e}}},{key:`clear`,value:function(){this.head=this.tail=null,this.length=0}},{key:`join`,value:function(e){if(this.length===0)return``;for(var t=this.head,n=``+t.data;t=t.next;)n+=e+t.data;return n}},{key:`concat`,value:function(e){if(this.length===0)return u.alloc(0);for(var t=u.allocUnsafe(e>>>0),n=this.head,r=0;n;)p(n.data,t,r),r+=n.data.length,n=n.next;return t}},{key:`consume`,value:function(e,t){var n;return ei.length?i.length:e;if(a===i.length?r+=i:r+=i.slice(0,e),e-=a,e===0){a===i.length?(++n,t.next?this.head=t.next:this.head=this.tail=null):(this.head=t,t.data=i.slice(a));break}++n}return this.length-=n,r}},{key:`_getBuffer`,value:function(e){var t=u.allocUnsafe(e),n=this.head,r=1;for(n.data.copy(t),e-=n.data.length;n=n.next;){var i=n.data,a=e>i.length?i.length:e;if(i.copy(t,t.length-e,0,a),e-=a,e===0){a===i.length?(++r,n.next?this.head=n.next:this.head=this.tail=null):(this.head=n,n.data=i.slice(a));break}++r}return this.length-=r,t}},{key:f,value:function(e,t){return d(this,r(r({},t),{},{depth:0,customInspect:!1}))}}]),e}()})),gt=A(((e,t)=>{function n(e,t){var n=this,a=this._readableState&&this._readableState.destroyed,s=this._writableState&&this._writableState.destroyed;return a||s?(t?t(e):e&&(this._writableState?this._writableState.errorEmitted||(this._writableState.errorEmitted=!0,process.nextTick(o,this,e)):process.nextTick(o,this,e)),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,function(e){!t&&e?n._writableState?n._writableState.errorEmitted?process.nextTick(i,n):(n._writableState.errorEmitted=!0,process.nextTick(r,n,e)):process.nextTick(r,n,e):t?(process.nextTick(i,n),t(e)):process.nextTick(i,n)}),this)}function r(e,t){o(e,t),i(e)}function i(e){e._writableState&&!e._writableState.emitClose||e._readableState&&!e._readableState.emitClose||e.emit(`close`)}function a(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finalCalled=!1,this._writableState.prefinished=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)}function o(e,t){e.emit(`error`,t)}function s(e,t){var n=e._readableState,r=e._writableState;n&&n.autoDestroy||r&&r.autoDestroy?e.destroy(t):e.emit(`error`,t)}t.exports={destroy:n,undestroy:a,errorOrDestroy:s}})),Q=A(((e,t)=>{function n(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}var r={};function i(e,t,i){i||=Error;function a(e,n,r){return typeof t==`string`?t:t(e,n,r)}var o=function(e){n(t,e);function t(t,n,r){return e.call(this,a(t,n,r))||this}return t}(i);o.prototype.name=i.name,o.prototype.code=e,r[e]=o}function a(e,t){if(Array.isArray(e)){var n=e.length;return e=e.map(function(e){return String(e)}),n>2?`one of ${t} ${e.slice(0,n-1).join(`, `)}, or `+e[n-1]:n===2?`one of ${t} ${e[0]} or ${e[1]}`:`of ${t} ${e[0]}`}else return`of ${t} ${String(e)}`}function o(e,t,n){return e.substr(!n||n<0?0:+n,t.length)===t}function s(e,t,n){return(n===void 0||n>e.length)&&(n=e.length),e.substring(n-t.length,n)===t}function c(e,t,n){return typeof n!=`number`&&(n=0),n+t.length>e.length?!1:e.indexOf(t,n)!==-1}i(`ERR_INVALID_OPT_VALUE`,function(e,t){return`The value "`+t+`" is invalid for option "`+e+`"`},TypeError),i(`ERR_INVALID_ARG_TYPE`,function(e,t,n){var r;typeof t==`string`&&o(t,`not `)?(r=`must not be`,t=t.replace(/^not /,``)):r=`must be`;var i=s(e,` argument`)?`The ${e} ${r} ${a(t,`type`)}`:`The "${e}" ${c(e,`.`)?`property`:`argument`} ${r} ${a(t,`type`)}`;return i+=`. Received type ${typeof n}`,i},TypeError),i(`ERR_STREAM_PUSH_AFTER_EOF`,`stream.push() after EOF`),i(`ERR_METHOD_NOT_IMPLEMENTED`,function(e){return`The `+e+` method is not implemented`}),i(`ERR_STREAM_PREMATURE_CLOSE`,`Premature close`),i(`ERR_STREAM_DESTROYED`,function(e){return`Cannot call `+e+` after a stream was destroyed`}),i(`ERR_MULTIPLE_CALLBACK`,`Callback called multiple times`),i(`ERR_STREAM_CANNOT_PIPE`,`Cannot pipe, not readable`),i(`ERR_STREAM_WRITE_AFTER_END`,`write after end`),i(`ERR_STREAM_NULL_VALUES`,`May not write null values to stream`,TypeError),i(`ERR_UNKNOWN_ENCODING`,function(e){return`Unknown encoding: `+e},TypeError),i(`ERR_STREAM_UNSHIFT_AFTER_END_EVENT`,`stream.unshift() after end event`),t.exports.codes=r})),_t=A(((e,t)=>{var n=Q().codes.ERR_INVALID_OPT_VALUE;function r(e,t,n){return e.highWaterMark==null?t?e[n]:null:e.highWaterMark}function i(e,t,i,a){var o=r(t,a,i);if(o!=null){if(!(isFinite(o)&&Math.floor(o)===o)||o<0)throw new n(a?i:`highWaterMark`,o);return Math.floor(o)}return e.objectMode?16:16*1024}t.exports={getHighWaterMark:i}})),vt=A(((e,t)=>{t.exports=n;function n(e,t){if(r(`noDeprecation`))return e;var n=!1;function i(){if(!n){if(r(`throwDeprecation`))throw Error(t);r(`traceDeprecation`)?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}return i}function r(e){try{if(!global.localStorage)return!1}catch{return!1}var t=global.localStorage[e];return t==null?!1:String(t).toLowerCase()===`true`}})),yt=A(((e,t)=>{t.exports=T;function n(e){var t=this;this.next=null,this.entry=null,this.finish=function(){ne(t,e)}}var r;T.WritableState=C;var i={deprecate:vt()},a=pt(),o=J().Buffer,s=(typeof global<`u`?global:typeof window<`u`?window:typeof self<`u`?self:{}).Uint8Array||function(){};function c(e){return o.from(e)}function l(e){return o.isBuffer(e)||e instanceof s}var u=gt(),d=_t().getHighWaterMark,f=Q().codes,p=f.ERR_INVALID_ARG_TYPE,m=f.ERR_METHOD_NOT_IMPLEMENTED,h=f.ERR_MULTIPLE_CALLBACK,g=f.ERR_STREAM_CANNOT_PIPE,_=f.ERR_STREAM_DESTROYED,v=f.ERR_STREAM_NULL_VALUES,y=f.ERR_STREAM_WRITE_AFTER_END,b=f.ERR_UNKNOWN_ENCODING,x=u.errorOrDestroy;Z()(T,a);function S(){}function C(e,t,i){r||=$(),e||={},typeof i!=`boolean`&&(i=t instanceof r),this.objectMode=!!e.objectMode,i&&(this.objectMode=this.objectMode||!!e.writableObjectMode),this.highWaterMark=d(this,e,`writableHighWaterMark`,i),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var a=e.decodeStrings===!1;this.decodeStrings=!a,this.defaultEncoding=e.defaultEncoding||`utf8`,this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){j(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=e.emitClose!==!1,this.autoDestroy=!!e.autoDestroy,this.bufferedRequestCount=0,this.corkedRequestsFree=new n(this)}C.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},(function(){try{Object.defineProperty(C.prototype,`buffer`,{get:i.deprecate(function(){return this.getBuffer()},`_writableState.buffer is deprecated. Use _writableState.getBuffer instead.`,`DEP0003`)})}catch{}})();var w;typeof Symbol==`function`&&Symbol.hasInstance&&typeof Function.prototype[Symbol.hasInstance]==`function`?(w=Function.prototype[Symbol.hasInstance],Object.defineProperty(T,Symbol.hasInstance,{value:function(e){return w.call(this,e)?!0:this===T?e&&e._writableState instanceof C:!1}})):w=function(e){return e instanceof this};function T(e){r||=$();var t=this instanceof r;if(!t&&!w.call(T,this))return new T(e);this._writableState=new C(e,this,t),this.writable=!0,e&&(typeof e.write==`function`&&(this._write=e.write),typeof e.writev==`function`&&(this._writev=e.writev),typeof e.destroy==`function`&&(this._destroy=e.destroy),typeof e.final==`function`&&(this._final=e.final)),a.call(this)}T.prototype.pipe=function(){x(this,new g)};function E(e,t){var n=new y;x(e,n),process.nextTick(t,n)}function ee(e,t,n,r){var i;return n===null?i=new v:typeof n!=`string`&&!t.objectMode&&(i=new p(`chunk`,[`string`,`Buffer`],n)),i?(x(e,i),process.nextTick(r,i),!1):!0}T.prototype.write=function(e,t,n){var r=this._writableState,i=!1,a=!r.objectMode&&l(e);return a&&!o.isBuffer(e)&&(e=c(e)),typeof t==`function`&&(n=t,t=null),a?t=`buffer`:t||=r.defaultEncoding,typeof n!=`function`&&(n=S),r.ending?E(this,n):(a||ee(this,r,e,n))&&(r.pendingcb++,i=te(this,r,a,e,t,n)),i},T.prototype.cork=function(){this._writableState.corked++},T.prototype.uncork=function(){var e=this._writableState;e.corked&&(e.corked--,!e.writing&&!e.corked&&!e.bufferProcessing&&e.bufferedRequest&&P(this,e))},T.prototype.setDefaultEncoding=function(e){if(typeof e==`string`&&(e=e.toLowerCase()),!([`hex`,`utf8`,`utf-8`,`ascii`,`binary`,`base64`,`ucs2`,`ucs-2`,`utf16le`,`utf-16le`,`raw`].indexOf((e+``).toLowerCase())>-1))throw new b(e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(T.prototype,`writableBuffer`,{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}});function D(e,t,n){return!e.objectMode&&e.decodeStrings!==!1&&typeof t==`string`&&(t=o.from(t,n)),t}Object.defineProperty(T.prototype,`writableHighWaterMark`,{enumerable:!1,get:function(){return this._writableState.highWaterMark}});function te(e,t,n,r,i,a){if(!n){var o=D(t,r,i);r!==o&&(n=!0,i=`buffer`,r=o)}var s=t.objectMode?1:r.length;t.length+=s;var c=t.length{var n=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};t.exports=c;var r=Tt(),i=yt();Z()(c,r);for(var a=n(i.prototype),o=0;o{var n=J(),r=n.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}r.from&&r.alloc&&r.allocUnsafe&&r.allocUnsafeSlow?t.exports=n:(i(n,e),e.Buffer=a);function a(e,t,n){return r(e,t,n)}a.prototype=Object.create(r.prototype),i(r,a),a.from=function(e,t,n){if(typeof e==`number`)throw TypeError(`Argument must not be a number`);return r(e,t,n)},a.alloc=function(e,t,n){if(typeof e!=`number`)throw TypeError(`Argument must be a number`);var i=r(e);return t===void 0?i.fill(0):typeof n==`string`?i.fill(t,n):i.fill(t),i},a.allocUnsafe=function(e){if(typeof e!=`number`)throw TypeError(`Argument must be a number`);return r(e)},a.allocUnsafeSlow=function(e){if(typeof e!=`number`)throw TypeError(`Argument must be a number`);return n.SlowBuffer(e)}})),xt=A((e=>{var t=bt().Buffer,n=t.isEncoding||function(e){switch(e=``+e,e&&e.toLowerCase()){case`hex`:case`utf8`:case`utf-8`:case`ascii`:case`binary`:case`base64`:case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:case`raw`:return!0;default:return!1}};function r(e){if(!e)return`utf8`;for(var t;;)switch(e){case`utf8`:case`utf-8`:return`utf8`;case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:return`utf16le`;case`latin1`:case`binary`:return`latin1`;case`base64`:case`ascii`:case`hex`:return e;default:if(t)return;e=(``+e).toLowerCase(),t=!0}}function i(e){var i=r(e);if(typeof i!=`string`&&(t.isEncoding===n||!n(e)))throw Error(`Unknown encoding: `+e);return i||e}e.StringDecoder=a;function a(e){this.encoding=i(e);var n;switch(this.encoding){case`utf16le`:this.text=f,this.end=p,n=4;break;case`utf8`:this.fillLast=l,n=4;break;case`base64`:this.text=m,this.end=h,n=3;break;default:this.write=g,this.end=_;return}this.lastNeed=0,this.lastTotal=0,this.lastChar=t.allocUnsafe(n)}a.prototype.write=function(e){if(e.length===0)return``;var t,n;if(this.lastNeed){if(t=this.fillLast(e),t===void 0)return``;n=this.lastNeed,this.lastNeed=0}else n=0;return n>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function s(e,t,n){var r=t.length-1;if(r=0?(i>0&&(e.lastNeed=i-1),i):--r=0?(i>0&&(e.lastNeed=i-2),i):--r=0?(i>0&&(i===2?i=0:e.lastNeed=i-3),i):0))}function c(e,t,n){if((t[0]&192)!=128)return e.lastNeed=0,`�`;if(e.lastNeed>1&&t.length>1){if((t[1]&192)!=128)return e.lastNeed=1,`�`;if(e.lastNeed>2&&t.length>2&&(t[2]&192)!=128)return e.lastNeed=2,`�`}}function l(e){var t=this.lastTotal-this.lastNeed,n=c(this,e,t);if(n!==void 0)return n;if(this.lastNeed<=e.length)return e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,t,0,e.length),this.lastNeed-=e.length}function u(e,t){var n=s(this,e,t);if(!this.lastNeed)return e.toString(`utf8`,t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString(`utf8`,t,r)}function d(e){var t=e&&e.length?this.write(e):``;return this.lastNeed?t+`�`:t}function f(e,t){if((e.length-t)%2==0){var n=e.toString(`utf16le`,t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString(`utf16le`,t,e.length-1)}function p(e){var t=e&&e.length?this.write(e):``;if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString(`utf16le`,0,n)}return t}function m(e,t){var n=(e.length-t)%3;return n===0?e.toString(`base64`,t):(this.lastNeed=3-n,this.lastTotal=3,n===1?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString(`base64`,t,e.length-n))}function h(e){var t=e&&e.length?this.write(e):``;return this.lastNeed?t+this.lastChar.toString(`base64`,0,3-this.lastNeed):t}function g(e){return e.toString(this.encoding)}function _(e){return e&&e.length?this.write(e):``}})),St=A(((e,t)=>{var n=Q().codes.ERR_STREAM_PREMATURE_CLOSE;function r(e){var t=!1;return function(){if(!t){t=!0;var n=[...arguments];e.apply(this,n)}}}function i(){}function a(e){return e.setHeader&&typeof e.abort==`function`}function o(e,t,s){if(typeof t==`function`)return o(e,null,t);t||={},s=r(s||i);var c=t.readable||t.readable!==!1&&e.readable,l=t.writable||t.writable!==!1&&e.writable,u=function(){e.writable||f()},d=e._writableState&&e._writableState.finished,f=function(){l=!1,d=!0,c||s.call(e)},p=e._readableState&&e._readableState.endEmitted,m=function(){c=!1,p=!0,l||s.call(e)},h=function(t){s.call(e,t)},g=function(){var t;if(c&&!p)return(!e._readableState||!e._readableState.ended)&&(t=new n),s.call(e,t);if(l&&!d)return(!e._writableState||!e._writableState.ended)&&(t=new n),s.call(e,t)},_=function(){e.req.on(`finish`,f)};return a(e)?(e.on(`complete`,f),e.on(`abort`,g),e.req?_():e.on(`request`,_)):l&&!e._writableState&&(e.on(`end`,u),e.on(`close`,u)),e.on(`end`,m),e.on(`finish`,f),t.error!==!1&&e.on(`error`,h),e.on(`close`,g),function(){e.removeListener(`complete`,f),e.removeListener(`abort`,g),e.removeListener(`request`,_),e.req&&e.req.removeListener(`finish`,f),e.removeListener(`end`,u),e.removeListener(`close`,u),e.removeListener(`finish`,f),e.removeListener(`end`,m),e.removeListener(`error`,h),e.removeListener(`close`,g)}}t.exports=o})),Ct=A(((e,t)=>{var n;function r(e,t,n){return t=i(t),t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e){var t=a(e,`string`);return typeof t==`symbol`?t:String(t)}function a(e,t){if(typeof e!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(typeof r!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}var o=St(),s=Symbol(`lastResolve`),c=Symbol(`lastReject`),l=Symbol(`error`),u=Symbol(`ended`),d=Symbol(`lastPromise`),f=Symbol(`handlePromise`),p=Symbol(`stream`);function m(e,t){return{value:e,done:t}}function h(e){var t=e[s];if(t!==null){var n=e[p].read();n!==null&&(e[d]=null,e[s]=null,e[c]=null,t(m(n,!1)))}}function g(e){process.nextTick(h,e)}function _(e,t){return function(n,r){e.then(function(){if(t[u]){n(m(void 0,!0));return}t[f](n,r)},r)}}var v=Object.getPrototypeOf(function(){}),y=Object.setPrototypeOf((n={get stream(){return this[p]},next:function(){var e=this,t=this[l];if(t!==null)return Promise.reject(t);if(this[u])return Promise.resolve(m(void 0,!0));if(this[p].destroyed)return new Promise(function(t,n){process.nextTick(function(){e[l]?n(e[l]):t(m(void 0,!0))})});var n=this[d],r;if(n)r=new Promise(_(n,this));else{var i=this[p].read();if(i!==null)return Promise.resolve(m(i,!1));r=new Promise(this[f])}return this[d]=r,r}},r(n,Symbol.asyncIterator,function(){return this}),r(n,`return`,function(){var e=this;return new Promise(function(t,n){e[p].destroy(null,function(e){if(e){n(e);return}t(m(void 0,!0))})})}),n),v);t.exports=function(e){var t,n=Object.create(y,(t={},r(t,p,{value:e,writable:!0}),r(t,s,{value:null,writable:!0}),r(t,c,{value:null,writable:!0}),r(t,l,{value:null,writable:!0}),r(t,u,{value:e._readableState.endEmitted,writable:!0}),r(t,f,{value:function(e,t){var r=n[p].read();r?(n[d]=null,n[s]=null,n[c]=null,e(m(r,!1))):(n[s]=e,n[c]=t)},writable:!0}),t));return n[d]=null,o(e,function(e){if(e&&e.code!==`ERR_STREAM_PREMATURE_CLOSE`){var t=n[c];t!==null&&(n[d]=null,n[s]=null,n[c]=null,t(e)),n[l]=e;return}var r=n[s];r!==null&&(n[d]=null,n[s]=null,n[c]=null,r(m(void 0,!0))),n[u]=!0}),e.on(`readable`,g.bind(null,n)),n}})),wt=A(((e,t)=>{t.exports=function(){throw Error(`Readable.from is not available in the browser`)}})),Tt=A(((e,t)=>{t.exports=E;var n;E.ReadableState=T,at().EventEmitter;var r=function(e,t){return e.listeners(t).length},i=pt(),a=J().Buffer,o=(typeof global<`u`?global:typeof window<`u`?window:typeof self<`u`?self:{}).Uint8Array||function(){};function s(e){return a.from(e)}function c(e){return a.isBuffer(e)||e instanceof o}var l=mt(),u=l&&l.debuglog?l.debuglog(`stream`):function(){},d=ht(),f=gt(),p=_t().getHighWaterMark,m=Q().codes,h=m.ERR_INVALID_ARG_TYPE,g=m.ERR_STREAM_PUSH_AFTER_EOF,_=m.ERR_METHOD_NOT_IMPLEMENTED,v=m.ERR_STREAM_UNSHIFT_AFTER_END_EVENT,y,b,x;Z()(E,i);var S=f.errorOrDestroy,C=[`error`,`close`,`destroy`,`pause`,`resume`];function w(e,t,n){if(typeof e.prependListener==`function`)return e.prependListener(t,n);!e._events||!e._events[t]?e.on(t,n):Array.isArray(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]}function T(e,t,r){n||=$(),e||={},typeof r!=`boolean`&&(r=t instanceof n),this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.readableObjectMode),this.highWaterMark=p(this,e,`readableHighWaterMark`,r),this.buffer=new d,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.paused=!0,this.emitClose=e.emitClose!==!1,this.autoDestroy=!!e.autoDestroy,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||`utf8`,this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(y||=xt().StringDecoder,this.decoder=new y(e.encoding),this.encoding=e.encoding)}function E(e){if(n||=$(),!(this instanceof E))return new E(e);var t=this instanceof n;this._readableState=new T(e,this,t),this.readable=!0,e&&(typeof e.read==`function`&&(this._read=e.read),typeof e.destroy==`function`&&(this._destroy=e.destroy)),i.call(this)}Object.defineProperty(E.prototype,`destroyed`,{enumerable:!1,get:function(){return this._readableState===void 0?!1:this._readableState.destroyed},set:function(e){this._readableState&&(this._readableState.destroyed=e)}}),E.prototype.destroy=f.destroy,E.prototype._undestroy=f.undestroy,E.prototype._destroy=function(e,t){t(e)},E.prototype.push=function(e,t){var n=this._readableState,r;return n.objectMode?r=!0:typeof e==`string`&&(t||=n.defaultEncoding,t!==n.encoding&&(e=a.from(e,t),t=``),r=!0),ee(this,e,t,!1,r)},E.prototype.unshift=function(e){return ee(this,e,null,!0,!1)};function ee(e,t,n,r,i){u(`readableAddChunk`,t);var o=e._readableState;if(t===null)o.reading=!1,j(e,o);else{var c;if(i||(c=te(o,t)),c)S(e,c);else if(o.objectMode||t&&t.length>0)if(typeof t!=`string`&&!o.objectMode&&Object.getPrototypeOf(t)!==a.prototype&&(t=s(t)),r)o.endEmitted?S(e,new v):D(e,o,t,!0);else if(o.ended)S(e,new g);else if(o.destroyed)return!1;else o.reading=!1,o.decoder&&!n?(t=o.decoder.write(t),o.objectMode||t.length!==0?D(e,o,t,!1):P(e,o)):D(e,o,t,!1);else r||(o.reading=!1,P(e,o))}return!o.ended&&(o.length=O?e=O:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}function A(e,t){return e<=0||t.length===0&&t.ended?0:t.objectMode?1:e===e?(e>t.highWaterMark&&(t.highWaterMark=k(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0)):t.flowing&&t.length?t.buffer.head.data.length:t.length}E.prototype.read=function(e){u(`read`,e),e=parseInt(e,10);var t=this._readableState,n=e;if(e!==0&&(t.emittedReadable=!1),e===0&&t.needReadable&&((t.highWaterMark===0?t.length>0:t.length>=t.highWaterMark)||t.ended))return u(`read: emitReadable`,t.length,t.ended),t.length===0&&t.ended?re(this):M(this),null;if(e=A(e,t),e===0&&t.ended)return t.length===0&&re(this),null;var r=t.needReadable;u(`need readable`,r),(t.length===0||t.length-e0?V(e,t):null;return i===null?(t.needReadable=t.length<=t.highWaterMark,e=0):(t.length-=e,t.awaitDrain=0),t.length===0&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&re(this)),i!==null&&this.emit(`data`,i),i};function j(e,t){if(u(`onEofChunk`),!t.ended){if(t.decoder){var n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,t.sync?M(e):(t.needReadable=!1,t.emittedReadable||(t.emittedReadable=!0,N(e)))}}function M(e){var t=e._readableState;u(`emitReadable`,t.needReadable,t.emittedReadable),t.needReadable=!1,t.emittedReadable||(u(`emitReadable`,t.flowing),t.emittedReadable=!0,process.nextTick(N,e))}function N(e){var t=e._readableState;u(`emitReadable_`,t.destroyed,t.length,t.ended),!t.destroyed&&(t.length||t.ended)&&(e.emit(`readable`),t.emittedReadable=!1),t.needReadable=!t.flowing&&!t.ended&&t.length<=t.highWaterMark,B(e)}function P(e,t){t.readingMore||(t.readingMore=!0,process.nextTick(F,e,t))}function F(e,t){for(;!t.reading&&!t.ended&&(t.length1&&U(i.pipes,e)!==-1)&&!l&&(u(`false write response, pause`,i.awaitDrain),i.awaitDrain++),n.pause())}function p(t){u(`onerror`,t),g(),e.removeListener(`error`,p),r(e,`error`)===0&&S(e,t)}w(e,`error`,p);function m(){e.removeListener(`finish`,h),g()}e.once(`close`,m);function h(){u(`onfinish`),e.removeListener(`close`,m),g()}e.once(`finish`,h);function g(){u(`unpipe`),n.unpipe(e)}return e.emit(`pipe`,n),i.flowing||(u(`pipe resume`),n.resume()),e};function I(e){return function(){var t=e._readableState;u(`pipeOnDrain`,t.awaitDrain),t.awaitDrain&&t.awaitDrain--,t.awaitDrain===0&&r(e,`data`)&&(t.flowing=!0,B(e))}}E.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(t.pipesCount===0)return this;if(t.pipesCount===1)return e&&e!==t.pipes?this:(e||=t.pipes,t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit(`unpipe`,this,n),this);if(!e){var r=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var a=0;a0,r.flowing!==!1&&this.resume()):e===`readable`&&!r.endEmitted&&!r.readableListening&&(r.readableListening=r.needReadable=!0,r.flowing=!1,r.emittedReadable=!1,u(`on readable`,r.length,r.reading),r.length?M(this):r.reading||process.nextTick(R,this)),n},E.prototype.addListener=E.prototype.on,E.prototype.removeListener=function(e,t){var n=i.prototype.removeListener.call(this,e,t);return e===`readable`&&process.nextTick(L,this),n},E.prototype.removeAllListeners=function(e){var t=i.prototype.removeAllListeners.apply(this,arguments);return(e===`readable`||e===void 0)&&process.nextTick(L,this),t};function L(e){var t=e._readableState;t.readableListening=e.listenerCount(`readable`)>0,t.resumeScheduled&&!t.paused?t.flowing=!0:e.listenerCount(`data`)>0&&e.resume()}function R(e){u(`readable nexttick read 0`),e.read(0)}E.prototype.resume=function(){var e=this._readableState;return e.flowing||(u(`resume`),e.flowing=!e.readableListening,z(this,e)),e.paused=!1,this};function z(e,t){t.resumeScheduled||(t.resumeScheduled=!0,process.nextTick(ne,e,t))}function ne(e,t){u(`resume`,t.reading),t.reading||e.read(0),t.resumeScheduled=!1,e.emit(`resume`),B(e),t.flowing&&!t.reading&&e.read(0)}E.prototype.pause=function(){return u(`call pause flowing=%j`,this._readableState.flowing),this._readableState.flowing!==!1&&(u(`pause`),this._readableState.flowing=!1,this.emit(`pause`)),this._readableState.paused=!0,this};function B(e){var t=e._readableState;for(u(`flow`,t.flowing);t.flowing&&e.read()!==null;);}E.prototype.wrap=function(e){var t=this,n=this._readableState,r=!1;for(var i in e.on(`end`,function(){if(u(`wrapped end`),n.decoder&&!n.ended){var e=n.decoder.end();e&&e.length&&t.push(e)}t.push(null)}),e.on(`data`,function(i){u(`wrapped data`),n.decoder&&(i=n.decoder.write(i)),!(n.objectMode&&i==null)&&(!n.objectMode&&(!i||!i.length)||t.push(i)||(r=!0,e.pause()))}),e)this[i]===void 0&&typeof e[i]==`function`&&(this[i]=function(t){return function(){return e[t].apply(e,arguments)}}(i));for(var a=0;a=t.length?(n=t.decoder?t.buffer.join(``):t.buffer.length===1?t.buffer.first():t.buffer.concat(t.length),t.buffer.clear()):n=t.buffer.consume(e,t.decoder),n}function re(e){var t=e._readableState;u(`endReadable`,t.endEmitted),t.endEmitted||(t.ended=!0,process.nextTick(H,t,e))}function H(e,t){if(u(`endReadableNT`,e.endEmitted,e.length),!e.endEmitted&&e.length===0&&(e.endEmitted=!0,t.readable=!1,t.emit(`end`),e.autoDestroy)){var n=t._writableState;(!n||n.autoDestroy&&n.finished)&&t.destroy()}}typeof Symbol==`function`&&(E.from=function(e,t){return x===void 0&&(x=wt()),x(E,e,t)});function U(e,t){for(var n=0,r=e.length;n{t.exports=l;var n=Q().codes,r=n.ERR_METHOD_NOT_IMPLEMENTED,i=n.ERR_MULTIPLE_CALLBACK,a=n.ERR_TRANSFORM_ALREADY_TRANSFORMING,o=n.ERR_TRANSFORM_WITH_LENGTH_0,s=$();Z()(l,s);function c(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(r===null)return this.emit(`error`,new i);n.writechunk=null,n.writecb=null,t!=null&&this.push(t),r(e);var a=this._readableState;a.reading=!1,(a.needReadable||a.length{t.exports=r;var n=Et();Z()(r,n);function r(e){if(!(this instanceof r))return new r(e);n.call(this,e)}r.prototype._transform=function(e,t,n){n(null,e)}})),Ot=A(((e,t)=>{var n;function r(e){var t=!1;return function(){t||(t=!0,e.apply(void 0,arguments))}}var i=Q().codes,a=i.ERR_MISSING_ARGS,o=i.ERR_STREAM_DESTROYED;function s(e){if(e)throw e}function c(e){return e.setHeader&&typeof e.abort==`function`}function l(e,t,i,a){a=r(a);var s=!1;e.on(`close`,function(){s=!0}),n===void 0&&(n=St()),n(e,{readable:t,writable:i},function(e){if(e)return a(e);s=!0,a()});var l=!1;return function(t){if(!s&&!l){if(l=!0,c(e))return e.abort();if(typeof e.destroy==`function`)return e.destroy();a(t||new o(`pipe`))}}}function u(e){e()}function d(e,t){return e.pipe(t)}function f(e){return!e.length||typeof e[e.length-1]!=`function`?s:e.pop()}function p(){var e=[...arguments],t=f(e);if(Array.isArray(e[0])&&(e=e[0]),e.length<2)throw new a(`streams`);var n,r=e.map(function(i,a){var o=a0,function(e){n||=e,e&&r.forEach(u),!o&&(r.forEach(u),t(n))})});return e.reduce(d)}t.exports=p})),kt=A(((e,t)=>{t.exports=r;var n=at().EventEmitter;Z()(r,n),r.Readable=Tt(),r.Writable=yt(),r.Duplex=$(),r.Transform=Et(),r.PassThrough=Dt(),r.finished=St(),r.pipeline=Ot(),r.Stream=r;function r(){n.call(this)}r.prototype.pipe=function(e,t){var r=this;function i(t){e.writable&&!1===e.write(t)&&r.pause&&r.pause()}r.on(`data`,i);function a(){r.readable&&r.resume&&r.resume()}e.on(`drain`,a),!e._isStdio&&(!t||t.end!==!1)&&(r.on(`end`,s),r.on(`close`,c));var o=!1;function s(){o||(o=!0,e.end())}function c(){o||(o=!0,typeof e.destroy==`function`&&e.destroy())}function l(e){if(u(),n.listenerCount(this,`error`)===0)throw e}r.on(`error`,l),e.on(`error`,l);function u(){r.removeListener(`data`,i),e.removeListener(`drain`,a),r.removeListener(`end`,s),r.removeListener(`close`,c),r.removeListener(`error`,l),e.removeListener(`error`,l),r.removeListener(`end`,u),r.removeListener(`close`,u),e.removeListener(`close`,u)}return r.on(`end`,u),r.on(`close`,u),e.on(`close`,u),e.emit(`pipe`,r),e}}))();async function*At(e,t,n){let r=1,i={chunks:[],length:0};for await(let a of n(e))for(i.chunks.push(a),i.length+=a.byteLength;i.length>t;){let e=i.chunks.length>1?Y.Buffer.concat(i.chunks):i.chunks[0];yield{partNumber:r,data:e.subarray(0,t)},i.chunks=[e.subarray(t)],i.length=i.chunks[0].byteLength,r+=1}yield{partNumber:r,data:i.chunks.length===1?i.chunks[0]:Y.Buffer.concat(i.chunks),lastPart:!0}}async function*jt(e,t){let n=1,r=0,i=t;for(;i{if(e instanceof Uint8Array)return jt(e,t);if(e instanceof kt.Readable)return At(e,t,Mt);if(e instanceof String||typeof e==`string`)return jt(Y.Buffer.from(e),t);if(typeof e.stream==`function`)return At(e.stream(),t,Nt);if(e instanceof ReadableStream)return At(e,t,Nt);throw Error(`Body Data is unsupported format, expected data to be one of: string | Uint8Array | Buffer | Readable | ReadableStream | Blob;.`)},Ft=class e extends lt.EventEmitter{static MIN_PART_SIZE=1024*1024*5;MAX_PARTS=1e4;queueSize=4;partSize;leavePartsOnError=!1;tags=[];client;params;totalBytes;totalBytesSource;bytesUploadedSoFar;abortController;concurrentUploaders=[];createMultiPartPromise;abortMultipartUploadCommand=null;uploadedParts=[];uploadEnqueuedPartsCount=0;expectedPartsCount;uploadId;uploadEvent;isMultiPart=!0;singleUploadResult;sent=!1;constructor(t){if(super(),this.queueSize=t.queueSize||this.queueSize,this.leavePartsOnError=t.leavePartsOnError||this.leavePartsOnError,this.tags=t.tags||this.tags,this.client=t.client,this.params=t.params,!this.params)throw Error(`InputError: Upload requires params to be passed to upload.`);this.totalBytes=this.params.ContentLength??dt(this.params.Body),this.totalBytesSource=ft(this.params.Body,this.params.ContentLength),this.bytesUploadedSoFar=0,this.abortController=t.abortController??new AbortController,this.partSize=t.partSize||Math.max(e.MIN_PART_SIZE,Math.ceil((this.totalBytes||0)/this.MAX_PARTS)),this.totalBytes!==void 0&&(this.expectedPartsCount=Math.ceil(this.totalBytes/this.partSize)),this.__validateInput()}async abort(){this.abortController.abort()}async done(){if(this.sent)throw Error(`@aws-sdk/lib-storage: this instance of Upload has already executed .done(). Create a new instance.`);return this.sent=!0,await Promise.race([this.__doMultipartUpload(),this.__abortTimeout(this.abortController.signal)])}on(e,t){return this.uploadEvent=e,super.on(e,t)}async __uploadUsingPut(e){this.isMultiPart=!1;let t={...this.params,Body:e.data},n=this.client.config,r=n.requestHandler,i=r instanceof lt.EventEmitter?r:null,a=t=>{this.bytesUploadedSoFar=t.loaded,this.totalBytes=t.total,this.__notifyProgress({loaded:this.bytesUploadedSoFar,total:this.totalBytes,part:e.partNumber,Key:this.params.Key,Bucket:this.params.Bucket})};i!==null&&i.on(`xhr.upload.progress`,a);let o=await Promise.all([this.client.send(new tt(t)),n?.endpoint?.()]),s=o[0],c=o[1];if(c||=D(await x(t,tt,{...n})),!c)throw Error(`Could not resolve endpoint from S3 "client.config.endpoint()" nor EndpointsV2.`);i!==null&&i.off(`xhr.upload.progress`,a);let l=this.params.Key.split(`/`).map(e=>S(e)).join(`/`),u=S(this.params.Bucket),d=(()=>{let e=c.hostname.startsWith(`${u}.`),t=this.client.config.forcePathStyle,n=c.port?`:${c.port}`:``;return t?`${c.protocol}//${c.hostname}${n}/${u}/${l}`:e?`${c.protocol}//${c.hostname}${n}/${l}`:`${c.protocol}//${u}.${c.hostname}${n}/${l}`})();this.singleUploadResult={...s,Bucket:this.params.Bucket,Key:this.params.Key,Location:d};let f=dt(e.data);this.__notifyProgress({loaded:f,total:f,part:1,Key:this.params.Key,Bucket:this.params.Bucket})}async __createMultipartUpload(){let e=await this.client.config.requestChecksumCalculation();if(!this.createMultiPartPromise){let t={...this.params,Body:void 0};e===`WHEN_SUPPORTED`&&(t.ChecksumAlgorithm=this.params.ChecksumAlgorithm||it.CRC32),this.createMultiPartPromise=this.client.send(new $e(t)).then(e=>(this.abortMultipartUploadCommand=new We({Bucket:this.params.Bucket,Key:this.params.Key,UploadId:e.UploadId}),e))}return this.createMultiPartPromise}async __doConcurrentUpload(e){for await(let t of e){if(this.uploadEnqueuedPartsCount>this.MAX_PARTS)throw Error(`Exceeded ${this.MAX_PARTS} parts in multipart upload to Bucket: ${this.params.Bucket} Key: ${this.params.Key}.`);if(this.abortController.signal.aborted)return;if(t.partNumber===1&&t.lastPart)return await this.__uploadUsingPut(t);if(!this.uploadId){let{UploadId:e}=await this.__createMultipartUpload();if(this.uploadId=e,this.abortController.signal.aborted)return}let e=dt(t.data)||0,n=this.client.config.requestHandler,r=n instanceof lt.EventEmitter?n:null,i=0,a=(n,r)=>{(Number(r.query.partNumber)||-1)===t.partNumber&&(n.total&&e&&(this.bytesUploadedSoFar+=n.loaded-i,i=n.loaded),this.__notifyProgress({loaded:this.bytesUploadedSoFar,total:this.totalBytes,part:t.partNumber,Key:this.params.Key,Bucket:this.params.Bucket}))};r!==null&&r.on(`xhr.upload.progress`,a),this.uploadEnqueuedPartsCount+=1,this.__validateUploadPart(t);let o=await this.client.send(new rt({...this.params,ContentLength:void 0,UploadId:this.uploadId,Body:t.data,PartNumber:t.partNumber}));if(r!==null&&r.off(`xhr.upload.progress`,a),this.abortController.signal.aborted)return;if(!o.ETag)throw Error(`Part ${t.partNumber} is missing ETag in UploadPart response. Missing Bucket CORS configuration for ETag header?`);this.uploadedParts.push({PartNumber:t.partNumber,ETag:o.ETag,...o.ChecksumCRC32&&{ChecksumCRC32:o.ChecksumCRC32},...o.ChecksumCRC32C&&{ChecksumCRC32C:o.ChecksumCRC32C},...o.ChecksumSHA1&&{ChecksumSHA1:o.ChecksumSHA1},...o.ChecksumSHA256&&{ChecksumSHA256:o.ChecksumSHA256}}),r===null&&(this.bytesUploadedSoFar+=e),this.__notifyProgress({loaded:this.bytesUploadedSoFar,total:this.totalBytes,part:t.partNumber,Key:this.params.Key,Bucket:this.params.Bucket})}}async __doMultipartUpload(){let e=Pt(this.params.Body,this.partSize),t=[];for(let n=0;n{t.push(e)});this.concurrentUploaders.push(n)}if(await Promise.all(this.concurrentUploaders),t.length>=1)throw await this.markUploadAsAborted(),t[0];if(this.abortController.signal.aborted)throw await this.markUploadAsAborted(),Object.assign(Error(`Upload aborted.`),{name:`AbortError`});let n;if(this.isMultiPart){let{expectedPartsCount:e,uploadedParts:t,totalBytes:r,totalBytesSource:i}=this;if(r!==void 0&&e!==void 0&&t.length!==e)throw Error(`Expected ${e} part(s) but uploaded ${t.length} part(s). +import{i as e,t}from"./compiler-runtime.js";import{n}from"./error-banner.js";import{C as r,Cr as i,Er as a,S as o,Sr as s,Tr as c,_ as l,b as u,c as d,d as f,f as p,g as m,l as h,s as g,u as _,v,vr as y,w as b,wr as x,x as S,xr as C,y as w}from"./awsClient.js";import{i as T}from"./SignatureV4.js";import{A as E,F as ee,M as D,N as te,P as O,c as k,j as A,k as ne,l as j,o as M,s as N}from"./index.browser.js";import{i as P}from"./index2.browser.js";import{Lr as F}from"./index.js";var I=()=>{let e=Array(8);for(let t=0;t<8;t++){let n=Array(512);for(let e=0;e<256;e++){let r=BigInt(e);for(let e=0;e<8*(t+1);e++)r&1n?r=r>>1n^11127430586519243189n:r>>=1n;n[e*2]=Number(r>>32n&4294967295n),n[e*2+1]=Number(r&4294967295n)}e[t]=new Uint32Array(n)}return e},L,R,z,B,V,H,U,W,G,K=()=>{L||(L=I(),[R,z,B,V,H,U,W,G]=L)},re=class{c1=0;c2=0;constructor(){K(),this.reset()}update(e){let t=e.length,n=0,r=this.c1,i=this.c2;for(;n+8<=t;){let t=((i^e[n++])&255)<<1,a=((i>>>8^e[n++])&255)<<1,o=((i>>>16^e[n++])&255)<<1,s=((i>>>24^e[n++])&255)<<1,c=((r^e[n++])&255)<<1,l=((r>>>8^e[n++])&255)<<1,u=((r>>>16^e[n++])&255)<<1,d=((r>>>24^e[n++])&255)<<1;r=G[t]^W[a]^U[o]^H[s]^V[c]^B[l]^z[u]^R[d],i=G[t+1]^W[a+1]^U[o+1]^H[s+1]^V[c+1]^B[l+1]^z[u+1]^R[d+1]}for(;n>>8|(r&255)<<24)>>>0,r=r>>>8^R[t],i^=R[t+1],n++}this.c1=r,this.c2=i}async digest(){let e=this.c1^4294967295,t=this.c2^4294967295;return new Uint8Array([e>>>24,e>>>16&255,e>>>8&255,e&255,t>>>24,t>>>16&255,t>>>8&255,t&255])}reset(){this.c1=4294967295,this.c2=4294967295}},ie={CrtCrc64Nvme:null},ae=(e,{requestChecksumRequired:t,requestAlgorithmMember:n,requestChecksumCalculation:i})=>{if(!n)return i===r.WHEN_SUPPORTED||t?o:void 0;if(e[n])return e[n]},oe=e=>e===S.MD5?`content-md5`:`x-amz-checksum-${e.toLowerCase()}`,se=(e,t)=>{let n=e.toLowerCase();for(let e of Object.keys(t))if(n===e.toLowerCase())return!0;return!1},ce=(e,t)=>{let n=e.toLowerCase();for(let e of Object.keys(t))if(e.toLowerCase().startsWith(n))return!0;return!1},le=e=>e!==void 0&&typeof e!=`string`&&!ArrayBuffer.isView(e)&&!k(e),ue=function(){function e(){this.crc32c=new de}return e.prototype.update=function(e){O(e)||this.crc32c.update(ee(e))},e.prototype.digest=function(){return ne(this,void 0,void 0,function(){return E(this,function(e){return[2,te(this.crc32c.digest())]})})},e.prototype.reset=function(){this.crc32c=new de},e}(),de=function(){function e(){this.checksum=4294967295}return e.prototype.update=function(e){var t,n;try{for(var r=A(e),i=r.next();!i.done;i=r.next()){var a=i.value;this.checksum=this.checksum>>>8^fe[(this.checksum^a)&255]}}catch(e){t={error:e}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return this},e.prototype.digest=function(){return(this.checksum^4294967295)>>>0},e}(),fe=D([0,4067132163,3778769143,324072436,3348797215,904991772,648144872,3570033899,2329499855,2024987596,1809983544,2575936315,1296289744,3207089363,2893594407,1578318884,274646895,3795141740,4049975192,51262619,3619967088,632279923,922689671,3298075524,2592579488,1760304291,2075979607,2312596564,1562183871,2943781820,3156637768,1313733451,549293790,3537243613,3246849577,871202090,3878099393,357341890,102525238,4101499445,2858735121,1477399826,1264559846,3107202533,1845379342,2677391885,2361733625,2125378298,820201905,3263744690,3520608582,598981189,4151959214,85089709,373468761,3827903834,3124367742,1213305469,1526817161,2842354314,2107672161,2412447074,2627466902,1861252501,1098587580,3004210879,2688576843,1378610760,2262928035,1955203488,1742404180,2511436119,3416409459,969524848,714683780,3639785095,205050476,4266873199,3976438427,526918040,1361435347,2739821008,2954799652,1114974503,2529119692,1691668175,2005155131,2247081528,3690758684,697762079,986182379,3366744552,476452099,3993867776,4250756596,255256311,1640403810,2477592673,2164122517,1922457750,2791048317,1412925310,1197962378,3037525897,3944729517,427051182,170179418,4165941337,746937522,3740196785,3451792453,1070968646,1905808397,2213795598,2426610938,1657317369,3053634322,1147748369,1463399397,2773627110,4215344322,153784257,444234805,3893493558,1021025245,3467647198,3722505002,797665321,2197175160,1889384571,1674398607,2443626636,1164749927,3070701412,2757221520,1446797203,137323447,4198817972,3910406976,461344835,3484808360,1037989803,781091935,3705997148,2460548119,1623424788,1939049696,2180517859,1429367560,2807687179,3020495871,1180866812,410100952,3927582683,4182430767,186734380,3756733383,763408580,1053836080,3434856499,2722870694,1344288421,1131464017,2971354706,1708204729,2545590714,2229949006,1988219213,680717673,3673779818,3383336350,1002577565,4010310262,493091189,238226049,4233660802,2987750089,1082061258,1395524158,2705686845,1972364758,2279892693,2494862625,1725896226,952904198,3399985413,3656866545,731699698,4283874585,222117402,510512622,3959836397,3280807620,837199303,582374963,3504198960,68661723,4135334616,3844915500,390545967,1230274059,3141532936,2825850620,1510247935,2395924756,2091215383,1878366691,2644384480,3553878443,565732008,854102364,3229815391,340358836,3861050807,4117890627,119113024,1493875044,2875275879,3090270611,1247431312,2660249211,1828433272,2141937292,2378227087,3811616794,291187481,34330861,4032846830,615137029,3603020806,3314634738,939183345,1776939221,2609017814,2295496738,2058945313,2926798794,1545135305,1330124605,3173225534,4084100981,17165430,307568514,3762199681,888469610,3332340585,3587147933,665062302,2042050490,2346497209,2559330125,1793573966,3190661285,1279665062,1595330642,2910671697]),pe=()=>P,me=[S.CRC32,S.CRC32C,S.CRC64NVME,S.SHA1,S.SHA256],he=[S.SHA256,S.SHA1,S.CRC32,S.CRC32C,S.CRC64NVME],ge=(e,t)=>{let{checksumAlgorithms:n={}}=t;switch(e){case S.MD5:return n?.MD5??t.md5;case S.CRC32:return n?.CRC32??pe();case S.CRC32C:return n?.CRC32C??ue;case S.CRC64NVME:return typeof ie.CrtCrc64Nvme==`function`?n?.CRC64NVME??ie.CrtCrc64Nvme:n?.CRC64NVME??re;case S.SHA1:return n?.SHA1??t.sha1;case S.SHA256:return n?.SHA256??t.sha256;default:if(n?.[e])return n[e];throw Error(`The checksum algorithm "${e}" is not supported by the client. Select one of ${me}, or provide an implementation to the client constructor checksums field.`)}},_e=(e,t)=>{let n=new e;return n.update(j(t||``)),n.digest()},ve={name:`flexibleChecksumsMiddleware`,step:`build`,tags:[`BODY_CHECKSUM`],override:!0},ye=(e,t)=>(n,i)=>async a=>{if(!T.isInstance(a.request)||ce(`x-amz-checksum-`,a.request.headers))return n(a);let{request:s,input:c}=a,{body:l,headers:u}=s,{base64Encoder:d,streamHasher:f}=e,{requestChecksumRequired:p,requestAlgorithmMember:m}=t,h=await e.requestChecksumCalculation(),g=m?.name,_=m?.httpHeader;g&&!c[g]&&(h===r.WHEN_SUPPORTED||p)&&(c[g]=o,_&&(u[_]=o));let v=ae(c,{requestChecksumRequired:p,requestAlgorithmMember:m?.name,requestChecksumCalculation:h}),b=l,x=u;if(v){switch(v){case S.CRC32:y(i,`FLEXIBLE_CHECKSUMS_REQ_CRC32`,`U`);break;case S.CRC32C:y(i,`FLEXIBLE_CHECKSUMS_REQ_CRC32C`,`V`);break;case S.CRC64NVME:y(i,`FLEXIBLE_CHECKSUMS_REQ_CRC64`,`W`);break;case S.SHA1:y(i,`FLEXIBLE_CHECKSUMS_REQ_SHA1`,`X`);break;case S.SHA256:y(i,`FLEXIBLE_CHECKSUMS_REQ_SHA256`,`Y`);break}let t=oe(v),n=ge(v,e);if(le(l)){let{getAwsChunkedEncodingStream:r,bodyLengthChecker:a}=e;b=r(typeof e.requestStreamBufferSize==`number`&&e.requestStreamBufferSize>=8*1024?M(l,e.requestStreamBufferSize,i.logger):l,{base64Encoder:d,bodyLengthChecker:a,checksumLocationName:t,checksumAlgorithmFn:n,streamHasher:f}),x={...u,"content-encoding":u[`content-encoding`]?`${u[`content-encoding`]},aws-chunked`:`aws-chunked`,"transfer-encoding":`chunked`,"x-amz-decoded-content-length":u[`content-length`],"x-amz-content-sha256":`STREAMING-UNSIGNED-PAYLOAD-TRAILER`,"x-amz-trailer":t},delete x[`content-length`]}else if(!se(t,u)){let e=await _e(n,l);x={...u,[t]:d(e)}}}try{return await n({...a,request:{...s,headers:x,body:b}})}catch(e){if(e instanceof Error&&e.name===`InvalidChunkSizeError`)try{e.message.endsWith(`.`)||(e.message+=`.`),e.message+=` Set [requestStreamBufferSize=number e.g. 65_536] in client constructor to instruct AWS SDK to buffer your input stream.`}catch{}throw e}},be={name:`flexibleChecksumsInputMiddleware`,toMiddleware:`serializerMiddleware`,relation:`before`,tags:[`BODY_CHECKSUM`],override:!0},xe=(e,t)=>(n,i)=>async a=>{let o=a.input,{requestValidationModeMember:s}=t,c=await e.requestChecksumCalculation(),l=await e.responseChecksumValidation();switch(c){case r.WHEN_REQUIRED:y(i,`FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED`,`a`);break;case r.WHEN_SUPPORTED:y(i,`FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED`,`Z`);break}switch(l){case b.WHEN_REQUIRED:y(i,`FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED`,`c`);break;case b.WHEN_SUPPORTED:y(i,`FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED`,`b`);break}return s&&!o[s]&&l===b.WHEN_SUPPORTED&&(o[s]=`ENABLED`),n(a)},Se=(e=[])=>{let t=[],n=he.length;for(let r of e){let e=he.indexOf(r);e===-1?t[n++]=r:t[e]=r}return t.filter(Boolean)},Ce=e=>{let t=e.lastIndexOf(`-`);if(t!==-1){let n=e.slice(t+1);if(!n.startsWith(`0`)){let e=parseInt(n,10);if(!isNaN(e)&&e>=1&&e<=1e4)return!0}}return!1},we=async(e,{checksumAlgorithmFn:t,base64Encoder:n})=>n(await _e(t,e)),Te=async(e,{config:t,responseAlgorithms:n,logger:r})=>{let i=Se(n),{body:a,headers:o}=e;for(let n of i){let i=oe(n),s=o[i];if(s){let o;try{o=ge(n,t)}catch(e){if(n===S.CRC64NVME){r?.warn(`Skipping ${S.CRC64NVME} checksum validation: ${e.message}`);continue}throw e}let{base64Encoder:c}=t;if(le(a)){e.body=N({expectedChecksum:s,checksumSourceLocation:i,checksum:new o,source:a,base64Encoder:c});return}let l=await we(a,{checksumAlgorithmFn:o,base64Encoder:c});if(l===s)break;throw Error(`Checksum mismatch: expected "${l}" but received "${s}" in response header "${i}".`)}}},Ee={name:`flexibleChecksumsResponseMiddleware`,toMiddleware:`deserializerMiddleware`,relation:`after`,tags:[`BODY_CHECKSUM`],override:!0},De=(e,t)=>(n,r)=>async i=>{if(!T.isInstance(i.request))return n(i);let a=i.input,o=await n(i),s=o.response,{requestValidationModeMember:c,responseAlgorithms:l}=t;if(c&&a[c]===`ENABLED`){let{clientName:t,commandName:n}=r,i=Object.keys(e.checksumAlgorithms??{}).filter(e=>{let t=oe(e);return s.headers[t]!==void 0}),a=Se([...l??[],...i]);if(t===`S3Client`&&n===`GetObjectCommand`&&a.every(e=>{let t=oe(e),n=s.headers[t];return!n||Ce(n)}))return o;await Te(s,{config:e,responseAlgorithms:a,logger:r.logger})}return o},Oe=(e,t)=>({applyToStack:n=>{n.add(ye(e,t),ve),n.addRelativeTo(xe(e,t),be),n.addRelativeTo(De(e,t),Ee)}}),ke=`content-length`,Ae=`x-amz-decoded-content-length`;function je(){return(e,t)=>async n=>{let{request:r}=n;if(T.isInstance(r)&&!(ke in r.headers)&&!(Ae in r.headers)){let e=`Are you using a Stream of unknown length as the Body of a PutObject request? Consider using Upload instead from @aws-sdk/lib-storage.`;typeof t?.logger?.warn==`function`&&!(t.logger instanceof x)?t.logger.warn(e):console.warn(e)}return e({...n})}}var Me={step:`finalizeRequest`,tags:[`CHECK_CONTENT_LENGTH_HEADER`],name:`getCheckContentLengthHeaderPlugin`,override:!0},Ne=e=>({applyToStack:e=>{e.add(je(),Me)}});function Pe(e){return t=>async n=>{let{CreateBucketConfiguration:r}=n.input,i=await e.region();return!r?.LocationConstraint&&!r?.Location&&i!==`us-east-1`&&(n.input.CreateBucketConfiguration=n.input.CreateBucketConfiguration??{},n.input.CreateBucketConfiguration.LocationConstraint=i),t(n)}}var Fe={step:`initialize`,tags:[`LOCATION_CONSTRAINT`,`CREATE_BUCKET_CONFIGURATION`],name:`locationConstraintMiddleware`,override:!0},Ie=e=>({applyToStack:t=>{t.add(Pe(e),Fe)}});function Le(e){return t=>async n=>{let r={...n.input};for(let t of[{target:`SSECustomerKey`,hash:`SSECustomerKeyMD5`},{target:`CopySourceSSECustomerKey`,hash:`CopySourceSSECustomerKeyMD5`}]){let n=r[t.target];if(n){let i;typeof n==`string`?ze(n,e)?i=e.base64Decoder(n):(i=e.utf8Decoder(n),r[t.target]=e.base64Encoder(i)):(i=ArrayBuffer.isView(n)?new Uint8Array(n.buffer,n.byteOffset,n.byteLength):new Uint8Array(n),r[t.target]=e.base64Encoder(i));let a=new e.md5;a.update(i),r[t.hash]=e.base64Encoder(await a.digest())}}return t({...n,input:r})}}var Re={name:`ssecMiddleware`,step:`initialize`,tags:[`SSE`],override:!0},q=e=>({applyToStack:t=>{t.add(Le(e),Re)}});function ze(e,t){if(!/^(?:[A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e))return!1;try{return t.base64Decoder(e).length===32}catch{return!1}}var Be=class extends c.classBuilder().ep({...w,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions()),u(n)]}).s(`AmazonS3`,`AbortMultipartUpload`,{}).n(`S3Client`,`AbortMultipartUploadCommand`).sc(d).build(){},Ve=class extends c.classBuilder().ep({...w,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions()),u(n),q(n)]}).s(`AmazonS3`,`CompleteMultipartUpload`,{}).n(`S3Client`,`CompleteMultipartUploadCommand`).sc(h).build(){},He=class extends c.classBuilder().ep({...w,UseS3ExpressControlEndpoint:{type:`staticContextParams`,value:!0},DisableAccessPoints:{type:`staticContextParams`,value:!0},Bucket:{type:`contextParams`,name:`Bucket`}}).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions()),u(n),Ie(n)]}).s(`AmazonS3`,`CreateBucket`,{}).n(`S3Client`,`CreateBucketCommand`).sc(_).build(){},Ue=class extends c.classBuilder().ep({...w,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions()),u(n),q(n)]}).s(`AmazonS3`,`CreateMultipartUpload`,{}).n(`S3Client`,`CreateMultipartUploadCommand`).sc(f).build(){},We=class extends c.classBuilder().ep({...w,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions()),u(n)]}).s(`AmazonS3`,`DeleteObject`,{}).n(`S3Client`,`DeleteObjectCommand`).sc(p).build(){},Ge=class extends c.classBuilder().ep({...w,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions()),Oe(n,{requestAlgorithmMember:{httpHeader:`x-amz-sdk-checksum-algorithm`,name:`ChecksumAlgorithm`},requestChecksumRequired:!1}),Ne(n),u(n),q(n)]}).s(`AmazonS3`,`PutObject`,{}).n(`S3Client`,`PutObjectCommand`).sc(m).build(){},Ke=class extends c.classBuilder().ep({...w,Bucket:{type:`contextParams`,name:`Bucket`}}).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions()),Oe(n,{requestAlgorithmMember:{httpHeader:`x-amz-sdk-checksum-algorithm`,name:`ChecksumAlgorithm`},requestChecksumRequired:!0}),u(n)]}).s(`AmazonS3`,`PutObjectTagging`,{}).n(`S3Client`,`PutObjectTaggingCommand`).sc(l).build(){},qe=class extends c.classBuilder().ep({...w,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[i(n,e.getEndpointParameterInstructions()),Oe(n,{requestAlgorithmMember:{httpHeader:`x-amz-sdk-checksum-algorithm`,name:`ChecksumAlgorithm`},requestChecksumRequired:!1}),u(n),q(n)]}).s(`AmazonS3`,`UploadPart`,{}).n(`S3Client`,`UploadPartCommand`).sc(v).build(){},Je={CRC32:`CRC32`,CRC32C:`CRC32C`,CRC64NVME:`CRC64NVME`,MD5:`MD5`,SHA1:`SHA1`,SHA256:`SHA256`,SHA512:`SHA512`,XXHASH128:`XXHASH128`,XXHASH3:`XXHASH3`,XXHASH64:`XXHASH64`},Ye=e(((e,t)=>{var n=typeof Reflect==`object`?Reflect:null,r=n&&typeof n.apply==`function`?n.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)},i=n&&typeof n.ownKeys==`function`?n.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};function a(e){console&&console.warn&&console.warn(e)}var o=Number.isNaN||function(e){return e!==e};function s(){s.init.call(this)}t.exports=s,t.exports.once=y,s.EventEmitter=s,s.prototype._events=void 0,s.prototype._eventsCount=0,s.prototype._maxListeners=void 0;var c=10;function l(e){if(typeof e!=`function`)throw TypeError(`The "listener" argument must be of type Function. Received type `+typeof e)}Object.defineProperty(s,"defaultMaxListeners",{enumerable:!0,get:function(){return c},set:function(e){if(typeof e!=`number`||e<0||o(e))throw RangeError(`The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received `+e+`.`);c=e}}),s.init=function(){(this._events===void 0||this._events===Object.getPrototypeOf(this)._events)&&(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},s.prototype.setMaxListeners=function(e){if(typeof e!=`number`||e<0||o(e))throw RangeError(`The value of "n" is out of range. It must be a non-negative number. Received `+e+`.`);return this._maxListeners=e,this};function u(e){return e._maxListeners===void 0?s.defaultMaxListeners:e._maxListeners}s.prototype.getMaxListeners=function(){return u(this)},s.prototype.emit=function(e){for(var t=[],n=1;n0&&(o=t[0]),o instanceof Error)throw o;var s=Error(`Unhandled error.`+(o?` (`+o.message+`)`:``));throw s.context=o,s}var c=a[e];if(c===void 0)return!1;if(typeof c==`function`)r(c,this,t);else for(var l=c.length,u=g(c,l),n=0;n0&&s.length>i&&!s.warned){s.warned=!0;var c=Error(`Possible EventEmitter memory leak detected. `+s.length+` `+String(t)+` listeners added. Use emitter.setMaxListeners() to increase limit`);c.name=`MaxListenersExceededWarning`,c.emitter=e,c.type=t,c.count=s.length,a(c)}return e}s.prototype.addListener=function(e,t){return d(this,e,t,!1)},s.prototype.on=s.prototype.addListener,s.prototype.prependListener=function(e,t){return d(this,e,t,!0)};function f(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,arguments.length===0?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function p(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},i=f.bind(r);return i.listener=n,r.wrapFn=i,i}s.prototype.once=function(e,t){return l(t),this.on(e,p(this,e,t)),this},s.prototype.prependOnceListener=function(e,t){return l(t),this.prependListener(e,p(this,e,t)),this},s.prototype.removeListener=function(e,t){var n,r,i,a,o;if(l(t),r=this._events,r===void 0||(n=r[e],n===void 0))return this;if(n===t||n.listener===t)--this._eventsCount===0?this._events=Object.create(null):(delete r[e],r.removeListener&&this.emit(`removeListener`,e,n.listener||t));else if(typeof n!=`function`){for(i=-1,a=n.length-1;a>=0;a--)if(n[a]===t||n[a].listener===t){o=n[a].listener,i=a;break}if(i<0)return this;i===0?n.shift():_(n,i),n.length===1&&(r[e]=n[0]),r.removeListener!==void 0&&this.emit(`removeListener`,e,o||t)}return this},s.prototype.off=s.prototype.removeListener,s.prototype.removeAllListeners=function(e){var t,n=this._events,r;if(n===void 0)return this;if(n.removeListener===void 0)return arguments.length===0?(this._events=Object.create(null),this._eventsCount=0):n[e]!==void 0&&(--this._eventsCount===0?this._events=Object.create(null):delete n[e]),this;if(arguments.length===0){var i=Object.keys(n),a;for(r=0;r=0;r--)this.removeListener(e,t[r]);return this};function m(e,t,n){var r=e._events;if(r===void 0)return[];var i=r[t];return i===void 0?[]:typeof i==`function`?n?[i.listener||i]:[i]:n?v(i):g(i,i.length)}s.prototype.listeners=function(e){return m(this,e,!0)},s.prototype.rawListeners=function(e){return m(this,e,!1)},s.listenerCount=function(e,t){return typeof e.listenerCount==`function`?e.listenerCount(t):h.call(e,t)},s.prototype.listenerCount=h;function h(e){var t=this._events;if(t!==void 0){var n=t[e];if(typeof n==`function`)return 1;if(n!==void 0)return n.length}return 0}s.prototype.eventNames=function(){return this._eventsCount>0?i(this._events):[]};function g(e,t){for(var n=Array(t),r=0;r{e.byteLength=c,e.toByteArray=u,e.fromByteArray=p;for(var t=[],n=[],r=typeof Uint8Array<`u`?Uint8Array:Array,i=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`,a=0,o=i.length;a0)throw Error(`Invalid string. Length must be a multiple of 4`);var n=e.indexOf(`=`);n===-1&&(n=t);var r=n===t?0:4-n%4;return[n,r]}function c(e){var t=s(e),n=t[0],r=t[1];return(n+r)*3/4-r}function l(e,t,n){return(t+n)*3/4-n}function u(e){var t,i=s(e),a=i[0],o=i[1],c=new r(l(e,a,o)),u=0,d=o>0?a-4:a,f;for(f=0;f>16&255,c[u++]=t>>8&255,c[u++]=t&255;return o===2&&(t=n[e.charCodeAt(f)]<<2|n[e.charCodeAt(f+1)]>>4,c[u++]=t&255),o===1&&(t=n[e.charCodeAt(f)]<<10|n[e.charCodeAt(f+1)]<<4|n[e.charCodeAt(f+2)]>>2,c[u++]=t>>8&255,c[u++]=t&255),c}function d(e){return t[e>>18&63]+t[e>>12&63]+t[e>>6&63]+t[e&63]}function f(e,t,n){for(var r,i=[],a=t;ac?c:s+o));return i===1?(n=e[r-1],a.push(t[n>>2]+t[n<<4&63]+`==`)):i===2&&(n=(e[r-2]<<8)+e[r-1],a.push(t[n>>10]+t[n>>4&63]+t[n<<2&63]+`=`)),a.join(``)}})),Ze=e((e=>{e.read=function(e,t,n,r,i){var a,o,s=i*8-r-1,c=(1<>1,u=-7,d=n?i-1:0,f=n?-1:1,p=e[t+d];for(d+=f,a=p&(1<<-u)-1,p>>=-u,u+=s;u>0;a=a*256+e[t+d],d+=f,u-=8);for(o=a&(1<<-u)-1,a>>=-u,u+=r;u>0;o=o*256+e[t+d],d+=f,u-=8);if(a===0)a=1-l;else if(a===c)return o?NaN:(p?-1:1)*(1/0);else o+=2**r,a-=l;return(p?-1:1)*o*2**(a-r)},e.write=function(e,t,n,r,i,a){var o,s,c,l=a*8-i-1,u=(1<>1,f=i===23?2**-24-2**-77:0,p=r?0:a-1,m=r?1:-1,h=+(t<0||t===0&&1/t<0);for(t=Math.abs(t),isNaN(t)||t===1/0?(s=+!!isNaN(t),o=u):(o=Math.floor(Math.log(t)/Math.LN2),t*(c=2**-o)<1&&(o--,c*=2),o+d>=1?t+=f/c:t+=f*2**(1-d),t*c>=2&&(o++,c/=2),o+d>=u?(s=0,o=u):o+d>=1?(s=(t*c-1)*2**i,o+=d):(s=t*2**(d-1)*2**i,o=0));i>=8;e[n+p]=s&255,p+=m,s/=256,i-=8);for(o=o<0;e[n+p]=o&255,p+=m,o/=256,l-=8);e[n+p-m]|=h*128}})),J=e((e=>{var t=Xe(),n=Ze(),r=typeof Symbol==`function`&&typeof Symbol.for==`function`?Symbol.for(`nodejs.util.inspect.custom`):null;e.Buffer=s,e.SlowBuffer=_,e.INSPECT_MAX_BYTES=50;var i=2147483647;e.kMaxLength=i,s.TYPED_ARRAY_SUPPORT=a(),!s.TYPED_ARRAY_SUPPORT&&typeof console<`u`&&typeof console.error==`function`&&console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support.");function a(){try{var e=new Uint8Array(1),t={foo:function(){return 42}};return Object.setPrototypeOf(t,Uint8Array.prototype),Object.setPrototypeOf(e,t),e.foo()===42}catch{return!1}}Object.defineProperty(s.prototype,"parent",{enumerable:!0,get:function(){if(s.isBuffer(this))return this.buffer}}),Object.defineProperty(s.prototype,"offset",{enumerable:!0,get:function(){if(s.isBuffer(this))return this.byteOffset}});function o(e){if(e>i)throw RangeError(`The value "`+e+`" is invalid for option "size"`);var t=new Uint8Array(e);return Object.setPrototypeOf(t,s.prototype),t}function s(e,t,n){if(typeof e==`number`){if(typeof t==`string`)throw TypeError(`The "string" argument must be of type string. Received type number`);return d(e)}return c(e,t,n)}s.poolSize=8192;function c(e,t,n){if(typeof e==`string`)return f(e,t);if(ArrayBuffer.isView(e))return p(e);if(e==null)throw TypeError(`The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type `+typeof e);if(K(e,ArrayBuffer)||e&&K(e.buffer,ArrayBuffer)||typeof SharedArrayBuffer<`u`&&(K(e,SharedArrayBuffer)||e&&K(e.buffer,SharedArrayBuffer)))return m(e,t,n);if(typeof e==`number`)throw TypeError(`The "value" argument must not be of type number. Received type number`);var r=e.valueOf&&e.valueOf();if(r!=null&&r!==e)return s.from(r,t,n);var i=h(e);if(i)return i;if(typeof Symbol<`u`&&Symbol.toPrimitive!=null&&typeof e[Symbol.toPrimitive]==`function`)return s.from(e[Symbol.toPrimitive](`string`),t,n);throw TypeError(`The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type `+typeof e)}s.from=function(e,t,n){return c(e,t,n)},Object.setPrototypeOf(s.prototype,Uint8Array.prototype),Object.setPrototypeOf(s,Uint8Array);function l(e){if(typeof e!=`number`)throw TypeError(`"size" argument must be of type number`);if(e<0)throw RangeError(`The value "`+e+`" is invalid for option "size"`)}function u(e,t,n){return l(e),e<=0||t===void 0?o(e):typeof n==`string`?o(e).fill(t,n):o(e).fill(t)}s.alloc=function(e,t,n){return u(e,t,n)};function d(e){return l(e),o(e<0?0:g(e)|0)}s.allocUnsafe=function(e){return d(e)},s.allocUnsafeSlow=function(e){return d(e)};function f(e,t){if((typeof t!=`string`||t===``)&&(t=`utf8`),!s.isEncoding(t))throw TypeError(`Unknown encoding: `+t);var n=v(e,t)|0,r=o(n),i=r.write(e,t);return i!==n&&(r=r.slice(0,i)),r}function p(e){for(var t=e.length<0?0:g(e.length)|0,n=o(t),r=0;r=i)throw RangeError(`Attempt to allocate Buffer larger than maximum size: 0x`+i.toString(16)+` bytes`);return e|0}function _(e){return+e!=e&&(e=0),s.alloc(+e)}s.isBuffer=function(e){return e!=null&&e._isBuffer===!0&&e!==s.prototype},s.compare=function(e,t){if(K(e,Uint8Array)&&(e=s.from(e,e.offset,e.byteLength)),K(t,Uint8Array)&&(t=s.from(t,t.offset,t.byteLength)),!s.isBuffer(e)||!s.isBuffer(t))throw TypeError(`The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array`);if(e===t)return 0;for(var n=e.length,r=t.length,i=0,a=Math.min(n,r);i2&&arguments[2]===!0;if(!r&&n===0)return 0;for(var i=!1;;)switch(t){case`ascii`:case`latin1`:case`binary`:return n;case`utf8`:case`utf-8`:return V(e).length;case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:return n*2;case`hex`:return n>>>1;case`base64`:return W(e).length;default:if(i)return r?-1:V(e).length;t=(``+t).toLowerCase(),i=!0}}s.byteLength=v;function y(e,t,n){var r=!1;if((t===void 0||t<0)&&(t=0),t>this.length||((n===void 0||n>this.length)&&(n=this.length),n<=0)||(n>>>=0,t>>>=0,n<=t))return``;for(e||=`utf8`;;)switch(e){case`hex`:return M(this,t,n);case`utf8`:case`utf-8`:return O(this,t,n);case`ascii`:return ne(this,t,n);case`latin1`:case`binary`:return j(this,t,n);case`base64`:return te(this,t,n);case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:return N(this,t,n);default:if(r)throw TypeError(`Unknown encoding: `+e);e=(e+``).toLowerCase(),r=!0}}s.prototype._isBuffer=!0;function b(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}s.prototype.swap16=function(){var e=this.length;if(e%2!=0)throw RangeError(`Buffer size must be a multiple of 16-bits`);for(var t=0;tn&&(t+=` ... `),``},r&&(s.prototype[r]=s.prototype.inspect),s.prototype.compare=function(e,t,n,r,i){if(K(e,Uint8Array)&&(e=s.from(e,e.offset,e.byteLength)),!s.isBuffer(e))throw TypeError(`The "target" argument must be one of type Buffer or Uint8Array. Received type `+typeof e);if(t===void 0&&(t=0),n===void 0&&(n=e?e.length:0),r===void 0&&(r=0),i===void 0&&(i=this.length),t<0||n>e.length||r<0||i>this.length)throw RangeError(`out of range index`);if(r>=i&&t>=n)return 0;if(r>=i)return-1;if(t>=n)return 1;if(t>>>=0,n>>>=0,r>>>=0,i>>>=0,this===e)return 0;for(var a=i-r,o=n-t,c=Math.min(a,o),l=this.slice(r,i),u=e.slice(t,n),d=0;d2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,re(n)&&(n=i?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(i)return-1;n=e.length-1}else if(n<0)if(i)n=0;else return-1;if(typeof t==`string`&&(t=s.from(t,r)),s.isBuffer(t))return t.length===0?-1:S(e,t,n,r,i);if(typeof t==`number`)return t&=255,typeof Uint8Array.prototype.indexOf==`function`?i?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):S(e,[t],n,r,i);throw TypeError(`val must be string, number or Buffer`)}function S(e,t,n,r,i){var a=1,o=e.length,s=t.length;if(r!==void 0&&(r=String(r).toLowerCase(),r===`ucs2`||r===`ucs-2`||r===`utf16le`||r===`utf-16le`)){if(e.length<2||t.length<2)return-1;a=2,o/=2,s/=2,n/=2}function c(e,t){return a===1?e[t]:e.readUInt16BE(t*a)}var l;if(i){var u=-1;for(l=n;lo&&(n=o-s),l=n;l>=0;l--){for(var d=!0,f=0;fi&&(r=i)):r=i;var a=t.length;r>a/2&&(r=a/2);for(var o=0;o>>=0,isFinite(n)?(n>>>=0,r===void 0&&(r=`utf8`)):(r=n,n=void 0);else throw Error(`Buffer.write(string, encoding, offset[, length]) is no longer supported`);var i=this.length-t;if((n===void 0||n>i)&&(n=i),e.length>0&&(n<0||t<0)||t>this.length)throw RangeError(`Attempt to write outside buffer bounds`);r||=`utf8`;for(var a=!1;;)switch(r){case`hex`:return C(this,e,t,n);case`utf8`:case`utf-8`:return w(this,e,t,n);case`ascii`:return T(this,e,t,n);case`latin1`:case`binary`:return E(this,e,t,n);case`base64`:return ee(this,e,t,n);case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:return D(this,e,t,n);default:if(a)throw TypeError(`Unknown encoding: `+r);r=(``+r).toLowerCase(),a=!0}},s.prototype.toJSON=function(){return{type:`Buffer`,data:Array.prototype.slice.call(this._arr||this,0)}};function te(e,n,r){return n===0&&r===e.length?t.fromByteArray(e):t.fromByteArray(e.slice(n,r))}function O(e,t,n){n=Math.min(e.length,n);for(var r=[],i=t;i239?4:a>223?3:a>191?2:1;if(i+s<=n){var c,l,u,d;switch(s){case 1:a<128&&(o=a);break;case 2:c=e[i+1],(c&192)==128&&(d=(a&31)<<6|c&63,d>127&&(o=d));break;case 3:c=e[i+1],l=e[i+2],(c&192)==128&&(l&192)==128&&(d=(a&15)<<12|(c&63)<<6|l&63,d>2047&&(d<55296||d>57343)&&(o=d));break;case 4:c=e[i+1],l=e[i+2],u=e[i+3],(c&192)==128&&(l&192)==128&&(u&192)==128&&(d=(a&15)<<18|(c&63)<<12|(l&63)<<6|u&63,d>65535&&d<1114112&&(o=d))}}o===null?(o=65533,s=1):o>65535&&(o-=65536,r.push(o>>>10&1023|55296),o=56320|o&1023),r.push(o),i+=s}return A(r)}var k=4096;function A(e){var t=e.length;if(t<=k)return String.fromCharCode.apply(String,e);for(var n=``,r=0;rr)&&(n=r);for(var i=``,a=t;an&&(e=n),t<0?(t+=n,t<0&&(t=0)):t>n&&(t=n),tn)throw RangeError(`Trying to access beyond buffer length`)}s.prototype.readUIntLE=function(e,t,n){e>>>=0,t>>>=0,n||P(e,t,this.length);for(var r=this[e],i=1,a=0;++a>>=0,t>>>=0,n||P(e,t,this.length);for(var r=this[e+--t],i=1;t>0&&(i*=256);)r+=this[e+--t]*i;return r},s.prototype.readUInt8=function(e,t){return e>>>=0,t||P(e,1,this.length),this[e]},s.prototype.readUInt16LE=function(e,t){return e>>>=0,t||P(e,2,this.length),this[e]|this[e+1]<<8},s.prototype.readUInt16BE=function(e,t){return e>>>=0,t||P(e,2,this.length),this[e]<<8|this[e+1]},s.prototype.readUInt32LE=function(e,t){return e>>>=0,t||P(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+this[e+3]*16777216},s.prototype.readUInt32BE=function(e,t){return e>>>=0,t||P(e,4,this.length),this[e]*16777216+(this[e+1]<<16|this[e+2]<<8|this[e+3])},s.prototype.readIntLE=function(e,t,n){e>>>=0,t>>>=0,n||P(e,t,this.length);for(var r=this[e],i=1,a=0;++a=i&&(r-=2**(8*t)),r},s.prototype.readIntBE=function(e,t,n){e>>>=0,t>>>=0,n||P(e,t,this.length);for(var r=t,i=1,a=this[e+--r];r>0&&(i*=256);)a+=this[e+--r]*i;return i*=128,a>=i&&(a-=2**(8*t)),a},s.prototype.readInt8=function(e,t){return e>>>=0,t||P(e,1,this.length),this[e]&128?(255-this[e]+1)*-1:this[e]},s.prototype.readInt16LE=function(e,t){e>>>=0,t||P(e,2,this.length);var n=this[e]|this[e+1]<<8;return n&32768?n|4294901760:n},s.prototype.readInt16BE=function(e,t){e>>>=0,t||P(e,2,this.length);var n=this[e+1]|this[e]<<8;return n&32768?n|4294901760:n},s.prototype.readInt32LE=function(e,t){return e>>>=0,t||P(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},s.prototype.readInt32BE=function(e,t){return e>>>=0,t||P(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},s.prototype.readFloatLE=function(e,t){return e>>>=0,t||P(e,4,this.length),n.read(this,e,!0,23,4)},s.prototype.readFloatBE=function(e,t){return e>>>=0,t||P(e,4,this.length),n.read(this,e,!1,23,4)},s.prototype.readDoubleLE=function(e,t){return e>>>=0,t||P(e,8,this.length),n.read(this,e,!0,52,8)},s.prototype.readDoubleBE=function(e,t){return e>>>=0,t||P(e,8,this.length),n.read(this,e,!1,52,8)};function F(e,t,n,r,i,a){if(!s.isBuffer(e))throw TypeError(`"buffer" argument must be a Buffer instance`);if(t>i||te.length)throw RangeError(`Index out of range`)}s.prototype.writeUIntLE=function(e,t,n,r){if(e=+e,t>>>=0,n>>>=0,!r){var i=2**(8*n)-1;F(this,e,t,n,i,0)}var a=1,o=0;for(this[t]=e&255;++o>>=0,n>>>=0,!r){var i=2**(8*n)-1;F(this,e,t,n,i,0)}var a=n-1,o=1;for(this[t+a]=e&255;--a>=0&&(o*=256);)this[t+a]=e/o&255;return t+n},s.prototype.writeUInt8=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,1,255,0),this[t]=e&255,t+1},s.prototype.writeUInt16LE=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,2,65535,0),this[t]=e&255,this[t+1]=e>>>8,t+2},s.prototype.writeUInt16BE=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=e&255,t+2},s.prototype.writeUInt32LE=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=e&255,t+4},s.prototype.writeUInt32BE=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e&255,t+4},s.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t>>>=0,!r){var i=2**(8*n-1);F(this,e,t,n,i-1,-i)}var a=0,o=1,s=0;for(this[t]=e&255;++a>0)-s&255;return t+n},s.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t>>>=0,!r){var i=2**(8*n-1);F(this,e,t,n,i-1,-i)}var a=n-1,o=1,s=0;for(this[t+a]=e&255;--a>=0&&(o*=256);)e<0&&s===0&&this[t+a+1]!==0&&(s=1),this[t+a]=(e/o>>0)-s&255;return t+n},s.prototype.writeInt8=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=e&255,t+1},s.prototype.writeInt16LE=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,2,32767,-32768),this[t]=e&255,this[t+1]=e>>>8,t+2},s.prototype.writeInt16BE=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=e&255,t+2},s.prototype.writeInt32LE=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,4,2147483647,-2147483648),this[t]=e&255,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},s.prototype.writeInt32BE=function(e,t,n){return e=+e,t>>>=0,n||F(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e&255,t+4};function I(e,t,n,r,i,a){if(n+r>e.length||n<0)throw RangeError(`Index out of range`)}function L(e,t,r,i,a){return t=+t,r>>>=0,a||I(e,t,r,4,34028234663852886e22,-34028234663852886e22),n.write(e,t,r,i,23,4),r+4}s.prototype.writeFloatLE=function(e,t,n){return L(this,e,t,!0,n)},s.prototype.writeFloatBE=function(e,t,n){return L(this,e,t,!1,n)};function R(e,t,r,i,a){return t=+t,r>>>=0,a||I(e,t,r,8,17976931348623157e292,-17976931348623157e292),n.write(e,t,r,i,52,8),r+8}s.prototype.writeDoubleLE=function(e,t,n){return R(this,e,t,!0,n)},s.prototype.writeDoubleBE=function(e,t,n){return R(this,e,t,!1,n)},s.prototype.copy=function(e,t,n,r){if(!s.isBuffer(e))throw TypeError(`argument should be a Buffer`);if(n||=0,!r&&r!==0&&(r=this.length),t>=e.length&&(t=e.length),t||=0,r>0&&r=this.length)throw RangeError(`Index out of range`);if(r<0)throw RangeError(`sourceEnd out of bounds`);r>this.length&&(r=this.length),e.length-t=0;--a)e[a+t]=this[a+n];else Uint8Array.prototype.set.call(e,this.subarray(n,r),t);return i},s.prototype.fill=function(e,t,n,r){if(typeof e==`string`){if(typeof t==`string`?(r=t,t=0,n=this.length):typeof n==`string`&&(r=n,n=this.length),r!==void 0&&typeof r!=`string`)throw TypeError(`encoding must be a string`);if(typeof r==`string`&&!s.isEncoding(r))throw TypeError(`Unknown encoding: `+r);if(e.length===1){var i=e.charCodeAt(0);(r===`utf8`&&i<128||r===`latin1`)&&(e=i)}}else typeof e==`number`?e&=255:typeof e==`boolean`&&(e=Number(e));if(t<0||this.length>>=0,n=n===void 0?this.length:n>>>0,e||=0;var a;if(typeof e==`number`)for(a=t;a55295&&n<57344){if(!i){if(n>56319){(t-=3)>-1&&a.push(239,191,189);continue}else if(o+1===r){(t-=3)>-1&&a.push(239,191,189);continue}i=n;continue}if(n<56320){(t-=3)>-1&&a.push(239,191,189),i=n;continue}n=(i-55296<<10|n-56320)+65536}else i&&(t-=3)>-1&&a.push(239,191,189);if(i=null,n<128){if(--t<0)break;a.push(n)}else if(n<2048){if((t-=2)<0)break;a.push(n>>6|192,n&63|128)}else if(n<65536){if((t-=3)<0)break;a.push(n>>12|224,n>>6&63|128,n&63|128)}else if(n<1114112){if((t-=4)<0)break;a.push(n>>18|240,n>>12&63|128,n>>6&63|128,n&63|128)}else throw Error(`Invalid code point`)}return a}function H(e){for(var t=[],n=0;n>8,i=n%256,a.push(i),a.push(r);return a}function W(e){return t.toByteArray(B(e))}function G(e,t,n,r){for(var i=0;i=t.length||i>=e.length);++i)t[i+n]=e[i];return i}function K(e,t){return e instanceof t||e!=null&&e.constructor!=null&&e.constructor.name!=null&&e.constructor.name===t.name}function re(e){return e!==e}var ie=(function(){for(var e=`0123456789abcdef`,t=Array(256),n=0;n<16;++n)for(var r=n*16,i=0;i<16;++i)t[r+i]=e[n]+e[i];return t})()})),Qe=t(),$e=Ye(),Y=J(),et={lstatSync:()=>{},isFileReadStream(e){return!1},runtime:`browser`},tt=e=>{if(e==null)return 0;if(typeof e==`string`)return Y.Buffer.byteLength(e);if(typeof e.byteLength==`number`)return e.byteLength;if(typeof e.length==`number`)return e.length;if(typeof e.size==`number`)return e.size;if(typeof e.start==`number`&&typeof e.end==`number`)return e.end+1-e.start;if(et.isFileReadStream(e))try{return et.lstatSync(e.path).size}catch{return}},X;(function(e){e.EMPTY_INPUT=`a null or undefined Body`,e.CONTENT_LENGTH=`the ContentLength property of the params set by the caller`,e.STRING_LENGTH=`the encoded byte length of the Body string`,e.TYPED_ARRAY=`the byteLength of a typed byte array such as Uint8Array`,e.LENGTH=`the value of Body.length`,e.SIZE=`the value of Body.size`,e.START_END_DIFF=`the numeric difference between Body.start and Body.end`,e.LSTAT=`the size of the file given by Body.path on disk as reported by lstatSync`})(X||={});var nt=(e,t)=>{if(t!=null)return X.CONTENT_LENGTH;if(e==null)return X.EMPTY_INPUT;if(typeof e==`string`)return X.STRING_LENGTH;if(typeof e.byteLength==`number`)return X.TYPED_ARRAY;if(typeof e.length==`number`)return X.LENGTH;if(typeof e.size==`number`)return X.SIZE;if(typeof e.start==`number`&&typeof e.end==`number`)return X.START_END_DIFF;if(et.isFileReadStream(e))try{return et.lstatSync(e.path).size,X.LSTAT}catch{return}},Z=e(((e,t)=>{typeof Object.create==`function`?t.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:t.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}})),rt=e(((e,t)=>{t.exports=Ye().EventEmitter})),it=e(((e,t)=>{t.exports={}})),at=e(((e,t)=>{function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function r(e){for(var t=1;t0?this.tail.next=t:this.head=t,this.tail=t,++this.length}},{key:`unshift`,value:function(e){var t={data:e,next:this.head};this.length===0&&(this.tail=t),this.head=t,++this.length}},{key:`shift`,value:function(){if(this.length!==0){var e=this.head.data;return this.length===1?this.head=this.tail=null:this.head=this.head.next,--this.length,e}}},{key:`clear`,value:function(){this.head=this.tail=null,this.length=0}},{key:`join`,value:function(e){if(this.length===0)return``;for(var t=this.head,n=``+t.data;t=t.next;)n+=e+t.data;return n}},{key:`concat`,value:function(e){if(this.length===0)return u.alloc(0);for(var t=u.allocUnsafe(e>>>0),n=this.head,r=0;n;)p(n.data,t,r),r+=n.data.length,n=n.next;return t}},{key:`consume`,value:function(e,t){var n;return ei.length?i.length:e;if(a===i.length?r+=i:r+=i.slice(0,e),e-=a,e===0){a===i.length?(++n,t.next?this.head=t.next:this.head=this.tail=null):(this.head=t,t.data=i.slice(a));break}++n}return this.length-=n,r}},{key:`_getBuffer`,value:function(e){var t=u.allocUnsafe(e),n=this.head,r=1;for(n.data.copy(t),e-=n.data.length;n=n.next;){var i=n.data,a=e>i.length?i.length:e;if(i.copy(t,t.length-e,0,a),e-=a,e===0){a===i.length?(++r,n.next?this.head=n.next:this.head=this.tail=null):(this.head=n,n.data=i.slice(a));break}++r}return this.length-=r,t}},{key:f,value:function(e,t){return d(this,r(r({},t),{},{depth:0,customInspect:!1}))}}]),e}()})),ot=e(((e,t)=>{function n(e,t){var n=this,a=this._readableState&&this._readableState.destroyed,s=this._writableState&&this._writableState.destroyed;return a||s?(t?t(e):e&&(this._writableState?this._writableState.errorEmitted||(this._writableState.errorEmitted=!0,process.nextTick(o,this,e)):process.nextTick(o,this,e)),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,function(e){!t&&e?n._writableState?n._writableState.errorEmitted?process.nextTick(i,n):(n._writableState.errorEmitted=!0,process.nextTick(r,n,e)):process.nextTick(r,n,e):t?(process.nextTick(i,n),t(e)):process.nextTick(i,n)}),this)}function r(e,t){o(e,t),i(e)}function i(e){e._writableState&&!e._writableState.emitClose||e._readableState&&!e._readableState.emitClose||e.emit(`close`)}function a(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finalCalled=!1,this._writableState.prefinished=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)}function o(e,t){e.emit(`error`,t)}function s(e,t){var n=e._readableState,r=e._writableState;n&&n.autoDestroy||r&&r.autoDestroy?e.destroy(t):e.emit(`error`,t)}t.exports={destroy:n,undestroy:a,errorOrDestroy:s}})),Q=e(((e,t)=>{function n(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}var r={};function i(e,t,i){i||=Error;function a(e,n,r){return typeof t==`string`?t:t(e,n,r)}var o=function(e){n(t,e);function t(t,n,r){return e.call(this,a(t,n,r))||this}return t}(i);o.prototype.name=i.name,o.prototype.code=e,r[e]=o}function a(e,t){if(Array.isArray(e)){var n=e.length;return e=e.map(function(e){return String(e)}),n>2?`one of ${t} ${e.slice(0,n-1).join(`, `)}, or `+e[n-1]:n===2?`one of ${t} ${e[0]} or ${e[1]}`:`of ${t} ${e[0]}`}else return`of ${t} ${String(e)}`}function o(e,t,n){return e.substr(!n||n<0?0:+n,t.length)===t}function s(e,t,n){return(n===void 0||n>e.length)&&(n=e.length),e.substring(n-t.length,n)===t}function c(e,t,n){return typeof n!=`number`&&(n=0),n+t.length>e.length?!1:e.indexOf(t,n)!==-1}i(`ERR_INVALID_OPT_VALUE`,function(e,t){return`The value "`+t+`" is invalid for option "`+e+`"`},TypeError),i(`ERR_INVALID_ARG_TYPE`,function(e,t,n){var r;typeof t==`string`&&o(t,`not `)?(r=`must not be`,t=t.replace(/^not /,``)):r=`must be`;var i=s(e,` argument`)?`The ${e} ${r} ${a(t,`type`)}`:`The "${e}" ${c(e,`.`)?`property`:`argument`} ${r} ${a(t,`type`)}`;return i+=`. Received type ${typeof n}`,i},TypeError),i(`ERR_STREAM_PUSH_AFTER_EOF`,`stream.push() after EOF`),i(`ERR_METHOD_NOT_IMPLEMENTED`,function(e){return`The `+e+` method is not implemented`}),i(`ERR_STREAM_PREMATURE_CLOSE`,`Premature close`),i(`ERR_STREAM_DESTROYED`,function(e){return`Cannot call `+e+` after a stream was destroyed`}),i(`ERR_MULTIPLE_CALLBACK`,`Callback called multiple times`),i(`ERR_STREAM_CANNOT_PIPE`,`Cannot pipe, not readable`),i(`ERR_STREAM_WRITE_AFTER_END`,`write after end`),i(`ERR_STREAM_NULL_VALUES`,`May not write null values to stream`,TypeError),i(`ERR_UNKNOWN_ENCODING`,function(e){return`Unknown encoding: `+e},TypeError),i(`ERR_STREAM_UNSHIFT_AFTER_END_EVENT`,`stream.unshift() after end event`),t.exports.codes=r})),st=e(((e,t)=>{var n=Q().codes.ERR_INVALID_OPT_VALUE;function r(e,t,n){return e.highWaterMark==null?t?e[n]:null:e.highWaterMark}function i(e,t,i,a){var o=r(t,a,i);if(o!=null){if(!(isFinite(o)&&Math.floor(o)===o)||o<0)throw new n(a?i:`highWaterMark`,o);return Math.floor(o)}return e.objectMode?16:16*1024}t.exports={getHighWaterMark:i}})),ct=e(((e,t)=>{t.exports=n;function n(e,t){if(r(`noDeprecation`))return e;var n=!1;function i(){if(!n){if(r(`throwDeprecation`))throw Error(t);r(`traceDeprecation`)?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}return i}function r(e){try{if(!global.localStorage)return!1}catch{return!1}var t=global.localStorage[e];return t==null?!1:String(t).toLowerCase()===`true`}})),lt=e(((e,t)=>{t.exports=T;function n(e){var t=this;this.next=null,this.entry=null,this.finish=function(){z(t,e)}}var r;T.WritableState=C;var i={deprecate:ct()},a=rt(),o=J().Buffer,s=(typeof global<`u`?global:typeof window<`u`?window:typeof self<`u`?self:{}).Uint8Array||function(){};function c(e){return o.from(e)}function l(e){return o.isBuffer(e)||e instanceof s}var u=ot(),d=st().getHighWaterMark,f=Q().codes,p=f.ERR_INVALID_ARG_TYPE,m=f.ERR_METHOD_NOT_IMPLEMENTED,h=f.ERR_MULTIPLE_CALLBACK,g=f.ERR_STREAM_CANNOT_PIPE,_=f.ERR_STREAM_DESTROYED,v=f.ERR_STREAM_NULL_VALUES,y=f.ERR_STREAM_WRITE_AFTER_END,b=f.ERR_UNKNOWN_ENCODING,x=u.errorOrDestroy;Z()(T,a);function S(){}function C(e,t,i){r||=$(),e||={},typeof i!=`boolean`&&(i=t instanceof r),this.objectMode=!!e.objectMode,i&&(this.objectMode=this.objectMode||!!e.writableObjectMode),this.highWaterMark=d(this,e,`writableHighWaterMark`,i),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var a=e.decodeStrings===!1;this.decodeStrings=!a,this.defaultEncoding=e.defaultEncoding||`utf8`,this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){ne(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=e.emitClose!==!1,this.autoDestroy=!!e.autoDestroy,this.bufferedRequestCount=0,this.corkedRequestsFree=new n(this)}C.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},(function(){try{Object.defineProperty(C.prototype,"buffer",{get:i.deprecate(function(){return this.getBuffer()},`_writableState.buffer is deprecated. Use _writableState.getBuffer instead.`,`DEP0003`)})}catch{}})();var w;typeof Symbol==`function`&&Symbol.hasInstance&&typeof Function.prototype[Symbol.hasInstance]==`function`?(w=Function.prototype[Symbol.hasInstance],Object.defineProperty(T,Symbol.hasInstance,{value:function(e){return w.call(this,e)?!0:this===T?e&&e._writableState instanceof C:!1}})):w=function(e){return e instanceof this};function T(e){r||=$();var t=this instanceof r;if(!t&&!w.call(T,this))return new T(e);this._writableState=new C(e,this,t),this.writable=!0,e&&(typeof e.write==`function`&&(this._write=e.write),typeof e.writev==`function`&&(this._writev=e.writev),typeof e.destroy==`function`&&(this._destroy=e.destroy),typeof e.final==`function`&&(this._final=e.final)),a.call(this)}T.prototype.pipe=function(){x(this,new g)};function E(e,t){var n=new y;x(e,n),process.nextTick(t,n)}function ee(e,t,n,r){var i;return n===null?i=new v:typeof n!=`string`&&!t.objectMode&&(i=new p(`chunk`,[`string`,`Buffer`],n)),i?(x(e,i),process.nextTick(r,i),!1):!0}T.prototype.write=function(e,t,n){var r=this._writableState,i=!1,a=!r.objectMode&&l(e);return a&&!o.isBuffer(e)&&(e=c(e)),typeof t==`function`&&(n=t,t=null),a?t=`buffer`:t||=r.defaultEncoding,typeof n!=`function`&&(n=S),r.ending?E(this,n):(a||ee(this,r,e,n))&&(r.pendingcb++,i=te(this,r,a,e,t,n)),i},T.prototype.cork=function(){this._writableState.corked++},T.prototype.uncork=function(){var e=this._writableState;e.corked&&(e.corked--,!e.writing&&!e.corked&&!e.bufferProcessing&&e.bufferedRequest&&N(this,e))},T.prototype.setDefaultEncoding=function(e){if(typeof e==`string`&&(e=e.toLowerCase()),!([`hex`,`utf8`,`utf-8`,`ascii`,`binary`,`base64`,`ucs2`,`ucs-2`,`utf16le`,`utf-16le`,`raw`].indexOf((e+``).toLowerCase())>-1))throw new b(e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(T.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}});function D(e,t,n){return!e.objectMode&&e.decodeStrings!==!1&&typeof t==`string`&&(t=o.from(t,n)),t}Object.defineProperty(T.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}});function te(e,t,n,r,i,a){if(!n){var o=D(t,r,i);r!==o&&(n=!0,i=`buffer`,r=o)}var s=t.objectMode?1:r.length;t.length+=s;var c=t.length{var n=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};t.exports=c;var r=ht(),i=lt();Z()(c,r);for(var a=n(i.prototype),o=0;o{var n=J(),r=n.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}r.from&&r.alloc&&r.allocUnsafe&&r.allocUnsafeSlow?t.exports=n:(i(n,e),e.Buffer=a);function a(e,t,n){return r(e,t,n)}a.prototype=Object.create(r.prototype),i(r,a),a.from=function(e,t,n){if(typeof e==`number`)throw TypeError(`Argument must not be a number`);return r(e,t,n)},a.alloc=function(e,t,n){if(typeof e!=`number`)throw TypeError(`Argument must be a number`);var i=r(e);return t===void 0?i.fill(0):typeof n==`string`?i.fill(t,n):i.fill(t),i},a.allocUnsafe=function(e){if(typeof e!=`number`)throw TypeError(`Argument must be a number`);return r(e)},a.allocUnsafeSlow=function(e){if(typeof e!=`number`)throw TypeError(`Argument must be a number`);return n.SlowBuffer(e)}})),dt=e((e=>{var t=ut().Buffer,n=t.isEncoding||function(e){switch(e=``+e,e&&e.toLowerCase()){case`hex`:case`utf8`:case`utf-8`:case`ascii`:case`binary`:case`base64`:case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:case`raw`:return!0;default:return!1}};function r(e){if(!e)return`utf8`;for(var t;;)switch(e){case`utf8`:case`utf-8`:return`utf8`;case`ucs2`:case`ucs-2`:case`utf16le`:case`utf-16le`:return`utf16le`;case`latin1`:case`binary`:return`latin1`;case`base64`:case`ascii`:case`hex`:return e;default:if(t)return;e=(``+e).toLowerCase(),t=!0}}function i(e){var i=r(e);if(typeof i!=`string`&&(t.isEncoding===n||!n(e)))throw Error(`Unknown encoding: `+e);return i||e}e.StringDecoder=a;function a(e){this.encoding=i(e);var n;switch(this.encoding){case`utf16le`:this.text=f,this.end=p,n=4;break;case`utf8`:this.fillLast=l,n=4;break;case`base64`:this.text=m,this.end=h,n=3;break;default:this.write=g,this.end=_;return}this.lastNeed=0,this.lastTotal=0,this.lastChar=t.allocUnsafe(n)}a.prototype.write=function(e){if(e.length===0)return``;var t,n;if(this.lastNeed){if(t=this.fillLast(e),t===void 0)return``;n=this.lastNeed,this.lastNeed=0}else n=0;return n>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function s(e,t,n){var r=t.length-1;if(r=0?(i>0&&(e.lastNeed=i-1),i):--r=0?(i>0&&(e.lastNeed=i-2),i):--r=0?(i>0&&(i===2?i=0:e.lastNeed=i-3),i):0))}function c(e,t,n){if((t[0]&192)!=128)return e.lastNeed=0,`�`;if(e.lastNeed>1&&t.length>1){if((t[1]&192)!=128)return e.lastNeed=1,`�`;if(e.lastNeed>2&&t.length>2&&(t[2]&192)!=128)return e.lastNeed=2,`�`}}function l(e){var t=this.lastTotal-this.lastNeed,n=c(this,e,t);if(n!==void 0)return n;if(this.lastNeed<=e.length)return e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,t,0,e.length),this.lastNeed-=e.length}function u(e,t){var n=s(this,e,t);if(!this.lastNeed)return e.toString(`utf8`,t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString(`utf8`,t,r)}function d(e){var t=e&&e.length?this.write(e):``;return this.lastNeed?t+`�`:t}function f(e,t){if((e.length-t)%2==0){var n=e.toString(`utf16le`,t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString(`utf16le`,t,e.length-1)}function p(e){var t=e&&e.length?this.write(e):``;if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString(`utf16le`,0,n)}return t}function m(e,t){var n=(e.length-t)%3;return n===0?e.toString(`base64`,t):(this.lastNeed=3-n,this.lastTotal=3,n===1?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString(`base64`,t,e.length-n))}function h(e){var t=e&&e.length?this.write(e):``;return this.lastNeed?t+this.lastChar.toString(`base64`,0,3-this.lastNeed):t}function g(e){return e.toString(this.encoding)}function _(e){return e&&e.length?this.write(e):``}})),ft=e(((e,t)=>{var n=Q().codes.ERR_STREAM_PREMATURE_CLOSE;function r(e){var t=!1;return function(){if(!t){t=!0;var n=[...arguments];e.apply(this,n)}}}function i(){}function a(e){return e.setHeader&&typeof e.abort==`function`}function o(e,t,s){if(typeof t==`function`)return o(e,null,t);t||={},s=r(s||i);var c=t.readable||t.readable!==!1&&e.readable,l=t.writable||t.writable!==!1&&e.writable,u=function(){e.writable||f()},d=e._writableState&&e._writableState.finished,f=function(){l=!1,d=!0,c||s.call(e)},p=e._readableState&&e._readableState.endEmitted,m=function(){c=!1,p=!0,l||s.call(e)},h=function(t){s.call(e,t)},g=function(){var t;if(c&&!p)return(!e._readableState||!e._readableState.ended)&&(t=new n),s.call(e,t);if(l&&!d)return(!e._writableState||!e._writableState.ended)&&(t=new n),s.call(e,t)},_=function(){e.req.on(`finish`,f)};return a(e)?(e.on(`complete`,f),e.on(`abort`,g),e.req?_():e.on(`request`,_)):l&&!e._writableState&&(e.on(`end`,u),e.on(`close`,u)),e.on(`end`,m),e.on(`finish`,f),t.error!==!1&&e.on(`error`,h),e.on(`close`,g),function(){e.removeListener(`complete`,f),e.removeListener(`abort`,g),e.removeListener(`request`,_),e.req&&e.req.removeListener(`finish`,f),e.removeListener(`end`,u),e.removeListener(`close`,u),e.removeListener(`finish`,f),e.removeListener(`end`,m),e.removeListener(`error`,h),e.removeListener(`close`,g)}}t.exports=o})),pt=e(((e,t)=>{var n;function r(e,t,n){return t=i(t),t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e){var t=a(e,`string`);return typeof t==`symbol`?t:String(t)}function a(e,t){if(typeof e!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(typeof r!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}var o=ft(),s=Symbol(`lastResolve`),c=Symbol(`lastReject`),l=Symbol(`error`),u=Symbol(`ended`),d=Symbol(`lastPromise`),f=Symbol(`handlePromise`),p=Symbol(`stream`);function m(e,t){return{value:e,done:t}}function h(e){var t=e[s];if(t!==null){var n=e[p].read();n!==null&&(e[d]=null,e[s]=null,e[c]=null,t(m(n,!1)))}}function g(e){process.nextTick(h,e)}function _(e,t){return function(n,r){e.then(function(){if(t[u]){n(m(void 0,!0));return}t[f](n,r)},r)}}var v=Object.getPrototypeOf(function(){}),y=Object.setPrototypeOf((n={get stream(){return this[p]},next:function(){var e=this,t=this[l];if(t!==null)return Promise.reject(t);if(this[u])return Promise.resolve(m(void 0,!0));if(this[p].destroyed)return new Promise(function(t,n){process.nextTick(function(){e[l]?n(e[l]):t(m(void 0,!0))})});var n=this[d],r;if(n)r=new Promise(_(n,this));else{var i=this[p].read();if(i!==null)return Promise.resolve(m(i,!1));r=new Promise(this[f])}return this[d]=r,r}},r(n,Symbol.asyncIterator,function(){return this}),r(n,`return`,function(){var e=this;return new Promise(function(t,n){e[p].destroy(null,function(e){if(e){n(e);return}t(m(void 0,!0))})})}),n),v);t.exports=function(e){var t,n=Object.create(y,(t={},r(t,p,{value:e,writable:!0}),r(t,s,{value:null,writable:!0}),r(t,c,{value:null,writable:!0}),r(t,l,{value:null,writable:!0}),r(t,u,{value:e._readableState.endEmitted,writable:!0}),r(t,f,{value:function(e,t){var r=n[p].read();r?(n[d]=null,n[s]=null,n[c]=null,e(m(r,!1))):(n[s]=e,n[c]=t)},writable:!0}),t));return n[d]=null,o(e,function(e){if(e&&e.code!==`ERR_STREAM_PREMATURE_CLOSE`){var t=n[c];t!==null&&(n[d]=null,n[s]=null,n[c]=null,t(e)),n[l]=e;return}var r=n[s];r!==null&&(n[d]=null,n[s]=null,n[c]=null,r(m(void 0,!0))),n[u]=!0}),e.on(`readable`,g.bind(null,n)),n}})),mt=e(((e,t)=>{t.exports=function(){throw Error(`Readable.from is not available in the browser`)}})),ht=e(((e,t)=>{t.exports=E;var n;E.ReadableState=T,Ye().EventEmitter;var r=function(e,t){return e.listeners(t).length},i=rt(),a=J().Buffer,o=(typeof global<`u`?global:typeof window<`u`?window:typeof self<`u`?self:{}).Uint8Array||function(){};function s(e){return a.from(e)}function c(e){return a.isBuffer(e)||e instanceof o}var l=it(),u=l&&l.debuglog?l.debuglog(`stream`):function(){},d=at(),f=ot(),p=st().getHighWaterMark,m=Q().codes,h=m.ERR_INVALID_ARG_TYPE,g=m.ERR_STREAM_PUSH_AFTER_EOF,_=m.ERR_METHOD_NOT_IMPLEMENTED,v=m.ERR_STREAM_UNSHIFT_AFTER_END_EVENT,y,b,x;Z()(E,i);var S=f.errorOrDestroy,C=[`error`,`close`,`destroy`,`pause`,`resume`];function w(e,t,n){if(typeof e.prependListener==`function`)return e.prependListener(t,n);!e._events||!e._events[t]?e.on(t,n):Array.isArray(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]}function T(e,t,r){n||=$(),e||={},typeof r!=`boolean`&&(r=t instanceof n),this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.readableObjectMode),this.highWaterMark=p(this,e,`readableHighWaterMark`,r),this.buffer=new d,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.paused=!0,this.emitClose=e.emitClose!==!1,this.autoDestroy=!!e.autoDestroy,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||`utf8`,this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(y||=dt().StringDecoder,this.decoder=new y(e.encoding),this.encoding=e.encoding)}function E(e){if(n||=$(),!(this instanceof E))return new E(e);var t=this instanceof n;this._readableState=new T(e,this,t),this.readable=!0,e&&(typeof e.read==`function`&&(this._read=e.read),typeof e.destroy==`function`&&(this._destroy=e.destroy)),i.call(this)}Object.defineProperty(E.prototype,"destroyed",{enumerable:!1,get:function(){return this._readableState===void 0?!1:this._readableState.destroyed},set:function(e){this._readableState&&(this._readableState.destroyed=e)}}),E.prototype.destroy=f.destroy,E.prototype._undestroy=f.undestroy,E.prototype._destroy=function(e,t){t(e)},E.prototype.push=function(e,t){var n=this._readableState,r;return n.objectMode?r=!0:typeof e==`string`&&(t||=n.defaultEncoding,t!==n.encoding&&(e=a.from(e,t),t=``),r=!0),ee(this,e,t,!1,r)},E.prototype.unshift=function(e){return ee(this,e,null,!0,!1)};function ee(e,t,n,r,i){u(`readableAddChunk`,t);var o=e._readableState;if(t===null)o.reading=!1,ne(e,o);else{var c;if(i||(c=te(o,t)),c)S(e,c);else if(o.objectMode||t&&t.length>0)if(typeof t!=`string`&&!o.objectMode&&Object.getPrototypeOf(t)!==a.prototype&&(t=s(t)),r)o.endEmitted?S(e,new v):D(e,o,t,!0);else if(o.ended)S(e,new g);else if(o.destroyed)return!1;else o.reading=!1,o.decoder&&!n?(t=o.decoder.write(t),o.objectMode||t.length!==0?D(e,o,t,!1):N(e,o)):D(e,o,t,!1);else r||(o.reading=!1,N(e,o))}return!o.ended&&(o.length=O?e=O:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}function A(e,t){return e<=0||t.length===0&&t.ended?0:t.objectMode?1:e===e?(e>t.highWaterMark&&(t.highWaterMark=k(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0)):t.flowing&&t.length?t.buffer.head.data.length:t.length}E.prototype.read=function(e){u(`read`,e),e=parseInt(e,10);var t=this._readableState,n=e;if(e!==0&&(t.emittedReadable=!1),e===0&&t.needReadable&&((t.highWaterMark===0?t.length>0:t.length>=t.highWaterMark)||t.ended))return u(`read: emitReadable`,t.length,t.ended),t.length===0&&t.ended?H(this):j(this),null;if(e=A(e,t),e===0&&t.ended)return t.length===0&&H(this),null;var r=t.needReadable;u(`need readable`,r),(t.length===0||t.length-e0?V(e,t):null;return i===null?(t.needReadable=t.length<=t.highWaterMark,e=0):(t.length-=e,t.awaitDrain=0),t.length===0&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&H(this)),i!==null&&this.emit(`data`,i),i};function ne(e,t){if(u(`onEofChunk`),!t.ended){if(t.decoder){var n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,t.sync?j(e):(t.needReadable=!1,t.emittedReadable||(t.emittedReadable=!0,M(e)))}}function j(e){var t=e._readableState;u(`emitReadable`,t.needReadable,t.emittedReadable),t.needReadable=!1,t.emittedReadable||(u(`emitReadable`,t.flowing),t.emittedReadable=!0,process.nextTick(M,e))}function M(e){var t=e._readableState;u(`emitReadable_`,t.destroyed,t.length,t.ended),!t.destroyed&&(t.length||t.ended)&&(e.emit(`readable`),t.emittedReadable=!1),t.needReadable=!t.flowing&&!t.ended&&t.length<=t.highWaterMark,B(e)}function N(e,t){t.readingMore||(t.readingMore=!0,process.nextTick(P,e,t))}function P(e,t){for(;!t.reading&&!t.ended&&(t.length1&&W(i.pipes,e)!==-1)&&!l&&(u(`false write response, pause`,i.awaitDrain),i.awaitDrain++),n.pause())}function p(t){u(`onerror`,t),g(),e.removeListener(`error`,p),r(e,`error`)===0&&S(e,t)}w(e,`error`,p);function m(){e.removeListener(`finish`,h),g()}e.once(`close`,m);function h(){u(`onfinish`),e.removeListener(`close`,m),g()}e.once(`finish`,h);function g(){u(`unpipe`),n.unpipe(e)}return e.emit(`pipe`,n),i.flowing||(u(`pipe resume`),n.resume()),e};function F(e){return function(){var t=e._readableState;u(`pipeOnDrain`,t.awaitDrain),t.awaitDrain&&t.awaitDrain--,t.awaitDrain===0&&r(e,`data`)&&(t.flowing=!0,B(e))}}E.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(t.pipesCount===0)return this;if(t.pipesCount===1)return e&&e!==t.pipes?this:(e||=t.pipes,t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit(`unpipe`,this,n),this);if(!e){var r=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var a=0;a0,r.flowing!==!1&&this.resume()):e===`readable`&&!r.endEmitted&&!r.readableListening&&(r.readableListening=r.needReadable=!0,r.flowing=!1,r.emittedReadable=!1,u(`on readable`,r.length,r.reading),r.length?j(this):r.reading||process.nextTick(L,this)),n},E.prototype.addListener=E.prototype.on,E.prototype.removeListener=function(e,t){var n=i.prototype.removeListener.call(this,e,t);return e===`readable`&&process.nextTick(I,this),n},E.prototype.removeAllListeners=function(e){var t=i.prototype.removeAllListeners.apply(this,arguments);return(e===`readable`||e===void 0)&&process.nextTick(I,this),t};function I(e){var t=e._readableState;t.readableListening=e.listenerCount(`readable`)>0,t.resumeScheduled&&!t.paused?t.flowing=!0:e.listenerCount(`data`)>0&&e.resume()}function L(e){u(`readable nexttick read 0`),e.read(0)}E.prototype.resume=function(){var e=this._readableState;return e.flowing||(u(`resume`),e.flowing=!e.readableListening,R(this,e)),e.paused=!1,this};function R(e,t){t.resumeScheduled||(t.resumeScheduled=!0,process.nextTick(z,e,t))}function z(e,t){u(`resume`,t.reading),t.reading||e.read(0),t.resumeScheduled=!1,e.emit(`resume`),B(e),t.flowing&&!t.reading&&e.read(0)}E.prototype.pause=function(){return u(`call pause flowing=%j`,this._readableState.flowing),this._readableState.flowing!==!1&&(u(`pause`),this._readableState.flowing=!1,this.emit(`pause`)),this._readableState.paused=!0,this};function B(e){var t=e._readableState;for(u(`flow`,t.flowing);t.flowing&&e.read()!==null;);}E.prototype.wrap=function(e){var t=this,n=this._readableState,r=!1;for(var i in e.on(`end`,function(){if(u(`wrapped end`),n.decoder&&!n.ended){var e=n.decoder.end();e&&e.length&&t.push(e)}t.push(null)}),e.on(`data`,function(i){u(`wrapped data`),n.decoder&&(i=n.decoder.write(i)),!(n.objectMode&&i==null)&&(!n.objectMode&&(!i||!i.length)||t.push(i)||(r=!0,e.pause()))}),e)this[i]===void 0&&typeof e[i]==`function`&&(this[i]=function(t){return function(){return e[t].apply(e,arguments)}}(i));for(var a=0;a=t.length?(n=t.decoder?t.buffer.join(``):t.buffer.length===1?t.buffer.first():t.buffer.concat(t.length),t.buffer.clear()):n=t.buffer.consume(e,t.decoder),n}function H(e){var t=e._readableState;u(`endReadable`,t.endEmitted),t.endEmitted||(t.ended=!0,process.nextTick(U,t,e))}function U(e,t){if(u(`endReadableNT`,e.endEmitted,e.length),!e.endEmitted&&e.length===0&&(e.endEmitted=!0,t.readable=!1,t.emit(`end`),e.autoDestroy)){var n=t._writableState;(!n||n.autoDestroy&&n.finished)&&t.destroy()}}typeof Symbol==`function`&&(E.from=function(e,t){return x===void 0&&(x=mt()),x(E,e,t)});function W(e,t){for(var n=0,r=e.length;n{t.exports=l;var n=Q().codes,r=n.ERR_METHOD_NOT_IMPLEMENTED,i=n.ERR_MULTIPLE_CALLBACK,a=n.ERR_TRANSFORM_ALREADY_TRANSFORMING,o=n.ERR_TRANSFORM_WITH_LENGTH_0,s=$();Z()(l,s);function c(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(r===null)return this.emit(`error`,new i);n.writechunk=null,n.writecb=null,t!=null&&this.push(t),r(e);var a=this._readableState;a.reading=!1,(a.needReadable||a.length{t.exports=r;var n=gt();Z()(r,n);function r(e){if(!(this instanceof r))return new r(e);n.call(this,e)}r.prototype._transform=function(e,t,n){n(null,e)}})),vt=e(((e,t)=>{var n;function r(e){var t=!1;return function(){t||(t=!0,e.apply(void 0,arguments))}}var i=Q().codes,a=i.ERR_MISSING_ARGS,o=i.ERR_STREAM_DESTROYED;function s(e){if(e)throw e}function c(e){return e.setHeader&&typeof e.abort==`function`}function l(e,t,i,a){a=r(a);var s=!1;e.on(`close`,function(){s=!0}),n===void 0&&(n=ft()),n(e,{readable:t,writable:i},function(e){if(e)return a(e);s=!0,a()});var l=!1;return function(t){if(!s&&!l){if(l=!0,c(e))return e.abort();if(typeof e.destroy==`function`)return e.destroy();a(t||new o(`pipe`))}}}function u(e){e()}function d(e,t){return e.pipe(t)}function f(e){return!e.length||typeof e[e.length-1]!=`function`?s:e.pop()}function p(){var e=[...arguments],t=f(e);if(Array.isArray(e[0])&&(e=e[0]),e.length<2)throw new a(`streams`);var n,r=e.map(function(i,a){var o=a0,function(e){n||=e,e&&r.forEach(u),!o&&(r.forEach(u),t(n))})});return e.reduce(d)}t.exports=p})),yt=e(((e,t)=>{t.exports=r;var n=Ye().EventEmitter;Z()(r,n),r.Readable=ht(),r.Writable=lt(),r.Duplex=$(),r.Transform=gt(),r.PassThrough=_t(),r.finished=ft(),r.pipeline=vt(),r.Stream=r;function r(){n.call(this)}r.prototype.pipe=function(e,t){var r=this;function i(t){e.writable&&!1===e.write(t)&&r.pause&&r.pause()}r.on(`data`,i);function a(){r.readable&&r.resume&&r.resume()}e.on(`drain`,a),!e._isStdio&&(!t||t.end!==!1)&&(r.on(`end`,s),r.on(`close`,c));var o=!1;function s(){o||(o=!0,e.end())}function c(){o||(o=!0,typeof e.destroy==`function`&&e.destroy())}function l(e){if(u(),n.listenerCount(this,`error`)===0)throw e}r.on(`error`,l),e.on(`error`,l);function u(){r.removeListener(`data`,i),e.removeListener(`drain`,a),r.removeListener(`end`,s),r.removeListener(`close`,c),r.removeListener(`error`,l),e.removeListener(`error`,l),r.removeListener(`end`,u),r.removeListener(`close`,u),e.removeListener(`close`,u)}return r.on(`end`,u),r.on(`close`,u),e.on(`close`,u),e.emit(`pipe`,r),e}}))();async function*bt(e,t,n){let r=1,i={chunks:[],length:0};for await(let a of n(e))for(i.chunks.push(a),i.length+=a.byteLength;i.length>t;){let e=i.chunks.length>1?Y.Buffer.concat(i.chunks):i.chunks[0];yield{partNumber:r,data:e.subarray(0,t)},i.chunks=[e.subarray(t)],i.length=i.chunks[0].byteLength,r+=1}yield{partNumber:r,data:i.chunks.length===1?i.chunks[0]:Y.Buffer.concat(i.chunks),lastPart:!0}}async function*xt(e,t){let n=1,r=0,i=t;for(;i{if(e instanceof Uint8Array)return xt(e,t);if(e instanceof yt.Readable)return bt(e,t,St);if(e instanceof String||typeof e==`string`)return xt(Y.Buffer.from(e),t);if(typeof e.stream==`function`)return bt(e.stream(),t,Ct);if(e instanceof ReadableStream)return bt(e,t,Ct);throw Error(`Body Data is unsupported format, expected data to be one of: string | Uint8Array | Buffer | Readable | ReadableStream | Blob;.`)},Tt=class e extends $e.EventEmitter{static MIN_PART_SIZE=1024*1024*5;MAX_PARTS=1e4;queueSize=4;partSize;leavePartsOnError=!1;tags=[];client;params;totalBytes;totalBytesSource;bytesUploadedSoFar;abortController;concurrentUploaders=[];createMultiPartPromise;abortMultipartUploadCommand=null;uploadedParts=[];uploadEnqueuedPartsCount=0;expectedPartsCount;uploadId;uploadEvent;isMultiPart=!0;singleUploadResult;sent=!1;constructor(t){if(super(),this.queueSize=t.queueSize||this.queueSize,this.leavePartsOnError=t.leavePartsOnError||this.leavePartsOnError,this.tags=t.tags||this.tags,this.client=t.client,this.params=t.params,!this.params)throw Error(`InputError: Upload requires params to be passed to upload.`);this.totalBytes=this.params.ContentLength??tt(this.params.Body),this.totalBytesSource=nt(this.params.Body,this.params.ContentLength),this.bytesUploadedSoFar=0,this.abortController=t.abortController??new AbortController,this.partSize=t.partSize||Math.max(e.MIN_PART_SIZE,Math.ceil((this.totalBytes||0)/this.MAX_PARTS)),this.totalBytes!==void 0&&(this.expectedPartsCount=Math.ceil(this.totalBytes/this.partSize)),this.__validateInput()}async abort(){this.abortController.abort()}async done(){if(this.sent)throw Error(`@aws-sdk/lib-storage: this instance of Upload has already executed .done(). Create a new instance.`);return this.sent=!0,await Promise.race([this.__doMultipartUpload(),this.__abortTimeout(this.abortController.signal)])}on(e,t){return this.uploadEvent=e,super.on(e,t)}async __uploadUsingPut(e){this.isMultiPart=!1;let t={...this.params,Body:e.data},n=this.client.config,r=n.requestHandler,i=r instanceof $e.EventEmitter?r:null,o=t=>{this.bytesUploadedSoFar=t.loaded,this.totalBytes=t.total,this.__notifyProgress({loaded:this.bytesUploadedSoFar,total:this.totalBytes,part:e.partNumber,Key:this.params.Key,Bucket:this.params.Bucket})};i!==null&&i.on(`xhr.upload.progress`,o);let c=await Promise.all([this.client.send(new Ge(t)),n?.endpoint?.()]),l=c[0],u=c[1];if(u||=a(await s(t,Ge,{...n})),!u)throw Error(`Could not resolve endpoint from S3 "client.config.endpoint()" nor EndpointsV2.`);i!==null&&i.off(`xhr.upload.progress`,o);let d=this.params.Key.split(`/`).map(e=>C(e)).join(`/`),f=C(this.params.Bucket),p=(()=>{let e=u.hostname.startsWith(`${f}.`),t=this.client.config.forcePathStyle,n=u.port?`:${u.port}`:``;return t?`${u.protocol}//${u.hostname}${n}/${f}/${d}`:e?`${u.protocol}//${u.hostname}${n}/${d}`:`${u.protocol}//${f}.${u.hostname}${n}/${d}`})();this.singleUploadResult={...l,Bucket:this.params.Bucket,Key:this.params.Key,Location:p};let m=tt(e.data);this.__notifyProgress({loaded:m,total:m,part:1,Key:this.params.Key,Bucket:this.params.Bucket})}async __createMultipartUpload(){let e=await this.client.config.requestChecksumCalculation();if(!this.createMultiPartPromise){let t={...this.params,Body:void 0};e===`WHEN_SUPPORTED`&&(t.ChecksumAlgorithm=this.params.ChecksumAlgorithm||Je.CRC32),this.createMultiPartPromise=this.client.send(new Ue(t)).then(e=>(this.abortMultipartUploadCommand=new Be({Bucket:this.params.Bucket,Key:this.params.Key,UploadId:e.UploadId}),e))}return this.createMultiPartPromise}async __doConcurrentUpload(e){for await(let t of e){if(this.uploadEnqueuedPartsCount>this.MAX_PARTS)throw Error(`Exceeded ${this.MAX_PARTS} parts in multipart upload to Bucket: ${this.params.Bucket} Key: ${this.params.Key}.`);if(this.abortController.signal.aborted)return;if(t.partNumber===1&&t.lastPart)return await this.__uploadUsingPut(t);if(!this.uploadId){let{UploadId:e}=await this.__createMultipartUpload();if(this.uploadId=e,this.abortController.signal.aborted)return}let e=tt(t.data)||0,n=this.client.config.requestHandler,r=n instanceof $e.EventEmitter?n:null,i=0,a=(n,r)=>{(Number(r.query.partNumber)||-1)===t.partNumber&&(n.total&&e&&(this.bytesUploadedSoFar+=n.loaded-i,i=n.loaded),this.__notifyProgress({loaded:this.bytesUploadedSoFar,total:this.totalBytes,part:t.partNumber,Key:this.params.Key,Bucket:this.params.Bucket}))};r!==null&&r.on(`xhr.upload.progress`,a),this.uploadEnqueuedPartsCount+=1,this.__validateUploadPart(t);let o=await this.client.send(new qe({...this.params,ContentLength:void 0,UploadId:this.uploadId,Body:t.data,PartNumber:t.partNumber}));if(r!==null&&r.off(`xhr.upload.progress`,a),this.abortController.signal.aborted)return;if(!o.ETag)throw Error(`Part ${t.partNumber} is missing ETag in UploadPart response. Missing Bucket CORS configuration for ETag header?`);this.uploadedParts.push({PartNumber:t.partNumber,ETag:o.ETag,...o.ChecksumCRC32&&{ChecksumCRC32:o.ChecksumCRC32},...o.ChecksumCRC32C&&{ChecksumCRC32C:o.ChecksumCRC32C},...o.ChecksumSHA1&&{ChecksumSHA1:o.ChecksumSHA1},...o.ChecksumSHA256&&{ChecksumSHA256:o.ChecksumSHA256}}),r===null&&(this.bytesUploadedSoFar+=e),this.__notifyProgress({loaded:this.bytesUploadedSoFar,total:this.totalBytes,part:t.partNumber,Key:this.params.Key,Bucket:this.params.Bucket})}}async __doMultipartUpload(){let e=wt(this.params.Body,this.partSize),t=[];for(let n=0;n{t.push(e)});this.concurrentUploaders.push(n)}if(await Promise.all(this.concurrentUploaders),t.length>=1)throw await this.markUploadAsAborted(),t[0];if(this.abortController.signal.aborted)throw await this.markUploadAsAborted(),Object.assign(Error(`Upload aborted.`),{name:`AbortError`});let n;if(this.isMultiPart){let{expectedPartsCount:e,uploadedParts:t,totalBytes:r,totalBytesSource:i}=this;if(r!==void 0&&e!==void 0&&t.length!==e)throw Error(`Expected ${e} part(s) but uploaded ${t.length} part(s). The expected part count is based on the byte-count of the input.params.Body, which was read from ${i} and is ${r}. If this is not correct, provide an override value by setting a number to input.params.ContentLength in bytes. -`);this.uploadedParts.sort((e,t)=>e.PartNumber-t.PartNumber);let a={...this.params,Body:void 0,UploadId:this.uploadId,MultipartUpload:{Parts:this.uploadedParts}};n=await this.client.send(new Je(a)),typeof n?.Location==`string`&&n.Location.includes(`%2F`)&&(n.Location=n.Location.replace(/%2F/g,`/`))}else n=this.singleUploadResult;return this.abortMultipartUploadCommand=null,this.tags.length&&await this.client.send(new nt({...this.params,Tagging:{TagSet:this.tags}})),n}async markUploadAsAborted(){this.uploadId&&!this.leavePartsOnError&&this.abortMultipartUploadCommand!==null&&(await this.client.send(this.abortMultipartUploadCommand),this.abortMultipartUploadCommand=null)}__notifyProgress(e){this.uploadEvent&&this.emit(this.uploadEvent,e)}async __abortTimeout(e){return new Promise((t,n)=>{e.onabort=()=>{let e=Error(`Upload aborted.`);e.name=`AbortError`,n(e)}})}__validateUploadPart(e){let t=dt(e.data);if(t===void 0)throw Error(`A dataPart was generated without a measurable data chunk size for part number ${e.partNumber}`);if(!(e.partNumber===1&&e.lastPart)&&!e.lastPart&&t!==this.partSize)throw Error(`The byte size for part number ${e.partNumber}, size ${t} does not match expected size ${this.partSize}`)}__validateInput(){if(!this.client)throw Error(`InputError: Upload requires a AWS client to do uploads with.`);if(this.partSize{n.invalidateQueries({queryKey:[`s3`,`buckets`,t.bucket,`objects`]})}},t[0]=n,t[1]=r),e(r)}async function Lt(e){let{bucket:t,key:n,file:r}=e;return await new Ft({client:F(),params:{Bucket:t,Key:n,Body:r,ContentType:r.type}}).done()}function Rt(){let t=(0,ct.c)(2),n=y(),r;return t[0]===n?r=t[1]:(r={mutationFn:zt,onSuccess:()=>{n.invalidateQueries({queryKey:[`s3`,`buckets`]})}},t[0]=n,t[1]=r),e(r)}async function zt(e){let{bucketName:t}=e,n=new Qe({Bucket:t});return await F().send(n)}function Bt(){let t=(0,ct.c)(2),n=y(),r;return t[0]===n?r=t[1]:(r={mutationFn:Vt,onSuccess:(e,t)=>{n.invalidateQueries({queryKey:[`s3`,`buckets`,t.bucket,`objects`]})}},t[0]=n,t[1]=r),e(r)}async function Vt(e){let{bucket:t,key:n}=e,r=new et({Bucket:t,Key:n});return await F().send(r)}export{Re as a,q as i,Bt as n,It as r,Rt as t}; \ No newline at end of file +`);this.uploadedParts.sort((e,t)=>e.PartNumber-t.PartNumber);let a={...this.params,Body:void 0,UploadId:this.uploadId,MultipartUpload:{Parts:this.uploadedParts}};n=await this.client.send(new Ve(a)),typeof n?.Location==`string`&&n.Location.includes(`%2F`)&&(n.Location=n.Location.replace(/%2F/g,`/`))}else n=this.singleUploadResult;return this.abortMultipartUploadCommand=null,this.tags.length&&await this.client.send(new Ke({...this.params,Tagging:{TagSet:this.tags}})),n}async markUploadAsAborted(){this.uploadId&&!this.leavePartsOnError&&this.abortMultipartUploadCommand!==null&&(await this.client.send(this.abortMultipartUploadCommand),this.abortMultipartUploadCommand=null)}__notifyProgress(e){this.uploadEvent&&this.emit(this.uploadEvent,e)}async __abortTimeout(e){return new Promise((t,n)=>{e.onabort=()=>{let e=Error(`Upload aborted.`);e.name=`AbortError`,n(e)}})}__validateUploadPart(e){let t=tt(e.data);if(t===void 0)throw Error(`A dataPart was generated without a measurable data chunk size for part number ${e.partNumber}`);if(!(e.partNumber===1&&e.lastPart)&&!e.lastPart&&t!==this.partSize)throw Error(`The byte size for part number ${e.partNumber}, size ${t} does not match expected size ${this.partSize}`)}__validateInput(){if(!this.client)throw Error(`InputError: Upload requires a AWS client to do uploads with.`);if(this.partSize{t.invalidateQueries({queryKey:[`s3`,`buckets`,n.bucket,`objects`]})}},e[0]=t,e[1]=r),n(r)}async function Dt(e){let{bucket:t,key:n,file:r}=e;return await new Tt({client:g(),params:{Bucket:t,Key:n,Body:r,ContentType:r.type}}).done()}function Ot(){let e=(0,Qe.c)(2),t=F(),r;return e[0]===t?r=e[1]:(r={mutationFn:kt,onSuccess:()=>{t.invalidateQueries({queryKey:[`s3`,`buckets`]})}},e[0]=t,e[1]=r),n(r)}async function kt(e){let{bucketName:t}=e,n=new He({Bucket:t});return await g().send(n)}function At(){let e=(0,Qe.c)(2),t=F(),r;return e[0]===t?r=e[1]:(r={mutationFn:jt,onSuccess:(e,n)=>{t.invalidateQueries({queryKey:[`s3`,`buckets`,n.bucket,`objects`]})}},e[0]=t,e[1]=r),n(r)}async function jt(e){let{bucket:t,key:n}=e,r=new We({Bucket:t,Key:n});return await g().send(r)}export{Oe as a,q as i,At as n,Et as r,Ot as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/select.js b/spinifex/services/spinifexui/frontend/dist/assets/select.js index f480ac31b..9e5b4ac1e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/select.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/select.js @@ -1 +1 @@ -import{t as e}from"./check.js";import{t}from"./chevron-down.js";import{$a as n,$i as r,Aa as i,Ai as a,Ao as o,Ba as s,Bi as c,Ca as l,Da as u,Di as d,Do as f,Ea as p,Ei as m,Fa as h,Fi as g,Fo as _,Ga as v,Gi as y,Ha as b,Ho as x,Ia as S,Ii as C,Ja as w,Jr as ee,Ka as T,Ki as E,La as D,Li as te,Ma as O,Mi as k,Mo as A,Na as j,Ni as M,No as N,Oa as P,Oi as F,Oo as I,Pa as ne,Pi as re,Po as L,Qi as ie,Ra as R,Ri as z,Sa as ae,Ta as B,Ti as V,Ua as H,Uo as U,Va as W,Wa as oe,Wi as G,Xr as se,Ya as ce,Yi as K,Yr as le,Zi as q,_a as ue,aa as de,ba as fe,ca as pe,cs as me,do as J,ea as he,fa as ge,fo as _e,ha as ve,hi as ye,ia as be,ii as xe,ja as Se,ji as Ce,jo as we,ka as Te,ki as Ee,ko as De,la as Oe,ma as Y,na as ke,ni as Ae,oa as je,oi as Me,po as Ne,qa as Pe,qr as Fe,ri as Ie,sa as Le,si as Re,so as ze,ta as Be,ti as Ve,uo as X,va as He,wa as Ue,wi as We,wo as Ge,xa as Ke,ya as qe,za as Je,zi as Ye}from"./index.js";var Xe=Ge(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),Z=x(),Q=me(U()),Ze=Q.createContext({formRef:{current:{fields:new Map}},errors:{},clearErrors:o,validationMode:`onSubmit`,submitAttemptedRef:{current:!1}});function Qe(){return Q.useContext(Ze)}function $e(e,t={}){let n=`rootStore`in e?e.rootStore:e,r=n.context.dataRef,{enabled:i=!0,event:a=`click`,toggle:o=!0,ignoreMouse:s=!1,stickIfOpen:c=!0,touchOpenDelay:l=0,reason:u=be}=t,d=Q.useRef(void 0),f=Y(),p=ve(),m=Q.useMemo(()=>({onPointerDown(e){d.current=e.pointerType},onMouseDown(e){let t=d.current,i=e.nativeEvent,m=n.select(`open`);if(e.button!==0||a===`click`||Te(t,!0)&&s)return;let g=r.current.openEvent,_=g?.type,v=n.select(`domReferenceElement`)!==e.currentTarget,y=m&&v||!(m&&o&&(!(g&&c)||_===`click`||_===`mousedown`)),b=R(i);if(h(b)){let e=q(u,i,b);y&&t===`touch`&&l>0?p.start(l,()=>{n.setOpen(!0,e)}):n.setOpen(y,e);return}let x=e.currentTarget;f.request(()=>{let e=q(u,i,x);y&&t===`touch`&&l>0?p.start(l,()=>{n.setOpen(!0,e)}):n.setOpen(y,e)})},onClick(e){if(a===`mousedown-only`)return;let t=d.current;if(a===`mousedown`&&t){d.current=void 0;return}if(Te(t,!0)&&s)return;let i=n.select(`open`),f=r.current.openEvent,m=n.select(`domReferenceElement`)!==e.currentTarget,h=i&&m||!(i&&o&&(!(f&&c)||P(f))),g=q(u,e.nativeEvent,e.currentTarget);h&&t===`touch`&&l>0?p.start(l,()=>{n.setOpen(!0,g)}):n.setOpen(h,g)},onKeyDown(){d.current=void 0}}),[r,a,s,n,c,o,f,p,l,u]);return Q.useMemo(()=>i?{reference:m}:De,[i,m])}var et=`Escape`;function tt(e,t,n){switch(e){case`vertical`:return t;case`horizontal`:return n;default:return t||n}}function nt(e,t){return tt(t,e===`ArrowUp`||e===`ArrowDown`,e===`ArrowLeft`||e===`ArrowRight`)}function rt(e,t,n){return tt(t,e===`ArrowDown`,n?e===`ArrowLeft`:e===`ArrowRight`)||e===`Enter`||e===` `||e===``}function it(e,t,n){return tt(t,n?e===s:e===W,e===Je)}function at(e,t,n,r){return t===`both`||t===`horizontal`&&r&&r>1?e===et:tt(t,n?e===W:e===s,e===b)}function ot(e,t){let n=`rootStore`in e?e.rootStore:e,a=n.useState(`open`),o=n.useState(`floatingElement`),s=n.useState(`domReferenceElement`),c=n.context.dataRef,{listRef:u,activeIndex:d,onNavigate:f=()=>{},enabled:m=!0,selectedIndex:h=null,allowEscape:g=!1,loopFocus:_=!1,nested:v=!1,rtl:y=!1,virtual:b=!1,focusItemOnOpen:x=`auto`,focusItemOnHover:w=!0,openOnArrowKeyDown:ee=!0,disabledIndices:T=void 0,orientation:E=`vertical`,parentOrientation:te,cols:A=1,id:N,resetOnPointerLeave:P=!0,externalTree:F}=t,I=Ye(j(o)),re=k(),L=M(F);X(()=>{c.current.orientation=E},[c,E]);let ie=ne(s),z=Q.useRef(x),V=Q.useRef(h??-1),H=Q.useRef(null),U=Q.useRef(!0),W=J(e=>{f(V.current===-1?null:V.current,e)}),G=Q.useRef(W),se=Q.useRef(!!o),ce=Q.useRef(a),K=Q.useRef(!1),le=Q.useRef(!1),de=Q.useRef(null),pe=Ye(T),me=Ye(a),he=Ye(h),ge=Ye(P),_e=J(()=>{function e(e){b?L?.events.emit(`virtualfocus`,e):de.current=C(e,{sync:K.current,preventScroll:!0})}let t=u.current[V.current],n=le.current;t&&e(t),(K.current?e=>e():requestAnimationFrame)(()=>{let r=u.current[V.current]||t;r&&(t||e(r),be&&(n||!U.current)&&r.scrollIntoView?.({block:`nearest`,inline:`nearest`}))})});X(()=>{m&&(a&&o?(V.current=h??-1,z.current&&h!=null&&(le.current=!0,W())):se.current&&(V.current=-1,G.current()))},[m,a,o,h,W]),X(()=>{if(m){if(!a){K.current=!1;return}if(o)if(d==null){if(K.current=!1,he.current!=null)return;if(se.current&&(V.current=-1,_e()),(!ce.current||!se.current)&&z.current&&(H.current!=null||z.current===!0&&H.current==null)){let e=0,t=()=>{u.current[0]==null?(e<2&&(e?requestAnimationFrame:queueMicrotask)(t),e+=1):(V.current=H.current==null||rt(H.current,E,y)||v?l(u):ae(u),H.current=null,W())};t()}}else B(u.current,d)||(V.current=d,_e(),le.current=!1)}},[m,a,o,d,he,v,u,E,y,W,_e,pe]),X(()=>{if(!m||o||!L||b||!se.current)return;let e=L.nodesRef.current,t=e.find(e=>e.id===re)?.context?.elements.floating,n=S(oe(o)),r=e.some(e=>e.context&&D(e.context.elements.floating,n));t&&!r&&U.current&&t.focus({preventScroll:!0})},[m,o,L,re,b]),X(()=>{G.current=W,ce.current=a,se.current=!!o}),X(()=>{a||(H.current=null,z.current=x)},[a,x]);let ve=d!=null,ye=J(e=>{if(!me.current)return;let t=u.current.indexOf(e.currentTarget);t!==-1&&(V.current!==t||d!==t)&&(V.current=t,W(e))}),be=Q.useMemo(()=>({onFocus(e){K.current=!0,ye(e)},onClick:({currentTarget:e})=>e.focus({preventScroll:!0}),onMouseMove(e){K.current=!0,le.current=!1,w&&ye(e)},onPointerLeave(e){if(!me.current||!U.current||e.pointerType===`touch`)return;K.current=!0;let t=e.relatedTarget;if(!(!w||u.current.includes(t))&&ge.current&&(de.current?.(),de.current=null,V.current=-1,W(e),!b)){let e=I.current,t=S(oe(e));e&&D(e,t)&&e.focus({preventScroll:!0})}}}),[ye,me,I,w,u,W,ge,b]),xe=Q.useCallback(()=>te??L?.nodesRef.current.find(e=>e.id===re)?.context?.dataRef?.current.orientation,[re,L,te]),Ce=J(e=>{if(U.current=!1,K.current=!0,e.which===229||!me.current&&e.currentTarget===I.current)return;if(v&&at(e.key,E,y,A)){nt(e.key,xe())||O(e),n.setOpen(!1,q(Be,e.nativeEvent)),Ne(s)&&(b?L?.events.emit(`virtualfocus`,s):s.focus());return}let t=V.current,r=l(u,T),i=ae(u,T);if(ie||(e.key===`Home`&&(O(e),V.current=r,W(e)),e.key===`End`&&(O(e),V.current=i,W(e))),A>1){let t=Array.from({length:u.current.length},()=>({width:1,height:1})),n=ue(t,A,!1),a=n.findIndex(e=>e!=null&&!p(u.current,e,T)),o=n.reduce((e,t,n)=>t!=null&&!p(u.current,t,T)?n:e,-1),s=n[Ke(n.map(e=>e==null?null:u.current[e]),{event:e,orientation:E,loopFocus:_,rtl:y,cols:A,disabledIndices:fe([...(typeof T==`function`?null:T)||u.current.map((e,t)=>p(u.current,t,T)?t:void 0),void 0],n),minIndex:a,maxIndex:o,prevIndex:qe(V.current>i?r:V.current,t,n,A,e.key===`ArrowDown`?`bl`:e.key===(y?`ArrowLeft`:`ArrowRight`)?`tr`:`tl`),stopEvent:!0})];if(s!=null&&(V.current=s,W(e)),E===`both`)return}if(nt(e.key,E)){if(O(e),a&&!b&&S(e.currentTarget.ownerDocument)===e.currentTarget){V.current=rt(e.key,E,y)?r:i,W(e);return}rt(e.key,E,y)?_?t>=i?g&&t!==u.current.length?V.current=-1:(K.current=!1,V.current=r):V.current=He(u.current,{startingIndex:t,disabledIndices:T}):V.current=Math.min(i,He(u.current,{startingIndex:t,disabledIndices:T})):_?t<=r?g&&t!==-1?V.current=u.current.length:(K.current=!1,V.current=i):V.current=He(u.current,{startingIndex:t,decrement:!0,disabledIndices:T}):V.current=Math.max(r,He(u.current,{startingIndex:t,decrement:!0,disabledIndices:T})),B(u.current,V.current)&&(V.current=-1),W(e)}}),we=Q.useMemo(()=>b&&a&&ve&&{"aria-activedescendant":`${N}-${d}`},[b,a,ve,N,d]),Te=Q.useMemo(()=>({"aria-orientation":E===`both`?void 0:E,...ie?{}:we,onKeyDown(e){if(e.key===`Tab`&&e.shiftKey&&a&&!b){let t=R(e.nativeEvent);if(t&&!D(I.current,t))return;O(e),n.setOpen(!1,q(r,e.nativeEvent)),Ne(s)&&s.focus();return}Ce(e)},onPointerMove(){U.current=!0}}),[we,Ce,I,E,ie,n,a,b,s]),Ee=Q.useMemo(()=>{function e(e){x===`auto`&&i(e.nativeEvent)&&(z.current=!b)}function t(e){z.current=x,x===`auto`&&Se(e.nativeEvent)&&(z.current=!0)}return{onKeyDown(e){let t=n.select(`open`);U.current=!1;let r=e.key.startsWith(`Arrow`),i=it(e.key,xe(),y),a=nt(e.key,E),o=(v?i:a)||e.key===`Enter`||e.key.trim()===``;if(b&&t)return Ce(e);if(!(!t&&!ee&&r)){if(o){let t=nt(e.key,xe());H.current=v&&t?null:e.key}if(v){i&&(O(e),t?(V.current=l(u,pe.current),W(e)):n.setOpen(!0,q(Be,e.nativeEvent,e.currentTarget)));return}a&&(he.current!=null&&(V.current=he.current),O(e),!t&&ee?n.setOpen(!0,q(Be,e.nativeEvent,e.currentTarget)):Ce(e),t&&W(e))}},onFocus(e){n.select(`open`)&&!b&&(V.current=-1,W(e))},onPointerDown:t,onPointerEnter:t,onMouseDown:e,onClick:e}},[Ce,pe,x,u,v,W,n,ee,E,xe,y,he,b]),De=Q.useMemo(()=>({...we,...Ee}),[we,Ee]);return Q.useMemo(()=>m?{reference:De,floating:Te,item:be,trigger:Ee}:{},[m,De,Te,Ee,be])}function st(e,t){let n=`rootStore`in e?e.rootStore:e,r=n.context.dataRef,i=n.useState(`open`),{listRef:a,elementsRef:o,activeIndex:s,onMatch:c,onTypingChange:l,enabled:u=!0,resetMs:d=750,selectedIndex:f=null}=t,p=ve(),m=Q.useRef(``),h=Q.useRef(f??s??-1),g=Q.useRef(null);X(()=>{!i&&f!==null||(p.clear(),g.current=null,m.current!==``&&(m.current=``))},[i,f,p]),X(()=>{i&&m.current===``&&(h.current=f??s??-1)},[i,f,s]);let _=J(e=>{e?r.current.typing||(r.current.typing=e,l?.(e)):r.current.typing&&(r.current.typing=e,l?.(e))}),v=J(e=>{function t(e){let t=o?.current[e];return!t||Ue(t)}function n(e,n,r=0){if(e.length===0)return-1;let i=(r%e.length+e.length)%e.length,a=n.toLocaleLowerCase();for(let n=0;n0&&e.key===` `&&(O(e),_(!0)),m.current.length>0&&m.current[0]!==` `&&n(r,m.current)===-1&&e.key!==` `&&_(!1),r==null||e.key.length!==1||e.ctrlKey||e.metaKey||e.altKey)return;i&&e.key!==` `&&(O(e),_(!0));let l=m.current===``;l&&(h.current=f??s??-1),r.every(e=>e?e[0]?.toLocaleLowerCase()!==e[1]?.toLocaleLowerCase():!0)&&m.current===e.key&&(m.current=``,h.current=g.current),m.current+=e.key,p.start(d,()=>{m.current=``,h.current=g.current,_(!1)});let u=((l?f??s??-1:h.current)??0)+1,v=n(r,m.current,u);v===-1?e.key!==` `&&(m.current=``,_(!1)):(c?.(v),g.current=v)}),y=J(e=>{let t=e.relatedTarget,r=n.select(`domReferenceElement`),i=n.select(`floatingElement`),a=D(r,t),o=D(i,t);a||o||(p.clear(),m.current=``,h.current=g.current,_(!1))}),b=Q.useMemo(()=>({onKeyDown:v,onBlur:y}),[v,y]),x=Q.useMemo(()=>({onKeyDown:v,onBlur:y}),[v,y]);return Q.useMemo(()=>u?{reference:b,floating:x}:{},[u,b,x])}function ct(e){let[t,n]=Q.useState({current:e,previous:null});return e!==t.current&&n({current:e,previous:t.current}),t.previous}var lt=Q.createContext(null),ut=Q.createContext(null);function dt(){let e=Q.useContext(lt);if(e===null)throw Error(N(60));return e}function ft(){let e=Q.useContext(ut);if(e===null)throw Error(N(61));return e}var pt=(e,t)=>Object.is(e,t);function mt(e,t,n){return e==null||t==null?Object.is(e,t):n(e,t)}function ht(e,t,n){return!e||e.length===0?!1:e.some(e=>e===void 0?!1:mt(t,e,n))}function gt(e,t,n){return!e||e.length===0?-1:e.findIndex(e=>e===void 0?!1:mt(e,t,n))}function _t(e,t,n){return e.filter(e=>!mt(t,e,n))}function vt(e){if(e==null)return``;if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return String(e)}}function yt(e){return e!=null&&e.length>0&&typeof e[0]==`object`&&e[0]!=null&&`items`in e[0]}function bt(e){if(!Array.isArray(e))return e!=null&&`null`in e;let t=e;if(yt(t)){for(let e of t)for(let t of e.items)if(t&&t.value==null&&t.label!=null)return!0;return!1}for(let e of t)if(e&&e.value==null&&e.label!=null)return!0;return!1}function xt(e,t){if(t&&e!=null)return t(e)??``;if(e&&typeof e==`object`){if(`label`in e&&e.label!=null)return String(e.label);if(`value`in e)return String(e.value)}return vt(e)}function St(e,t){return t&&e!=null?t(e)??``:e&&typeof e==`object`&&`value`in e&&`label`in e?vt(e.value):vt(e)}function Ct(e,t,n){function r(){return xt(e,n)}if(n&&e!=null)return n(e);if(e&&typeof e==`object`&&`label`in e&&e.label!=null)return e.label;if(t&&!Array.isArray(t))return t[e]??r();if(Array.isArray(t)){let n=t,i=yt(n)?n.flatMap(e=>e.items):n;if(typeof e!=`object`||!e){let t=i.find(t=>t.value===e);return t&&t.label!=null?t.label:r()}if(`value`in e){let t=i.find(t=>t&&t.value===e.value);if(t&&t.label!=null)return t.label}}return r()}function wt(e,t,n){return e.reduce((e,r,i)=>(i>0&&e.push(`, `),e.push((0,Z.jsx)(Q.Fragment,{children:Ct(r,t,n)},i)),e),[])}var $={id:F(e=>e.id),labelId:F(e=>e.labelId),modal:F(e=>e.modal),multiple:F(e=>e.multiple),items:F(e=>e.items),itemToStringLabel:F(e=>e.itemToStringLabel),itemToStringValue:F(e=>e.itemToStringValue),isItemEqualToValue:F(e=>e.isItemEqualToValue),value:F(e=>e.value),hasSelectedValue:F(e=>{let{value:t,multiple:n,itemToStringValue:r}=e;return t==null?!1:n&&Array.isArray(t)?t.length>0:St(t,r)!==``}),hasNullItemLabel:F((e,t)=>t?bt(e.items):!1),open:F(e=>e.open),mounted:F(e=>e.mounted),forceMount:F(e=>e.forceMount),transitionStatus:F(e=>e.transitionStatus),openMethod:F(e=>e.openMethod),activeIndex:F(e=>e.activeIndex),selectedIndex:F(e=>e.selectedIndex),isActive:F((e,t)=>e.activeIndex===t),isSelected:F((e,t,n)=>{let r=e.isItemEqualToValue,i=e.value;return e.multiple?Array.isArray(i)&&i.some(e=>mt(n,e,r)):e.selectedIndex===t&&e.selectedIndex!==null?!0:mt(n,i,r)}),isSelectedByFocus:F((e,t)=>e.selectedIndex===t),popupProps:F(e=>e.popupProps),triggerProps:F(e=>e.triggerProps),triggerElement:F(e=>e.triggerElement),positionerElement:F(e=>e.positionerElement),listElement:F(e=>e.listElement),scrollUpArrowVisible:F(e=>e.scrollUpArrowVisible),scrollDownArrowVisible:F(e=>e.scrollDownArrowVisible),hasScrollArrows:F(e=>e.hasScrollArrows)};function Tt(e,t=-(2**53-1),n=2**53-1){return Math.max(t,Math.min(e,n))}function Et(e,t){return Math.max(0,e-t)}function Dt(e,t){if(t<=0)return 0;let n=Tt(e,0,t),r=n,i=t-n,a=r<=1,o=i<=1;return a&&o?r<=i?0:t:a?0:o?t:n}function Ot(e){let{id:t,value:n,defaultValue:r=null,onValueChange:i,open:o,defaultOpen:s=!1,onOpenChange:c,name:l,form:u,autoComplete:f,disabled:p=!1,readOnly:h=!1,required:g=!1,modal:_=!0,actionsRef:y,inputRef:b,onOpenChangeComplete:x,items:S,multiple:C=!1,itemToStringLabel:ee,itemToStringValue:T,isItemEqualToValue:E=pt,highlightItemOnHover:D=!0,children:O}=e,{clearErrors:k}=Qe(),{setDirty:j,setTouched:M,setFocused:N,shouldValidateOnChange:P,validityData:F,setFilled:ne,name:re,disabled:ie,validation:R,validationMode:ae}=w(),B=v({id:t}),H=ie||p,U=re??l,[W,oe]=Le({controlled:n,default:C?r??I:r,name:`Select`,state:`value`}),[G,se]=Le({controlled:o,default:s,name:`Select`,state:`open`}),ce=Q.useRef([]),K=Q.useRef([]),le=Q.useRef(null),ue=Q.useRef(null),de=Q.useRef(0),fe=Q.useRef(null),me=Q.useRef([]),he=Q.useRef(!1),ge=Q.useRef(!1),_e=Q.useRef(null),ve=Q.useRef({allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1}),ye=Q.useRef(!1),{mounted:be,setMounted:Se,transitionStatus:Ce}=pe(G),{openMethod:Te,triggerProps:Ee}=Ve(G),Y=A(()=>new m({id:B,labelId:void 0,modal:_,multiple:C,itemToStringLabel:ee,itemToStringValue:T,isItemEqualToValue:E,value:W,open:G,mounted:be,transitionStatus:Ce,items:S,forceMount:!1,openMethod:null,activeIndex:null,selectedIndex:null,popupProps:{},triggerProps:{},triggerElement:null,positionerElement:null,listElement:null,scrollUpArrowVisible:!1,scrollDownArrowVisible:!1,hasScrollArrows:!1})).current,Me=d(Y,$.activeIndex),Ne=d(Y,$.selectedIndex),Pe=d(Y,$.triggerElement),Fe=d(Y,$.positionerElement),Ie=ct(Te),Re=Te??Ie,ze=Q.useMemo(()=>C&&Array.isArray(W)&&W.length===0?``:St(W,T),[C,W,T]),Be=Q.useMemo(()=>C&&Array.isArray(W)?W.map(e=>St(e,T)):St(W,T),[C,W,T]);je(Ye(Y.state.triggerElement),{id:B,value:W,getValue:J(()=>Be)});let He=Q.useRef(W);X(()=>{W!==He.current&&Y.set(`forceMount`,!0)},[Y,W]),X(()=>{ne(C?Array.isArray(W)&&W.length>0:W!=null)},[C,W,ne]),X(function(){if(G)return;let e=me.current;if(C){let t=Array.isArray(W)?W:[];if(t.length===0){Y.set(`selectedIndex`,null);return}let n=t[t.length-1],r=gt(e,n,E);Y.set(`selectedIndex`,r===-1?null:r);return}let t=gt(e,W,E);Y.set(`selectedIndex`,t===-1?null:t)},[C,G,W,me,E,Y]),Ae(W,()=>{k(U),j(W!==F.initialValue),P()?R.commit(W):R.commit(W,!0)});let Ue=J((e,t)=>{if(c?.(e,t),!t.isCanceled&&(se(e),!e&&(t.reason===`focus-out`||t.reason===`outside-press`)&&(M(!0),N(!1),ae===`onBlur`&&R.commit(W)),!e&&Y.state.activeIndex!==null)){let e=ce.current[Y.state.activeIndex];queueMicrotask(()=>{e?.setAttribute(`tabindex`,`-1`)})}}),Ge=J(()=>{Se(!1),Y.update({activeIndex:null,openMethod:null}),x?.(!1)});Oe({enabled:!y,open:G,ref:le,onComplete(){G||Ge()}}),Q.useImperativeHandle(y,()=>({unmount:Ge}),[Ge]);let Ke=J((e,t)=>{i?.(e,t),!t.isCanceled&&oe(e)}),qe=J(()=>{let e=Y.state.listElement||le.current;if(!e)return;let t=Et(e.scrollHeight,e.clientHeight),n=Dt(e.scrollTop,t),r=n>0,i=nL(Xe(),Ee,B?{id:B}:De),[Xe,Ee,B]);xe(()=>{Y.update({popupProps:Ze(),triggerProps:tt})}),X(()=>{Y.update({id:B,modal:_,multiple:C,value:W,open:G,mounted:be,transitionStatus:Ce,popupProps:Ze(),triggerProps:tt,items:S,itemToStringLabel:ee,itemToStringValue:T,isItemEqualToValue:E,openMethod:Re})},[Y,B,_,C,W,G,be,Ce,Ze,tt,S,ee,T,E,Re]);let nt=Q.useMemo(()=>({store:Y,name:U,required:g,disabled:H,readOnly:h,multiple:C,highlightItemOnHover:D,setValue:Ke,setOpen:Ue,listRef:ce,popupRef:le,scrollHandlerRef:ue,handleScrollArrowVisibility:qe,scrollArrowsMountedCountRef:de,getItemProps:et,events:Je.context.events,valueRef:fe,valuesRef:me,labelsRef:K,typingRef:he,selectionRef:ve,selectedItemTextRef:_e,validation:R,onOpenChangeComplete:x,keyboardActiveRef:ge,alignItemWithTriggerActiveRef:ye,initialValueRef:He}),[Y,U,g,H,h,C,D,Ke,Ue,et,Je.context.events,R,x,qe]),rt=we(b,R.inputRef),it=C&&Array.isArray(W)&&W.length>0,at=C?void 0:U,dt=Q.useMemo(()=>!C||!Array.isArray(W)||!U?null:W.map(e=>{let t=St(e,T);return(0,Z.jsx)(`input`,{type:`hidden`,form:u,name:U,value:t},t)}),[C,W,u,U,T]);return(0,Z.jsx)(lt.Provider,{value:nt,children:(0,Z.jsxs)(ut.Provider,{value:Je,children:[O,(0,Z.jsx)(`input`,{...R.getInputValidationProps({onFocus(){Y.state.triggerElement?.focus({focusVisible:!0})},onChange(e){if(e.nativeEvent.defaultPrevented)return;let t=e.currentTarget.value,n=q(ke,e.nativeEvent);function r(){if(C)return;let e=me.current.find(e=>St(e,T).toLowerCase()===t.toLowerCase()||xt(e,ee).toLowerCase()===t.toLowerCase());e!=null&&(j(e!==F.initialValue),Ke(e,n),P()&&R.commit(e))}Y.set(`forceMount`,!0),queueMicrotask(r)}}),id:B&&at==null?`${B}-hidden-input`:void 0,form:u,name:at,autoComplete:f,value:ze,disabled:H,required:g&&!it,readOnly:h,ref:rt,style:U?z:te,tabIndex:-1,"aria-hidden":!0,suppressHydrationWarning:!0}),dt]})})}function kt(e,t){return e??t}function At(e){let t=e.getBoundingClientRect(),n=_e(e),r=n.getComputedStyle(e,`::before`),i=n.getComputedStyle(e,`::after`);if(!(r.content!==`none`||i.content!==`none`))return t;let a=parseFloat(r.width)||0,o=parseFloat(r.height)||0,s=parseFloat(i.width)||0,c=parseFloat(i.height)||0,l=Math.max(t.width,a,s),u=Math.max(t.height,o,c),d=l-t.width,f=u-t.height;return{left:t.left-d/2,right:t.right+d/2,top:t.top-f/2,bottom:t.bottom+f/2}}var jt=2,Mt=400,Nt=200,Pt={...y,...ce,value:()=>null},Ft=Q.forwardRef(function(e,t){let{render:n,className:r,id:i,disabled:a=!1,nativeButton:o=!0,style:s,...c}=e,{setTouched:l,setFocused:u,validationMode:p,state:m,disabled:h}=w(),{labelId:g}=T(),{store:_,setOpen:y,selectionRef:b,validation:x,readOnly:S,required:C,alignItemWithTriggerActiveRef:ee,disabled:E,keyboardActiveRef:te}=dt(),O=h||E||a,k=d(_,$.open),A=d(_,$.value),M=d(_,$.triggerProps),N=d(_,$.positionerElement),P=d(_,$.listElement),F=d(_,$.id),I=d(_,$.labelId),ne=d(_,$.hasSelectedValue),re=!ne&&k,R=d(_,$.hasNullItemLabel,re),z=i??F,ae=kt(g,I);v({id:z});let B=Ye(N),V=Q.useRef(null),{getButtonProps:H,buttonRef:U}=ze({disabled:O,native:o}),W=we(t,V,U,J(e=>{_.set(`triggerElement`,e)})),G=ve(),se=ve(),ce=ve(),K=ve();Q.useEffect(()=>{if(k)return ne||R?K.start(Nt,()=>{b.current.allowUnselectedMouseUp=!0,ce.start(Nt,()=>{b.current.allowSelectedMouseUp=!0})}):ce.start(Mt,()=>{b.current.allowUnselectedMouseUp=!0,b.current.allowSelectedMouseUp=!0}),()=>{ce.clear(),K.clear()};b.current={allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1},se.clear()},[k,ne,R,b,se,ce,K]);let le=Q.useMemo(()=>P?.id??j(N)?.id,[P,N]),ue=L(M,{id:z,role:`combobox`,"aria-expanded":k?`true`:`false`,"aria-haspopup":`listbox`,"aria-controls":k?le:void 0,"aria-labelledby":ae,"aria-readonly":S||void 0,"aria-required":C||void 0,tabIndex:O?-1:0,ref:W,onFocus(e){u(!0),k&&ee.current&&y(!1,q(ke,e.nativeEvent)),G.start(0,()=>{_.set(`forceMount`,!0)})},onBlur(e){D(N,e.relatedTarget)||(l(!0),u(!1),p===`onBlur`&&x.commit(A))},onPointerMove(){te.current=!1},onKeyDown(){te.current=!0},onMouseDown(e){if(k)return;let t=oe(e.currentTarget);function n(e){if(!V.current)return;let t=e.target;if(D(V.current,t)||D(B.current,t)||t===V.current)return;let n=At(V.current);e.clientX>=n.left-jt&&e.clientX<=n.right+jt&&e.clientY>=n.top-jt&&e.clientY<=n.bottom+jt||y(!1,q(ie,e))}se.start(0,()=>{t.addEventListener(`mouseup`,n,{once:!0})})}},x.getValidationProps,c,H);ue.role=`combobox`;let de={...m,open:k,disabled:O,value:A,readOnly:S,placeholder:!ne};return f(`button`,e,{ref:[t,V],state:de,stateAttributesMapping:Pt,props:ue})}),It={value:()=>null},Lt=Q.forwardRef(function(e,t){let{className:n,render:r,children:i,placeholder:a,style:o,...s}=e,{store:c,valueRef:l}=dt(),u=d(c,$.value),p=d(c,$.items),m=d(c,$.itemToStringLabel),h=d(c,$.hasSelectedValue),g=!h&&a!=null&&i==null,_=d(c,$.hasNullItemLabel,g),v={value:u,placeholder:!h},y=null;return y=typeof i==`function`?i(u):i??(!h&&a!=null&&!_?a:Array.isArray(u)?wt(u,p,m):Ct(u,p,m)),f(`span`,e,{state:v,ref:[t,l],props:[{children:y},s],stateAttributesMapping:It})}),Rt=Q.forwardRef(function(e,t){let{className:n,render:r,style:i,...a}=e,{store:o}=dt();return f(`span`,e,{state:{open:d(o,$.open)},ref:t,props:[{"aria-hidden":!0,children:`▼`},a],stateAttributesMapping:E})}),zt=Q.createContext(void 0),Bt=Q.forwardRef(function(e,t){let{store:n}=dt(),r=d(n,$.mounted),i=d(n,$.forceMount);return r||i?(0,Z.jsx)(zt.Provider,{value:!0,children:(0,Z.jsx)(re,{ref:t,...e})}):null}),Vt=Q.createContext({register:()=>{},unregister:()=>{},subscribeMapChange:()=>()=>{},elementsRef:{current:[]},nextIndexRef:{current:0}});function Ht(){return Q.useContext(Vt)}function Ut(e){let{children:t,elementsRef:n,labelsRef:r,onMapChange:i}=e,a=J(i),o=Q.useRef(0),s=A(Gt).current,c=A(Wt).current,[l,u]=Q.useState(0),d=Q.useRef(l),f=J((e,t)=>{c.set(e,t??null),d.current+=1,u(d.current)}),p=J(e=>{c.delete(e),d.current+=1,u(d.current)}),m=Q.useMemo(()=>{let e=new Map;return Array.from(c.keys()).filter(e=>e.isConnected).sort(Kt).forEach((t,n)=>{let r=c.get(t)??{};e.set(t,{...r,index:n})}),e},[c,l]);X(()=>{if(typeof MutationObserver!=`function`||m.size===0)return;let e=new MutationObserver(e=>{let t=new Set,n=e=>t.has(e)?t.delete(e):t.add(e);e.forEach(e=>{e.removedNodes.forEach(n),e.addedNodes.forEach(n)}),t.size===0&&(d.current+=1,u(d.current))});return m.forEach((t,n)=>{n.parentElement&&e.observe(n.parentElement,{childList:!0})}),()=>{e.disconnect()}},[m]),X(()=>{d.current===l&&(n.current.length!==m.size&&(n.current.length=m.size),r&&r.current.length!==m.size&&(r.current.length=m.size),o.current=m.size),a(m)},[a,m,n,r,l]),X(()=>()=>{n.current=[]},[n]),X(()=>()=>{r&&(r.current=[])},[r]);let h=J(e=>(s.add(e),()=>{s.delete(e)}));X(()=>{s.forEach(e=>e(m))},[s,m]);let g=Q.useMemo(()=>({register:f,unregister:p,subscribeMapChange:h,elementsRef:n,labelsRef:r,nextIndexRef:o}),[f,p,h,n,r,o]);return(0,Z.jsx)(Vt.Provider,{value:g,children:t})}function Wt(){return new Map}function Gt(){return new Set}function Kt(e,t){let n=e.compareDocumentPosition(t);return n&Node.DOCUMENT_POSITION_FOLLOWING||n&Node.DOCUMENT_POSITION_CONTAINED_BY?-1:n&Node.DOCUMENT_POSITION_PRECEDING||n&Node.DOCUMENT_POSITION_CONTAINS?1:0}var qt=Q.createContext(void 0);function Jt(){let e=Q.useContext(qt);if(!e)throw Error(N(59));return e}function Yt(e,t){e&&Object.assign(e.style,t)}var Xt={position:`relative`,maxHeight:`100%`,overflowX:`hidden`,overflowY:`auto`},Zt=20;function Qt(e,t,n,r){let[i,a]=Q.useState(!1);X(()=>{if(!e||!t||n==null){a(!1);return}let r=oe(n).documentElement.clientWidth,i=n.offsetWidth;a(r>0&&i>0&&i>=r-Zt)},[e,t,n]),Ie(e&&(!t||i),r)}var $t={position:`fixed`},en=Q.forwardRef(function(e,t){let{anchor:n,positionMethod:r=`absolute`,className:i,render:a,side:o=`bottom`,align:s=`center`,sideOffset:c=0,alignOffset:l=0,collisionBoundary:u=`clipping-ancestors`,collisionPadding:f,arrowPadding:p=5,sticky:m=!1,disableAnchorTracking:h,alignItemWithTrigger:_=!0,collisionAvoidance:v=g,style:y,...b}=e,{store:x,listRef:S,labelsRef:C,alignItemWithTriggerActiveRef:w,selectedItemTextRef:ee,valuesRef:T,initialValueRef:E,popupRef:D,setValue:te}=dt(),O=ft(),k=d(x,$.open),A=d(x,$.mounted),j=d(x,$.modal),M=d(x,$.value),N=d(x,$.openMethod),P=d(x,$.positionerElement),F=d(x,$.triggerElement),I=d(x,$.isItemEqualToValue),ne=d(x,$.transitionStatus),re=Q.useRef(null),L=Q.useRef(null),[ie,R]=Q.useState(_),z=A&&ie&&N!==`touch`;!A&&ie!==_&&R(_),X(()=>{A||($.scrollUpArrowVisible(x.state)&&x.set(`scrollUpArrowVisible`,!1),$.scrollDownArrowVisible(x.state)&&x.set(`scrollDownArrowVisible`,!1))},[x,A]),Q.useImperativeHandle(w,()=>z),Qt((z||j)&&k,N===`touch`,P,F);let ae=le({anchor:n,floatingRootContext:O,positionMethod:r,mounted:A,side:o,sideOffset:c,align:s,alignOffset:l,arrowPadding:p,collisionBoundary:u,collisionPadding:f,sticky:m,disableAnchorTracking:h??z,collisionAvoidance:v,keepMounted:!0}),B=z?`none`:ae.side,V=z?$t:ae.positionerStyles,H=Fe(e,{open:k,side:B,align:ae.align,anchorHidden:ae.anchorHidden},{styles:V,transitionStatus:ne,props:b,refs:[t,J(e=>{x.set(`positionerElement`,e)})],hidden:!A,inert:!k}),U=Q.useRef(0),W=J(e=>{if(e.size===0&&U.current===0||T.current.length===0)return;let t=U.current;if(U.current=e.size,e.size===t)return;let n=q(ke);if(t!==0&&!x.state.multiple&&M!==null&>(T.current,M,I)===-1){let e=E.current,t=e!=null&>(T.current,e,I)!==-1?e:null;te(t,n),t===null&&(x.set(`selectedIndex`,null),ee.current=null)}if(t!==0&&x.state.multiple&&Array.isArray(M)){let e=e=>gt(T.current,e,I)!==-1,t=M.filter(t=>e(t));(t.length!==M.length||t.some(e=>!ht(M,e,I)))&&(te(t,n),t.length===0&&(x.set(`selectedIndex`,null),ee.current=null))}if(k&&z){x.update({scrollUpArrowVisible:!1,scrollDownArrowVisible:!1});let e={height:``};Yt(P,e),Yt(D.current,e)}}),oe=Q.useMemo(()=>({...ae,side:B,alignItemWithTriggerActive:z,setControlledAlignItemWithTrigger:R,scrollUpArrowRef:re,scrollDownArrowRef:L}),[ae,B,z,R]);return(0,Z.jsx)(Ut,{elementsRef:S,labelsRef:C,onMapChange:W,children:(0,Z.jsxs)(qt.Provider,{value:oe,children:[A&&j&&(0,Z.jsx)(Me,{inert:Re(!k),cutout:F}),H]})})}),tn=`base-ui-disable-scrollbar`,nn={className:tn,getElement(e){return(0,Z.jsx)(`style`,{nonce:e,href:tn,precedence:`base-ui:low`,children:`.${tn}{scrollbar-width:none}.${tn}::-webkit-scrollbar{display:none}`})}},rn=Q.createContext(void 0);function an(e){let t=Q.useContext(rn);if(t===void 0&&!e)throw Error(N(69));return t}var on=Q.createContext(void 0),sn={disableStyleElements:!1};function cn(){return Q.useContext(on)??sn}var ln={...G,...ge},un=Q.forwardRef(function(e,t){let{render:n,className:r,style:i,finalFocus:a,...o}=e,{store:s,popupRef:l,onOpenChangeComplete:u,setOpen:p,valueRef:m,selectedItemTextRef:h,keyboardActiveRef:g,multiple:_,handleScrollArrowVisibility:v,scrollHandlerRef:y,listRef:b,highlightItemOnHover:x}=dt(),{side:S,align:C,alignItemWithTriggerActive:w,isPositioned:T,setControlledAlignItemWithTrigger:E,scrollDownArrowRef:D,scrollUpArrowRef:te}=Jt(),O=an(!0)!=null,k=ft(),A=se(),{nonce:j,disableStyleElements:M}=cn(),N=d(s,$.id),P=d(s,$.open),F=d(s,$.mounted),I=d(s,$.popupProps),ne=d(s,$.transitionStatus),re=d(s,$.triggerElement),L=d(s,$.positionerElement),ie=d(s,$.listElement),R=Q.useRef(!1),z=Q.useRef(!1),ae=Q.useRef({}),B=Y(),V=J(e=>{if(!L||!l.current||!z.current)return;if(R.current||!w){v();return}let t=L.style.top===`0px`,n=L.style.bottom===`0px`;if(!t&&!n){v();return}let r=pn(L),i=mn(L.getBoundingClientRect().height,`y`,r),a=oe(L),o=getComputedStyle(L),s=parseFloat(o.marginTop),c=parseFloat(o.marginBottom),u=dn(getComputedStyle(l.current)),d=Math.min(a.documentElement.clientHeight-s-c,u),f=e.scrollTop,p=fn(e),m=0,h=null,g=!1,_=!1,y=e=>{L.style.height=`${e}px`},b=(t,n)=>{let r=Tt(t,0,d-i);r>0&&y(i+r),e.scrollTop=n,d-(i+r)<=1&&(R.current=!0),v()},x=t?p-f:f,S=Math.min(i+x,d);if(m=S,x<=1){b(x,t?p:0);return}if(d-S>1?t?_=!0:h=0:(g=!0,n&&f1&&(e.scrollTop=n)}(g||m>=d-1)&&(R.current=!0),v()});Q.useImperativeHandle(y,()=>V,[V]),Oe({open:P,ref:l,onComplete(){P&&u?.(!0)}});let U={open:P,transitionStatus:ne,side:S,align:C};X(()=>{!L||!l.current||Object.keys(ae.current).length||(ae.current={top:L.style.top||`0`,left:L.style.left||`0`,right:L.style.right,height:L.style.height,bottom:L.style.bottom,minHeight:L.style.minHeight,maxHeight:L.style.maxHeight,marginTop:L.style.marginTop,marginBottom:L.style.marginBottom})},[l,L]),X(()=>{P||w||(z.current=!1,R.current=!1,Yt(L,ae.current))},[P,w,L,l]),X(()=>{let e=l.current;if(!P||!re||!L||!e||w&&!T||s.state.transitionStatus===`ending`)return;if(!w){z.current=!0,B.request(v),e.style.removeProperty(`--transform-origin`);return}let t=_n(e);e.style.removeProperty(`--transform-origin`);try{let t=h.current,n=m.current,r=getComputedStyle(L),i=getComputedStyle(e),a=oe(re),o=_e(L),c=pn(re),l=hn(re.getBoundingClientRect(),c),u=hn(L.getBoundingClientRect(),c),d=l.height,f=ie||e,p=f.scrollHeight,g=parseFloat(i.borderBottomWidth),_=parseFloat(r.marginTop)||10,y=parseFloat(r.marginBottom)||10,S=parseFloat(r.minHeight)||100,C=dn(i),w=a.documentElement.clientHeight-_-y,ee=a.documentElement.clientWidth,T=w-l.bottom+d,D,te=A===`rtl`?l.right-u.width:l.left,O=0;if(t&&n){let e=hn(n.getBoundingClientRect(),c);D=hn(t.getBoundingClientRect(),c),te=u.left+(A===`rtl`?e.right-D.right:e.left-D.left);let r=e.top-l.top+e.height/2;O=D.top-u.top+D.height/2-r}let k=T+O+y+g,j=Math.min(w,k),M=w-_-y,N=k-j,P=ee-5;L.style.left=`${Tt(te,5,P-u.width)}px`,L.style.height=`${j}px`,L.style.maxHeight=`auto`,L.style.marginTop=`${_}px`,L.style.marginBottom=`${y}px`,e.style.height=`100%`;let F=fn(f),I=N>=F-1;I&&(j=Math.min(w,u.height)-(N-F));let ne=l.top<20||l.bottom>w-20||Math.ceil(j)+1=M?`0`:`${e}px`,L.style.height=`${j}px`,f.scrollTop=fn(f)}else L.style.bottom=`0`,f.scrollTop=N;if(D){let t=u.top,n=u.height,r=D.top+D.height/2,i=Tt(n>0?(r-t)/n*100:50,0,100);e.style.setProperty(`--transform-origin`,`50% ${i}%`)}(V===w||j>=C)&&(R.current=!0),v(),x&&s.state.selectedIndex===null&&s.state.activeIndex===null&&b.current[0]!=null&&s.set(`activeIndex`,0),z.current=!0}finally{t()}},[s,P,L,re,m,h,l,v,w,E,B,D,te,ie,b,x,A,T]),Q.useEffect(()=>{if(!w||!L||!P)return;let e=_e(L);function t(e){p(!1,q(de,e))}return c(e,`resize`,t)},[p,w,L,P]);let W={...ie?{role:`presentation`,"aria-orientation":void 0}:{role:`listbox`,"aria-multiselectable":_||void 0,id:`${N}-list`},onKeyDown(e){g.current=!0,O&&ye.has(e.key)&&e.stopPropagation()},onMouseMove(){g.current=!1},onScroll(e){ie||V(e.currentTarget)},...w&&{style:ie?{height:`100%`}:Xt}},G=f(`div`,e,{ref:[t,l],state:U,stateAttributesMapping:ln,props:[I,W,ee(ne),{className:!ie&&w?nn.className:void 0},o]});return(0,Z.jsxs)(Q.Fragment,{children:[!M&&nn.getElement(j),(0,Z.jsx)(Ce,{context:k,modal:!1,disabled:!F,returnFocus:a,restoreFocus:!0,children:G})]})});function dn(e){let t=e.maxHeight||``;return t.endsWith(`px`)&&parseFloat(t)||1/0}function fn(e){return Et(e.scrollHeight,e.clientHeight)}function pn(e){return Ee.getScale(e)}function mn(e,t,n){return e/n[t]}function hn(e,t){return u({x:mn(e.x,`x`,t),y:mn(e.y,`y`,t),width:mn(e.width,`x`,t),height:mn(e.height,`y`,t)})}var gn=[[`transform`,`none`],[`scale`,`1`],[`translate`,`0 0`]];function _n(e){let{style:t}=e,n={};for(let[e,r]of gn)n[e]=t.getPropertyValue(e),t.setProperty(e,r,`important`);return()=>{for(let[e]of gn){let r=n[e];r?t.setProperty(e,r):t.removeProperty(e)}}}var vn=Q.forwardRef(function(e,t){let{className:n,render:r,style:i,...a}=e,{store:o,scrollHandlerRef:s}=dt(),{alignItemWithTriggerActive:c}=Jt(),l=d(o,$.hasScrollArrows),u=d(o,$.openMethod),p=d(o,$.multiple),m={id:`${d(o,$.id)}-list`,role:`listbox`,"aria-multiselectable":p||void 0,onScroll(e){s.current?.(e.currentTarget)},...c&&{style:Xt},className:l&&u!==`touch`?nn.className:void 0};return f(`div`,e,{ref:[t,J(e=>{o.set(`listElement`,e)})],props:[m,a]})}),yn=function(e){return e[e.None=0]=`None`,e[e.GuessFromOrder=1]=`GuessFromOrder`,e}({});function bn(e={}){let{label:t,metadata:n,textRef:r,indexGuessBehavior:i,index:a}=e,{register:o,unregister:s,subscribeMapChange:c,elementsRef:l,labelsRef:u,nextIndexRef:d}=Ht(),f=Q.useRef(-1),[p,m]=Q.useState(a??(i===yn.GuessFromOrder?()=>{if(f.current===-1){let e=d.current;d.current+=1,f.current=e}return f.current}:-1)),h=Q.useRef(null),g=Q.useCallback(e=>{if(h.current=e,p!==-1&&e!==null&&(l.current[p]=e,u)){let n=t!==void 0;u.current[p]=n?t:r?.current?.textContent??e.textContent}},[p,l,u,t,r]);return X(()=>{if(a!=null)return;let e=h.current;if(e)return o(e,n),()=>{s(e)}},[a,o,s,n]),X(()=>{if(a==null)return c(e=>{let t=h.current?e.get(h.current)?.index:null;t!=null&&m(t)})},[a,c,m]),Q.useMemo(()=>({ref:g,index:p}),[p,g])}var xn=Q.createContext(void 0);function Sn(){let e=Q.useContext(xn);if(!e)throw Error(N(57));return e}var Cn=Q.memo(Q.forwardRef(function(e,t){let{render:n,className:r,value:i=null,label:a,disabled:o=!1,nativeButton:s=!1,style:c,...l}=e,u=Q.useRef(null),p=bn({label:a,textRef:u,indexGuessBehavior:yn.GuessFromOrder}),{store:m,getItemProps:h,setOpen:g,setValue:_,selectionRef:v,typingRef:y,valuesRef:b,multiple:x,selectedItemTextRef:S}=dt(),C=d(m,$.isActive,p.index),w=d(m,$.isSelected,p.index,i),ee=d(m,$.isSelectedByFocus,p.index),T=d(m,$.isItemEqualToValue),E=p.index,D=E!==-1,te=Q.useRef(null),O=Ye(E);X(()=>{if(!D)return;let e=b.current;return e[E]=i,()=>{delete e[E]}},[D,E,i,b]),X(()=>{if(!D)return;let e=m.state.value,t=e;x&&Array.isArray(e)&&e.length>0&&(t=e[e.length-1]),t!==void 0&&mt(i,t,T)&&(m.set(`selectedIndex`,E),u.current&&(S.current=u.current))},[D,E,x,T,m,i,S]);let k={disabled:o,selected:w,highlighted:C},A=h({active:C,selected:w});A.id=void 0;let j=Q.useRef(null),M=Q.useRef(`mouse`),N=Q.useRef(!1),{getButtonProps:P,buttonRef:F}=ze({disabled:o,focusableWhenDisabled:!0,native:s,composite:!0});function I(e){let t=m.state.value;if(x){let n=Array.isArray(t)?t:[];_(w?_t(n,i,T):[...n,i],q(he,e))}else _(i,q(he,e)),g(!1,q(he,e))}let ne={role:`option`,"aria-selected":w,tabIndex:C?0:-1,onTouchStart(){v.current={allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1}},onKeyDown(e){j.current=e.key,m.set(`activeIndex`,E),e.key===` `&&y.current&&e.preventDefault()},onClick(e){N.current=!1,!(e.type===`keydown`&&j.current===null)&&(o||e.type===`keydown`&&j.current===` `&&y.current||M.current!==`touch`&&!C||(j.current=null,I(e.nativeEvent)))},onPointerEnter(e){M.current=e.pointerType},onPointerDown(e){M.current=e.pointerType,N.current=!0},onMouseUp(){if(o)return;if(N.current){N.current=!1;return}let e=!v.current.allowSelectedMouseUp&&w,t=!v.current.allowUnselectedMouseUp&&!w;e||t||M.current!==`touch`&&!C||te.current?.click()}},re=f(`div`,e,{ref:[F,t,p.ref,te],state:k,props:[A,ne,l,P]}),L=Q.useMemo(()=>({selected:w,indexRef:O,textRef:u,selectedByFocus:ee,hasRegistered:D}),[w,O,u,ee,D]);return(0,Z.jsx)(xn.Provider,{value:L,children:re})})),wn=Q.forwardRef(function(e,t){let n=e.keepMounted??!1,{selected:r}=Sn();return n||r?(0,Z.jsx)(Tn,{...e,ref:t}):null}),Tn=Q.memo(Q.forwardRef((e,t)=>{let{render:n,className:r,style:i,keepMounted:a,...o}=e,{selected:s}=Sn(),c=Q.useRef(null),{transitionStatus:l,setMounted:u}=pe(s),d=f(`span`,e,{ref:[t,c],state:{selected:s,transitionStatus:l},props:[{"aria-hidden":!0,children:`✔️`},o],stateAttributesMapping:ge});return Oe({open:s,ref:c,onComplete(){s||u(!1)}}),d})),En=Q.memo(Q.forwardRef(function(e,t){let{indexRef:n,textRef:r,selectedByFocus:i,hasRegistered:a}=Sn(),{selectedItemTextRef:o}=dt(),{className:s,render:c,style:l,...u}=e;return f(`div`,e,{ref:[Q.useCallback(e=>{if(!e||!a)return;let t=o.current===null||!o.current.isConnected;(i||t&&n.current===0)&&(o.current=e)},[o,n,i,a]),t,r],props:u})})),Dn=Q.forwardRef(function(e,t){let{render:n,className:r,style:i,direction:a,keepMounted:o=!1,...s}=e,c=a===`up`,{store:l,popupRef:u,listRef:p,handleScrollArrowVisibility:m,scrollArrowsMountedCountRef:h}=dt(),{side:g,scrollDownArrowRef:_,scrollUpArrowRef:v}=Jt(),y=d(l,c?$.scrollUpArrowVisible:$.scrollDownArrowVisible),b=d(l,$.openMethod),x=y&&b!==`touch`,S=ve(),C=c?v:_,{transitionStatus:w,setMounted:ee}=pe(x);X(()=>(h.current+=1,l.state.hasScrollArrows||l.set(`hasScrollArrows`,!0),()=>{h.current=Math.max(0,h.current-1),h.current===0&&l.state.hasScrollArrows&&l.set(`hasScrollArrows`,!1)}),[l,h]),Oe({open:x,ref:C,onComplete(){x||ee(!1)}});let T=f(`div`,e,{ref:[t,C],state:{direction:a,visible:x,side:g,transitionStatus:w},props:[{"aria-hidden":!0,children:c?`▲`:`▼`,style:{position:`absolute`},onMouseMove(e){if(e.movementX===0&&e.movementY===0||S.isStarted())return;l.set(`activeIndex`,null);function t(){let e=l.state.listElement??u.current;if(!e)return;l.set(`activeIndex`,null),m();let n=Et(e.scrollHeight,e.clientHeight),r=Dt(e.scrollTop,n),i=r===(c?0:n),a=p.current;if(r!==e.scrollTop&&(e.scrollTop=r),a.length===0&&l.set(c?`scrollUpArrowVisible`:`scrollDownArrowVisible`,!i),i){S.clear();return}if(a.length>0){let t=C.current?.offsetHeight||0;e.scrollTop=On(a,c,r,e.clientHeight,t,n)}S.start(40,t)}S.start(40,t)},onMouseLeave(){S.clear()}},s]});return x||o?T:null});function On(e,t,n,r,i,a){if(t){let t=0,r=n+i-1;for(let n=0;n=r){t=n;break}}let o=Math.max(0,t-1),s=e[o];return os){o=Math.max(0,t-1);break}}let c=Math.min(e.length-1,o+1),l=e[c];return c>o&&l?Dt(l.offsetTop+l.offsetHeight-r+i,a):a}var kn=Q.forwardRef(function(e,t){return(0,Z.jsx)(Dn,{...e,ref:t,direction:`down`})}),An=Q.forwardRef(function(e,t){return(0,Z.jsx)(Dn,{...e,ref:t,direction:`up`})}),jn=Q.createContext(void 0);function Mn(){let e=Q.useContext(jn);if(e===void 0)throw Error(N(56));return e}var Nn=Q.forwardRef(function(e,t){let{className:n,render:r,style:i,...a}=e,[o,s]=Q.useState(),c=Q.useMemo(()=>({labelId:o,setLabelId:s}),[o,s]),l=f(`div`,e,{ref:t,props:[{role:`group`,"aria-labelledby":o},a]});return(0,Z.jsx)(jn.Provider,{value:c,children:l})}),Pn=Q.forwardRef(function(e,t){let{className:n,render:r,id:i,style:a,...o}=e,{setLabelId:s}=Mn(),c=Pe(i);return X(()=>{s(c)},[c,s]),f(`div`,e,{ref:t,props:[{id:c},o]})}),Fn=_(),In=Ot;function Ln(e){let t=(0,Fn.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`scroll-my-1 p-1`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,Z.jsx)(Nn,{className:a,"data-slot":`select-group`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function Rn(e){let t=(0,Fn.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`flex flex-1 text-left`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,Z.jsx)(Lt,{className:a,"data-slot":`select-value`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function zn(e){let r=(0,Fn.c)(15),i,a,o,s,c;r[0]===e?(i=r[1],a=r[2],o=r[3],s=r[4],c=r[5]):({className:a,size:c,id:o,children:i,...s}=e,r[0]=e,r[1]=i,r[2]=a,r[3]=o,r[4]=s,r[5]=c);let l=c===void 0?`default`:c,u;r[6]===a?u=r[7]:(u=n(`flex w-fit items-center justify-between gap-1.5 rounded-md border border-input bg-input/20 px-2 py-1.5 text-xs/relaxed whitespace-nowrap transition-colors outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-7 data-[size=sm]:h-6 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5`,a),r[6]=a,r[7]=u);let d;r[8]===Symbol.for(`react.memo_cache_sentinel`)?(d=(0,Z.jsx)(Rt,{render:(0,Z.jsx)(t,{className:`pointer-events-none size-3.5 text-muted-foreground`})}),r[8]=d):d=r[8];let f;return r[9]!==i||r[10]!==o||r[11]!==s||r[12]!==l||r[13]!==u?(f=(0,Z.jsxs)(Ft,{className:u,"data-size":l,"data-slot":`select-trigger`,id:o,...s,children:[i,d]}),r[9]=i,r[10]=o,r[11]=s,r[12]=l,r[13]=u,r[14]=f):f=r[14],f}function Bn(e){let t=(0,Fn.c)(26),r,i,a,o,s,c,l,u;t[0]===e?(r=t[1],i=t[2],a=t[3],o=t[4],s=t[5],c=t[6],l=t[7],u=t[8]):({className:i,children:r,side:o,sideOffset:s,align:c,alignOffset:l,alignItemWithTrigger:u,...a}=e,t[0]=e,t[1]=r,t[2]=i,t[3]=a,t[4]=o,t[5]=s,t[6]=c,t[7]=l,t[8]=u);let d=o===void 0?`bottom`:o,f=s===void 0?4:s,p=c===void 0?`center`:c,m=l===void 0?0:l,h=u===void 0?!0:u,g;t[9]===i?g=t[10]:(g=n(`data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-closed:animate-out data-open:animate-in relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100`,i),t[9]=i,t[10]=g);let _;t[11]===Symbol.for(`react.memo_cache_sentinel`)?(_=(0,Z.jsx)(Wn,{}),t[11]=_):_=t[11];let v;t[12]===r?v=t[13]:(v=(0,Z.jsx)(vn,{children:r}),t[12]=r,t[13]=v);let y;t[14]===Symbol.for(`react.memo_cache_sentinel`)?(y=(0,Z.jsx)(Gn,{}),t[14]=y):y=t[14];let b;t[15]!==a||t[16]!==g||t[17]!==v?(b=(0,Z.jsxs)(un,{className:g,"data-slot":`select-content`,...a,children:[_,v,y]}),t[15]=a,t[16]=g,t[17]=v,t[18]=b):b=t[18];let x;return t[19]!==p||t[20]!==h||t[21]!==m||t[22]!==d||t[23]!==f||t[24]!==b?(x=(0,Z.jsx)(Bt,{children:(0,Z.jsx)(en,{align:p,alignItemWithTrigger:h,alignOffset:m,className:`isolate z-50`,side:d,sideOffset:f,children:b})}),t[19]=p,t[20]=h,t[21]=m,t[22]=d,t[23]=f,t[24]=b,t[25]=x):x=t[25],x}function Vn(e){let t=(0,Fn.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`px-2 py-1.5 text-xs text-muted-foreground`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,Z.jsx)(Pn,{className:a,"data-slot":`select-label`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function Hn(t){let r=(0,Fn.c)(13),i,a,o;r[0]===t?(i=r[1],a=r[2],o=r[3]):({className:a,children:i,...o}=t,r[0]=t,r[1]=i,r[2]=a,r[3]=o);let s;r[4]===a?s=r[5]:(s=n(`relative flex min-h-7 w-full cursor-default items-center gap-2 rounded-md px-2 py-1 text-xs/relaxed outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2`,a),r[4]=a,r[5]=s);let c;r[6]===i?c=r[7]:(c=(0,Z.jsx)(En,{className:`flex flex-1 shrink-0 gap-2 whitespace-nowrap`,children:i}),r[6]=i,r[7]=c);let l;r[8]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,Z.jsx)(wn,{render:(0,Z.jsx)(`span`,{className:`pointer-events-none absolute right-2 flex items-center justify-center`}),children:(0,Z.jsx)(e,{className:`pointer-events-none`})}),r[8]=l):l=r[8];let u;return r[9]!==o||r[10]!==s||r[11]!==c?(u=(0,Z.jsxs)(Cn,{className:s,"data-slot":`select-item`,...o,children:[c,l]}),r[9]=o,r[10]=s,r[11]=c,r[12]=u):u=r[12],u}function Un(e){let t=(0,Fn.c)(8),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`pointer-events-none -mx-1 my-1 h-px bg-border/50`,r),t[3]=r,t[4]=a);let o;return t[5]!==i||t[6]!==a?(o=(0,Z.jsx)(K,{className:a,"data-slot":`select-separator`,...i}),t[5]=i,t[6]=a,t[7]=o):o=t[7],o}function Wn(e){let t=(0,Fn.c)(9),r,i;t[0]===e?(r=t[1],i=t[2]):({className:r,...i}=e,t[0]=e,t[1]=r,t[2]=i);let a;t[3]===r?a=t[4]:(a=n(`top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-3.5`,r),t[3]=r,t[4]=a);let o;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(o=(0,Z.jsx)(Xe,{}),t[5]=o):o=t[5];let s;return t[6]!==i||t[7]!==a?(s=(0,Z.jsx)(An,{className:a,"data-slot":`select-scroll-up-button`,...i,children:o}),t[6]=i,t[7]=a,t[8]=s):s=t[8],s}function Gn(e){let r=(0,Fn.c)(9),i,a;r[0]===e?(i=r[1],a=r[2]):({className:i,...a}=e,r[0]=e,r[1]=i,r[2]=a);let o;r[3]===i?o=r[4]:(o=n(`bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-3.5`,i),r[3]=i,r[4]=o);let s;r[5]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,Z.jsx)(t,{}),r[5]=s):s=r[5];let c;return r[6]!==a||r[7]!==o?(c=(0,Z.jsx)(kn,{className:o,"data-slot":`select-scroll-down-button`,...a,children:s}),r[6]=a,r[7]=o,r[8]=c):c=r[8],c}export{Vn as a,Rn as c,Hn as i,bn as l,Bn as n,Un as o,Ln as r,zn as s,In as t,Ut as u}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{a as i,c as a,f as o,i as s,l as c,n as l,o as u,u as d}from"./dist.js";import{w as f}from"./SignatureV4.js";import{t as p}from"./check.js";import{t as m}from"./chevron-down.js";import{_ as h,b as g,o as _,r as v,s as y}from"./button.js";import{S as b,_ as x,b as S,f as ee,g as te,l as ne,n as C,o as w,t as T,u as E,v as D,x as O,y as k}from"./createBaseUIEventDetails.js";import{$n as A,An as j,Bn as M,Cn as re,Dn as N,En as ie,Fn as P,Gn as F,Gt as ae,Hn as I,Ht as L,In as oe,Jn as R,Jt as z,Ln as B,Nr as V,On as H,Pn as U,Qn as W,Qt as G,Sn as se,Sr as K,Tn as q,Ut as ce,Vn as le,Wt as ue,Xn as de,Yn as fe,Yt as pe,Zn as me,Zt as he,_n as J,_r as ge,an as _e,ar as ve,bn as ye,br as be,cr as xe,dr as Se,er as Ce,fr as we,gn as Te,gr as Ee,hn as De,hr as Oe,ir as ke,kn as Ae,lr as je,mn as Me,mr as Y,nr as Ne,or as Pe,pr as Fe,qn as Ie,rr as Le,sr as Re,tr as ze,ur as Be,vn as X,vr as Ve,wn as He,xn as Ue,xr as We,yn as Ge,yr as Ke,zn as qe}from"./index.js";import{n as Je,r as Ye,t as Xe}from"./useCompositeListItem.js";var Ze=V(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),Z=e(),Q=t(n()),Qe=Q.createContext({formRef:{current:{fields:new Map}},errors:{},clearErrors:u,validationMode:`onSubmit`,submitAttemptedRef:{current:!1}});function $e(){return Q.useContext(Qe)}function et(e,t={}){let{enabled:n=!0,event:r=`click`,toggle:a=!0,ignoreMouse:o=!1,stickIfOpen:s=!0,touchOpenDelay:c=0,reason:l=te}=t,u=`rootStore`in e?e.rootStore:e,d=u.context.dataRef,f=Q.useRef(void 0),p=Ie(),m=R(),h=Q.useMemo(()=>{function e(e,t,n,r){let i=T(l,t,n);e&&r===`touch`&&c>0?m.start(c,()=>{u.setOpen(!0,i)}):u.setOpen(e,i)}function t(e,t,n){let r=d.current.openEvent,i=u.select(`domReferenceElement`)!==t;return e&&i||!e||!a?!0:r&&s?!n(r.type):!1}return{onPointerDown(e){f.current=e.pointerType},onMouseDown(n){let i=f.current,a=n.nativeEvent,s=u.select(`open`);if(n.button!==0||r===`click`||Pe(i,!0)&&o)return;let c=t(s,n.currentTarget,e=>e===`click`||e===`mousedown`),l=S(a);if(we(l)){e(c,a,l,i);return}let d=n.currentTarget;p.request(()=>{e(c,a,d,i)})},onClick(n){if(r===`mousedown-only`)return;let i=f.current;if(r===`mousedown`&&i){f.current=void 0;return}Pe(i,!0)&&o||e(t(u.select(`open`),n.currentTarget,e=>e===`click`||e===`mousedown`||e===`keydown`||e===`keyup`),n.nativeEvent,n.currentTarget,i)},onKeyDown(){f.current=void 0}}},[d,r,o,l,u,s,a,p,m,c]);return Q.useMemo(()=>n?{reference:h}:i,[n,h])}var tt=`Escape`;function nt(e,t,n){switch(e){case`vertical`:return t;case`horizontal`:return n;default:return t||n}}function rt(e,t){return nt(t,e===`ArrowUp`||e===`ArrowDown`,e===`ArrowLeft`||e===`ArrowRight`)}function it(e,t,n){return nt(t,e===`ArrowDown`,n?e===`ArrowLeft`:e===`ArrowRight`)||e===`Enter`||e===` `||e===``}function at(e,t,n){return nt(t,n?e===Y:e===Oe,e===Fe)}function ot(e,t,n,r){return t===`both`||t===`horizontal`&&r&&r>1?e===tt:nt(t,n?e===Oe:e===Y,e===Ee)}function st(e,t){let{listRef:n,activeIndex:r,onNavigate:i=()=>{},enabled:a=!0,selectedIndex:o=null,allowEscape:s=!1,loopFocus:c=!1,nested:l=!1,rtl:u=!1,virtual:d=!1,focusItemOnOpen:f=`auto`,focusItemOnHover:p=!0,openOnArrowKeyDown:m=!0,disabledIndices:h=void 0,orientation:v=`vertical`,parentOrientation:b,cols:x=1,id:ee,resetOnPointerLeave:te=!0,externalTree:ne}=t,C=`rootStore`in e?e.rootStore:e,j=C.useState(`open`),M=C.useState(`floatingElement`),N=C.useState(`domReferenceElement`),P=C.context.dataRef,F=Be(M),ae=Se(N),I=Ae(F),L=se(),oe=re(ne),R=Q.useRef(f),z=Q.useRef(o??-1),B=Q.useRef(null),V=Q.useRef(!0),H=y(e=>{i(z.current===-1?null:z.current,e)}),U=Q.useRef(H),G=Q.useRef(!!M),K=Q.useRef(j),q=Q.useRef(!1),ce=Q.useRef(!1),le=Q.useRef(null),ue=Ae(h),pe=Ae(j),he=Ae(o),J=Ae(te),ge=Ie(),_e=Ie(),ve=y(()=>{function e(e){d?oe?.events.emit(`virtualfocus`,e):le.current=ie(e,{sync:q.current,preventScroll:!0})}let t=n.current[z.current],r=ce.current;t&&e(t),(q.current?e=>e():e=>ge.request(e))(()=>{let i=n.current[z.current]||t;i&&(t||e(i),De&&(r||!V.current)&&i.scrollIntoView?.({block:`nearest`,inline:`nearest`}))})});_(()=>{P.current.orientation=v},[P,v]),_(()=>{a&&(j&&M?(z.current=o??-1,R.current&&o!=null&&(ce.current=!0,H())):G.current&&(z.current=-1,U.current()))},[a,j,M,o,H]),_(()=>{if(a){if(!j){q.current=!1;return}if(M)if(r==null){if(q.current=!1,he.current!=null)return;if(G.current&&(z.current=-1,ve()),(!K.current||!G.current)&&R.current&&(B.current!=null||R.current===!0&&B.current==null)){let e=0,t=()=>{n.current[0]==null?(e<2&&(e?e=>_e.request(e):queueMicrotask)(t),e+=1):(z.current=B.current==null||it(B.current,v,u)||l?ze(n):Ce(n),B.current=null,H())};t()}}else Le(n.current,r)||(z.current=r,ve(),ce.current=!1)}},[a,j,M,r,he,l,n,v,u,H,ve,_e]),_(()=>{if(!a||M||!oe||d||!G.current)return;let e=oe.nodesRef.current,t=e.find(e=>e.id===L)?.context?.elements.floating,n=D(O(M)),r=e.some(e=>e.context&&k(e.context.elements.floating,n));t&&!r&&V.current&&t.focus({preventScroll:!0})},[a,M,oe,L,d]),_(()=>{U.current=H,K.current=j,G.current=!!M}),_(()=>{j||(B.current=null,R.current=f)},[j,f]);let ye=r!=null,be=y(e=>{if(!pe.current)return;let t=n.current.indexOf(e.currentTarget);t!==-1&&(z.current!==t||r!==t)&&(z.current=t,H(e))}),we=y(()=>b??oe?.nodesRef.current.find(e=>e.id===L)?.context?.dataRef?.current.orientation),Te=y(()=>ze(n,ue.current)),Ee=y(e=>{if(V.current=!1,q.current=!0,e.which===229||!pe.current&&e.currentTarget===I.current)return;if(l&&ot(e.key,v,u,x)){rt(e.key,we())||je(e),C.setOpen(!1,T(E,e.nativeEvent)),g(N)&&(d?oe?.events.emit(`virtualfocus`,N):N.focus());return}let t=z.current,r=ze(n,h),i=Ce(n,h);if(ae||(e.key===`Home`&&(je(e),z.current=r,H(e)),e.key===`End`&&(je(e),z.current=i,H(e))),x>1){let t=Array.from({length:n.current.length},()=>({width:1,height:1})),a=fe(t,x,!1),o=a.findIndex(e=>e!=null&&!ke(n.current,e,h)),s=a.reduce((e,t,r)=>t!=null&&!ke(n.current,t,h)?r:e,-1),l=a[A(a.map(e=>e==null?null:n.current[e]),{event:e,orientation:v,loopFocus:c,rtl:u,cols:x,disabledIndices:W([...(typeof h==`function`?null:h)||n.current.map((e,t)=>ke(n.current,t,h)?t:void 0),void 0],a),minIndex:o,maxIndex:s,prevIndex:me(z.current>i?r:z.current,t,a,x,e.key===`ArrowDown`?`bl`:e.key===(u?`ArrowLeft`:`ArrowRight`)?`tr`:`tl`),stopEvent:!0})];if(l!=null&&(z.current=l,H(e)),v===`both`)return}if(rt(e.key,v)){if(je(e),j&&!d&&D(e.currentTarget.ownerDocument)===e.currentTarget){z.current=it(e.key,v,u)?r:i,H(e);return}it(e.key,v,u)?c?t>=i?s&&t!==n.current.length?z.current=-1:(q.current=!1,z.current=r):z.current=de(n.current,{startingIndex:t,disabledIndices:h}):z.current=Math.min(i,de(n.current,{startingIndex:t,disabledIndices:h})):c?t<=r?s&&t!==-1?z.current=n.current.length:(q.current=!1,z.current=i):z.current=de(n.current,{startingIndex:t,decrement:!0,disabledIndices:h}):z.current=Math.max(r,de(n.current,{startingIndex:t,decrement:!0,disabledIndices:h})),Le(n.current,z.current)&&(z.current=-1),H(e)}}),De=Q.useMemo(()=>({onFocus(e){q.current=!0,be(e)},onClick:({currentTarget:e})=>e.focus({preventScroll:!0}),onMouseMove(e){q.current=!0,ce.current=!1,p&&be(e)},onPointerLeave(e){if(!pe.current||!V.current||e.pointerType===`touch`)return;q.current=!0;let t=e.relatedTarget;if(!(!p||n.current.includes(t))&&J.current&&(le.current?.(),le.current=null,z.current=-1,H(e),!d)){let e=I.current,t=D(O(e));e&&k(e,t)&&e.focus({preventScroll:!0})}}}),[be,pe,I,p,n,H,J,d]),Oe=Q.useMemo(()=>d&&j&&ye&&{"aria-activedescendant":`${ee}-${r}`},[d,j,ye,ee,r]),Me=Q.useMemo(()=>({"aria-orientation":v===`both`?void 0:v,...ae?{}:Oe,onKeyDown(e){if(e.key===`Tab`&&e.shiftKey&&j&&!d){let t=S(e.nativeEvent);if(t&&!k(I.current,t))return;je(e),C.setOpen(!1,T(w,e.nativeEvent)),g(N)&&N.focus();return}Ee(e)},onPointerMove(){V.current=!0}}),[Oe,Ee,I,v,ae,C,j,d,N]),Y=Q.useMemo(()=>{function e(e){C.setOpen(!0,T(E,e.nativeEvent,e.currentTarget))}function t(e){f===`auto`&&Re(e.nativeEvent)&&(R.current=!d)}function n(e){R.current=f,f===`auto`&&xe(e.nativeEvent)&&(R.current=!0)}return{onKeyDown(t){let n=C.select(`open`);V.current=!1;let r=t.key.startsWith(`Arrow`),i=at(t.key,we(),u),a=rt(t.key,v),o=(l?i:a)||t.key===`Enter`||t.key.trim()===``;if(d&&n)return Ee(t);if(!(!n&&!m&&r)){if(o){let e=rt(t.key,we());B.current=l&&e?null:t.key}if(l){i&&(je(t),n?(z.current=Te(),H(t)):e(t));return}a&&(he.current!=null&&(z.current=he.current),je(t),!n&&m?e(t):Ee(t),n&&H(t))}},onFocus(e){C.select(`open`)&&!d&&(z.current=-1,H(e))},onPointerDown:n,onPointerEnter:n,onMouseDown:t,onClick:t}},[Ee,f,Te,l,H,C,m,v,we,u,he,d]),Ne=Q.useMemo(()=>({...Oe,...Y}),[Oe,Y]);return Q.useMemo(()=>a?{reference:Ne,floating:Me,item:De,trigger:Y}:{},[a,Ne,Me,Y,De])}function ct(e,t){let{listRef:n,elementsRef:r,activeIndex:i,onMatch:a,onTyping:o,enabled:s=!0,resetMs:c=750,selectedIndex:l=null}=t,u=`rootStore`in e?e.rootStore:e,d=u.useState(`open`),f=R(),p=Q.useRef(``),m=Q.useRef(l??i??-1),h=Q.useRef(null),g=y(e=>{function t(e){let t=r?.current[e];return!t||Ne(t)}function s(e,n,r=0){if(e.length===0)return-1;let i=(r%e.length+e.length)%e.length,a=n.toLocaleLowerCase();for(let n=0;n0&&e.key===` `&&(je(e),o?.(!0)),p.current.length>0&&p.current[0]!==` `&&s(u,p.current)===-1&&e.key!==` `&&o?.(!1),u==null||e.key.length!==1||e.ctrlKey||e.metaKey||e.altKey)return;d&&e.key!==` `&&(je(e),o?.(!0));let g=p.current===``;g&&(m.current=l??i??-1),u.every(e=>e?e[0]?.toLocaleLowerCase()!==e[1]?.toLocaleLowerCase():!0)&&p.current===e.key&&(p.current=``,m.current=h.current),p.current+=e.key,f.start(c,()=>{p.current=``,m.current=h.current,o?.(!1)});let _=((g?l??i??-1:m.current)??0)+1,v=s(u,p.current,_);v===-1?e.key!==` `&&(p.current=``,o?.(!1)):(a?.(v),h.current=v)}),v=y(e=>{let t=e.relatedTarget,n=u.select(`domReferenceElement`),r=u.select(`floatingElement`);k(n,t)||k(r,t)||(f.clear(),p.current=``,m.current=h.current,o?.(!1))});_(()=>{!d&&l!==null||(f.clear(),h.current=null,p.current!==``&&(p.current=``))},[d,l,f]),_(()=>{d&&p.current===``&&(m.current=l??i??-1)},[d,l,i]);let b=Q.useMemo(()=>({onKeyDown:g,onBlur:v}),[g,v]);return Q.useMemo(()=>s?{reference:b,floating:b}:{},[s,b])}function lt(e){let t=Q.useRef(``),n=Q.useCallback(n=>{n.defaultPrevented||(t.current=n.pointerType,e(n,n.pointerType))},[e]);return{onClick:Q.useCallback(n=>{if(n.detail===0){e(n,`keyboard`);return}`pointerType`in n?e(n,n.pointerType):e(n,t.current),t.current=``},[e]),onPointerDown:n}}function ut(e,t){let n=Q.useRef(e),r=y(t);_(()=>{n.current!==e&&r(n.current)},[e,r]),_(()=>{n.current=e},[e])}function dt(e,t){let{onClick:n,onPointerDown:r}=lt(y((n,r)=>{(typeof e==`function`?e():e)||t(r||(ge?`touch`:``))}));return Q.useMemo(()=>({onClick:n,onPointerDown:r}),[n,r])}function ft(e){let[t,n]=Q.useState(null),r=dt(e,n);return ut(e,t=>{t&&!e&&n(null)}),Q.useMemo(()=>({openMethod:t,triggerProps:r}),[t,r])}function pt(e){let[t,n]=Q.useState({current:e,previous:null});return e!==t.current&&n({current:e,previous:t.current}),t.previous}var mt=Q.createContext(null),ht=Q.createContext(null);function gt(){let e=Q.useContext(mt);if(e===null)throw Error(d(60));return e}function _t(){let e=Q.useContext(ht);if(e===null)throw Error(d(61));return e}var vt=(e,t)=>Object.is(e,t);function yt(e,t,n){return e==null||t==null?Object.is(e,t):n(e,t)}function bt(e,t,n){return!e||e.length===0?!1:e.some(e=>e===void 0?!1:yt(t,e,n))}function xt(e,t,n){return!e||e.length===0?-1:e.findIndex(e=>e===void 0?!1:yt(e,t,n))}function St(e,t,n){return e.filter(e=>!yt(t,e,n))}function Ct(e){if(e==null)return``;if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return String(e)}}function wt(e){return e!=null&&e.length>0&&typeof e[0]==`object`&&e[0]!=null&&`items`in e[0]}function Tt(e){if(!Array.isArray(e))return e!=null&&`null`in e;let t=e;if(wt(t)){for(let e of t)for(let t of e.items)if(t&&t.value==null&&t.label!=null)return!0;return!1}for(let e of t)if(e&&e.value==null&&e.label!=null)return!0;return!1}function Et(e,t){if(t&&e!=null)return t(e)??``;if(e&&typeof e==`object`){if(`label`in e&&e.label!=null)return String(e.label);if(`value`in e)return String(e.value)}return Ct(e)}function Dt(e,t){return t&&e!=null?t(e)??``:e&&typeof e==`object`&&`value`in e&&`label`in e?Ct(e.value):Ct(e)}function Ot(e,t,n){function r(){return Et(e,n)}if(n&&e!=null)return n(e);if(e&&typeof e==`object`&&`label`in e&&e.label!=null)return e.label;if(t&&!Array.isArray(t))return t[e]??r();if(Array.isArray(t)){let n=t,i=wt(n)?n.flatMap(e=>e.items):n;if(typeof e!=`object`||!e){let t=i.find(t=>t.value===e);return t&&t.label!=null?t.label:r()}if(`value`in e){let t=i.find(t=>t&&t.value===e.value);if(t&&t.label!=null)return t.label}}return r()}function kt(e,t,n){return e.reduce((e,r,i)=>(i>0&&e.push(`, `),e.push((0,Z.jsx)(Q.Fragment,{children:Ot(r,t,n)},i)),e),[])}var $={id:X(e=>e.id),labelId:X(e=>e.labelId),modal:X(e=>e.modal),multiple:X(e=>e.multiple),items:X(e=>e.items),itemToStringLabel:X(e=>e.itemToStringLabel),itemToStringValue:X(e=>e.itemToStringValue),isItemEqualToValue:X(e=>e.isItemEqualToValue),value:X(e=>e.value),hasSelectedValue:X(e=>{let{value:t,multiple:n,itemToStringValue:r}=e;return t==null?!1:n&&Array.isArray(t)?t.length>0:Dt(t,r)!==``}),hasNullItemLabel:X((e,t)=>t?Tt(e.items):!1),open:X(e=>e.open),mounted:X(e=>e.mounted),forceMount:X(e=>e.forceMount),transitionStatus:X(e=>e.transitionStatus),openMethod:X(e=>e.openMethod),activeIndex:X(e=>e.activeIndex),selectedIndex:X(e=>e.selectedIndex),isActive:X((e,t)=>e.activeIndex===t),isSelected:X((e,t,n)=>{let r=e.isItemEqualToValue,i=e.value;return e.multiple?Array.isArray(i)&&i.some(e=>yt(n,e,r)):e.selectedIndex===t&&e.selectedIndex!==null?!0:yt(n,i,r)}),isSelectedByFocus:X((e,t)=>e.selectedIndex===t),popupProps:X(e=>e.popupProps),triggerProps:X(e=>e.triggerProps),triggerElement:X(e=>e.triggerElement),positionerElement:X(e=>e.positionerElement),listElement:X(e=>e.listElement),popupSide:X(e=>e.popupSide),scrollUpArrowVisible:X(e=>e.scrollUpArrowVisible),scrollDownArrowVisible:X(e=>e.scrollDownArrowVisible),hasScrollArrows:X(e=>e.hasScrollArrows)};function At(e,t=-(2**53-1),n=2**53-1){return Math.max(t,Math.min(e,n))}function jt(e,t){return Math.max(0,e-t)}function Mt(e,t){if(t<=0)return 0;let n=At(e,0,t),r=n,i=t-n,a=r<=1,o=i<=1;return a&&o?r<=i?0:t:a?0:o?t:n}function Nt(e){let{id:t,value:n,defaultValue:r=null,onValueChange:l,open:u,defaultOpen:d=!1,onOpenChange:f,name:p,form:m,autoComplete:h,disabled:g=!1,readOnly:v=!1,required:b=!1,modal:x=!0,actionsRef:S,inputRef:te,onOpenChangeComplete:ne,items:C,multiple:w=!1,itemToStringLabel:E,itemToStringValue:D,isItemEqualToValue:O=vt,highlightItemOnHover:k=!0,children:A}=e,{clearErrors:j}=$e(),{setDirty:re,setTouched:ie,setFocused:P,shouldValidateOnChange:F,validityData:ae,setFilled:L,name:oe,disabled:R,validation:z,validationMode:B}=We(),V=Ke({id:t}),U=R||g,W=oe??p,[G,se]=M({controlled:n,default:w?r??s:r,name:`Select`,state:`value`}),[K,q]=M({controlled:u,default:d,name:`Select`,state:`open`}),ce=Q.useRef([]),ue=Q.useRef([]),de=Q.useRef(null),fe=Q.useRef(null),me=Q.useRef(0),he=Q.useRef(null),ge=Q.useRef([]),_e=Q.useRef(!1),ve=Q.useRef(!1),be=Q.useRef(null),xe=Q.useRef(null),Se=Q.useRef({allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1,dragY:0}),Ce=Q.useRef(!1),{mounted:we,setMounted:Ee,transitionStatus:Oe}=le(K),{openMethod:ke,triggerProps:je}=ft(K),Y=c(()=>new Te({id:V,labelId:void 0,modal:x,multiple:w,itemToStringLabel:E,itemToStringValue:D,isItemEqualToValue:O,value:G,open:K,mounted:we,transitionStatus:Oe,items:C,forceMount:!1,openMethod:null,activeIndex:null,selectedIndex:null,popupProps:{},triggerProps:{},triggerElement:null,positionerElement:null,listElement:null,popupSide:null,scrollUpArrowVisible:!1,scrollDownArrowVisible:!1,hasScrollArrows:!1})).current,Ne=J(Y,$.activeIndex),Pe=J(Y,$.selectedIndex),Fe=J(Y,$.triggerElement),Ie=J(Y,$.positionerElement),Le=pt(ke),Re=ke??Le,ze=Q.useMemo(()=>w&&Array.isArray(G)&&G.length===0?``:Dt(G,D),[w,G,D]),Be=Q.useMemo(()=>w&&Array.isArray(G)?G.map(e=>Dt(e,D)):Dt(G,D),[w,G,D]);qe(Ae(Y.state.triggerElement),V,G,y(()=>Be));let X=Q.useRef(G),Ve=w?Array.isArray(G)&&G.length>0:G!=null;_(()=>{G!==X.current&&Y.set(`forceMount`,!0)},[Y,G]),_(()=>{L(Ve)},[Ve,L]),_(function(){let e=ge.current,t;if(w){let n=Array.isArray(G)?G:[];if(n.length===0)t=null;else{let r=n[n.length-1],i=xt(e,r,O);t=i===-1?null:i}}else{let n=xt(e,G,O);t=n===-1?null:n}t===null&&(xe.current=null),!K&&Y.set(`selectedIndex`,t)},[Ve,w,K,G,ge,O,Y,xe]),ut(G,()=>{j(W),re(G!==ae.initialValue),F()?z.commit(G):z.commit(G,!0)});let He=y((e,t)=>{if(f?.(e,t),!t.isCanceled&&(q(e),!e&&(t.reason===`focus-out`||t.reason===`outside-press`)&&(ie(!0),P(!1),B===`onBlur`&&z.commit(G)),!e&&Y.state.activeIndex!==null)){let e=ce.current[Y.state.activeIndex];queueMicrotask(()=>{e?.setAttribute(`tabindex`,`-1`)})}}),Ue=y(()=>{Ee(!1),Y.update({activeIndex:null,openMethod:null}),ne?.(!1)});I({enabled:!S,open:K,ref:de,onComplete(){K||Ue()}}),Q.useImperativeHandle(S,()=>({unmount:Ue}),[Ue]);let Ge=y((e,t)=>{l?.(e,t),!t.isCanceled&&se(e)}),Je=y(()=>{let e=Y.state.listElement||de.current;if(!e)return;let t=jt(e.scrollHeight,e.clientHeight),n=Mt(e.scrollTop,t),r=n>0,i=n{let e=o(tt.reference,Qe.reference,Ze.reference,Xe.reference,je);return V&&(e.id=V),e},[Xe.reference,tt.reference,Qe.reference,Ze.reference,je,V]),rt=Q.useMemo(()=>o(De,tt.floating,Qe.floating,Ze.floating),[tt.floating,Qe.floating,Ze.floating]),it=Qe.item??i;pe(()=>{Y.update({popupProps:rt,triggerProps:nt})}),_(()=>{Y.update({id:V,modal:x,multiple:w,value:G,open:K,mounted:we,transitionStatus:Oe,popupProps:rt,triggerProps:nt,items:C,itemToStringLabel:E,itemToStringValue:D,isItemEqualToValue:O,openMethod:Re})},[Y,V,x,w,G,K,we,Oe,rt,nt,C,E,D,O,Re]);let at=Q.useMemo(()=>({store:Y,name:W,required:b,disabled:U,readOnly:v,multiple:w,highlightItemOnHover:k,setValue:Ge,setOpen:He,listRef:ce,popupRef:de,scrollHandlerRef:fe,handleScrollArrowVisibility:Je,scrollArrowsMountedCountRef:me,itemProps:it,events:Ye.context.events,valueRef:he,valuesRef:ge,labelsRef:ue,typingRef:_e,selectionRef:Se,firstItemTextRef:be,selectedItemTextRef:xe,validation:z,onOpenChangeComplete:ne,keyboardActiveRef:ve,alignItemWithTriggerActiveRef:Ce,initialValueRef:X}),[Y,W,b,U,v,w,k,Ge,He,it,Ye.context.events,z,ne,Je]),ot=a(te,z.inputRef),lt=w&&Array.isArray(G)&&G.length>0,dt=w?void 0:W,gt=Q.useMemo(()=>!w||!Array.isArray(G)||!W?null:G.map(e=>{let t=Dt(e,D);return(0,Z.jsx)(`input`,{type:`hidden`,form:m,name:W,value:t},t)}),[w,G,m,W,D]);return(0,Z.jsx)(mt.Provider,{value:at,children:(0,Z.jsxs)(ht.Provider,{value:Ye,children:[A,(0,Z.jsx)(`input`,{...z.getInputValidationProps({onFocus(){Y.state.triggerElement?.focus({focusVisible:!0})},onChange(e){if(e.nativeEvent.defaultPrevented||U||v){e.preventBaseUIHandler?.();return}let t=e.currentTarget.value,n=T(ee,e.nativeEvent);function r(){if(w)return;let e=ge.current.find(e=>Dt(e,D).toLowerCase()===t.toLowerCase()||Et(e,E).toLowerCase()===t.toLowerCase());e!=null&&(re(e!==ae.initialValue),Ge(e,n),F()&&z.commit(e))}Y.set(`forceMount`,!0),queueMicrotask(r)}}),id:V&&dt==null?`${V}-hidden-input`:void 0,form:m,name:dt,autoComplete:h,value:ze,disabled:U,required:b&&!lt,readOnly:v,ref:ot,style:W?H:N,tabIndex:-1,"aria-hidden":!0,suppressHydrationWarning:!0}),gt]})})}function Pt(e,t){return e??t}function Ft(e){let t=e.getBoundingClientRect(),n=h(e),r=n.getComputedStyle(e,`::before`),i=n.getComputedStyle(e,`::after`);if(!(r.content!==`none`||i.content!==`none`))return t;let a=parseFloat(r.width)||0,o=parseFloat(r.height)||0,s=parseFloat(i.width)||0,c=parseFloat(i.height)||0,l=Math.max(t.width,a,s),u=Math.max(t.height,o,c),d=l-t.width,f=u-t.height;return{left:t.left-d/2,right:t.right+d/2,top:t.top-f/2,bottom:t.bottom+f/2}}var It=2,Lt=400,Rt={...P,...K,popupSide:e=>e?{"data-popup-side":e}:null,value:()=>null},zt=Q.forwardRef(function(e,t){let{render:n,className:r,id:i,disabled:s=!1,nativeButton:c=!0,style:u,...d}=e,{setTouched:f,setFocused:p,validationMode:m,state:h,disabled:g}=We(),{labelId:_}=be(),{store:b,setOpen:x,selectionRef:S,validation:te,readOnly:ne,required:w,alignItemWithTriggerActiveRef:E,disabled:D,keyboardActiveRef:A}=gt(),j=g||D||s,M=J(b,$.open),re=J(b,$.mounted),N=J(b,$.value),ie=J(b,$.triggerProps),P=J(b,$.positionerElement),F=J(b,$.listElement),ae=J(b,$.popupSide),I=J(b,$.id),L=J(b,$.labelId),oe=J(b,$.hasSelectedValue),z=re&&P?ae:null,B=i??I,V=Pt(_,L);Ke({id:B});let H=Ae(P),U=Q.useRef(null),{getButtonProps:W,buttonRef:G}=v({disabled:j,native:c}),se=a(t,U,G,y(e=>{b.set(`triggerElement`,e)})),K=R(),q=R(),ce=R();Q.useEffect(()=>{if(M)return ce.start(Lt,()=>{S.current.allowUnselectedMouseUp=!0,S.current.allowSelectedMouseUp=!0}),()=>{ce.clear()};S.current={allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1,dragY:0},q.clear()},[M,S,q,ce]);let le=o(ie,{id:B,role:`combobox`,"aria-expanded":M?`true`:`false`,"aria-haspopup":`listbox`,"aria-controls":M?F?.id??Be(P)?.id:void 0,"aria-labelledby":V,"aria-readonly":ne||void 0,"aria-required":w||void 0,tabIndex:j?-1:0,ref:se,onFocus(e){p(!0),M&&E.current&&x(!1,T(ee,e.nativeEvent)),K.start(0,()=>{b.set(`forceMount`,!0)})},onBlur(e){k(P,e.relatedTarget)||(f(!0),p(!1),m===`onBlur`&&te.commit(N))},onPointerMove(){A.current=!1},onKeyDown(){A.current=!0},onMouseDown(e){if(M)return;let t=O(e.currentTarget);function n(e){if(!U.current)return;let t=e.target;if(k(U.current,t)||k(H.current,t)||t===U.current)return;let n=Ft(U.current);e.clientX>=n.left-It&&e.clientX<=n.right+It&&e.clientY>=n.top-It&&e.clientY<=n.bottom+It||x(!1,T(C,e))}q.start(0,()=>{t.addEventListener(`mouseup`,n,{once:!0})})}},te.getValidationProps,d,W);le.role=`combobox`;let ue={...h,open:M,disabled:j,value:N,readOnly:ne,popupSide:z,placeholder:!oe};return l(`button`,e,{ref:[t,U],state:ue,stateAttributesMapping:Rt,props:le})}),Bt={value:()=>null},Vt=Q.forwardRef(function(e,t){let{className:n,render:r,children:i,placeholder:a,style:o,...s}=e,{store:c,valueRef:u}=gt(),d=J(c,$.value),f=J(c,$.items),p=J(c,$.itemToStringLabel),m=J(c,$.hasSelectedValue),h=!m&&a!=null&&i==null,g=J(c,$.hasNullItemLabel,h),_={value:d,placeholder:!m},v=null;return v=typeof i==`function`?i(d):i??(!m&&a!=null&&!g?a:Array.isArray(d)?kt(d,f,p):Ot(d,f,p)),l(`span`,e,{state:_,ref:[t,u],props:[{children:v},s],stateAttributesMapping:Bt})}),Ht=Q.forwardRef(function(e,t){let{render:n,className:r,style:i,...a}=e,{store:o}=gt();return l(`span`,e,{state:{open:J(o,$.open)},ref:t,props:[{"aria-hidden":!0,children:`▼`},a],stateAttributesMapping:oe})}),Ut=Q.createContext(void 0),Wt=Q.forwardRef(function(e,t){let{store:n}=gt(),r=J(n,$.mounted),i=J(n,$.forceMount);return r||i?(0,Z.jsx)(Ut.Provider,{value:!0,children:(0,Z.jsx)(He,{ref:t,...e})}):null}),Gt=Q.createContext(void 0);function Kt(){let e=Q.useContext(Gt);if(!e)throw Error(d(59));return e}function qt(e,t){e&&Object.assign(e.style,t)}var Jt={position:`relative`,maxHeight:`100%`,overflowX:`hidden`,overflowY:`auto`},Yt=20;function Xt(e,t,n,r){let[i,a]=Q.useState(!1);_(()=>{if(!e||!t||n==null){a(!1);return}let r=O(n).documentElement.clientWidth,i=n.offsetWidth;a(r>0&&i>0&&i>=r-Yt)},[e,t,n]),z(e&&(!t||i),r)}var Zt={position:`fixed`},Qt=Q.forwardRef(function(e,t){let{anchor:n,positionMethod:r=`absolute`,className:i,render:a,side:o=`bottom`,align:s=`center`,sideOffset:c=0,alignOffset:l=0,collisionBoundary:u=`clipping-ancestors`,collisionPadding:d,arrowPadding:f=5,sticky:p=!1,disableAnchorTracking:m,alignItemWithTrigger:h=!0,collisionAvoidance:g=q,style:v,...b}=e,{store:x,listRef:S,labelsRef:te,alignItemWithTriggerActiveRef:ne,selectedItemTextRef:C,valuesRef:w,initialValueRef:E,popupRef:D,setValue:O}=gt(),k=_t(),A=J(x,$.open),j=J(x,$.mounted),M=J(x,$.modal),re=J(x,$.value),N=J(x,$.openMethod),ie=J(x,$.positionerElement),P=J(x,$.triggerElement),F=J(x,$.isItemEqualToValue),ae=J(x,$.transitionStatus),I=Q.useRef(null),oe=Q.useRef(null),[R,z]=Q.useState(h),B=j&&R&&N!==`touch`;!j&&R!==h&&z(h),_(()=>{j||($.scrollUpArrowVisible(x.state)&&x.set(`scrollUpArrowVisible`,!1),$.scrollDownArrowVisible(x.state)&&x.set(`scrollDownArrowVisible`,!1))},[x,j]),Q.useImperativeHandle(ne,()=>B),Xt((B||M)&&A,N===`touch`,ie,P);let V=ue({anchor:n,floatingRootContext:k,positionMethod:r,mounted:j,side:o,sideOffset:c,align:s,alignOffset:l,arrowPadding:f,collisionBoundary:u,collisionPadding:d,sticky:p,disableAnchorTracking:m??B,collisionAvoidance:g,keepMounted:!0}),H=B?`none`:V.side,U=B?Zt:V.positionerStyles,W={open:A,side:H,align:V.align,anchorHidden:V.anchorHidden};_(()=>{x.set(`popupSide`,V.side)},[x,V.side]);let se=L(e,W,{styles:U,transitionStatus:ae,props:b,refs:[t,y(e=>{x.set(`positionerElement`,e)})],hidden:!j,inert:!A}),K=Q.useRef(0),ce=y(e=>{if(e.size===0&&K.current===0||w.current.length===0)return;let t=K.current;if(K.current=e.size,e.size===t)return;let n=T(ee);if(t!==0&&!x.state.multiple&&re!==null&&xt(w.current,re,F)===-1){let e=E.current,t=e!=null&&xt(w.current,e,F)!==-1?e:null;O(t,n),t===null&&(x.set(`selectedIndex`,null),C.current=null)}if(t!==0&&x.state.multiple&&Array.isArray(re)){let e=e=>xt(w.current,e,F)!==-1,t=re.filter(t=>e(t));(t.length!==re.length||t.some(e=>!bt(re,e,F)))&&(O(t,n),t.length===0&&(x.set(`selectedIndex`,null),C.current=null))}if(A&&B){x.update({scrollUpArrowVisible:!1,scrollDownArrowVisible:!1});let e={height:``};qt(ie,e),qt(D.current,e)}}),le=Q.useMemo(()=>({...V,side:H,alignItemWithTriggerActive:B,setControlledAlignItemWithTrigger:z,scrollUpArrowRef:I,scrollDownArrowRef:oe}),[V,H,B,z]);return(0,Z.jsx)(Ye,{elementsRef:S,labelsRef:te,onMapChange:ce,children:(0,Z.jsxs)(Gt.Provider,{value:le,children:[j&&M&&(0,Z.jsx)(he,{inert:G(!A),cutout:P}),se]})})}),$t=`base-ui-disable-scrollbar`,en={className:$t,getElement(e){return(0,Z.jsx)(`style`,{nonce:e,href:$t,precedence:`base-ui:low`,children:`.${$t}{scrollbar-width:none}.${$t}::-webkit-scrollbar{display:none}`})}},tn=Q.createContext(void 0);function nn(e){let t=Q.useContext(tn);if(t===void 0&&!e)throw Error(d(69));return t}var rn=Q.createContext(void 0),an={disableStyleElements:!1};function on(){return Q.useContext(rn)??an}var sn={...U,...F},cn=Q.forwardRef(function(e,t){let{render:n,className:r,style:i,finalFocus:a,...o}=e,{store:s,popupRef:c,onOpenChangeComplete:u,setOpen:d,valueRef:f,firstItemTextRef:p,selectedItemTextRef:m,keyboardActiveRef:g,multiple:v,handleScrollArrowVisibility:b,scrollHandlerRef:S,listRef:ee,highlightItemOnHover:te}=gt(),{side:ne,align:C,alignItemWithTriggerActive:w,isPositioned:E,setControlledAlignItemWithTrigger:D,scrollDownArrowRef:k,scrollUpArrowRef:A}=Kt(),M=nn(!0)!=null,re=_t(),N=ae(),{nonce:ie,disableStyleElements:P}=on(),F=J(s,$.id),L=J(s,$.open),oe=J(s,$.mounted),R=J(s,$.popupProps),z=J(s,$.transitionStatus),B=J(s,$.triggerElement),V=J(s,$.positionerElement),H=J(s,$.listElement),U=Q.useRef(!1),W=Q.useRef(!1),G=Q.useRef({}),se=Ie(),K=y(e=>{if(!V||!c.current||!W.current)return;if(U.current||!w){b();return}let t=V.style.top===`0px`,n=V.style.bottom===`0px`;if(!t&&!n){b();return}let r=dn(V),i=fn(V.getBoundingClientRect().height,`y`,r),a=O(V),o=getComputedStyle(V),s=parseFloat(o.marginTop),l=parseFloat(o.marginBottom),u=ln(getComputedStyle(c.current)),d=Math.min(a.documentElement.clientHeight-s-l,u),f=e.scrollTop,p=un(e),m=0,h=null,g=!1,_=!1,v=e=>{V.style.height=`${e}px`},y=(t,n)=>{let r=At(t,0,d-i);r>0&&v(i+r),e.scrollTop=n,d-(i+r)<=1&&(U.current=!0),b()},x=t?p-f:f,S=Math.min(i+x,d);if(m=S,x<=1){y(x,t?p:0);return}if(d-S>1?t?_=!0:h=0:(g=!0,n&&f1&&(e.scrollTop=n)}(g||m>=d-1)&&(U.current=!0),b()});Q.useImperativeHandle(S,()=>K,[K]),I({open:L,ref:c,onComplete(){L&&u?.(!0)}});let q={open:L,transitionStatus:z,side:ne,align:C};_(()=>{!V||!c.current||Object.keys(G.current).length||(G.current={top:V.style.top||`0`,left:V.style.left||`0`,right:V.style.right,height:V.style.height,bottom:V.style.bottom,minHeight:V.style.minHeight,maxHeight:V.style.maxHeight,marginTop:V.style.marginTop,marginBottom:V.style.marginBottom})},[c,V]),_(()=>{L||w||(W.current=!1,U.current=!1,qt(V,G.current))},[L,w,V,c]),_(()=>{let e=c.current;if(!L||!B||!V||!e||w&&!E||s.state.transitionStatus===`ending`)return;if(!w){W.current=!0,se.request(b),e.style.removeProperty(`--transform-origin`);return}let t=hn(e);e.style.removeProperty(`--transform-origin`);try{let t=m.current;t?.isConnected||(t=!$.hasSelectedValue(s.state)&&p.current?.isConnected?p.current:null);let n=f.current,r=getComputedStyle(V),i=getComputedStyle(e),a=O(B),o=h(V),c=dn(B),l=pn(B.getBoundingClientRect(),c),u=pn(V.getBoundingClientRect(),c),d=l.height,g=H||e,_=g.scrollHeight,v=parseFloat(i.borderBottomWidth),y=parseFloat(r.marginTop)||10,x=parseFloat(r.marginBottom)||10,S=parseFloat(r.minHeight)||100,ne=ln(i),C=a.documentElement.clientHeight-y-x,w=a.documentElement.clientWidth,T=C-l.bottom+d,E,k=N===`rtl`?l.right-u.width:l.left,A=0;if(t&&n){let e=pn(n.getBoundingClientRect(),c);E=pn(t.getBoundingClientRect(),c),k=u.left+(N===`rtl`?e.right-E.right:e.left-E.left);let r=e.top-l.top+e.height/2;A=E.top-u.top+E.height/2-r}let j=T+A+x+v,M=Math.min(C,j),re=C-y-x,ie=j-M,P=w-5;V.style.left=`${At(k,5,P-u.width)}px`,V.style.height=`${M}px`,V.style.maxHeight=`auto`,V.style.marginTop=`${y}px`,V.style.marginBottom=`${x}px`,e.style.height=`100%`;let F=un(g),ae=ie>=F-1;ae&&(M=Math.min(C,u.height)-(ie-F));let I=l.top<20||l.bottom>C-20||Math.ceil(M)+1=re?`0`:`${e}px`,V.style.height=`${M}px`,g.scrollTop=un(g)}else V.style.bottom=`0`,g.scrollTop=ie;if(E){let t=u.top,n=u.height,r=E.top+E.height/2,i=At(n>0?(r-t)/n*100:50,0,100);e.style.setProperty(`--transform-origin`,`50% ${i}%`)}(oe===C||M>=ne)&&(U.current=!0),b(),te&&s.state.selectedIndex===null&&s.state.activeIndex===null&&ee.current[0]!=null&&s.set(`activeIndex`,0),W.current=!0}finally{t()}},[s,L,V,B,f,p,m,c,b,w,D,se,k,A,H,ee,te,N,E]),Q.useEffect(()=>{if(!w||!V||!L)return;let e=h(V);function t(e){d(!1,T(x,e))}return j(e,`resize`,t)},[d,w,V,L]);let le={...H?{role:`presentation`,"aria-orientation":void 0}:{role:`listbox`,"aria-multiselectable":v||void 0,id:`${F}-list`},onKeyDown(e){g.current=!0,M&&_e.has(e.key)&&e.stopPropagation()},onMouseMove(){g.current=!1},onScroll(e){H||K(e.currentTarget)},...w&&{style:H?{height:`100%`}:Jt}},ue=l(`div`,e,{ref:[t,c],state:q,stateAttributesMapping:sn,props:[R,le,ce(z),{className:!H&&w?en.className:void 0},o]});return(0,Z.jsxs)(Q.Fragment,{children:[!P&&en.getElement(ie),(0,Z.jsx)(Ue,{context:re,modal:!1,disabled:!oe,returnFocus:a,restoreFocus:!0,children:ue})]})});function ln(e){let t=e.maxHeight||``;return t.endsWith(`px`)&&parseFloat(t)||1/0}function un(e){return jt(e.scrollHeight,e.clientHeight)}function dn(e){return Ge.getScale(e)}function fn(e,t,n){return e/n[t]}function pn(e,t){return ve({x:fn(e.x,`x`,t),y:fn(e.y,`y`,t),width:fn(e.width,`x`,t),height:fn(e.height,`y`,t)})}var mn=[[`transform`,`none`],[`scale`,`1`],[`translate`,`0 0`]];function hn(e){let{style:t}=e,n={};for(let[e,r]of mn)n[e]=t.getPropertyValue(e),t.setProperty(e,r,`important`);return()=>{for(let[e]of mn){let r=n[e];r?t.setProperty(e,r):t.removeProperty(e)}}}var gn=Q.forwardRef(function(e,t){let{render:n,className:r,style:i,...a}=e,{store:o,scrollHandlerRef:s}=gt(),{alignItemWithTriggerActive:c}=Kt(),u=J(o,$.hasScrollArrows),d=J(o,$.openMethod),f=J(o,$.multiple),p={id:`${J(o,$.id)}-list`,role:`listbox`,"aria-multiselectable":f||void 0,onScroll(e){s.current?.(e.currentTarget)},...c&&{style:Jt},className:u&&d!==`touch`?en.className:void 0};return l(`div`,e,{ref:[t,y(e=>{o.set(`listElement`,e)})],props:[p,a]})}),_n=Q.createContext(void 0);function vn(){let e=Q.useContext(_n);if(!e)throw Error(d(57));return e}var yn=Q.memo(Q.forwardRef(function(e,t){let{render:n,className:r,style:i,value:a=null,label:o,disabled:s=!1,nativeButton:c=!1,...u}=e,d=Q.useRef(null),f=Je({label:o,textRef:d,indexGuessBehavior:Xe.GuessFromOrder}),{store:p,itemProps:m,setOpen:h,setValue:g,selectionRef:y,typingRef:b,valuesRef:x,multiple:S,selectedItemTextRef:ee}=gt(),te=J(p,$.isActive,f.index),C=J(p,$.isSelected,f.index,a),w=J(p,$.isSelectedByFocus,f.index),E=J(p,$.isItemEqualToValue),D=f.index,O=D!==-1,k=Q.useRef(null);_(()=>{if(!O)return;let e=x.current;return e[D]=a,()=>{delete e[D]}},[O,D,a,x]),_(()=>{if(!O)return;let e=p.state.value,t=e;S&&Array.isArray(e)&&e.length>0&&(t=e[e.length-1]),t!==void 0&&yt(a,t,E)&&(p.set(`selectedIndex`,D),d.current&&(ee.current=d.current))},[O,D,S,E,p,a,ee]);let A=Q.useRef(null),j=Q.useRef(`mouse`),M=Q.useRef(!1),{getButtonProps:re,buttonRef:N}=v({disabled:s,focusableWhenDisabled:!0,native:c,composite:!0}),ie={disabled:s,selected:C,highlighted:te};function P(e){let t=p.state.value;if(S){let n=Array.isArray(t)?t:[];g(C?St(n,a,E):[...n,a],T(ne,e))}else g(a,T(ne,e)),h(!1,T(ne,e))}function F(){y.current.dragY=0}let ae={role:`option`,"aria-selected":C,tabIndex:te?0:-1,onKeyDown(e){A.current=e.key,p.set(`activeIndex`,D),e.key===` `&&b.current&&e.preventDefault()},onClick(e){let t=e.type===`click`&&j.current!==`touch`,n=e.nativeEvent.pointerType,r=t&&Re(e.nativeEvent)&&(n!==void 0||te),i=t&&!r&&!M.current;M.current=!1,!(e.type===`keydown`&&A.current===null)&&(s||e.type===`keydown`&&A.current===` `&&b.current||i||(A.current=null,P(e.nativeEvent)))},onPointerEnter(e){j.current=e.pointerType},onPointerMove(e){if(e.pointerType===`mouse`&&e.buttons===1){let t=y.current;t.dragY+=e.movementY,t.dragY**2>=64&&(t.allowUnselectedMouseUp=!0)}},onPointerDown(e){j.current=e.pointerType,M.current=!0,F()},onMouseUp(){if(F(),s||j.current===`touch`||M.current)return;let e=!y.current.allowSelectedMouseUp&&C,t=!y.current.allowUnselectedMouseUp&&!C;e||t||(M.current=!0,k.current?.click(),M.current=!1)}},I=l(`div`,e,{ref:[N,t,f.ref,k],state:ie,props:[m,ae,u,re]}),L=Q.useMemo(()=>({selected:C,index:D,textRef:d,selectedByFocus:w,hasRegistered:O}),[C,D,d,w,O]);return(0,Z.jsx)(_n.Provider,{value:L,children:I})})),bn=Q.forwardRef(function(e,t){let n=e.keepMounted??!1,{selected:r}=vn();return n||r?(0,Z.jsx)(xn,{...e,ref:t}):null}),xn=Q.memo(Q.forwardRef((e,t)=>{let{render:n,className:r,style:i,keepMounted:a,...o}=e,{selected:s}=vn(),c=Q.useRef(null),{transitionStatus:u,setMounted:d}=le(s),f=l(`span`,e,{ref:[t,c],state:{selected:s,transitionStatus:u},props:[{"aria-hidden":!0,children:`✔️`},o],stateAttributesMapping:F});return I({open:s,ref:c,onComplete(){s||d(!1)}}),f})),Sn=Q.memo(Q.forwardRef(function(e,t){let{index:n,textRef:r,selectedByFocus:i,hasRegistered:a}=vn(),{firstItemTextRef:o,selectedItemTextRef:s}=gt(),{render:c,className:u,style:d,...f}=e;return l(`div`,e,{ref:[Q.useCallback(e=>{e&&(a&&n===0&&(o.current=e),a&&i&&(s.current=e))},[o,s,n,i,a]),t,r],props:f})})),Cn=Q.forwardRef(function(e,t){let{render:n,className:r,style:i,direction:a,keepMounted:o=!1,...s}=e,c=a===`up`,{store:u,popupRef:d,listRef:f,handleScrollArrowVisibility:p,scrollArrowsMountedCountRef:m}=gt(),{side:h,scrollDownArrowRef:g,scrollUpArrowRef:v}=Kt(),y=J(u,c?$.scrollUpArrowVisible:$.scrollDownArrowVisible),b=J(u,$.openMethod),x=y&&b!==`touch`,S=R(),ee=c?v:g,{transitionStatus:te,setMounted:ne}=le(x);_(()=>(m.current+=1,u.state.hasScrollArrows||u.set(`hasScrollArrows`,!0),()=>{m.current=Math.max(0,m.current-1),m.current===0&&u.state.hasScrollArrows&&u.set(`hasScrollArrows`,!1)}),[u,m]),I({open:x,ref:ee,onComplete(){x||ne(!1)}});let C=l(`div`,e,{ref:[t,ee],state:{direction:a,visible:x,side:h,transitionStatus:te},props:[{"aria-hidden":!0,children:c?`▲`:`▼`,style:{position:`absolute`},onMouseMove(e){if(e.movementX===0&&e.movementY===0||S.isStarted())return;u.set(`activeIndex`,null);function t(){let e=u.state.listElement??d.current;if(!e)return;u.set(`activeIndex`,null),p();let n=jt(e.scrollHeight,e.clientHeight),r=Mt(e.scrollTop,n),i=r===(c?0:n),a=f.current;if(r!==e.scrollTop&&(e.scrollTop=r),a.length===0&&u.set(c?`scrollUpArrowVisible`:`scrollDownArrowVisible`,!i),i){S.clear();return}if(a.length>0){let t=ee.current?.offsetHeight||0;e.scrollTop=wn(a,c,r,e.clientHeight,t,n)}S.start(40,t)}S.start(40,t)},onMouseLeave(){S.clear()}},s]});return x||o?C:null});function wn(e,t,n,r,i,a){if(t){let t=0,r=n+i-1;for(let n=0;n=r){t=n;break}}let o=Math.max(0,t-1),s=e[o];return os){o=Math.max(0,t-1);break}}let c=Math.min(e.length-1,o+1),l=e[c];return c>o&&l?Mt(l.offsetTop+l.offsetHeight-r+i,a):a}var Tn=Q.forwardRef(function(e,t){return(0,Z.jsx)(Cn,{...e,ref:t,direction:`down`})}),En=Q.forwardRef(function(e,t){return(0,Z.jsx)(Cn,{...e,ref:t,direction:`up`})}),Dn=Q.createContext(void 0);function On(){let e=Q.useContext(Dn);if(e===void 0)throw Error(d(56));return e}var kn=Q.forwardRef(function(e,t){let{render:n,className:r,style:i,...a}=e,[o,s]=Q.useState(),c=Q.useMemo(()=>({labelId:o,setLabelId:s}),[o,s]),u=l(`div`,e,{ref:t,props:[{role:`group`,"aria-labelledby":o},a]});return(0,Z.jsx)(Dn.Provider,{value:c,children:u})}),An=Q.forwardRef(function(e,t){let{render:n,className:r,style:i,id:a,...o}=e,{setLabelId:s}=On(),c=b(a);return _(()=>{s(c)},[c,s]),l(`div`,e,{ref:t,props:[{id:c},o]})}),jn=r(),Mn=Nt;function Nn(e){let t=(0,jn.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=f(`scroll-my-1 p-1`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,Z.jsx)(kn,{className:i,"data-slot":`select-group`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Pn(e){let t=(0,jn.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=f(`flex flex-1 text-left`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,Z.jsx)(Vt,{className:i,"data-slot":`select-value`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Fn(e){let t=(0,jn.c)(15),n,r,i,a,o;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5]):({className:r,size:o,id:i,children:n,...a}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o);let s=o===void 0?`default`:o,c;t[6]===r?c=t[7]:(c=f(`flex w-fit items-center justify-between gap-1.5 rounded-md border border-input bg-input/20 px-2 py-1.5 text-xs/relaxed whitespace-nowrap transition-colors outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-7 data-[size=sm]:h-6 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5`,r),t[6]=r,t[7]=c);let l;t[8]===Symbol.for(`react.memo_cache_sentinel`)?(l=(0,Z.jsx)(Ht,{render:(0,Z.jsx)(m,{className:`pointer-events-none size-3.5 text-muted-foreground`})}),t[8]=l):l=t[8];let u;return t[9]!==n||t[10]!==i||t[11]!==a||t[12]!==s||t[13]!==c?(u=(0,Z.jsxs)(zt,{className:c,"data-size":s,"data-slot":`select-trigger`,id:i,...a,children:[n,l]}),t[9]=n,t[10]=i,t[11]=a,t[12]=s,t[13]=c,t[14]=u):u=t[14],u}function In(e){let t=(0,jn.c)(26),n,r,i,a,o,s,c,l;t[0]===e?(n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6],c=t[7],l=t[8]):({className:r,children:n,side:a,sideOffset:o,align:s,alignOffset:c,alignItemWithTrigger:l,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i,t[4]=a,t[5]=o,t[6]=s,t[7]=c,t[8]=l);let u=a===void 0?`bottom`:a,d=o===void 0?4:o,p=s===void 0?`center`:s,m=c===void 0?0:c,h=l===void 0?!0:l,g;t[9]===r?g=t[10]:(g=f(`data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-closed:animate-out data-open:animate-in relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100`,r),t[9]=r,t[10]=g);let _;t[11]===Symbol.for(`react.memo_cache_sentinel`)?(_=(0,Z.jsx)(Bn,{}),t[11]=_):_=t[11];let v;t[12]===n?v=t[13]:(v=(0,Z.jsx)(gn,{children:n}),t[12]=n,t[13]=v);let y;t[14]===Symbol.for(`react.memo_cache_sentinel`)?(y=(0,Z.jsx)(Vn,{}),t[14]=y):y=t[14];let b;t[15]!==i||t[16]!==g||t[17]!==v?(b=(0,Z.jsxs)(cn,{className:g,"data-slot":`select-content`,...i,children:[_,v,y]}),t[15]=i,t[16]=g,t[17]=v,t[18]=b):b=t[18];let x;return t[19]!==p||t[20]!==h||t[21]!==m||t[22]!==u||t[23]!==d||t[24]!==b?(x=(0,Z.jsx)(Wt,{children:(0,Z.jsx)(Qt,{align:p,alignItemWithTrigger:h,alignOffset:m,className:`isolate z-50`,side:u,sideOffset:d,children:b})}),t[19]=p,t[20]=h,t[21]=m,t[22]=u,t[23]=d,t[24]=b,t[25]=x):x=t[25],x}function Ln(e){let t=(0,jn.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=f(`px-2 py-1.5 text-xs text-muted-foreground`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,Z.jsx)(An,{className:i,"data-slot":`select-label`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Rn(e){let t=(0,jn.c)(13),n,r,i;t[0]===e?(n=t[1],r=t[2],i=t[3]):({className:r,children:n,...i}=e,t[0]=e,t[1]=n,t[2]=r,t[3]=i);let a;t[4]===r?a=t[5]:(a=f(`relative flex min-h-7 w-full cursor-default items-center gap-2 rounded-md px-2 py-1 text-xs/relaxed outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2`,r),t[4]=r,t[5]=a);let o;t[6]===n?o=t[7]:(o=(0,Z.jsx)(Sn,{className:`flex flex-1 shrink-0 gap-2 whitespace-nowrap`,children:n}),t[6]=n,t[7]=o);let s;t[8]===Symbol.for(`react.memo_cache_sentinel`)?(s=(0,Z.jsx)(bn,{render:(0,Z.jsx)(`span`,{className:`pointer-events-none absolute right-2 flex items-center justify-center`}),children:(0,Z.jsx)(p,{className:`pointer-events-none`})}),t[8]=s):s=t[8];let c;return t[9]!==i||t[10]!==a||t[11]!==o?(c=(0,Z.jsxs)(yn,{className:a,"data-slot":`select-item`,...i,children:[o,s]}),t[9]=i,t[10]=a,t[11]=o,t[12]=c):c=t[12],c}function zn(e){let t=(0,jn.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=f(`pointer-events-none -mx-1 my-1 h-px bg-border/50`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,Z.jsx)(B,{className:i,"data-slot":`select-separator`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Bn(e){let t=(0,jn.c)(9),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=f(`top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-3.5`,n),t[3]=n,t[4]=i);let a;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,Z.jsx)(Ze,{}),t[5]=a):a=t[5];let o;return t[6]!==r||t[7]!==i?(o=(0,Z.jsx)(En,{className:i,"data-slot":`select-scroll-up-button`,...r,children:a}),t[6]=r,t[7]=i,t[8]=o):o=t[8],o}function Vn(e){let t=(0,jn.c)(9),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=f(`bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-3.5`,n),t[3]=n,t[4]=i);let a;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,Z.jsx)(m,{}),t[5]=a):a=t[5];let o;return t[6]!==r||t[7]!==i?(o=(0,Z.jsx)(Tn,{className:i,"data-slot":`select-scroll-down-button`,...r,children:a}),t[6]=r,t[7]=i,t[8]=o):o=t[8],o}export{Ln as a,Pn as c,Rn as i,In as n,zn as o,Nn as r,Fn as s,Mn as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/service-metrics.js b/spinifex/services/spinifexui/frontend/dist/assets/service-metrics.js index ac33428af..40a9eb30e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/service-metrics.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/service-metrics.js @@ -1 +1 @@ -import{t as e}from"./useQuery.js";import{Fo as t,Ho as n,gr as r,zo as i}from"./index.js";import{t as a}from"./page-heading.js";import{n as o}from"./admin.js";import{t as s}from"./badge.js";import{a as c,o as l,r as u,t as d}from"./card.js";var f=t(),p=n();function m(){let t=(0,f.c)(46),{isAdmin:n}=r(),m;t[0]===n?m=t[1]:(m={...o,enabled:n,refetchInterval:5e3},t[0]=n,t[1]=m);let{data:y}=e(m);if(!n)throw i({to:`/`});let x=y?.encoding,S;t[2]===y?.db_nodes?S=t[3]:(S=y?.db_nodes??[],t[2]=y?.db_nodes,t[3]=S);let C=S,w;t[4]===y?.shard_nodes?w=t[5]:(w=y?.shard_nodes??[],t[4]=y?.shard_nodes,t[5]=w);let T=w,E;t[6]===C?E=t[7]:(E=C.length>0&&C.every(g),t[6]=C,t[7]=E);let D=E,O;t[8]===C?O=t[9]:(O=C.some(h),t[8]=C,t[9]=O);let k=O,A;t[10]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,p.jsx)(a,{title:`Service Metrics`}),t[10]=A):A=t[10];let j;t[11]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,p.jsx)(c,{children:(0,p.jsx)(l,{className:`text-sm`,children:`Predastore (S3) Overview`})}),t[11]=j):j=t[11];let M;t[12]===Symbol.for(`react.memo_cache_sentinel`)?(M=(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`Encoding`}),t[12]=M):M=t[12];let N;t[13]===x?N=t[14]:(N=(0,p.jsxs)(`div`,{children:[M,(0,p.jsx)(`div`,{className:`mt-1`,children:x?(0,p.jsxs)(s,{variant:`outline`,children:[x.type,` RS(`,x.data_shards,`,`,x.parity_shards,`)`]}):(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`-`})})]}),t[13]=x,t[14]=N);let P;t[15]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`DB Nodes`}),t[15]=P):P=t[15];let F;t[16]===C.length?F=t[17]:(F=(0,p.jsxs)(`div`,{children:[P,(0,p.jsx)(`div`,{className:`mt-1 font-mono font-medium`,children:C.length})]}),t[16]=C.length,t[17]=F);let I;t[18]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`Shard Nodes`}),t[18]=I):I=t[18];let L;t[19]===T.length?L=t[20]:(L=(0,p.jsxs)(`div`,{children:[I,(0,p.jsx)(`div`,{className:`mt-1 font-mono font-medium`,children:T.length})]}),t[19]=T.length,t[20]=L);let R;t[21]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`Cluster Health`}),t[21]=R):R=t[21];let z;t[22]!==D||t[23]!==k||t[24]!==C.length?(z=(0,p.jsxs)(`div`,{children:[R,(0,p.jsx)(`div`,{className:`mt-1`,children:(0,p.jsx)(_,{nodeCount:C.length,allHealthy:D,anyUnhealthy:k})})]}),t[22]=D,t[23]=k,t[24]=C.length,t[25]=z):z=t[25];let B;t[26]!==F||t[27]!==L||t[28]!==z||t[29]!==N?(B=(0,p.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 text-xs md:grid-cols-4`,children:[N,F,L,z]}),t[26]=F,t[27]=L,t[28]=z,t[29]=N,t[30]=B):B=t[30];let V;t[31]===x?V=t[32]:(V=x&&(0,p.jsxs)(`p`,{className:`mt-3 text-[0.625rem] text-muted-foreground`,children:[`Reed-Solomon erasure coding: `,x.data_shards,` data shards + `,x.parity_shards,` parity`,` `,x.parity_shards===1?`shard`:`shards`,`. Can tolerate `,x.parity_shards,` shard`,` `,x.parity_shards===1?`failure`:`failures`,` without data loss.`]}),t[31]=x,t[32]=V);let H;t[33]!==B||t[34]!==V?(H=(0,p.jsxs)(d,{children:[j,(0,p.jsxs)(u,{children:[B,V]})]}),t[33]=B,t[34]=V,t[35]=H):H=t[35];let U;t[36]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,p.jsx)(c,{children:(0,p.jsx)(l,{className:`text-sm`,children:`DB Consensus Nodes (Raft)`})}),t[36]=U):U=t[36];let W;t[37]===C?W=t[38]:(W=(0,p.jsxs)(d,{children:[U,(0,p.jsx)(u,{children:C.length===0?(0,p.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No data`}):(0,p.jsx)(v,{nodes:C})})]}),t[37]=C,t[38]=W);let G;t[39]===Symbol.for(`react.memo_cache_sentinel`)?(G=(0,p.jsx)(c,{children:(0,p.jsx)(l,{className:`text-sm`,children:`Object Storage Shard Nodes`})}),t[39]=G):G=t[39];let K;t[40]===T?K=t[41]:(K=(0,p.jsxs)(d,{children:[G,(0,p.jsx)(u,{children:T.length===0?(0,p.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No data`}):(0,p.jsx)(b,{nodes:T})})]}),t[40]=T,t[41]=K);let q;return t[42]!==H||t[43]!==W||t[44]!==K?(q=(0,p.jsxs)(p.Fragment,{children:[A,(0,p.jsxs)(`div`,{className:`space-y-6`,children:[H,W,K]})]}),t[42]=H,t[43]=W,t[44]=K,t[45]=q):q=t[45],q}function h(e){return!e.healthy}function g(e){return e.healthy}function _(e){let t=(0,f.c)(4),{nodeCount:n,allHealthy:r,anyUnhealthy:i}=e;if(n===0){let e;return t[0]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,p.jsx)(s,{variant:`secondary`,children:`Unknown`}),t[0]=e):e=t[0],e}if(r){let e;return t[1]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,p.jsx)(s,{variant:`default`,children:`Healthy`}),t[1]=e):e=t[1],e}if(i){let e;return t[2]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,p.jsx)(s,{variant:`destructive`,children:`Degraded`}),t[2]=e):e=t[2],e}let a;return t[3]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,p.jsx)(s,{variant:`secondary`,children:`Unknown`}),t[3]=a):a=t[3],a}function v(e){let t=(0,f.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,p.jsx)(`thead`,{children:(0,p.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Node`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Host`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Port`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Health`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`State`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Leader`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Term`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Commit Index`}),(0,p.jsx)(`th`,{className:`pb-1 font-medium`,children:`Applied Index`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(y),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,p.jsx)(`div`,{className:`overflow-x-auto`,children:(0,p.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,p.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function y(e){return(0,p.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.id}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.host}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.port}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,p.jsx)(s,{variant:e.healthy?`default`:`destructive`,className:`text-[0.625rem]`,children:e.healthy?`Healthy`:`Unreachable`})}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.state?(0,p.jsx)(s,{variant:e.state===`Leader`?`default`:`secondary`,className:`text-[0.625rem]`,children:e.state}):(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`-`})}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.leader??`-`}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.term??`-`}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.commit_index??`-`}),(0,p.jsx)(`td`,{className:`py-1.5 font-mono`,children:e.applied_index??`-`})]},e.id)}function b(e){let t=(0,f.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,p.jsx)(`thead`,{children:(0,p.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Node`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Host`}),(0,p.jsx)(`th`,{className:`pb-1 font-medium`,children:`Port`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(x),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,p.jsx)(`div`,{className:`overflow-x-auto`,children:(0,p.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,p.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function x(e){return(0,p.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.id}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.host}),(0,p.jsx)(`td`,{className:`py-1.5 font-mono`,children:e.port})]},e.id)}export{m as component}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./useQuery.js";import{n as r}from"./redirect.js";import{Bt as i}from"./index.js";import{t as a}from"./page-heading.js";import{n as o}from"./admin.js";import{t as s}from"./badge.js";import{a as c,o as l,r as u,t as d}from"./card.js";var f=t(),p=e();function m(){let e=(0,f.c)(46),{isAdmin:t}=i(),m;e[0]===t?m=e[1]:(m={...o,enabled:t,refetchInterval:5e3},e[0]=t,e[1]=m);let{data:y}=n(m);if(!t)throw r({to:`/`});let x=y?.encoding,S;e[2]===y?.db_nodes?S=e[3]:(S=y?.db_nodes??[],e[2]=y?.db_nodes,e[3]=S);let C=S,w;e[4]===y?.shard_nodes?w=e[5]:(w=y?.shard_nodes??[],e[4]=y?.shard_nodes,e[5]=w);let T=w,E;e[6]===C?E=e[7]:(E=C.length>0&&C.every(g),e[6]=C,e[7]=E);let D=E,O;e[8]===C?O=e[9]:(O=C.some(h),e[8]=C,e[9]=O);let k=O,A;e[10]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,p.jsx)(a,{title:`Service Metrics`}),e[10]=A):A=e[10];let j;e[11]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,p.jsx)(c,{children:(0,p.jsx)(l,{className:`text-sm`,children:`Predastore (S3) Overview`})}),e[11]=j):j=e[11];let M;e[12]===Symbol.for(`react.memo_cache_sentinel`)?(M=(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`Encoding`}),e[12]=M):M=e[12];let N;e[13]===x?N=e[14]:(N=(0,p.jsxs)(`div`,{children:[M,(0,p.jsx)(`div`,{className:`mt-1`,children:x?(0,p.jsxs)(s,{variant:`outline`,children:[x.type,` RS(`,x.data_shards,`,`,x.parity_shards,`)`]}):(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`-`})})]}),e[13]=x,e[14]=N);let P;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`DB Nodes`}),e[15]=P):P=e[15];let F;e[16]===C.length?F=e[17]:(F=(0,p.jsxs)(`div`,{children:[P,(0,p.jsx)(`div`,{className:`mt-1 font-mono font-medium`,children:C.length})]}),e[16]=C.length,e[17]=F);let I;e[18]===Symbol.for(`react.memo_cache_sentinel`)?(I=(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`Shard Nodes`}),e[18]=I):I=e[18];let L;e[19]===T.length?L=e[20]:(L=(0,p.jsxs)(`div`,{children:[I,(0,p.jsx)(`div`,{className:`mt-1 font-mono font-medium`,children:T.length})]}),e[19]=T.length,e[20]=L);let R;e[21]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`Cluster Health`}),e[21]=R):R=e[21];let z;e[22]!==D||e[23]!==k||e[24]!==C.length?(z=(0,p.jsxs)(`div`,{children:[R,(0,p.jsx)(`div`,{className:`mt-1`,children:(0,p.jsx)(_,{nodeCount:C.length,allHealthy:D,anyUnhealthy:k})})]}),e[22]=D,e[23]=k,e[24]=C.length,e[25]=z):z=e[25];let B;e[26]!==F||e[27]!==L||e[28]!==z||e[29]!==N?(B=(0,p.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 text-xs md:grid-cols-4`,children:[N,F,L,z]}),e[26]=F,e[27]=L,e[28]=z,e[29]=N,e[30]=B):B=e[30];let V;e[31]===x?V=e[32]:(V=x&&(0,p.jsxs)(`p`,{className:`mt-3 text-[0.625rem] text-muted-foreground`,children:[`Reed-Solomon erasure coding: `,x.data_shards,` data shards + `,x.parity_shards,` parity`,` `,x.parity_shards===1?`shard`:`shards`,`. Can tolerate `,x.parity_shards,` shard`,` `,x.parity_shards===1?`failure`:`failures`,` without data loss.`]}),e[31]=x,e[32]=V);let H;e[33]!==B||e[34]!==V?(H=(0,p.jsxs)(d,{children:[j,(0,p.jsxs)(u,{children:[B,V]})]}),e[33]=B,e[34]=V,e[35]=H):H=e[35];let U;e[36]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,p.jsx)(c,{children:(0,p.jsx)(l,{className:`text-sm`,children:`DB Consensus Nodes (Raft)`})}),e[36]=U):U=e[36];let W;e[37]===C?W=e[38]:(W=(0,p.jsxs)(d,{children:[U,(0,p.jsx)(u,{children:C.length===0?(0,p.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No data`}):(0,p.jsx)(v,{nodes:C})})]}),e[37]=C,e[38]=W);let G;e[39]===Symbol.for(`react.memo_cache_sentinel`)?(G=(0,p.jsx)(c,{children:(0,p.jsx)(l,{className:`text-sm`,children:`Object Storage Shard Nodes`})}),e[39]=G):G=e[39];let K;e[40]===T?K=e[41]:(K=(0,p.jsxs)(d,{children:[G,(0,p.jsx)(u,{children:T.length===0?(0,p.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No data`}):(0,p.jsx)(b,{nodes:T})})]}),e[40]=T,e[41]=K);let q;return e[42]!==H||e[43]!==W||e[44]!==K?(q=(0,p.jsxs)(p.Fragment,{children:[A,(0,p.jsxs)(`div`,{className:`space-y-6`,children:[H,W,K]})]}),e[42]=H,e[43]=W,e[44]=K,e[45]=q):q=e[45],q}function h(e){return!e.healthy}function g(e){return e.healthy}function _(e){let t=(0,f.c)(4),{nodeCount:n,allHealthy:r,anyUnhealthy:i}=e;if(n===0){let e;return t[0]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,p.jsx)(s,{variant:`secondary`,children:`Unknown`}),t[0]=e):e=t[0],e}if(r){let e;return t[1]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,p.jsx)(s,{variant:`default`,children:`Healthy`}),t[1]=e):e=t[1],e}if(i){let e;return t[2]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,p.jsx)(s,{variant:`destructive`,children:`Degraded`}),t[2]=e):e=t[2],e}let a;return t[3]===Symbol.for(`react.memo_cache_sentinel`)?(a=(0,p.jsx)(s,{variant:`secondary`,children:`Unknown`}),t[3]=a):a=t[3],a}function v(e){let t=(0,f.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,p.jsx)(`thead`,{children:(0,p.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Node`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Host`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Port`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Health`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`State`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Leader`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Term`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Commit Index`}),(0,p.jsx)(`th`,{className:`pb-1 font-medium`,children:`Applied Index`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(y),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,p.jsx)(`div`,{className:`overflow-x-auto`,children:(0,p.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,p.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function y(e){return(0,p.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.id}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.host}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.port}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4`,children:(0,p.jsx)(s,{variant:e.healthy?`default`:`destructive`,className:`text-[0.625rem]`,children:e.healthy?`Healthy`:`Unreachable`})}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4`,children:e.state?(0,p.jsx)(s,{variant:e.state===`Leader`?`default`:`secondary`,className:`text-[0.625rem]`,children:e.state}):(0,p.jsx)(`span`,{className:`text-muted-foreground`,children:`-`})}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.leader??`-`}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.term??`-`}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.commit_index??`-`}),(0,p.jsx)(`td`,{className:`py-1.5 font-mono`,children:e.applied_index??`-`})]},e.id)}function b(e){let t=(0,f.c)(5),{nodes:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,p.jsx)(`thead`,{children:(0,p.jsxs)(`tr`,{className:`border-b text-left text-muted-foreground`,children:[(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Node`}),(0,p.jsx)(`th`,{className:`pr-4 pb-1 font-medium`,children:`Host`}),(0,p.jsx)(`th`,{className:`pb-1 font-medium`,children:`Port`})]})}),t[0]=r):r=t[0];let i;t[1]===n?i=t[2]:(i=n.map(x),t[1]=n,t[2]=i);let a;return t[3]===i?a=t[4]:(a=(0,p.jsx)(`div`,{className:`overflow-x-auto`,children:(0,p.jsxs)(`table`,{className:`w-full text-xs`,children:[r,(0,p.jsx)(`tbody`,{children:i})]})}),t[3]=i,t[4]=a),a}function x(e){return(0,p.jsxs)(`tr`,{className:`border-b last:border-0`,children:[(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono font-medium`,children:e.id}),(0,p.jsx)(`td`,{className:`py-1.5 pr-4 font-mono`,children:e.host}),(0,p.jsx)(`td`,{className:`py-1.5 font-mono`,children:e.port})]},e.id)}export{m as component}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/state-badge.js b/spinifex/services/spinifexui/frontend/dist/assets/state-badge.js index a5a502200..fc88eac0d 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/state-badge.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/state-badge.js @@ -1 +1 @@ -import{$a as e,Fo as t,Ho as n}from"./index.js";var r=t(),i=n(),a=new Set([`running`,`available`,`completed`,`Active`]),o=new Set([`stopped`,`error`]),s=new Set([`pending`,`shutting-down`,`stopping`]);function c(e){return e&&a.has(e)?`bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100`:e&&o.has(e)?`bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100`:e&&s.has(e)?`bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100`:e===`terminated`?`bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400`:`bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100`}function l(t){let n=(0,r.c)(5),{state:a}=t,o;n[0]===a?o=n[1]:(o=e(`rounded-full px-2 py-1 text-xs`,c(a)),n[0]=a,n[1]=o);let s;return n[2]!==a||n[3]!==o?(s=(0,i.jsx)(`div`,{className:o,children:a}),n[2]=a,n[3]=o,n[4]=s):s=n[4],s}export{l as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{w as n}from"./SignatureV4.js";var r=t(),i=e(),a=new Set([`running`,`available`,`completed`,`Active`]),o=new Set([`stopped`,`error`]),s=new Set([`pending`,`shutting-down`,`stopping`]);function c(e){return e&&a.has(e)?`bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100`:e&&o.has(e)?`bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100`:e&&s.has(e)?`bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100`:e===`terminated`?`bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400`:`bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100`}function l(e){let t=(0,r.c)(5),{state:a}=e,o;t[0]===a?o=t[1]:(o=n(`rounded-full px-2 py-1 text-xs`,c(a)),t[0]=a,t[1]=o);let s;return t[2]!==a||t[3]!==o?(s=(0,i.jsx)(`div`,{className:o,children:a}),t[2]=a,t[3]=o,t[4]=s):s=t[4],s}export{l as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/subnet-calculator.js b/spinifex/services/spinifexui/frontend/dist/assets/subnet-calculator.js new file mode 100644 index 000000000..17bcadf6f --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/subnet-calculator.js @@ -0,0 +1 @@ +function e(e){let t=e.split(`/`),n=t[0]??``,r=t[1]??`0`,i=Number.parseInt(r,10),a=n.split(`.`).map(e=>Number.parseInt(e,10)),o=a[0]??0,s=a[1]??0,c=a[2]??0,l=a[3]??0;return{network:((o<<24|s<<16|c<<8|l)>>>0&(i===0?0:4294967295<<32-i>>>0))>>>0,prefix:i}}function t(e){return`${e>>>24&255}.${e>>>16&255}.${e>>>8&255}.${e&255}`}function n(e){return 2**(32-e)}function r(e){let t=e+4;return Math.min(t,28)}function i(i,a,o){let{network:s,prefix:c}=e(i),l=r(c),u=n(l),d=n(c)/2,f=[];for(let e=0;e>>0;f.push({cidr:`${t(n)}/${l}`,label:`Public Subnet ${e+1}`})}let p=[];for(let e=0;e>>0;p.push({cidr:`${t(n)}/${l}`,label:`Private Subnet ${e+1}`})}return{publicSubnets:f,privateSubnets:p}}function a(t,r){let i=e(t),a=e(r),o=i.network+n(i.prefix)-1>>>0,s=a.network+n(a.prefix)-1>>>0;return i.network<=s&&a.network<=o}function o(t,r){let i=e(t),a=e(r),o=i.network+n(i.prefix)-1>>>0,s=a.network+n(a.prefix)-1>>>0;return a.network>=i.network&&s<=o}function s(e,t=16,n=28){let r=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/.exec(e);if(!r||[Number.parseInt(r[1]??`0`,10),Number.parseInt(r[2]??`0`,10),Number.parseInt(r[3]??`0`,10),Number.parseInt(r[4]??`0`,10)].some(e=>e>255))return!1;let i=Number.parseInt(r[5]??`0`,10);return i>=t&&i<=n}export{s as i,o as n,a as r,i as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/system-managed.js b/spinifex/services/spinifexui/frontend/dist/assets/system-managed.js index bff20f580..bc81e759e 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/system-managed.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/system-managed.js @@ -1 +1 @@ -function e(e){return e.Tags?.some(e=>e.Key===`spinifex:managed-by`)??!1}export{e as t}; \ No newline at end of file +function e(e){return e.Tags?.some(e=>e.Key===`spinifex:managed-by`)??!1}function t(e){return e.Tags?.some(e=>e.Key===`spinifex:managed-by`&&e.Value===`eks`)??!1}export{e as n,t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/tabs.js b/spinifex/services/spinifexui/frontend/dist/assets/tabs.js index 5daaeb874..a5a747726 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/tabs.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/tabs.js @@ -1 +1 @@ -import{c as e,i as t,l as n,n as r,s as i,t as a,u as o}from"./select.js";import{$a as s,Ca as c,Ci as l,Do as u,Ea as d,Fo as f,Ho as p,Ia as m,La as h,No as g,Oo as _,Ra as v,Sa as y,Si as b,Ta as x,Uo as S,Wa as C,Xa as w,Xi as T,Xr as E,Zi as D,_a as O,_i as k,ba as A,bi as j,ca as M,co as N,cs as P,da as F,di as I,do as L,fa as R,fi as z,jo as B,ko as V,la as H,li as ee,lo as te,mi as U,na as W,pi as ne,qa as G,sa as re,si as ie,so as ae,ui as K,uo as q,va as oe,vi as se,xa as ce,xi as le,ya as ue,yi as de}from"./index.js";import{a as fe,r as pe,t as me}from"./field.js";var J=f(),Y=P(S(),1),X=p();function he(e,t){let n=new Map;for(let e of t)e.Key!==void 0&&n.set(e.Key,e.Value??``);let r={};for(let t of e)r[t.key]=n.get(t.key)??``;return r}function ge(e,t){if(e.type===`int`){if(t.trim()===``)return`Value is required`;if(!/^-?\d+$/.test(t))return`Must be a whole number`;let n=Number(t);if(e.min!==void 0&&ne.max)return`Must be at most ${e.max}`}}function _e(t){let n=(0,J.c)(56),{specs:o,attributes:s,onSubmit:c,isPending:l,error:u,isSuccess:d}=t,f;n[0]!==s||n[1]!==o?(f=he(o,s),n[0]=s,n[1]=o,n[2]=f):f=n[2];let p=f,[m,h]=(0,Y.useState)(p),g;n[3]===Symbol.for(`react.memo_cache_sentinel`)?(g=(e,t)=>{h(n=>({...n,[e]:t}))},n[3]=g):g=n[3];let _=g,v;if(n[4]!==o||n[5]!==m){v={};for(let e of o)v[e.key]=ge(e,m[e.key]??``);n[4]=o,n[5]=m,n[6]=v}else v=n[6];let y=Object.values(v).some(Boolean),b;if(n[7]!==p||n[8]!==o||n[9]!==m){let e;n[11]!==p||n[12]!==m?(e=e=>(m[e.key]??``)!==(p[e.key]??``),n[11]=p,n[12]=m,n[13]=e):e=n[13];let t;n[14]===m?t=n[15]:(t=e=>({key:e.key,value:m[e.key]??``}),n[14]=m,n[15]=t),b=o.filter(e).map(t),n[7]=p,n[8]=o,n[9]=m,n[10]=b}else b=n[10];let x=b,S;n[16]===p?S=n[17]:(S=()=>{h(p)},n[16]=p,n[17]=S);let C=S,E;n[18]!==x||n[19]!==y||n[20]!==c?(E=e=>{e.preventDefault(),!(y||x.length===0)&&c(x)},n[18]=x,n[19]=y,n[20]=c,n[21]=E):E=n[21];let D;n[22]===u?D=n[23]:(D=u&&(0,X.jsx)(`div`,{className:`mb-4 rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive`,role:`alert`,children:u.message||`Failed to save attributes`}),n[22]=u,n[23]=D);let O;n[24]!==x.length||n[25]!==d?(O=d&&x.length===0&&(0,X.jsx)(`div`,{className:`mb-4 rounded-md border border-emerald-500 bg-emerald-500/10 p-3 text-sm text-emerald-700 dark:text-emerald-400`,role:`status`,children:`Attributes saved.`}),n[24]=x.length,n[25]=d,n[26]=O):O=n[26];let k;if(n[27]!==v||n[28]!==o||n[29]!==m){let t;n[31]!==v||n[32]!==m?(t=t=>{let n=`attr-${t.key}`,o=m[t.key]??``,s=v[t.key];return(0,X.jsxs)(me,{children:[(0,X.jsx)(fe,{children:(0,X.jsx)(`label`,{htmlFor:n,children:t.label})}),t.type===`bool`&&(0,X.jsx)(`input`,{"aria-label":t.label,checked:o===`true`,id:n,onChange:e=>_(t.key,e.target.checked?`true`:`false`),type:`checkbox`}),t.type===`int`&&(0,X.jsx)(T,{"aria-invalid":!!s,id:n,inputMode:`numeric`,onChange:e=>_(t.key,e.target.value),value:o}),t.type===`text`&&(0,X.jsx)(T,{id:n,onChange:e=>_(t.key,e.target.value),placeholder:t.placeholder,value:o}),t.type===`select`&&(0,X.jsxs)(a,{onValueChange:e=>_(t.key,e??``),value:o,children:[(0,X.jsx)(i,{className:`w-full`,id:n,children:(0,X.jsx)(e,{})}),(0,X.jsx)(r,{children:t.options.map(ve)})]}),t.description&&(0,X.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:t.description}),(0,X.jsx)(pe,{errors:[s?{message:s}:void 0]})]},t.key)},n[31]=v,n[32]=m,n[33]=t):t=n[33],k=o.map(t),n[27]=v,n[28]=o,n[29]=m,n[30]=k}else k=n[30];let A;n[34]===k?A=n[35]:(A=(0,X.jsx)(`div`,{className:`grid gap-4 p-4 sm:grid-cols-2`,children:k}),n[34]=k,n[35]=A);let j=x.length===0?`No changes`:`${x.length} change${x.length===1?``:`s`} pending`,M;n[36]===j?M=n[37]:(M=(0,X.jsx)(`span`,{className:`mr-auto text-xs text-muted-foreground`,children:j}),n[36]=j,n[37]=M);let N=l||x.length===0,P;n[38]!==C||n[39]!==N?(P=(0,X.jsx)(w,{disabled:N,onClick:C,size:`sm`,type:`button`,variant:`secondary`,children:`Reset`}),n[38]=C,n[39]=N,n[40]=P):P=n[40];let F=l||y||x.length===0,I=l?`Saving…`:`Save changes`,L;n[41]!==F||n[42]!==I?(L=(0,X.jsx)(w,{disabled:F,size:`sm`,type:`submit`,children:I}),n[41]=F,n[42]=I,n[43]=L):L=n[43];let R;n[44]!==M||n[45]!==P||n[46]!==L?(R=(0,X.jsxs)(`div`,{className:`flex items-center justify-end gap-2 border-t p-4`,children:[M,P,L]}),n[44]=M,n[45]=P,n[46]=L,n[47]=R):R=n[47];let z;n[48]!==R||n[49]!==A?(z=(0,X.jsxs)(`div`,{className:`rounded-lg border bg-card`,children:[A,R]}),n[48]=R,n[49]=A,n[50]=z):z=n[50];let B;return n[51]!==z||n[52]!==E||n[53]!==D||n[54]!==O?(B=(0,X.jsxs)(`form`,{onSubmit:E,children:[D,O,z]}),n[51]=z,n[52]=E,n[53]=D,n[54]=O,n[55]=B):B=n[55],B}function ve(e){return(0,X.jsx)(t,{value:e,children:e},e)}var ye=[{key:`deletion_protection.enabled`,label:`Deletion protection`,type:`bool`},{key:`load_balancing.cross_zone.enabled`,label:`Cross-zone load balancing`,type:`bool`},{key:`idle_timeout.timeout_seconds`,label:`Idle timeout (s)`,type:`int`,min:1,max:4e3},{key:`client_keep_alive.seconds`,label:`Client keep-alive (s)`,type:`int`,min:60,max:604800},{key:`routing.http2.enabled`,label:`HTTP/2`,type:`bool`},{key:`routing.http.drop_invalid_header_fields.enabled`,label:`Drop invalid header fields`,type:`bool`},{key:`routing.http.preserve_host_header.enabled`,label:`Preserve host header`,type:`bool`},{key:`routing.http.xff_client_port.enabled`,label:`XFF client port`,type:`bool`},{key:`routing.http.xff_header_processing.mode`,label:`XFF header processing`,type:`select`,options:[`append`,`preserve`,`remove`]},{key:`routing.http.desync_mitigation_mode`,label:`Desync mitigation mode`,type:`select`,options:[`defensive`,`strictest`,`monitor`]},{key:`routing.http.x_amzn_tls_version_and_cipher_suite.enabled`,label:`Add TLS version & cipher headers`,type:`bool`},{key:`waf.fail_open.enabled`,label:`WAF fail open`,type:`bool`},{key:`zonal_shift.config.enabled`,label:`Zonal shift`,type:`bool`},{key:`access_logs.s3.enabled`,label:`Access logs: enabled`,type:`bool`},{key:`access_logs.s3.bucket`,label:`Access logs: S3 bucket`,type:`text`},{key:`access_logs.s3.prefix`,label:`Access logs: S3 prefix`,type:`text`},{key:`connection_logs.s3.enabled`,label:`Connection logs: enabled`,type:`bool`},{key:`connection_logs.s3.bucket`,label:`Connection logs: S3 bucket`,type:`text`},{key:`connection_logs.s3.prefix`,label:`Connection logs: S3 prefix`,type:`text`}],be=[{key:`deregistration_delay.timeout_seconds`,label:`Deregistration delay (s)`,type:`int`,min:0,max:3600},{key:`stickiness.enabled`,label:`Stickiness`,type:`bool`},{key:`stickiness.type`,label:`Stickiness type`,type:`select`,options:[`lb_cookie`,`source_ip`]},{key:`stickiness.lb_cookie.duration_seconds`,label:`Stickiness lb_cookie duration (s)`,type:`int`,min:1,max:604800},{key:`load_balancing.cross_zone.enabled`,label:`Cross-zone load balancing`,type:`select`,options:[`true`,`false`,`use_load_balancer_configuration`]},{key:`load_balancing.algorithm.type`,label:`Load balancing algorithm`,type:`select`,options:[`round_robin`,`least_outstanding_requests`]},{key:`slow_start.duration_seconds`,label:`Slow start duration (s)`,type:`int`,min:0,max:900}],xe=Y.createContext(void 0);function Z(){let e=Y.useContext(xe);if(e===void 0)throw Error(g(64));return e}var Se=function(e){return e.activationDirection=`data-activation-direction`,e.orientation=`data-orientation`,e}({}),Q={tabActivationDirection:e=>({[Se.activationDirection]:e})},Ce=Y.forwardRef(function(e,t){let{className:n,defaultValue:r=0,onValueChange:i,orientation:a=`horizontal`,render:s,value:c,style:l,...d}=e,f=Object.hasOwn(e,`defaultValue`),p=Y.useRef([]),[m,h]=Y.useState(()=>new Map),[g,_]=re({controlled:c,default:r,name:`Tabs`,state:`value`}),v=c!==void 0,[y,b]=Y.useState(()=>new Map),x=Y.useCallback(e=>{if(e===void 0)return null;for(let[t,n]of y.entries())if(n!=null&&e===(n.value??n.index))return t;return null},[y]),[S,C]=Y.useState(()=>({previousValue:g,tabActivationDirection:`none`})),{previousValue:w,tabActivationDirection:T}=S,E=T,D=!1;w!==g&&(E=we(w,g,a,y),D=w!=null&&g!=null&&x(g)==null);let O=D?w:g,k=w!==O||T!==E;q(()=>{k&&C({previousValue:O,tabActivationDirection:E})},[O,k,E]);let A=L((e,t)=>{t.activationDirection=we(g,e,a,y),i?.(e,t),!t.isCanceled&&_(e)}),j=L((e,t)=>{h(n=>{if(n.get(e)===t)return n;let r=new Map(n);return r.set(e,t),r})}),M=L((e,t)=>{h(n=>{if(!n.has(e)||n.get(e)!==t)return n;let r=new Map(n);return r.delete(e),r})}),N=Y.useCallback(e=>m.get(e),[m]),P=Y.useCallback(e=>{for(let t of y.values())if(e===t?.value)return t?.id},[y]),F=Y.useMemo(()=>({getTabElementBySelectedValue:x,getTabIdByPanelValue:P,getTabPanelIdByValue:N,onValueChange:A,orientation:a,registerMountedTabPanel:j,setTabMap:b,unregisterMountedTabPanel:M,tabActivationDirection:E,value:g}),[x,P,N,A,a,j,b,M,E,g]),I=Y.useMemo(()=>{for(let e of y.values())if(e!=null&&e.value===g)return e},[y,g]),R=Y.useMemo(()=>{for(let e of y.values())if(e!=null&&!e.disabled)return e.value},[y]);q(()=>{if(v||y.size===0)return;let e=I?.disabled;if(f&&e&&g===r||!e&&!(I==null&&g!==null))return;let t=R??null;g!==t&&(_(t),C(e=>e.tabActivationDirection===`none`?e:{...e,tabActivationDirection:`none`}))},[r,R,f,v,I,_,y,g]);let z=u(`div`,e,{state:{orientation:a,tabActivationDirection:E},ref:t,props:d,stateAttributesMapping:Q});return(0,X.jsx)(xe.Provider,{value:F,children:(0,X.jsx)(o,{elementsRef:p,children:z})})});function we(e,t,n,r){if(e==null||t==null)return`none`;let i=null,a=null;for(let[n,o]of r.entries()){if(o==null)continue;let r=o.value??o.index;if(e===r&&(i=n),t===r&&(a=n),i!=null&&a!=null)break}if(i==null||a==null)return i!==a&&(typeof e==`number`||typeof e==`string`)&&typeof e==typeof t?n===`horizontal`?t>e?`right`:`left`:t>e?`down`:`up`:`none`;let o=i.getBoundingClientRect(),s=a.getBoundingClientRect();if(n===`horizontal`){if(s.lefto.left)return`right`}else{if(s.topo.top)return`down`}return`none`}var Te=`data-composite-item-active`;function Ee(e={}){let{highlightItemOnHover:t,highlightedIndex:r,onHighlightedIndexChange:i}=te(),{ref:a,index:o}=n(e),s=r===o,c=Y.useRef(null),l=B(a,c);return{compositeProps:Y.useMemo(()=>({tabIndex:s?0:-1,onFocus(){i(o)},onMouseMove(){let e=c.current;if(!t||!e)return;let n=e.hasAttribute(`disabled`)||e.ariaDisabled===`true`;!s&&!n&&e.focus()}}),[s,i,o,t]),compositeRef:l,index:o}}var $=Y.createContext(void 0);function De(){let e=Y.useContext($);if(e===void 0)throw Error(g(65));return e}var Oe=Y.forwardRef(function(e,t){let{className:n,disabled:r=!1,render:i,value:a,id:o,nativeButton:s=!0,style:c,...l}=e,{value:d,getTabPanelIdByValue:f,orientation:p}=Z(),{activateOnFocus:g,highlightedTabIndex:_,onTabActivation:v,registerTabResizeObserverElement:y,setHighlightedTabIndex:b,tabsListElement:x}=De(),S=G(o),{compositeProps:w,compositeRef:T,index:E}=Ee({metadata:Y.useMemo(()=>({disabled:r,id:S,value:a}),[r,S,a])}),O=a===d,k=Y.useRef(!1),A=Y.useRef(null);Y.useEffect(()=>{let e=A.current;if(e)return y(e)},[y]),q(()=>{if(k.current){k.current=!1;return}if(!(O&&E>-1&&_!==E))return;let e=x;if(e!=null){let t=m(C(e));if(t&&h(e,t))return}r||b(E)},[O,E,_,b,r,x]);let{getButtonProps:j,buttonRef:M}=ae({disabled:r,native:s,focusableWhenDisabled:!0}),N=f(a),P=Y.useRef(!1),F=Y.useRef(!1);function I(e){O||r||v(a,D(W,e.nativeEvent,void 0,{activationDirection:`none`}))}function L(e){O||(E>-1&&!r&&b(E),!r&&g&&(!P.current||P.current&&F.current)&&v(a,D(W,e.nativeEvent,void 0,{activationDirection:`none`})))}function R(e){if(O||r)return;P.current=!0;function t(){P.current=!1,F.current=!1}(!e.button||e.button===0)&&(F.current=!0,C(e.currentTarget).addEventListener(`pointerup`,t,{once:!0}))}return u(`button`,e,{state:{disabled:r,active:O,orientation:p},ref:[t,M,T,A],props:[w,{role:`tab`,"aria-controls":N,"aria-selected":O,id:S,onClick:I,onFocus:L,onPointerDown:R,[Te]:O?``:void 0,onKeyDownCapture(){k.current=!0}},l,j]})}),ke=function(e){return e.index=`data-index`,e.activationDirection=`data-activation-direction`,e.orientation=`data-orientation`,e.hidden=`data-hidden`,e[e.startingStyle=F.startingStyle]=`startingStyle`,e[e.endingStyle=F.endingStyle]=`endingStyle`,e}({}),Ae={...Q,...R},je=Y.forwardRef(function(e,t){let{className:r,value:i,render:a,keepMounted:o=!1,style:s,...c}=e,{value:l,getTabIdByPanelValue:d,orientation:f,tabActivationDirection:p,registerMountedTabPanel:m,unregisterMountedTabPanel:h}=Z(),g=G(),{ref:_,index:v}=n({metadata:Y.useMemo(()=>({id:g,value:i}),[g,i])}),y=i===l,{mounted:b,transitionStatus:x,setMounted:S}=M(y),C=!b,w=d(i),T={hidden:C,orientation:f,tabActivationDirection:p,transitionStatus:x},E=Y.useRef(null),D=u(`div`,e,{state:T,ref:[t,_,E],props:[{"aria-labelledby":w,hidden:C,id:g,role:`tabpanel`,tabIndex:y?0:-1,inert:ie(!y),[ke.index]:v},c],stateAttributesMapping:Ae});return H({open:y,ref:E,onComplete(){y||S(!1)}}),q(()=>{if(!(C&&!o)&&g!=null)return m(i,g),()=>{h(i,g)}},[C,o,i,g,m,h]),o||b?D:null});function Me(e){return e==null||e.hasAttribute(`disabled`)||e.getAttribute(`aria-disabled`)===`true`}var Ne=[];function Pe(e){let{itemSizes:t,cols:n=1,loopFocus:r=!0,onLoop:i,dense:a=!1,orientation:o=`both`,direction:s,highlightedIndex:u,onHighlightedIndexChange:f,rootRef:p,enableHomeAndEndKeys:m=!1,stopEventPropagation:h=!1,disabledIndices:g,modifierKeys:_=Ne}=e,[S,C]=Y.useState(0),w=n>1,T=Y.useRef(null),E=B(T,p),D=Y.useRef([]),M=Y.useRef(!1),N=u??S,P=L((e,t=!1)=>{if((f??C)(e),t){let t=D.current[e];l(T.current,t,s,o)}}),F=L(e=>{if(e.size===0||M.current)return;M.current=!0;let t=Array.from(e.keys()),n=t.find(e=>e?.hasAttribute(`data-composite-item-active`))??null,r=n?t.indexOf(n):-1;r!==-1&&P(r),l(T.current,n,s,o)}),R=L((e,t,n)=>i?i?.(e,t,n,D):n),V=Y.useMemo(()=>({"aria-orientation":o===`both`?void 0:o,ref:E,onFocus(e){let t=T.current,n=v(e.nativeEvent);!t||n==null||!b(n)||n.setSelectionRange(0,n.value.length??0)},onKeyDown(e){let l=m?ee:I;if(!l.has(e.key)||Fe(e,_)||!T.current)return;let u=s===`rtl`,f=u?z:ne,p={horizontal:f,vertical:K,both:f}[o],S=u?ne:z,C={horizontal:S,vertical:U,both:S}[o],E=v(e.nativeEvent);if(E!=null&&b(E)&&!Me(E)){let t=E.selectionStart,n=E.selectionEnd,r=E.value??``;if(t==null||e.shiftKey||t!==n||e.key!==C&&t0)return}let M=N,F=c(D,g),L=y(D,g);if(w){let i=t||Array.from({length:D.current.length},()=>({width:1,height:1})),s=O(i,n,a),c=s.findIndex(e=>e!=null&&!d(D.current,e,g)),l=s.reduce((e,t,n)=>t!=null&&!d(D.current,t,g)?n:e,-1);M=s[ce(s.map(e=>e==null?null:D.current[e]),{event:e,orientation:o,loopFocus:r,onLoop:R,cols:n,disabledIndices:A([...g||D.current.map((e,t)=>d(D.current,t)?t:void 0),void 0],s),minIndex:c,maxIndex:l,prevIndex:ue(N>L?F:N,i,s,n,e.key===`ArrowDown`?`bl`:e.key===`ArrowRight`?`tr`:`tl`),rtl:u})]}let B={horizontal:[f],vertical:[K],both:[f,K]}[o],V={horizontal:[S],vertical:[U],both:[S,U]}[o],H=w?l:{horizontal:m?se:k,vertical:m?le:j,both:l}[o];m&&(e.key===`Home`?M=F:e.key===`End`&&(M=L)),M===N&&(B.includes(e.key)||V.includes(e.key))&&(r&&M===L&&B.includes(e.key)?(M=F,i&&(M=i(e,N,M,D))):r&&M===F&&V.includes(e.key)?(M=L,i&&(M=i(e,N,M,D))):M=oe(D.current,{startingIndex:M,decrement:V.includes(e.key),disabledIndices:g})),M!==N&&!x(D.current,M)&&(h&&e.stopPropagation(),H.has(e.key)&&e.preventDefault(),P(M,!0),queueMicrotask(()=>{D.current[M]?.focus()}))}}),[n,a,s,g,D,m,N,w,t,r,i,R,E,_,P,o,h]);return Y.useMemo(()=>({props:V,highlightedIndex:N,onHighlightedIndexChange:P,elementsRef:D,disabledIndices:g,onMapChange:F,relayKeyboardEvent:V.onKeyDown}),[V,N,P,D,g,F])}function Fe(e,t){for(let n of de.values())if(!t.includes(n)&&e.getModifierState(n))return!0;return!1}function Ie(e){let{render:t,className:n,style:r,refs:i=_,props:a=_,state:s=V,stateAttributesMapping:c,highlightedIndex:l,onHighlightedIndexChange:d,orientation:f,dense:p,itemSizes:m,loopFocus:h,onLoop:g,cols:v,enableHomeAndEndKeys:y,onMapChange:b,stopEventPropagation:x=!0,rootRef:S,disabledIndices:C,modifierKeys:w,highlightItemOnHover:T=!1,tag:D=`div`,...O}=e,{props:k,highlightedIndex:A,onHighlightedIndexChange:j,elementsRef:M,onMapChange:P,relayKeyboardEvent:F}=Pe({itemSizes:m,cols:v,loopFocus:h,onLoop:g,dense:p,orientation:f,highlightedIndex:l,onHighlightedIndexChange:d,rootRef:S,stopEventPropagation:x,enableHomeAndEndKeys:y,direction:E(),disabledIndices:C,modifierKeys:w}),I=u(D,e,{state:s,ref:i,props:[k,...a,O],stateAttributesMapping:c}),L=Y.useMemo(()=>({highlightedIndex:A,onHighlightedIndexChange:j,highlightItemOnHover:T,relayKeyboardEvent:F}),[A,j,T,F]);return(0,X.jsx)(N.Provider,{value:L,children:(0,X.jsx)(o,{elementsRef:M,onMapChange:e=>{b?.(e),P(e)},children:I})})}var Le=Y.forwardRef(function(e,t){let{activateOnFocus:n=!1,className:r,loopFocus:i=!0,render:a,style:o,...s}=e,{onValueChange:c,orientation:l,value:u,setTabMap:d,tabActivationDirection:f}=Z(),[p,m]=Y.useState(0),[h,g]=Y.useState(null),v=Y.useRef(new Set),y=Y.useRef(new Set),b=Y.useRef(null),x=L(()=>{v.current.forEach(e=>{e()})});Y.useEffect(()=>{if(typeof ResizeObserver>`u`)return;let e=new ResizeObserver(()=>{v.current.size&&x()});return b.current=e,h&&e.observe(h),y.current.forEach(t=>{e.observe(t)}),()=>{e.disconnect(),b.current=null}},[h,x]);let S=L(e=>(v.current.add(e),()=>{v.current.delete(e)})),C=L(e=>(y.current.add(e),b.current?.observe(e),()=>{y.current.delete(e),b.current?.unobserve(e)})),w=L((e,t)=>{e!==u&&c(e,t)}),T={orientation:l,tabActivationDirection:f},E={"aria-orientation":l===`vertical`?`vertical`:void 0,role:`tablist`},D=Y.useMemo(()=>({activateOnFocus:n,highlightedTabIndex:p,registerIndicatorUpdateListener:S,registerTabResizeObserverElement:C,onTabActivation:w,setHighlightedTabIndex:m,tabsListElement:h}),[n,p,S,C,w,m,h]);return(0,X.jsx)($.Provider,{value:D,children:(0,X.jsx)(Ie,{render:a,className:r,style:o,state:T,refs:[t,g],props:[E,s],stateAttributesMapping:Q,highlightedIndex:p,enableHomeAndEndKeys:!0,loopFocus:i,orientation:l,onHighlightedIndexChange:m,onMapChange:d,disabledIndices:_})})});function Re(e){let t=(0,J.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=s(`flex flex-col gap-4`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,X.jsx)(Ce,{className:i,"data-slot":`tabs`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function ze(e){let t=(0,J.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=s(`inline-flex items-center gap-1 border-b`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,X.jsx)(Le,{className:i,"data-slot":`tabs-list`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Be(e){let t=(0,J.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=s(`inline-flex items-center justify-center border-b-2 border-transparent px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-selected:border-primary data-selected:text-foreground`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,X.jsx)(Oe,{className:i,"data-slot":`tabs-tab`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Ve(e){let t=(0,J.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=s(`focus-visible:outline-none`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,X.jsx)(je,{className:i,"data-slot":`tabs-panel`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}export{_e as a,Be as i,ze as n,ye as o,Ve as r,be as s,Re as t}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{a as i,c as a,i as o,n as s,u as c}from"./dist.js";import{w as l}from"./SignatureV4.js";import{a as u,i as d,o as f,r as p,s as m}from"./button.js";import{S as h,b as g,c as _,d as v,f as y,i as b,t as x,v as S,x as C,y as w}from"./createBaseUIEventDetails.js";import{$n as T,Bn as E,Gn as D,Gt as O,Hn as k,Qn as A,Qt as j,Vn as M,Wn as N,Xn as P,Yn as F,Zn as I,an as L,cn as R,dn as z,en as B,er as V,fn as H,in as U,ir as W,ln as G,nn as K,pn as q,rn as J,rr as ee,sn as te,tn as ne,tr as re,un as ie}from"./index.js";import{n as ae,r as oe}from"./useCompositeListItem.js";var Y=t(n()),se=Y.createContext(void 0);function X(){let e=Y.useContext(se);if(e===void 0)throw Error(c(64));return e}var ce=function(e){return e.activationDirection=`data-activation-direction`,e.orientation=`data-orientation`,e}({}),Z={tabActivationDirection:e=>({[ce.activationDirection]:e})},Q=e(),le=Y.forwardRef(function(e,t){let{className:n,defaultValue:r=0,onValueChange:i,orientation:a=`horizontal`,render:o,value:c,style:l,...u}=e,d=e.defaultValue!==void 0,p=Y.useRef([]),[h,g]=Y.useState(()=>new Map),[y,S]=E({controlled:c,default:r,name:`Tabs`,state:`value`}),C=c!==void 0,[w,T]=Y.useState(()=>new Map),D=Y.useCallback(e=>{if(e===void 0)return null;for(let[t,n]of w.entries())if(n!=null&&e===(n.value??n.index))return t;return null},[w]),[O,k]=Y.useState(()=>({previousValue:y,tabActivationDirection:`none`})),{previousValue:A,tabActivationDirection:j}=O,M=j,N=!1;A!==y&&(M=ue(A,y,a,w),N=A!=null&&y!=null&&D(y)==null);let P=N?A:y,F=A!==P||j!==M;f(()=>{F&&k({previousValue:P,tabActivationDirection:M})},[P,F,M]);let I=m((e,t)=>{t.activationDirection=ue(y,e,a,w),i?.(e,t),!t.isCanceled&&S(e)}),L=m((e,t)=>{i?.(e,x(t,void 0,void 0,{activationDirection:`none`}))}),R=m((e,t)=>{g(n=>{if(n.get(e)===t)return n;let r=new Map(n);return r.set(e,t),r})}),z=m((e,t)=>{g(n=>{if(!n.has(e)||n.get(e)!==t)return n;let r=new Map(n);return r.delete(e),r})}),B=Y.useCallback(e=>h.get(e),[h]),V=Y.useCallback(e=>{for(let t of w.values())if(e===t?.value)return t?.id},[w]),H=Y.useMemo(()=>({getTabElementBySelectedValue:D,getTabIdByPanelValue:V,getTabPanelIdByValue:B,onValueChange:I,orientation:a,registerMountedTabPanel:R,setTabMap:T,unregisterMountedTabPanel:z,tabActivationDirection:M,value:y}),[D,V,B,I,a,R,T,z,M,y]),U=Y.useMemo(()=>{for(let e of w.values())if(e!=null&&e.value===y)return e},[w,y]),W=Y.useMemo(()=>{for(let e of w.values())if(e!=null&&!e.disabled)return e.value},[w]),G=Y.useRef(!d),K=Y.useRef(d),q=Y.useRef(!1);f(()=>{if(C)return;function e(e,t){S(e),k(t=>t.previousValue===e&&t.tabActivationDirection===`none`?t:{previousValue:e,tabActivationDirection:`none`}),L(e,t),G.current=!1}if(w.size===0){if(!q.current||y===null)return;e(null,v);return}q.current=!0;let t=U?.disabled,n=U==null&&y!==null;if(!t&&y===r&&(K.current=!1),K.current&&t&&y===r)return;let i=G.current;if(t||n){let n=W??null;if(y===n){G.current=!1;return}let r=v;i?r=_:t&&(r=b),e(n,r);return}i&&U!=null&&(L(y,_),G.current=!1)},[r,W,C,L,U,S,w,y]);let J=s(`div`,e,{state:{orientation:a,tabActivationDirection:M},ref:t,props:u,stateAttributesMapping:Z});return(0,Q.jsx)(se.Provider,{value:H,children:(0,Q.jsx)(oe,{elementsRef:p,children:J})})});function ue(e,t,n,r){if(e==null||t==null)return`none`;let i=null,a=null;for(let[n,o]of r.entries()){if(o==null)continue;let r=o.value??o.index;if(e===r&&(i=n),t===r&&(a=n),i!=null&&a!=null)break}if(i==null||a==null)return i!==a&&(typeof e==`number`||typeof e==`string`)&&typeof e==typeof t?n===`horizontal`?t>e?`right`:`left`:t>e?`down`:`up`:`none`;let o=i.getBoundingClientRect(),s=a.getBoundingClientRect();if(n===`horizontal`){if(s.lefto.left)return`right`}else{if(s.topo.top)return`down`}return`none`}var de=`data-composite-item-active`;function fe(e={}){let{highlightItemOnHover:t,highlightedIndex:n,onHighlightedIndexChange:r}=u(),{ref:i,index:o}=ae(e),s=n===o,c=Y.useRef(null),l=a(i,c);return{compositeProps:Y.useMemo(()=>({tabIndex:s?0:-1,onFocus(){r(o)},onMouseMove(){let e=c.current;if(!t||!e)return;let n=e.hasAttribute(`disabled`)||e.ariaDisabled===`true`;!s&&!n&&e.focus()}}),[s,r,o,t]),compositeRef:l,index:o}}var pe=Y.createContext(void 0);function me(){let e=Y.useContext(pe);if(e===void 0)throw Error(c(65));return e}var he=Y.forwardRef(function(e,t){let{className:n,disabled:r=!1,render:i,value:a,id:o,nativeButton:c=!0,style:l,...u}=e,{value:d,getTabPanelIdByValue:m,orientation:g}=X(),{activateOnFocus:_,highlightedTabIndex:v,onTabActivation:b,registerTabResizeObserverElement:T,setHighlightedTabIndex:E,tabsListElement:D}=me(),O=h(o),{compositeProps:k,compositeRef:A,index:j}=fe({metadata:Y.useMemo(()=>({disabled:r,id:O,value:a}),[r,O,a])}),M=a===d,N=Y.useRef(!1),P=Y.useRef(null);Y.useEffect(()=>{let e=P.current;if(e)return T(e)},[T]),f(()=>{if(N.current){N.current=!1;return}if(!(M&&j>-1&&v!==j))return;let e=D;if(e!=null){let t=S(C(e));if(t&&w(e,t))return}r||E(j)},[M,j,v,E,r,D]);let{getButtonProps:F,buttonRef:I}=p({disabled:r,native:c,focusableWhenDisabled:!0}),L=m(a),R=Y.useRef(!1),z=Y.useRef(!1);function B(e){M||r||b(a,x(y,e.nativeEvent,void 0,{activationDirection:`none`}))}function V(e){M||(j>-1&&!r&&E(j),!r&&_&&(!R.current||R.current&&z.current)&&b(a,x(y,e.nativeEvent,void 0,{activationDirection:`none`})))}function H(e){if(M||r)return;R.current=!0;function t(){R.current=!1,z.current=!1}(!e.button||e.button===0)&&(z.current=!0,C(e.currentTarget).addEventListener(`pointerup`,t,{once:!0}))}return s(`button`,e,{state:{disabled:r,active:M,orientation:g},ref:[t,I,A,P],props:[k,{role:`tab`,"aria-controls":L,"aria-selected":M,id:O,onClick:B,onFocus:V,onPointerDown:H,[de]:M?``:void 0,onKeyDownCapture(){N.current=!0}},u,F]})}),ge=function(e){return e.index=`data-index`,e.activationDirection=`data-activation-direction`,e.orientation=`data-orientation`,e.hidden=`data-hidden`,e[e.startingStyle=N.startingStyle]=`startingStyle`,e[e.endingStyle=N.endingStyle]=`endingStyle`,e}({}),_e={...Z,...D},ve=Y.forwardRef(function(e,t){let{className:n,value:r,render:i,keepMounted:a=!1,style:o,...c}=e,{value:l,getTabIdByPanelValue:u,orientation:d,tabActivationDirection:p,registerMountedTabPanel:m,unregisterMountedTabPanel:g}=X(),_=h(),{ref:v,index:y}=ae({metadata:Y.useMemo(()=>({id:_,value:r}),[_,r])}),b=r===l,{mounted:x,transitionStatus:S,setMounted:C}=M(b),w=!x,T=u(r),E={hidden:w,orientation:d,tabActivationDirection:p,transitionStatus:S},D=Y.useRef(null),O=s(`div`,e,{state:E,ref:[t,v,D],props:[{"aria-labelledby":T,hidden:w,id:_,role:`tabpanel`,tabIndex:b?0:-1,inert:j(!b),[ge.index]:y},c],stateAttributesMapping:_e});return k({open:b,ref:D,onComplete(){b||C(!1)}}),f(()=>{if(!(w&&!a)&&_!=null)return m(r,_),()=>{g(r,_)}},[w,a,r,_,m,g]),a||x?O:null});function ye(e){return e==null||e.hasAttribute(`disabled`)||e.getAttribute(`aria-disabled`)===`true`}var be=[];function xe(e){let{itemSizes:t,cols:n=1,loopFocus:r=!0,onLoop:i,dense:o=!1,orientation:s=`both`,direction:c,highlightedIndex:l,onHighlightedIndexChange:u,rootRef:d,enableHomeAndEndKeys:f=!1,stopEventPropagation:p=!1,disabledIndices:h,modifierKeys:_=be}=e,[v,y]=Y.useState(0),b=n>1,x=Y.useRef(null),S=a(x,d),C=Y.useRef([]),w=Y.useRef(!1),E=l??v,D=m((e,t=!1)=>{if((u??y)(e),t){let t=C.current[e];q(x.current,t,c,s)}}),O=m(e=>{if(e.size===0||w.current)return;w.current=!0;let t=Array.from(e.keys()),n=t.find(e=>e?.hasAttribute(`data-composite-item-active`))??null,r=n?t.indexOf(n):-1;r!==-1&&D(r),q(x.current,n,c,s)}),k=m((e,t,n)=>i?i?.(e,t,n,C):n),j=Y.useMemo(()=>({"aria-orientation":s===`both`?void 0:s,ref:S,onFocus(e){let t=x.current,n=g(e.nativeEvent);!t||n==null||!H(n)||n.setSelectionRange(0,n.value.length??0)},onKeyDown(e){let a=f?L:ne;if(!a.has(e.key)||Se(e,_)||!x.current)return;let l=c===`rtl`,u=l?K:J,d={horizontal:u,vertical:B,both:u}[s],m=l?J:K,v={horizontal:m,vertical:U,both:m}[s],y=g(e.nativeEvent);if(y!=null&&H(y)&&!ye(y)){let t=y.selectionStart,n=y.selectionEnd,r=y.value??``;if(t==null||e.shiftKey||t!==n||e.key!==v&&t0)return}let S=E,w=re(C,h),O=V(C,h);if(b){let i=t||Array.from({length:C.current.length},()=>({width:1,height:1})),a=F(i,n,o),c=a.findIndex(e=>e!=null&&!W(C.current,e,h)),u=a.reduce((e,t,n)=>t!=null&&!W(C.current,t,h)?n:e,-1);S=a[T(a.map(e=>e==null?null:C.current[e]),{event:e,orientation:s,loopFocus:r,onLoop:k,cols:n,disabledIndices:A([...h||C.current.map((e,t)=>W(C.current,t)?t:void 0),void 0],a),minIndex:c,maxIndex:u,prevIndex:I(E>O?w:E,i,a,n,e.key===`ArrowDown`?`bl`:e.key===`ArrowRight`?`tr`:`tl`),rtl:l})]}let j={horizontal:[u],vertical:[B],both:[u,B]}[s],M={horizontal:[m],vertical:[U],both:[m,U]}[s],N=b?a:{horizontal:f?R:te,vertical:f?z:ie,both:a}[s];f&&(e.key===`Home`?S=w:e.key===`End`&&(S=O)),S===E&&(j.includes(e.key)||M.includes(e.key))&&(r&&S===O&&j.includes(e.key)?(S=w,i&&(S=i(e,E,S,C))):r&&S===w&&M.includes(e.key)?(S=O,i&&(S=i(e,E,S,C))):S=P(C.current,{startingIndex:S,decrement:M.includes(e.key),disabledIndices:h})),S!==E&&!ee(C.current,S)&&(p&&e.stopPropagation(),N.has(e.key)&&e.preventDefault(),D(S,!0),queueMicrotask(()=>{C.current[S]?.focus()}))}}),[n,o,c,h,C,f,E,b,t,r,i,k,S,_,D,s,p]);return Y.useMemo(()=>({props:j,highlightedIndex:E,onHighlightedIndexChange:D,elementsRef:C,disabledIndices:h,onMapChange:O,relayKeyboardEvent:j.onKeyDown}),[j,E,D,C,h,O])}function Se(e,t){for(let n of G.values())if(!t.includes(n)&&e.getModifierState(n))return!0;return!1}function Ce(e){let{render:t,className:n,style:r,refs:a=o,props:c=o,state:l=i,stateAttributesMapping:u,highlightedIndex:f,onHighlightedIndexChange:p,orientation:m,dense:h,itemSizes:g,loopFocus:_,onLoop:v,cols:y,enableHomeAndEndKeys:b,onMapChange:x,stopEventPropagation:S=!0,rootRef:C,disabledIndices:w,modifierKeys:T,highlightItemOnHover:E=!1,tag:D=`div`,...k}=e,{props:A,highlightedIndex:j,onHighlightedIndexChange:M,elementsRef:N,onMapChange:P,relayKeyboardEvent:F}=xe({itemSizes:g,cols:y,loopFocus:_,onLoop:v,dense:h,orientation:m,highlightedIndex:f,onHighlightedIndexChange:p,rootRef:C,stopEventPropagation:S,enableHomeAndEndKeys:b,direction:O(),disabledIndices:w,modifierKeys:T}),I=s(D,e,{state:l,ref:a,props:[A,...c,k],stateAttributesMapping:u}),L=Y.useMemo(()=>({highlightedIndex:j,onHighlightedIndexChange:M,highlightItemOnHover:E,relayKeyboardEvent:F}),[j,M,E,F]);return(0,Q.jsx)(d.Provider,{value:L,children:(0,Q.jsx)(oe,{elementsRef:N,onMapChange:e=>{x?.(e),P(e)},children:I})})}var we=Y.forwardRef(function(e,t){let{activateOnFocus:n=!1,className:r,loopFocus:i=!0,render:a,style:s,...c}=e,{onValueChange:l,orientation:u,value:d,setTabMap:f,tabActivationDirection:p}=X(),[h,g]=Y.useState(0),[_,v]=Y.useState(null),y=Y.useRef(new Set),b=Y.useRef(new Set),x=Y.useRef(null);Y.useEffect(()=>{if(typeof ResizeObserver>`u`)return;let e=new ResizeObserver(()=>{y.current.forEach(e=>{e()})});return x.current=e,_&&e.observe(_),b.current.forEach(t=>{e.observe(t)}),()=>{e.disconnect(),x.current=null}},[_]);let S=m(e=>(y.current.add(e),()=>{y.current.delete(e)})),C=m(e=>(b.current.add(e),x.current?.observe(e),()=>{b.current.delete(e),x.current?.unobserve(e)})),w=m((e,t)=>{e!==d&&l(e,t)}),T={orientation:u,tabActivationDirection:p},E={"aria-orientation":u===`vertical`?`vertical`:void 0,role:`tablist`},D=Y.useMemo(()=>({activateOnFocus:n,highlightedTabIndex:h,registerIndicatorUpdateListener:S,registerTabResizeObserverElement:C,onTabActivation:w,setHighlightedTabIndex:g,tabsListElement:_}),[n,h,S,C,w,g,_]);return(0,Q.jsx)(pe.Provider,{value:D,children:(0,Q.jsx)(Ce,{render:a,className:r,style:s,state:T,refs:[t,v],props:[E,c],stateAttributesMapping:Z,highlightedIndex:h,enableHomeAndEndKeys:!0,loopFocus:i,orientation:u,onHighlightedIndexChange:g,onMapChange:f,disabledIndices:o})})}),$=r();function Te(e){let t=(0,$.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=l(`flex flex-col gap-4`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,Q.jsx)(le,{className:i,"data-slot":`tabs`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Ee(e){let t=(0,$.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=l(`inline-flex items-center gap-1 border-b`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,Q.jsx)(we,{className:i,"data-slot":`tabs-list`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function De(e){let t=(0,$.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=l(`inline-flex items-center justify-center border-b-2 border-transparent px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-selected:border-primary data-selected:text-foreground`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,Q.jsx)(he,{className:i,"data-slot":`tabs-tab`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}function Oe(e){let t=(0,$.c)(8),n,r;t[0]===e?(n=t[1],r=t[2]):({className:n,...r}=e,t[0]=e,t[1]=n,t[2]=r);let i;t[3]===n?i=t[4]:(i=l(`focus-visible:outline-none`,n),t[3]=n,t[4]=i);let a;return t[5]!==r||t[6]!==i?(a=(0,Q.jsx)(ve,{className:i,"data-slot":`tabs-panel`,...r}),t[5]=r,t[6]=i,t[7]=a):a=t[7],a}export{De as i,Ee as n,Oe as r,Te as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/tags-editor.js b/spinifex/services/spinifexui/frontend/dist/assets/tags-editor.js new file mode 100644 index 000000000..b6fc76311 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/tags-editor.js @@ -0,0 +1 @@ +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{c as i,i as a,n as o,s,t as c}from"./select.js";import{t as l}from"./plus.js";import{t as u}from"./trash-2.js";import{t as d}from"./button.js";import{Rn as f}from"./index.js";import{a as p,r as m,t as h}from"./field.js";var ee=r(),te=t(n(),1),g=e();function ne(e,t){let n=new Map;for(let e of t)e.Key!==void 0&&n.set(e.Key,e.Value??``);let r={};for(let t of e)r[t.key]=n.get(t.key)??``;return r}function _(e,t){if(e.type===`int`){if(t.trim()===``)return`Value is required`;if(!/^-?\d+$/.test(t))return`Must be a whole number`;let n=Number(t);if(e.min!==void 0&&ne.max)return`Must be at most ${e.max}`}}function v(e){let t=(0,ee.c)(56),{specs:n,attributes:r,onSubmit:a,isPending:l,error:u,isSuccess:v}=e,b;t[0]!==r||t[1]!==n?(b=ne(n,r),t[0]=r,t[1]=n,t[2]=b):b=t[2];let x=b,[S,C]=(0,te.useState)(x),w;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(w=(e,t)=>{C(n=>({...n,[e]:t}))},t[3]=w):w=t[3];let T=w,E;if(t[4]!==n||t[5]!==S){E={};for(let e of n)E[e.key]=_(e,S[e.key]??``);t[4]=n,t[5]=S,t[6]=E}else E=t[6];let D=Object.values(E).some(Boolean),O;if(t[7]!==x||t[8]!==n||t[9]!==S){let e;t[11]!==x||t[12]!==S?(e=e=>(S[e.key]??``)!==(x[e.key]??``),t[11]=x,t[12]=S,t[13]=e):e=t[13];let r;t[14]===S?r=t[15]:(r=e=>({key:e.key,value:S[e.key]??``}),t[14]=S,t[15]=r),O=n.filter(e).map(r),t[7]=x,t[8]=n,t[9]=S,t[10]=O}else O=t[10];let k=O,A;t[16]===x?A=t[17]:(A=()=>{C(x)},t[16]=x,t[17]=A);let j=A,M;t[18]!==k||t[19]!==D||t[20]!==a?(M=e=>{e.preventDefault(),!(D||k.length===0)&&a(k)},t[18]=k,t[19]=D,t[20]=a,t[21]=M):M=t[21];let N;t[22]===u?N=t[23]:(N=u&&(0,g.jsx)(`div`,{className:`mb-4 rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive`,role:`alert`,children:u.message||`Failed to save attributes`}),t[22]=u,t[23]=N);let P;t[24]!==k.length||t[25]!==v?(P=v&&k.length===0&&(0,g.jsx)(`div`,{className:`mb-4 rounded-md border border-emerald-500 bg-emerald-500/10 p-3 text-sm text-emerald-700 dark:text-emerald-400`,role:`status`,children:`Attributes saved.`}),t[24]=k.length,t[25]=v,t[26]=P):P=t[26];let F;if(t[27]!==E||t[28]!==n||t[29]!==S){let e;t[31]!==E||t[32]!==S?(e=e=>{let t=`attr-${e.key}`,n=S[e.key]??``,r=E[e.key];return(0,g.jsxs)(h,{children:[(0,g.jsx)(p,{children:(0,g.jsx)(`label`,{htmlFor:t,children:e.label})}),e.type===`bool`&&(0,g.jsx)(`input`,{"aria-label":e.label,checked:n===`true`,id:t,onChange:t=>T(e.key,t.target.checked?`true`:`false`),type:`checkbox`}),e.type===`int`&&(0,g.jsx)(f,{"aria-invalid":!!r,id:t,inputMode:`numeric`,onChange:t=>T(e.key,t.target.value),value:n}),e.type===`text`&&(0,g.jsx)(f,{id:t,onChange:t=>T(e.key,t.target.value),placeholder:e.placeholder,value:n}),e.type===`select`&&(0,g.jsxs)(c,{onValueChange:t=>T(e.key,t??``),value:n,children:[(0,g.jsx)(s,{className:`w-full`,id:t,children:(0,g.jsx)(i,{})}),(0,g.jsx)(o,{children:e.options.map(y)})]}),e.description&&(0,g.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e.description}),(0,g.jsx)(m,{errors:[r?{message:r}:void 0]})]},e.key)},t[31]=E,t[32]=S,t[33]=e):e=t[33],F=n.map(e),t[27]=E,t[28]=n,t[29]=S,t[30]=F}else F=t[30];let I;t[34]===F?I=t[35]:(I=(0,g.jsx)(`div`,{className:`grid gap-4 p-4 sm:grid-cols-2`,children:F}),t[34]=F,t[35]=I);let L=k.length===0?`No changes`:`${k.length} change${k.length===1?``:`s`} pending`,R;t[36]===L?R=t[37]:(R=(0,g.jsx)(`span`,{className:`mr-auto text-xs text-muted-foreground`,children:L}),t[36]=L,t[37]=R);let z=l||k.length===0,B;t[38]!==j||t[39]!==z?(B=(0,g.jsx)(d,{disabled:z,onClick:j,size:`sm`,type:`button`,variant:`secondary`,children:`Reset`}),t[38]=j,t[39]=z,t[40]=B):B=t[40];let V=l||D||k.length===0,H=l?`Saving…`:`Save changes`,U;t[41]!==V||t[42]!==H?(U=(0,g.jsx)(d,{disabled:V,size:`sm`,type:`submit`,children:H}),t[41]=V,t[42]=H,t[43]=U):U=t[43];let W;t[44]!==R||t[45]!==B||t[46]!==U?(W=(0,g.jsxs)(`div`,{className:`flex items-center justify-end gap-2 border-t p-4`,children:[R,B,U]}),t[44]=R,t[45]=B,t[46]=U,t[47]=W):W=t[47];let G;t[48]!==W||t[49]!==I?(G=(0,g.jsxs)(`div`,{className:`rounded-lg border bg-card`,children:[I,W]}),t[48]=W,t[49]=I,t[50]=G):G=t[50];let K;return t[51]!==G||t[52]!==M||t[53]!==N||t[54]!==P?(K=(0,g.jsxs)(`form`,{onSubmit:M,children:[N,P,G]}),t[51]=G,t[52]=M,t[53]=N,t[54]=P,t[55]=K):K=t[55],K}function y(e){return(0,g.jsx)(a,{value:e,children:e},e)}var b=[{key:`deletion_protection.enabled`,label:`Deletion protection`,type:`bool`},{key:`load_balancing.cross_zone.enabled`,label:`Cross-zone load balancing`,type:`bool`},{key:`idle_timeout.timeout_seconds`,label:`Idle timeout (s)`,type:`int`,min:1,max:4e3},{key:`client_keep_alive.seconds`,label:`Client keep-alive (s)`,type:`int`,min:60,max:604800},{key:`routing.http2.enabled`,label:`HTTP/2`,type:`bool`},{key:`routing.http.drop_invalid_header_fields.enabled`,label:`Drop invalid header fields`,type:`bool`},{key:`routing.http.preserve_host_header.enabled`,label:`Preserve host header`,type:`bool`},{key:`routing.http.xff_client_port.enabled`,label:`XFF client port`,type:`bool`},{key:`routing.http.xff_header_processing.mode`,label:`XFF header processing`,type:`select`,options:[`append`,`preserve`,`remove`]},{key:`routing.http.desync_mitigation_mode`,label:`Desync mitigation mode`,type:`select`,options:[`defensive`,`strictest`,`monitor`]},{key:`routing.http.x_amzn_tls_version_and_cipher_suite.enabled`,label:`Add TLS version & cipher headers`,type:`bool`},{key:`waf.fail_open.enabled`,label:`WAF fail open`,type:`bool`},{key:`zonal_shift.config.enabled`,label:`Zonal shift`,type:`bool`},{key:`access_logs.s3.enabled`,label:`Access logs: enabled`,type:`bool`},{key:`access_logs.s3.bucket`,label:`Access logs: S3 bucket`,type:`text`},{key:`access_logs.s3.prefix`,label:`Access logs: S3 prefix`,type:`text`},{key:`connection_logs.s3.enabled`,label:`Connection logs: enabled`,type:`bool`},{key:`connection_logs.s3.bucket`,label:`Connection logs: S3 bucket`,type:`text`},{key:`connection_logs.s3.prefix`,label:`Connection logs: S3 prefix`,type:`text`}],x=[{key:`deregistration_delay.timeout_seconds`,label:`Deregistration delay (s)`,type:`int`,min:0,max:3600},{key:`stickiness.enabled`,label:`Stickiness`,type:`bool`},{key:`stickiness.type`,label:`Stickiness type`,type:`select`,options:[`lb_cookie`,`source_ip`]},{key:`stickiness.lb_cookie.duration_seconds`,label:`Stickiness lb_cookie duration (s)`,type:`int`,min:1,max:604800},{key:`load_balancing.cross_zone.enabled`,label:`Cross-zone load balancing`,type:`select`,options:[`true`,`false`,`use_load_balancer_configuration`]},{key:`load_balancing.algorithm.type`,label:`Load balancing algorithm`,type:`select`,options:[`round_robin`,`least_outstanding_requests`]},{key:`slow_start.duration_seconds`,label:`Slow start duration (s)`,type:`int`,min:0,max:900}];function S(e){return e.map((e,t)=>({id:t,key:e.Key??``,value:e.Value??``}))}function C(e){return e.map(e=>({key:e.key.trim(),value:e.value})).filter(e=>e.key.length>0)}function w(e){let t=(0,ee.c)(66),{tags:n,onSubmit:r,isPending:i,error:a,isSuccess:o}=e,s;t[0]===n?s=t[1]:(s=S(n),t[0]=n,t[1]=s);let c=s,[p,m]=(0,te.useState)(c),[h,ne]=(0,te.useState)(c.length),_;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(_=(e,t)=>{m(n=>n.map(n=>n.id===e?{...n,...t}:n))},t[2]=_):_=t[2];let v=_,y;t[3]===h?y=t[4]:(y=()=>{m(e=>[...e,{id:h,key:``,value:``}]),ne(D)},t[3]=h,t[4]=y);let b=y,x;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(x=e=>{m(t=>t.filter(t=>t.id!==e))},t[5]=x):x=t[5];let w=x,O;t[6]===c?O=t[7]:(O=()=>{m(c)},t[6]=c,t[7]=O);let k=O,A;t[8]===p?A=t[9]:(A=p.some(E),t[8]=p,t[9]=A);let j=A,M,N,P,F;if(t[10]!==j||t[11]!==p){P=C(p);let e=P.map(T);M=new Set(e).size!==e.length,N=j||M,F=JSON.stringify(P),t[10]=j,t[11]=p,t[12]=M,t[13]=N,t[14]=P,t[15]=F}else M=t[12],N=t[13],P=t[14],F=t[15];let I;t[16]===c?I=t[17]:(I=JSON.stringify(C(c)),t[16]=c,t[17]=I);let L=F!==I,R;t[18]!==L||t[19]!==N||t[20]!==P||t[21]!==r?(R=e=>{e.preventDefault(),!(N||!L)&&r(P)},t[18]=L,t[19]=N,t[20]=P,t[21]=r,t[22]=R):R=t[22];let z;t[23]===a?z=t[24]:(z=a&&(0,g.jsx)(`div`,{className:`mb-4 rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive`,role:`alert`,children:a.message||`Failed to save tags`}),t[23]=a,t[24]=z);let B;t[25]!==L||t[26]!==o?(B=o&&!L&&(0,g.jsx)(`div`,{className:`mb-4 rounded-md border border-emerald-500 bg-emerald-500/10 p-3 text-sm text-emerald-700 dark:text-emerald-400`,role:`status`,children:`Tags saved.`}),t[25]=L,t[26]=o,t[27]=B):B=t[27];let V;t[28]===p.length?V=t[29]:(V=p.length===0&&(0,g.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`No tags.`}),t[28]=p.length,t[29]=V);let H;if(t[30]!==p){let e;t[32]===Symbol.for(`react.memo_cache_sentinel`)?(e=e=>(0,g.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,g.jsx)(f,{"aria-label":`Tag key`,onChange:t=>v(e.id,{key:t.target.value}),placeholder:`Key`,value:e.key}),(0,g.jsx)(f,{"aria-label":`Tag value`,onChange:t=>v(e.id,{value:t.target.value}),placeholder:`Value`,value:e.value}),(0,g.jsx)(d,{"aria-label":`Remove tag`,onClick:()=>w(e.id),size:`icon`,type:`button`,variant:`ghost`,children:(0,g.jsx)(u,{className:`size-4`})})]},e.id),t[32]=e):e=t[32],H=p.map(e),t[30]=p,t[31]=H}else H=t[31];let U;t[33]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,g.jsx)(l,{className:`size-4`}),t[33]=U):U=t[33];let W;t[34]===b?W=t[35]:(W=(0,g.jsx)(`div`,{children:(0,g.jsxs)(d,{onClick:b,size:`sm`,type:`button`,variant:`secondary`,children:[U,` Add tag`]})}),t[34]=b,t[35]=W);let G;t[36]===j?G=t[37]:(G=j&&(0,g.jsx)(`p`,{className:`text-xs text-destructive`,children:`Every tag with a value needs a key.`}),t[36]=j,t[37]=G);let K;t[38]===M?K=t[39]:(K=M&&(0,g.jsx)(`p`,{className:`text-xs text-destructive`,children:`Tag keys must be unique.`}),t[38]=M,t[39]=K);let q;t[40]!==V||t[41]!==H||t[42]!==W||t[43]!==G||t[44]!==K?(q=(0,g.jsxs)(`div`,{className:`flex flex-col gap-2 p-4`,children:[V,H,W,G,K]}),t[40]=V,t[41]=H,t[42]=W,t[43]=G,t[44]=K,t[45]=q):q=t[45];let re=L?`Unsaved changes`:`No changes`,J;t[46]===re?J=t[47]:(J=(0,g.jsx)(`span`,{className:`mr-auto text-xs text-muted-foreground`,children:re}),t[46]=re,t[47]=J);let ie=i||!L,Y;t[48]!==k||t[49]!==ie?(Y=(0,g.jsx)(d,{disabled:ie,onClick:k,size:`sm`,type:`button`,variant:`secondary`,children:`Reset`}),t[48]=k,t[49]=ie,t[50]=Y):Y=t[50];let ae=i||N||!L,oe=i?`Saving…`:`Save changes`,X;t[51]!==ae||t[52]!==oe?(X=(0,g.jsx)(d,{disabled:ae,size:`sm`,type:`submit`,children:oe}),t[51]=ae,t[52]=oe,t[53]=X):X=t[53];let Z;t[54]!==J||t[55]!==Y||t[56]!==X?(Z=(0,g.jsxs)(`div`,{className:`flex items-center justify-end gap-2 border-t p-4`,children:[J,Y,X]}),t[54]=J,t[55]=Y,t[56]=X,t[57]=Z):Z=t[57];let Q;t[58]!==q||t[59]!==Z?(Q=(0,g.jsxs)(`div`,{className:`rounded-lg border bg-card`,children:[q,Z]}),t[58]=q,t[59]=Z,t[60]=Q):Q=t[60];let $;return t[61]!==z||t[62]!==B||t[63]!==Q||t[64]!==R?($=(0,g.jsxs)(`form`,{onSubmit:R,children:[z,B,Q]}),t[61]=z,t[62]=B,t[63]=Q,t[64]=R,t[65]=$):$=t[65],$}function T(e){return e.key}function E(e){return e.key.trim().length===0&&e.value.length>0}function D(e){return e+1}export{x as i,v as n,b as r,w as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/target-group-form.js b/spinifex/services/spinifexui/frontend/dist/assets/target-group-form.js index 81a7bd065..a64e6f738 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/target-group-form.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/target-group-form.js @@ -1 +1 @@ -import{c as e,i as t,n as ee,s as te,t as ne}from"./select.js";import{t as n}from"./plus.js";import{t as re}from"./trash-2.js";import{Fo as r,Ho as ie,Xa as ae,Xi as i,ao as a}from"./index.js";import{n as oe}from"./zod.js";import{a as o,r as s,t as c}from"./field.js";import{n as se,r as ce,t as le}from"./collapsible.js";var l=r(),u=ie();function d(t){let r=(0,l.c)(174),{form:ie,vpcs:a}=t,{control:d,register:f,watch:fe,setValue:p,getValues:m,formState:h}=ie,{errors:g}=h,_,v,y,b,x,S,C,w,T;if(r[0]!==d||r[1]!==g.healthCheck?.healthyThresholdCount||r[2]!==g.healthCheck?.intervalSeconds||r[3]!==g.healthCheck?.matcher||r[4]!==g.healthCheck?.path||r[5]!==g.healthCheck?.port||r[6]!==g.healthCheck?.timeoutSeconds||r[7]!==g.healthCheck?.unhealthyThresholdCount||r[8]!==g.name||r[9]!==g.port||r[10]!==g.vpcId||r[11]!==m||r[12]!==f||r[13]!==p||r[14]!==a||r[15]!==fe){let t=fe(`tags`),n;r[25]===Symbol.for(`react.memo_cache_sentinel`)?(n=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`tg-name`,children:`Name`})}),r[25]=n):n=r[25];let ie=!!g.name,l;r[26]===f?l=r[27]:(l=f(`name`),r[26]=f,r[27]=l);let h;r[28]!==ie||r[29]!==l?(h=(0,u.jsx)(i,{"aria-invalid":ie,id:`tg-name`,placeholder:`my-target-group`,...l}),r[28]=ie,r[29]=l,r[30]=h):h=r[30];let E;r[31]===g.name?E=r[32]:(E=(0,u.jsx)(s,{errors:[g.name]}),r[31]=g.name,r[32]=E),r[33]!==h||r[34]!==E?(x=(0,u.jsxs)(c,{children:[n,h,E]}),r[33]=h,r[34]=E,r[35]=x):x=r[35];let D;r[36]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`tg-protocol`,children:`Protocol`})}),r[36]=D):D=r[36],r[37]===d?S=r[38]:(S=(0,u.jsxs)(c,{children:[D,(0,u.jsx)(oe,{control:d,name:`protocol`,render:de})]}),r[37]=d,r[38]=S);let O;r[39]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`tg-port`,children:`Port`})}),r[39]=O):O=r[39];let k=!!g.port,A;r[40]===f?A=r[41]:(A=f(`port`,{valueAsNumber:!0}),r[40]=f,r[41]=A);let j;r[42]!==k||r[43]!==A?(j=(0,u.jsx)(i,{"aria-invalid":k,id:`tg-port`,inputMode:`numeric`,placeholder:`80`,type:`number`,...A}),r[42]=k,r[43]=A,r[44]=j):j=r[44];let M;r[45]===g.port?M=r[46]:(M=(0,u.jsx)(s,{errors:[g.port]}),r[45]=g.port,r[46]=M),r[47]!==j||r[48]!==M?(C=(0,u.jsxs)(c,{children:[O,j,M]}),r[47]=j,r[48]=M,r[49]=C):C=r[49];let pe;r[50]===Symbol.for(`react.memo_cache_sentinel`)?(pe=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`tg-vpc`,children:`VPC`})}),r[50]=pe):pe=r[50];let N;r[51]!==g.vpcId||r[52]!==a?(N=t=>{let{field:n}=t;return(0,u.jsxs)(ne,{onValueChange:e=>n.onChange(e),value:n.value??``,children:[(0,u.jsx)(te,{"aria-invalid":!!g.vpcId,className:`w-full`,id:`tg-vpc`,children:(0,u.jsx)(e,{placeholder:`Select VPC`})}),(0,u.jsx)(ee,{children:a.map(ue)})]})},r[51]=g.vpcId,r[52]=a,r[53]=N):N=r[53];let P;r[54]!==d||r[55]!==N?(P=(0,u.jsx)(oe,{control:d,name:`vpcId`,render:N}),r[54]=d,r[55]=N,r[56]=P):P=r[56];let F;r[57]===g.vpcId?F=r[58]:(F=(0,u.jsx)(s,{errors:[g.vpcId]}),r[57]=g.vpcId,r[58]=F),r[59]!==P||r[60]!==F?(w=(0,u.jsxs)(c,{children:[pe,P,F]}),r[59]=P,r[60]=F,r[61]=w):w=r[61];let me;r[62]===Symbol.for(`react.memo_cache_sentinel`)?(me=(0,u.jsx)(ce,{className:`text-left text-sm font-medium text-muted-foreground hover:text-foreground`,render:(0,u.jsx)(`button`,{"aria-label":`Health check settings`,type:`button`}),children:`Health check settings`}),r[62]=me):me=r[62];let he;r[63]===Symbol.for(`react.memo_cache_sentinel`)?(he=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`hc-path`,children:`Path`})}),r[63]=he):he=r[63];let ge=!!g.healthCheck?.path,I;r[64]===f?I=r[65]:(I=f(`healthCheck.path`),r[64]=f,r[65]=I);let L;r[66]!==ge||r[67]!==I?(L=(0,u.jsx)(i,{"aria-invalid":ge,id:`hc-path`,placeholder:`/`,...I}),r[66]=ge,r[67]=I,r[68]=L):L=r[68];let _e=g.healthCheck?.path,R;r[69]===_e?R=r[70]:(R=(0,u.jsx)(s,{errors:[_e]}),r[69]=_e,r[70]=R);let z;r[71]!==L||r[72]!==R?(z=(0,u.jsxs)(c,{children:[he,L,R]}),r[71]=L,r[72]=R,r[73]=z):z=r[73];let ve;r[74]===Symbol.for(`react.memo_cache_sentinel`)?(ve=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`hc-port`,children:`Port`})}),r[74]=ve):ve=r[74];let ye=!!g.healthCheck?.port,B;r[75]===f?B=r[76]:(B=f(`healthCheck.port`),r[75]=f,r[76]=B);let V;r[77]!==ye||r[78]!==B?(V=(0,u.jsx)(i,{"aria-invalid":ye,id:`hc-port`,placeholder:`traffic-port or numeric`,...B}),r[77]=ye,r[78]=B,r[79]=V):V=r[79];let be=g.healthCheck?.port,xe;r[80]===be?xe=r[81]:(xe=(0,u.jsx)(s,{errors:[be]}),r[80]=be,r[81]=xe);let H;r[82]!==V||r[83]!==xe?(H=(0,u.jsxs)(c,{children:[ve,V,xe]}),r[82]=V,r[83]=xe,r[84]=H):H=r[84];let Se;r[85]===Symbol.for(`react.memo_cache_sentinel`)?(Se=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`hc-interval`,children:`Interval (seconds)`})}),r[85]=Se):Se=r[85];let Ce=!!g.healthCheck?.intervalSeconds,U;r[86]===f?U=r[87]:(U=f(`healthCheck.intervalSeconds`,{valueAsNumber:!0}),r[86]=f,r[87]=U);let W;r[88]!==Ce||r[89]!==U?(W=(0,u.jsx)(i,{"aria-invalid":Ce,id:`hc-interval`,inputMode:`numeric`,type:`number`,...U}),r[88]=Ce,r[89]=U,r[90]=W):W=r[90];let we=g.healthCheck?.intervalSeconds,G;r[91]===we?G=r[92]:(G=(0,u.jsx)(s,{errors:[we]}),r[91]=we,r[92]=G);let K;r[93]!==W||r[94]!==G?(K=(0,u.jsxs)(c,{children:[Se,W,G]}),r[93]=W,r[94]=G,r[95]=K):K=r[95];let Te;r[96]===Symbol.for(`react.memo_cache_sentinel`)?(Te=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`hc-timeout`,children:`Timeout (seconds)`})}),r[96]=Te):Te=r[96];let Ee=!!g.healthCheck?.timeoutSeconds,q;r[97]===f?q=r[98]:(q=f(`healthCheck.timeoutSeconds`,{valueAsNumber:!0}),r[97]=f,r[98]=q);let J;r[99]!==Ee||r[100]!==q?(J=(0,u.jsx)(i,{"aria-invalid":Ee,id:`hc-timeout`,inputMode:`numeric`,type:`number`,...q}),r[99]=Ee,r[100]=q,r[101]=J):J=r[101];let De=g.healthCheck?.timeoutSeconds,Y;r[102]===De?Y=r[103]:(Y=(0,u.jsx)(s,{errors:[De]}),r[102]=De,r[103]=Y);let X;r[104]!==J||r[105]!==Y?(X=(0,u.jsxs)(c,{children:[Te,J,Y]}),r[104]=J,r[105]=Y,r[106]=X):X=r[106];let Oe;r[107]===Symbol.for(`react.memo_cache_sentinel`)?(Oe=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`hc-healthy`,children:`Healthy threshold`})}),r[107]=Oe):Oe=r[107];let ke=!!g.healthCheck?.healthyThresholdCount,Z;r[108]===f?Z=r[109]:(Z=f(`healthCheck.healthyThresholdCount`,{valueAsNumber:!0}),r[108]=f,r[109]=Z);let Q;r[110]!==ke||r[111]!==Z?(Q=(0,u.jsx)(i,{"aria-invalid":ke,id:`hc-healthy`,inputMode:`numeric`,type:`number`,...Z}),r[110]=ke,r[111]=Z,r[112]=Q):Q=r[112];let Ae=g.healthCheck?.healthyThresholdCount,je;r[113]===Ae?je=r[114]:(je=(0,u.jsx)(s,{errors:[Ae]}),r[113]=Ae,r[114]=je);let Me;r[115]!==Q||r[116]!==je?(Me=(0,u.jsxs)(c,{children:[Oe,Q,je]}),r[115]=Q,r[116]=je,r[117]=Me):Me=r[117];let Ne;r[118]===Symbol.for(`react.memo_cache_sentinel`)?(Ne=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`hc-unhealthy`,children:`Unhealthy threshold`})}),r[118]=Ne):Ne=r[118];let Pe=!!g.healthCheck?.unhealthyThresholdCount,$;r[119]===f?$=r[120]:($=f(`healthCheck.unhealthyThresholdCount`,{valueAsNumber:!0}),r[119]=f,r[120]=$);let Fe;r[121]!==Pe||r[122]!==$?(Fe=(0,u.jsx)(i,{"aria-invalid":Pe,id:`hc-unhealthy`,inputMode:`numeric`,type:`number`,...$}),r[121]=Pe,r[122]=$,r[123]=Fe):Fe=r[123];let Ie=g.healthCheck?.unhealthyThresholdCount,Le;r[124]===Ie?Le=r[125]:(Le=(0,u.jsx)(s,{errors:[Ie]}),r[124]=Ie,r[125]=Le);let Re;r[126]!==Fe||r[127]!==Le?(Re=(0,u.jsxs)(c,{children:[Ne,Fe,Le]}),r[126]=Fe,r[127]=Le,r[128]=Re):Re=r[128];let ze;r[129]===Symbol.for(`react.memo_cache_sentinel`)?(ze=(0,u.jsx)(o,{children:(0,u.jsx)(`label`,{htmlFor:`hc-matcher`,children:`Matcher (HTTP codes)`})}),r[129]=ze):ze=r[129];let Be=!!g.healthCheck?.matcher,Ve;r[130]===f?Ve=r[131]:(Ve=f(`healthCheck.matcher`),r[130]=f,r[131]=Ve);let He;r[132]!==Be||r[133]!==Ve?(He=(0,u.jsx)(i,{"aria-invalid":Be,id:`hc-matcher`,placeholder:`200 or 200-299 or 200,201`,...Ve}),r[132]=Be,r[133]=Ve,r[134]=He):He=r[134];let Ue=g.healthCheck?.matcher,We;r[135]===Ue?We=r[136]:(We=(0,u.jsx)(s,{errors:[Ue]}),r[135]=Ue,r[136]=We);let Ge;r[137]!==He||r[138]!==We?(Ge=(0,u.jsxs)(c,{children:[ze,He,We]}),r[137]=He,r[138]=We,r[139]=Ge):Ge=r[139],r[140]!==z||r[141]!==H||r[142]!==K||r[143]!==X||r[144]!==Me||r[145]!==Re||r[146]!==Ge?(T=(0,u.jsxs)(le,{children:[me,(0,u.jsxs)(se,{className:`mt-3 space-y-4 border-l-2 border-muted pl-4`,children:[z,H,K,X,Me,Re,Ge]})]}),r[140]=z,r[141]=H,r[142]=K,r[143]=X,r[144]=Me,r[145]=Re,r[146]=Ge,r[147]=T):T=r[147],_=c,r[148]===Symbol.for(`react.memo_cache_sentinel`)?(b=(0,u.jsx)(o,{children:`Tags`}),r[148]=b):b=r[148],v=`space-y-2`;let Ke;r[149]!==m||r[150]!==f||r[151]!==p?(Ke=(e,t)=>(0,u.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,u.jsx)(i,{placeholder:`Key`,...f(`tags.${t}.key`)}),(0,u.jsx)(i,{placeholder:`Value`,...f(`tags.${t}.value`)}),(0,u.jsx)(ae,{onClick:()=>p(`tags`,m(`tags`).filter((e,ee)=>ee!==t)),size:`icon`,type:`button`,variant:`ghost`,children:(0,u.jsx)(re,{className:`size-3.5`})})]},t),r[149]=m,r[150]=f,r[151]=p,r[152]=Ke):Ke=r[152],y=t.map(Ke),r[0]=d,r[1]=g.healthCheck?.healthyThresholdCount,r[2]=g.healthCheck?.intervalSeconds,r[3]=g.healthCheck?.matcher,r[4]=g.healthCheck?.path,r[5]=g.healthCheck?.port,r[6]=g.healthCheck?.timeoutSeconds,r[7]=g.healthCheck?.unhealthyThresholdCount,r[8]=g.name,r[9]=g.port,r[10]=g.vpcId,r[11]=m,r[12]=f,r[13]=p,r[14]=a,r[15]=fe,r[16]=_,r[17]=v,r[18]=y,r[19]=b,r[20]=x,r[21]=S,r[22]=C,r[23]=w,r[24]=T}else _=r[16],v=r[17],y=r[18],b=r[19],x=r[20],S=r[21],C=r[22],w=r[23],T=r[24];let E;r[153]!==m||r[154]!==p?(E=()=>p(`tags`,[...m(`tags`),{key:``,value:``}]),r[153]=m,r[154]=p,r[155]=E):E=r[155];let D;r[156]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,u.jsx)(n,{className:`size-3.5`}),r[156]=D):D=r[156];let O;r[157]===E?O=r[158]:(O=(0,u.jsxs)(ae,{onClick:E,size:`sm`,type:`button`,variant:`outline`,children:[D,`Add tag`]}),r[157]=E,r[158]=O);let k;r[159]!==O||r[160]!==v||r[161]!==y?(k=(0,u.jsxs)(`div`,{className:v,children:[y,O]}),r[159]=O,r[160]=v,r[161]=y,r[162]=k):k=r[162];let A;r[163]!==_||r[164]!==k||r[165]!==b?(A=(0,u.jsxs)(_,{children:[b,k]}),r[163]=_,r[164]=k,r[165]=b,r[166]=A):A=r[166];let j;return r[167]!==A||r[168]!==x||r[169]!==S||r[170]!==C||r[171]!==w||r[172]!==T?(j=(0,u.jsxs)(u.Fragment,{children:[x,S,C,w,T,A]}),r[167]=A,r[168]=x,r[169]=S,r[170]=C,r[171]=w,r[172]=T,r[173]=j):j=r[173],j}function ue(e){let ee=a(e.Tags);return(0,u.jsx)(t,{value:e.VpcId??``,children:ee?`${e.VpcId} (${ee})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)}function de(n){let{field:re}=n;return(0,u.jsxs)(ne,{onValueChange:re.onChange,value:re.value,children:[(0,u.jsx)(te,{className:`w-full`,id:`tg-protocol`,children:(0,u.jsx)(e,{})}),(0,u.jsx)(ee,{children:(0,u.jsx)(t,{value:`HTTP`,children:`HTTP`})})]})}export{d as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{A as ee}from"./SignatureV4.js";import{c as te,i as ne,n as re,s as ie,t as ae}from"./select.js";import{t as oe}from"./plus.js";import{t as se}from"./trash-2.js";import{t as ce}from"./button.js";import{Rn as n}from"./index.js";import{a as le,n as ue}from"./zod.js";import{a as r,r as i,t as a}from"./field.js";import{n as de,r as fe,t as pe}from"./collapsible.js";var me=t(),o=e();function he(e){let t=(0,me.c)(147),{form:ee,vpcs:ne,allowedProtocols:he}=e,ve;t[0]===he?ve=t[1]:(ve=he===void 0?[`HTTP`,`HTTPS`,`TCP`,`UDP`,`TLS`,`TCP_UDP`]:he,t[0]=he,t[1]=ve);let ye=ve,{control:s,register:c,setValue:l,getValues:u,formState:be}=ee,{errors:d}=be,xe;t[2]===s?xe=t[3]:(xe={control:s,name:`tags`},t[2]=s,t[3]=xe);let Se=le(xe),Ce;t[4]===s?Ce=t[5]:(Ce={control:s,name:`protocol`},t[4]=s,t[5]=Ce);let we=le(Ce),f=we===`HTTP`||we===`HTTPS`,Te;t[6]===l?Te=t[7]:(Te=e=>{e&&(l(`protocol`,e),l(`healthCheck.protocol`,e===`HTTP`||e===`HTTPS`?`HTTP`:`TCP`))},t[6]=l,t[7]=Te);let Ee=Te,De;t[8]===Symbol.for(`react.memo_cache_sentinel`)?(De=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`tg-name`,children:`Name`})}),t[8]=De):De=t[8];let Oe=!!d.name,p;t[9]===c?p=t[10]:(p=c(`name`),t[9]=c,t[10]=p);let m;t[11]!==Oe||t[12]!==p?(m=(0,o.jsx)(n,{"aria-invalid":Oe,id:`tg-name`,placeholder:`my-target-group`,...p}),t[11]=Oe,t[12]=p,t[13]=m):m=t[13];let h;t[14]===d.name?h=t[15]:(h=(0,o.jsx)(i,{errors:[d.name]}),t[14]=d.name,t[15]=h);let g;t[16]!==m||t[17]!==h?(g=(0,o.jsxs)(a,{children:[De,m,h]}),t[16]=m,t[17]=h,t[18]=g):g=t[18];let ke;t[19]===Symbol.for(`react.memo_cache_sentinel`)?(ke=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`tg-protocol`,children:`Protocol`})}),t[19]=ke):ke=t[19];let _;t[20]!==ye||t[21]!==Ee?(_=e=>{let{field:t}=e;return(0,o.jsxs)(ae,{onValueChange:Ee,value:t.value,children:[(0,o.jsx)(ie,{className:`w-full`,id:`tg-protocol`,children:(0,o.jsx)(te,{})}),(0,o.jsx)(re,{children:ye.map(_e)})]})},t[20]=ye,t[21]=Ee,t[22]=_):_=t[22];let v;t[23]!==s||t[24]!==_?(v=(0,o.jsxs)(a,{children:[ke,(0,o.jsx)(ue,{control:s,name:`protocol`,render:_})]}),t[23]=s,t[24]=_,t[25]=v):v=t[25];let Ae;t[26]===Symbol.for(`react.memo_cache_sentinel`)?(Ae=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`tg-port`,children:`Port`})}),t[26]=Ae):Ae=t[26];let je=!!d.port,y;t[27]===c?y=t[28]:(y=c(`port`,{valueAsNumber:!0}),t[27]=c,t[28]=y);let b;t[29]!==je||t[30]!==y?(b=(0,o.jsx)(n,{"aria-invalid":je,id:`tg-port`,inputMode:`numeric`,placeholder:`80`,type:`number`,...y}),t[29]=je,t[30]=y,t[31]=b):b=t[31];let x;t[32]===d.port?x=t[33]:(x=(0,o.jsx)(i,{errors:[d.port]}),t[32]=d.port,t[33]=x);let S;t[34]!==b||t[35]!==x?(S=(0,o.jsxs)(a,{children:[Ae,b,x]}),t[34]=b,t[35]=x,t[36]=S):S=t[36];let Me;t[37]===Symbol.for(`react.memo_cache_sentinel`)?(Me=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`tg-vpc`,children:`VPC`})}),t[37]=Me):Me=t[37];let C;t[38]!==d.vpcId||t[39]!==ne?(C=e=>{let{field:t}=e;return(0,o.jsxs)(ae,{onValueChange:e=>t.onChange(e),value:t.value??``,children:[(0,o.jsx)(ie,{"aria-invalid":!!d.vpcId,className:`w-full`,id:`tg-vpc`,children:(0,o.jsx)(te,{placeholder:`Select VPC`})}),(0,o.jsx)(re,{children:ne.map(ge)})]})},t[38]=d.vpcId,t[39]=ne,t[40]=C):C=t[40];let w;t[41]!==s||t[42]!==C?(w=(0,o.jsx)(ue,{control:s,name:`vpcId`,render:C}),t[41]=s,t[42]=C,t[43]=w):w=t[43];let T;t[44]===d.vpcId?T=t[45]:(T=(0,o.jsx)(i,{errors:[d.vpcId]}),t[44]=d.vpcId,t[45]=T);let E;t[46]!==w||t[47]!==T?(E=(0,o.jsxs)(a,{children:[Me,w,T]}),t[46]=w,t[47]=T,t[48]=E):E=t[48];let Ne;t[49]===Symbol.for(`react.memo_cache_sentinel`)?(Ne=(0,o.jsx)(fe,{className:`text-left text-sm font-medium text-muted-foreground hover:text-foreground`,render:(0,o.jsx)(`button`,{"aria-label":`Health check settings`,type:`button`}),children:`Health check settings`}),t[49]=Ne):Ne=t[49];let D;t[50]!==d.healthCheck?.path||t[51]!==f||t[52]!==c?(D=f&&(0,o.jsxs)(a,{children:[(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`hc-path`,children:`Path`})}),(0,o.jsx)(n,{"aria-invalid":!!d.healthCheck?.path,id:`hc-path`,placeholder:`/`,...c(`healthCheck.path`)}),(0,o.jsx)(i,{errors:[d.healthCheck?.path]})]}),t[50]=d.healthCheck?.path,t[51]=f,t[52]=c,t[53]=D):D=t[53];let Pe;t[54]===Symbol.for(`react.memo_cache_sentinel`)?(Pe=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`hc-port`,children:`Port`})}),t[54]=Pe):Pe=t[54];let Fe=!!d.healthCheck?.port,O;t[55]===c?O=t[56]:(O=c(`healthCheck.port`),t[55]=c,t[56]=O);let k;t[57]!==Fe||t[58]!==O?(k=(0,o.jsx)(n,{"aria-invalid":Fe,id:`hc-port`,placeholder:`traffic-port or numeric`,...O}),t[57]=Fe,t[58]=O,t[59]=k):k=t[59];let Ie=d.healthCheck?.port,A;t[60]===Ie?A=t[61]:(A=(0,o.jsx)(i,{errors:[Ie]}),t[60]=Ie,t[61]=A);let j;t[62]!==k||t[63]!==A?(j=(0,o.jsxs)(a,{children:[Pe,k,A]}),t[62]=k,t[63]=A,t[64]=j):j=t[64];let Le;t[65]===Symbol.for(`react.memo_cache_sentinel`)?(Le=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`hc-interval`,children:`Interval (seconds)`})}),t[65]=Le):Le=t[65];let Re=!!d.healthCheck?.intervalSeconds,M;t[66]===c?M=t[67]:(M=c(`healthCheck.intervalSeconds`,{valueAsNumber:!0}),t[66]=c,t[67]=M);let N;t[68]!==Re||t[69]!==M?(N=(0,o.jsx)(n,{"aria-invalid":Re,id:`hc-interval`,inputMode:`numeric`,type:`number`,...M}),t[68]=Re,t[69]=M,t[70]=N):N=t[70];let ze=d.healthCheck?.intervalSeconds,P;t[71]===ze?P=t[72]:(P=(0,o.jsx)(i,{errors:[ze]}),t[71]=ze,t[72]=P);let F;t[73]!==N||t[74]!==P?(F=(0,o.jsxs)(a,{children:[Le,N,P]}),t[73]=N,t[74]=P,t[75]=F):F=t[75];let Be;t[76]===Symbol.for(`react.memo_cache_sentinel`)?(Be=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`hc-timeout`,children:`Timeout (seconds)`})}),t[76]=Be):Be=t[76];let Ve=!!d.healthCheck?.timeoutSeconds,I;t[77]===c?I=t[78]:(I=c(`healthCheck.timeoutSeconds`,{valueAsNumber:!0}),t[77]=c,t[78]=I);let L;t[79]!==Ve||t[80]!==I?(L=(0,o.jsx)(n,{"aria-invalid":Ve,id:`hc-timeout`,inputMode:`numeric`,type:`number`,...I}),t[79]=Ve,t[80]=I,t[81]=L):L=t[81];let He=d.healthCheck?.timeoutSeconds,R;t[82]===He?R=t[83]:(R=(0,o.jsx)(i,{errors:[He]}),t[82]=He,t[83]=R);let z;t[84]!==L||t[85]!==R?(z=(0,o.jsxs)(a,{children:[Be,L,R]}),t[84]=L,t[85]=R,t[86]=z):z=t[86];let Ue;t[87]===Symbol.for(`react.memo_cache_sentinel`)?(Ue=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`hc-healthy`,children:`Healthy threshold`})}),t[87]=Ue):Ue=t[87];let We=!!d.healthCheck?.healthyThresholdCount,B;t[88]===c?B=t[89]:(B=c(`healthCheck.healthyThresholdCount`,{valueAsNumber:!0}),t[88]=c,t[89]=B);let V;t[90]!==We||t[91]!==B?(V=(0,o.jsx)(n,{"aria-invalid":We,id:`hc-healthy`,inputMode:`numeric`,type:`number`,...B}),t[90]=We,t[91]=B,t[92]=V):V=t[92];let Ge=d.healthCheck?.healthyThresholdCount,H;t[93]===Ge?H=t[94]:(H=(0,o.jsx)(i,{errors:[Ge]}),t[93]=Ge,t[94]=H);let U;t[95]!==V||t[96]!==H?(U=(0,o.jsxs)(a,{children:[Ue,V,H]}),t[95]=V,t[96]=H,t[97]=U):U=t[97];let Ke;t[98]===Symbol.for(`react.memo_cache_sentinel`)?(Ke=(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`hc-unhealthy`,children:`Unhealthy threshold`})}),t[98]=Ke):Ke=t[98];let qe=!!d.healthCheck?.unhealthyThresholdCount,W;t[99]===c?W=t[100]:(W=c(`healthCheck.unhealthyThresholdCount`,{valueAsNumber:!0}),t[99]=c,t[100]=W);let G;t[101]!==qe||t[102]!==W?(G=(0,o.jsx)(n,{"aria-invalid":qe,id:`hc-unhealthy`,inputMode:`numeric`,type:`number`,...W}),t[101]=qe,t[102]=W,t[103]=G):G=t[103];let Je=d.healthCheck?.unhealthyThresholdCount,K;t[104]===Je?K=t[105]:(K=(0,o.jsx)(i,{errors:[Je]}),t[104]=Je,t[105]=K);let q;t[106]!==G||t[107]!==K?(q=(0,o.jsxs)(a,{children:[Ke,G,K]}),t[106]=G,t[107]=K,t[108]=q):q=t[108];let J;t[109]!==d.healthCheck?.matcher||t[110]!==f||t[111]!==c?(J=f&&(0,o.jsxs)(a,{children:[(0,o.jsx)(r,{children:(0,o.jsx)(`label`,{htmlFor:`hc-matcher`,children:`Matcher (HTTP codes)`})}),(0,o.jsx)(n,{"aria-invalid":!!d.healthCheck?.matcher,id:`hc-matcher`,placeholder:`200 or 200-299 or 200,201`,...c(`healthCheck.matcher`)}),(0,o.jsx)(i,{errors:[d.healthCheck?.matcher]})]}),t[109]=d.healthCheck?.matcher,t[110]=f,t[111]=c,t[112]=J):J=t[112];let Y;t[113]!==D||t[114]!==j||t[115]!==F||t[116]!==z||t[117]!==U||t[118]!==q||t[119]!==J?(Y=(0,o.jsxs)(pe,{children:[Ne,(0,o.jsxs)(de,{className:`mt-3 space-y-4 border-l-2 border-muted pl-4`,children:[D,j,F,z,U,q,J]})]}),t[113]=D,t[114]=j,t[115]=F,t[116]=z,t[117]=U,t[118]=q,t[119]=J,t[120]=Y):Y=t[120];let Ye;t[121]===Symbol.for(`react.memo_cache_sentinel`)?(Ye=(0,o.jsx)(r,{children:`Tags`}),t[121]=Ye):Ye=t[121];let X;if(t[122]!==u||t[123]!==c||t[124]!==l||t[125]!==Se){let e;t[127]!==u||t[128]!==c||t[129]!==l?(e=(e,t)=>(0,o.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,o.jsx)(n,{placeholder:`Key`,...c(`tags.${t}.key`)}),(0,o.jsx)(n,{placeholder:`Value`,...c(`tags.${t}.value`)}),(0,o.jsx)(ce,{onClick:()=>l(`tags`,u(`tags`).filter((e,ee)=>ee!==t)),size:`icon`,type:`button`,variant:`ghost`,children:(0,o.jsx)(se,{className:`size-3.5`})})]},t),t[127]=u,t[128]=c,t[129]=l,t[130]=e):e=t[130],X=Se.map(e),t[122]=u,t[123]=c,t[124]=l,t[125]=Se,t[126]=X}else X=t[126];let Z;t[131]!==u||t[132]!==l?(Z=()=>l(`tags`,[...u(`tags`),{key:``,value:``}]),t[131]=u,t[132]=l,t[133]=Z):Z=t[133];let Xe;t[134]===Symbol.for(`react.memo_cache_sentinel`)?(Xe=(0,o.jsx)(oe,{className:`size-3.5`}),t[134]=Xe):Xe=t[134];let Q;t[135]===Z?Q=t[136]:(Q=(0,o.jsxs)(ce,{onClick:Z,size:`sm`,type:`button`,variant:`outline`,children:[Xe,`Add tag`]}),t[135]=Z,t[136]=Q);let $;t[137]!==X||t[138]!==Q?($=(0,o.jsxs)(a,{children:[Ye,(0,o.jsxs)(`div`,{className:`space-y-2`,children:[X,Q]})]}),t[137]=X,t[138]=Q,t[139]=$):$=t[139];let Ze;return t[140]!==g||t[141]!==v||t[142]!==S||t[143]!==E||t[144]!==Y||t[145]!==$?(Ze=(0,o.jsxs)(o.Fragment,{children:[g,v,S,E,Y,$]}),t[140]=g,t[141]=v,t[142]=S,t[143]=E,t[144]=Y,t[145]=$,t[146]=Ze):Ze=t[146],Ze}function ge(e){let t=ee(e.Tags);return(0,o.jsx)(ne,{value:e.VpcId??``,children:t?`${e.VpcId} (${t})`:`${e.VpcId} (${e.CidrBlock})`},e.VpcId)}function _e(e){return(0,o.jsx)(ne,{value:e,children:e},e)}export{he as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/textarea.js b/spinifex/services/spinifexui/frontend/dist/assets/textarea.js index 89c767cc2..a1692a6eb 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/textarea.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/textarea.js @@ -1 +1 @@ -import{$a as e,Fo as t,Ho as n}from"./index.js";var r=t(),i=n();function a(t){let n=(0,r.c)(8),a,o;n[0]===t?(a=n[1],o=n[2]):({className:a,...o}=t,n[0]=t,n[1]=a,n[2]=o);let s;n[3]===a?s=n[4]:(s=e(`flex field-sizing-content min-h-16 w-full resize-none rounded-md border border-input bg-input/20 px-2 py-2 text-sm transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-xs/relaxed dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40`,a),n[3]=a,n[4]=s);let c;return n[5]!==o||n[6]!==s?(c=(0,i.jsx)(`textarea`,{className:s,"data-slot":`textarea`,...o}),n[5]=o,n[6]=s,n[7]=c):c=n[7],c}export{a as t}; \ No newline at end of file +import{n as e,t}from"./compiler-runtime.js";import{w as n}from"./SignatureV4.js";var r=t(),i=e();function a(e){let t=(0,r.c)(8),a,o;t[0]===e?(a=t[1],o=t[2]):({className:a,...o}=e,t[0]=e,t[1]=a,t[2]=o);let s;t[3]===a?s=t[4]:(s=n(`flex field-sizing-content min-h-16 w-full resize-none rounded-md border border-input bg-input/20 px-2 py-2 text-sm transition-colors outline-none placeholder:text-muted-foreground autofill:[-webkit-text-fill-color:var(--color-foreground)] autofill:[transition:background-color_9999s_ease-in-out_0s] focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-xs/relaxed dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40`,a),t[3]=a,t[4]=s);let c;return t[5]!==o||t[6]!==s?(c=(0,i.jsx)(`textarea`,{className:s,"data-slot":`textarea`,...o}),t[5]=o,t[6]=s,t[7]=c):c=t[7],c}export{a as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/trash-2.js b/spinifex/services/spinifexui/frontend/dist/assets/trash-2.js index cf7b1d720..9562321f8 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/trash-2.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/trash-2.js @@ -1 +1 @@ -import{wo as e}from"./index.js";var t=e(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]);export{t}; \ No newline at end of file +import{Nr as e}from"./index.js";var t=e(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/triangle-alert.js b/spinifex/services/spinifexui/frontend/dist/assets/triangle-alert.js index c4294f2ee..cd2ba3fa4 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/triangle-alert.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/triangle-alert.js @@ -1 +1 @@ -import{wo as e}from"./index.js";var t=e(`triangle-alert`,[[`path`,{d:`m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3`,key:`wmoenq`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]);export{t}; \ No newline at end of file +import{Nr as e}from"./index.js";var t=e(`triangle-alert`,[[`path`,{d:`m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3`,key:`wmoenq`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]);export{t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/upload-button.js b/spinifex/services/spinifexui/frontend/dist/assets/upload-button.js index 133091256..b214c33c0 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/upload-button.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/upload-button.js @@ -1 +1 @@ -import{t as e}from"./error-banner.js";import{t}from"./download.js";import{t as n}from"./trash-2.js";import{a as r,i,n as a}from"./s3.js";import{Cr as o,Dt as s,Fo as c,Ho as l,Lo as u,Uo as d,Xa as f,cs as p,dr as m,jt as h,no as g,or as _,ro as v,sr as y,wo as b,xt as x}from"./index.js";import{t as S}from"./delete-confirmation-dialog.js";var C=b(`file`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}]]),w=b(`folder`,[[`path`,{d:`M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z`,key:`1kt360`}]]),T=b(`upload`,[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]),E=e=>(e,t)=>async n=>{let r=await e(n),{response:i}=r;if(o.isInstance(i)&&i.headers.expires){i.headers.expiresstring=i.headers.expires;try{m(i.headers.expires)}catch(e){t.logger?.warn(`AWS SDK Warning for ${t.clientName}::${t.commandName} response parsing (${i.headers.expires}): ${e}`),delete i.headers.expires}}return r},D={tags:[`S3`],name:`s3ExpiresMiddleware`,override:!0,relation:`after`,toMiddleware:`deserializerMiddleware`},O=e=>({applyToStack:t=>{t.addRelativeTo(E(e),D)}}),k=class extends _.classBuilder().ep({...h,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,a){return[y(n,e.getEndpointParameterInstructions()),r(n,{requestChecksumRequired:!1,requestValidationModeMember:`ChecksumMode`,responseAlgorithms:[`CRC64NVME`,`CRC32`,`CRC32C`,`SHA256`,`SHA1`,`SHA512`,`MD5`,`XXHASH64`,`XXHASH3`,`XXHASH128`]}),i(n),O(n)]}).s(`AmazonS3`,`GetObject`,{}).n(`S3Client`,`GetObjectCommand`).sc(s).build(){},A=c(),j=l();function M(e){let t=(0,A.c)(9),{folderName:n,fullPrefix:r,bucketName:i}=e,a;t[0]!==i||t[1]!==r?(a={bucket:i,_splat:r},t[0]=i,t[1]=r,t[2]=a):a=t[2];let o;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(o=(0,j.jsx)(w,{className:`size-5 text-muted-foreground`}),t[3]=o):o=t[3];let s;t[4]===n?s=t[5]:(s=(0,j.jsx)(`span`,{className:`font-medium`,children:n}),t[4]=n,t[5]=s);let c;return t[6]!==a||t[7]!==s?(c=(0,j.jsxs)(u,{className:`flex items-center gap-3 rounded-lg border bg-card p-4 transition-colors hover:bg-accent`,params:a,to:`/s3/ls/$bucket/$`,children:[o,s]}),t[6]=a,t[7]=s,t[8]=c):c=t[8],c}var N=p(d(),1);function P({object:r,bucketName:i,displayName:o,fullKey:s}){let[c,l]=(0,N.useState)(!1),[u,d]=(0,N.useState)(null),f=a();async function p(){d(null);try{let e=new k({Bucket:i,Key:s}),t=await x().send(e);if(t.Body){let e=await t.Body.transformToByteArray(),n=URL.createObjectURL(new Blob([e])),r=document.createElement(`a`);r.href=n,r.download=o,document.body.append(r),r.click(),r.remove(),URL.revokeObjectURL(n)}}catch(e){d(Error(`Failed to download object`,{cause:e}))}}async function m(){try{await f.mutateAsync({bucket:i,key:s})}finally{l(!1)}}return(0,j.jsxs)(`div`,{children:[f.error&&(0,j.jsx)(e,{error:f.error,msg:`Failed to delete object`}),u&&(0,j.jsx)(e,{error:u,msg:`Failed to download object`}),(0,j.jsxs)(`div`,{className:`flex items-center justify-between rounded-lg border bg-card p-4`,children:[(0,j.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,j.jsx)(C,{className:`size-5 text-muted-foreground`}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{className:`font-medium`,children:o}),r.LastModified&&(0,j.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[`Last Modified: `,g(r.LastModified)]})]})]}),(0,j.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,j.jsx)(`div`,{className:`text-sm text-muted-foreground`,children:v(r.Size??0)}),(0,j.jsx)(`button`,{"aria-label":`Download ${o}`,className:`rounded-md p-2 transition-colors hover:bg-accent`,onClick:p,type:`button`,children:(0,j.jsx)(t,{className:`size-4`})}),(0,j.jsx)(`button`,{"aria-label":`Delete ${o}`,className:`rounded-md p-2 text-destructive transition-colors hover:bg-accent`,onClick:()=>l(!0),type:`button`,children:(0,j.jsx)(n,{className:`size-4`})})]}),(0,j.jsx)(S,{description:(0,j.jsxs)(j.Fragment,{children:[`Are you sure you want to delete `,(0,j.jsx)(`strong`,{children:o}),`? This action cannot be undone.`]}),isPending:f.isPending,onConfirm:m,onOpenChange:l,open:c,title:`Delete Object`})]})]},r.Key)}function F({bucket:e,prefix:t=``,isPending:n,onUpload:r}){let i=(0,N.useRef)(null);async function a(n){let a=n.target.files;if(!(!a||a.length===0))try{await Promise.all([...a].map(async n=>await r({bucket:e,key:`${t}${n.name}`,file:n})))}finally{i.current&&(i.current.value=``)}}return(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(`input`,{accept:`*/*`,"aria-label":`Choose files to upload`,className:`hidden`,multiple:!0,onChange:a,ref:i,type:`file`}),(0,j.jsxs)(f,{disabled:n,onClick:()=>i.current?.click(),size:`sm`,children:[(0,j.jsx)(T,{className:`mr-2 size-4`}),n?`Uploading…`:`Upload Files`]})]})}export{P as n,M as r,F as t}; \ No newline at end of file +import{n as e,o as t,r as n,t as r}from"./compiler-runtime.js";import{t as i}from"./error-banner.js";import{Cr as a,Dr as o,Or as s,Tr as c,p as l,s as u,y as d}from"./awsClient.js";import{D as f,O as p}from"./SignatureV4.js";import{t as m}from"./download.js";import{t as h}from"./trash-2.js";import{t as g}from"./button.js";import{w as _}from"./index.browser.js";import{a as v,i as y,n as b}from"./s3.js";import{Nr as x}from"./index.js";import{t as S}from"./delete-confirmation-dialog.js";var C=x(`file`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}]]),w=x(`folder`,[[`path`,{d:`M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z`,key:`1kt360`}]]),T=x(`upload`,[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]),E=e=>(e,t)=>async n=>{let r=await e(n),{response:i}=r;if(o.isInstance(i)&&i.headers.expires){i.headers.expiresstring=i.headers.expires;try{_(i.headers.expires)}catch(e){t.logger?.warn(`AWS SDK Warning for ${t.clientName}::${t.commandName} response parsing (${i.headers.expires}): ${e}`),delete i.headers.expires}}return r},D={tags:[`S3`],name:`s3ExpiresMiddleware`,override:!0,relation:`after`,toMiddleware:`deserializerMiddleware`},O=e=>({applyToStack:t=>{t.addRelativeTo(E(e),D)}}),k=class extends c.classBuilder().ep({...d,Bucket:{type:`contextParams`,name:`Bucket`},Key:{type:`contextParams`,name:`Key`}}).m(function(e,t,n,r){return[a(n,e.getEndpointParameterInstructions()),v(n,{requestChecksumRequired:!1,requestValidationModeMember:`ChecksumMode`,responseAlgorithms:[`CRC64NVME`,`CRC32`,`CRC32C`,`SHA256`,`SHA1`,`SHA512`,`MD5`,`XXHASH64`,`XXHASH3`,`XXHASH128`]}),y(n),O(n)]}).s(`AmazonS3`,`GetObject`,{}).n(`S3Client`,`GetObjectCommand`).sc(l).build(){},A=r(),j=e();function M(e){let t=(0,A.c)(9),{folderName:n,fullPrefix:r,bucketName:i}=e,a;t[0]!==i||t[1]!==r?(a={bucket:i,_splat:r},t[0]=i,t[1]=r,t[2]=a):a=t[2];let o;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(o=(0,j.jsx)(w,{className:`size-5 text-muted-foreground`}),t[3]=o):o=t[3];let c;t[4]===n?c=t[5]:(c=(0,j.jsx)(`span`,{className:`font-medium`,children:n}),t[4]=n,t[5]=c);let l;return t[6]!==a||t[7]!==c?(l=(0,j.jsxs)(s,{className:`flex items-center gap-3 rounded-lg border bg-card p-4 transition-colors hover:bg-accent`,params:a,to:`/s3/ls/$bucket/$`,children:[o,c]}),t[6]=a,t[7]=c,t[8]=l):l=t[8],l}var N=t(n(),1);function P({object:e,bucketName:t,displayName:n,fullKey:r}){let[a,o]=(0,N.useState)(!1),[s,c]=(0,N.useState)(null),l=b();async function d(){c(null);try{let e=new k({Bucket:t,Key:r}),i=await u().send(e);if(i.Body){let e=await i.Body.transformToByteArray(),t=URL.createObjectURL(new Blob([e])),r=document.createElement(`a`);r.href=t,r.download=n,document.body.append(r),r.click(),r.remove(),URL.revokeObjectURL(t)}}catch(e){c(Error(`Failed to download object`,{cause:e}))}}async function g(){try{await l.mutateAsync({bucket:t,key:r})}finally{o(!1)}}return(0,j.jsxs)(`div`,{children:[l.error&&(0,j.jsx)(i,{error:l.error,msg:`Failed to delete object`}),s&&(0,j.jsx)(i,{error:s,msg:`Failed to download object`}),(0,j.jsxs)(`div`,{className:`flex items-center justify-between rounded-lg border bg-card p-4`,children:[(0,j.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,j.jsx)(C,{className:`size-5 text-muted-foreground`}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{className:`font-medium`,children:n}),e.LastModified&&(0,j.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[`Last Modified: `,f(e.LastModified)]})]})]}),(0,j.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,j.jsx)(`div`,{className:`text-sm text-muted-foreground`,children:p(e.Size??0)}),(0,j.jsx)(`button`,{"aria-label":`Download ${n}`,className:`rounded-md p-2 transition-colors hover:bg-accent`,onClick:d,type:`button`,children:(0,j.jsx)(m,{className:`size-4`})}),(0,j.jsx)(`button`,{"aria-label":`Delete ${n}`,className:`rounded-md p-2 text-destructive transition-colors hover:bg-accent`,onClick:()=>o(!0),type:`button`,children:(0,j.jsx)(h,{className:`size-4`})})]}),(0,j.jsx)(S,{description:(0,j.jsxs)(j.Fragment,{children:[`Are you sure you want to delete `,(0,j.jsx)(`strong`,{children:n}),`? This action cannot be undone.`]}),isPending:l.isPending,onConfirm:g,onOpenChange:o,open:a,title:`Delete Object`})]})]},e.Key)}function F({bucket:e,prefix:t=``,isPending:n,onUpload:r}){let i=(0,N.useRef)(null);async function a(n){let a=n.target.files;if(!(!a||a.length===0))try{await Promise.all([...a].map(async n=>await r({bucket:e,key:`${t}${n.name}`,file:n})))}finally{i.current&&(i.current.value=``)}}return(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(`input`,{accept:`*/*`,"aria-label":`Choose files to upload`,className:`hidden`,multiple:!0,onChange:a,ref:i,type:`file`}),(0,j.jsxs)(g,{disabled:n,onClick:()=>i.current?.click(),size:`sm`,children:[(0,j.jsx)(T,{className:`mr-2 size-4`}),n?`Uploading…`:`Upload Files`]})]})}export{P as n,M as r,F as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/use-copy-to-clipboard.js b/spinifex/services/spinifexui/frontend/dist/assets/use-copy-to-clipboard.js index e8095f89e..7239fe3d8 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/use-copy-to-clipboard.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/use-copy-to-clipboard.js @@ -1 +1 @@ -import{Fo as e,Uo as t,cs as n}from"./index.js";var r=e(),i=n(t(),1),a=2e3;function o(){let e=(0,r.c)(5),[t,n]=(0,i.useState)(!1),o=(0,i.useRef)(null),s,c;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(s=()=>()=>{o.current&&clearTimeout(o.current)},c=[],e[0]=s,e[1]=c):(s=e[0],c=e[1]),(0,i.useEffect)(s,c);let l;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=async e=>{await navigator.clipboard.writeText(e),n(!0),o.current&&clearTimeout(o.current),o.current=setTimeout(()=>n(!1),a)},e[2]=l):l=e[2];let u=l,d;return e[3]===t?d=e[4]:(d={copied:t,copy:u},e[3]=t,e[4]=d),d}export{o as t}; \ No newline at end of file +import{o as e,r as t,t as n}from"./compiler-runtime.js";var r=n(),i=e(t(),1),a=2e3;function o(){let e=(0,r.c)(5),[t,n]=(0,i.useState)(!1),o=(0,i.useRef)(null),s,c;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(s=()=>()=>{o.current&&clearTimeout(o.current)},c=[],e[0]=s,e[1]=c):(s=e[0],c=e[1]),(0,i.useEffect)(s,c);let l;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(l=async e=>{await navigator.clipboard.writeText(e),n(!0),o.current&&clearTimeout(o.current),o.current=setTimeout(()=>n(!1),a)},e[2]=l):l=e[2];let u=l,d;return e[3]===t?d=e[4]:(d={copied:t,copy:u},e[3]=t,e[4]=d),d}export{o as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/useBaseQuery.js b/spinifex/services/spinifexui/frontend/dist/assets/useBaseQuery.js index 7519e6d21..401119b7c 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/useBaseQuery.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/useBaseQuery.js @@ -1 +1 @@ -import{$o as e,Go as t,Ho as n,Jo as r,Ko as i,Qo as a,Uo as o,Vo as s,Xo as c,Zo as l,as as u,cs as d,es as f,is as p,ns as m,os as h,qo as g,rs as _,ts as v}from"./index.js";var y=class extends h{constructor(e,t){super(),this.options=t,this.#e=e,this.#s=null,this.#o=g(),this.bindMethods(),this.setOptions(t)}#e;#t=void 0;#n=void 0;#r=void 0;#i;#a;#o;#s;#c;#l;#u;#d;#f;#p;#m=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(this.#t.addObserver(this),x(this.#t,this.options)?this.#h():this.updateResult(),this.#y())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return S(this.#t,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return S(this.#t,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#b(),this.#x(),this.#t.removeObserver(this)}setOptions(t){let n=this.options,r=this.#t;if(this.options=this.#e.defaultQueryOptions(t),this.options.enabled!==void 0&&typeof this.options.enabled!=`boolean`&&typeof this.options.enabled!=`function`&&typeof e(this.options.enabled,this.#t)!=`boolean`)throw Error(`Expected enabled to be a boolean or a callback that returns a boolean`);this.#S(),this.#t.setOptions(this.options),n._defaulted&&!v(this.options,n)&&this.#e.getQueryCache().notify({type:`observerOptionsUpdated`,query:this.#t,observer:this});let i=this.hasListeners();i&&C(this.#t,r,this.options,n)&&this.#h(),this.updateResult(),i&&(this.#t!==r||e(this.options.enabled,this.#t)!==e(n.enabled,this.#t)||f(this.options.staleTime,this.#t)!==f(n.staleTime,this.#t))&&this.#g();let a=this.#_();i&&(this.#t!==r||e(this.options.enabled,this.#t)!==e(n.enabled,this.#t)||a!==this.#p)&&this.#v(a)}getOptimisticResult(e){let t=this.#e.getQueryCache().build(this.#e,e),n=this.createResult(t,e);return T(this,n)&&(this.#r=n,this.#a=this.options,this.#i=this.#t.state),n}getCurrentResult(){return this.#r}trackResult(e,t){return new Proxy(e,{get:(e,n)=>(this.trackProp(n),t?.(n),n===`promise`&&(this.trackProp(`data`),!this.options.experimental_prefetchInRender&&this.#o.status===`pending`&&this.#o.reject(Error(`experimental_prefetchInRender feature flag is not enabled`))),Reflect.get(e,n))})}trackProp(e){this.#m.add(e)}getCurrentQuery(){return this.#t}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){let t=this.#e.defaultQueryOptions(e),n=this.#e.getQueryCache().build(this.#e,t);return n.fetch().then(()=>this.createResult(n,t))}fetch(e){return this.#h({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#r))}#h(e){this.#S();let t=this.#t.fetch(this.options,e);return e?.throwOnError||(t=t.catch(l)),t}#g(){this.#b();let e=f(this.options.staleTime,this.#t);if(r.isServer()||this.#r.isStale||!c(e))return;let t=_(this.#r.dataUpdatedAt,e)+1;this.#d=p.setTimeout(()=>{this.#r.isStale||this.updateResult()},t)}#_(){return(typeof this.options.refetchInterval==`function`?this.options.refetchInterval(this.#t):this.options.refetchInterval)??!1}#v(t){this.#x(),this.#p=t,!(r.isServer()||e(this.options.enabled,this.#t)===!1||!c(this.#p)||this.#p===0)&&(this.#f=p.setInterval(()=>{(this.options.refetchIntervalInBackground||u.isFocused())&&this.#h()},this.#p))}#y(){this.#g(),this.#v(this.#_())}#b(){this.#d!==void 0&&(p.clearTimeout(this.#d),this.#d=void 0)}#x(){this.#f!==void 0&&(p.clearInterval(this.#f),this.#f=void 0)}createResult(n,r){let i=this.#t,o=this.options,s=this.#r,c=this.#i,l=this.#a,u=n===i?this.#n:n.state,{state:d}=n,f={...d},p=!1,m;if(r._optimisticResults){let e=this.hasListeners(),a=!e&&x(n,r),s=e&&C(n,i,r,o);(a||s)&&(f={...f,...t(d.data,n.options)}),r._optimisticResults===`isRestoring`&&(f.fetchStatus=`idle`)}let{error:h,errorUpdatedAt:_,status:v}=f;m=f.data;let y=!1;if(r.placeholderData!==void 0&&m===void 0&&v===`pending`){let e;s?.isPlaceholderData&&r.placeholderData===l?.placeholderData?(e=s.data,y=!0):e=typeof r.placeholderData==`function`?r.placeholderData(this.#u?.state.data,this.#u):r.placeholderData,e!==void 0&&(v=`success`,m=a(s?.data,e,r),p=!0)}if(r.select&&m!==void 0&&!y)if(s&&m===c?.data&&r.select===this.#c)m=this.#l;else try{this.#c=r.select,m=r.select(m),m=a(s?.data,m,r),this.#l=m,this.#s=null}catch(e){this.#s=e}this.#s&&(h=this.#s,m=this.#l,_=Date.now(),v=`error`);let b=f.fetchStatus===`fetching`,S=v===`pending`,T=v===`error`,E=S&&b,D=m!==void 0,O={status:v,fetchStatus:f.fetchStatus,isPending:S,isSuccess:v===`success`,isError:T,isInitialLoading:E,isLoading:E,data:m,dataUpdatedAt:f.dataUpdatedAt,error:h,errorUpdatedAt:_,failureCount:f.fetchFailureCount,failureReason:f.fetchFailureReason,errorUpdateCount:f.errorUpdateCount,isFetched:n.isFetched(),isFetchedAfterMount:f.dataUpdateCount>u.dataUpdateCount||f.errorUpdateCount>u.errorUpdateCount,isFetching:b,isRefetching:b&&!S,isLoadingError:T&&!D,isPaused:f.fetchStatus===`paused`,isPlaceholderData:p,isRefetchError:T&&D,isStale:w(n,r),refetch:this.refetch,promise:this.#o,isEnabled:e(r.enabled,n)!==!1};if(this.options.experimental_prefetchInRender){let e=O.data!==void 0,t=O.status===`error`&&!e,r=n=>{t?n.reject(O.error):e&&n.resolve(O.data)},a=()=>{r(this.#o=O.promise=g())},o=this.#o;switch(o.status){case`pending`:n.queryHash===i.queryHash&&r(o);break;case`fulfilled`:(t||O.data!==o.value)&&a();break;case`rejected`:(!t||O.error!==o.reason)&&a();break}}return O}updateResult(){let e=this.#r,t=this.createResult(this.#t,this.options);this.#i=this.#t.state,this.#a=this.options,this.#i.data!==void 0&&(this.#u=this.#t),!v(t,e)&&(this.#r=t,this.#C({listeners:(()=>{if(!e)return!0;let{notifyOnChangeProps:t}=this.options,n=typeof t==`function`?t():t;if(n===`all`||!n&&!this.#m.size)return!0;let r=new Set(n??this.#m);return this.options.throwOnError&&r.add(`error`),Object.keys(this.#r).some(t=>{let n=t;return this.#r[n]!==e[n]&&r.has(n)})})()}))}#S(){let e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#t)return;let t=this.#t;this.#t=e,this.#n=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#y()}#C(e){i.batch(()=>{e.listeners&&this.listeners.forEach(e=>{e(this.#r)}),this.#e.getQueryCache().notify({query:this.#t,type:`observerResultsUpdated`})})}};function b(t,n){return e(n.enabled,t)!==!1&&t.state.data===void 0&&!(t.state.status===`error`&&e(n.retryOnMount,t)===!1)}function x(e,t){return b(e,t)||e.state.data!==void 0&&S(e,t,t.refetchOnMount)}function S(t,n,r){if(e(n.enabled,t)!==!1&&f(n.staleTime,t)!==`static`){let e=typeof r==`function`?r(t):r;return e===`always`||e!==!1&&w(t,n)}return!1}function C(t,n,r,i){return(t!==n||e(i.enabled,t)===!1)&&(!r.suspense||t.state.status!==`error`)&&w(t,r)}function w(t,n){return e(n.enabled,t)!==!1&&t.isStaleByTime(f(n.staleTime,t))}function T(e,t){return!v(e.getCurrentResult(),t)}n();var E=d(o(),1),D=E.createContext(!1),O=()=>E.useContext(D);D.Provider;function k(){let e=!1;return{clearReset:()=>{e=!1},reset:()=>{e=!0},isReset:()=>e}}var A=E.createContext(k()),j=()=>E.useContext(A),M=(e,t,n)=>{let r=n?.state.error&&typeof e.throwOnError==`function`?m(e.throwOnError,[n.state.error,n]):e.throwOnError;(e.suspense||e.experimental_prefetchInRender||r)&&(t.isReset()||(e.retryOnMount=!1))},N=e=>{E.useEffect(()=>{e.clearReset()},[e])},P=({result:e,errorResetBoundary:t,throwOnError:n,query:r,suspense:i})=>e.isError&&!t.isReset()&&!e.isFetching&&r&&(i&&e.data===void 0||m(n,[e.error,r])),F=(e,t)=>t.state.data===void 0,I=e=>{if(e.suspense){let t=1e3,n=e=>e===`static`?e:Math.max(e??t,t),r=e.staleTime;e.staleTime=typeof r==`function`?(...e)=>n(r(...e)):n(r),typeof e.gcTime==`number`&&(e.gcTime=Math.max(e.gcTime,t))}},L=(e,t)=>e.isLoading&&e.isFetching&&!t,R=(e,t)=>e?.suspense&&t.isPending,z=(e,t,n)=>t.fetchOptimistic(e).catch(()=>{n.clearReset()});function B(e,t,n){let a=O(),o=j(),c=s(n),u=c.defaultQueryOptions(e);c.getDefaultOptions().queries?._experimental_beforeQuery?.(u);let d=c.getQueryCache().get(u.queryHash);u._optimisticResults=a?`isRestoring`:`optimistic`,I(u),M(u,o,d),N(o);let f=!c.getQueryCache().get(u.queryHash),[p]=E.useState(()=>new t(c,u)),m=p.getOptimisticResult(u),h=!a&&e.subscribed!==!1;if(E.useSyncExternalStore(E.useCallback(e=>{let t=h?p.subscribe(i.batchCalls(e)):l;return p.updateResult(),t},[p,h]),()=>p.getCurrentResult(),()=>p.getCurrentResult()),E.useEffect(()=>{p.setOptions(u)},[u,p]),R(u,m))throw z(u,p,o);if(P({result:m,errorResetBoundary:o,throwOnError:u.throwOnError,query:d,suspense:u.suspense}))throw m.error;return c.getDefaultOptions().queries?._experimental_afterQuery?.(u,m),u.experimental_prefetchInRender&&!r.isServer()&&L(m,a)&&(f?z(u,p,o):d?.promise)?.catch(l).finally(()=>{p.updateResult()}),u.notifyOnChangeProps?m:p.trackResult(m)}export{F as n,y as r,B as t}; \ No newline at end of file +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{$r as r,Br as i,Gr as a,Hr as o,Jr as s,Kr as c,Lr as l,Qr as u,Vr as d,Wr as f,Xr as p,Yr as m,Zr as h,ei as g,qr as _,zr as v}from"./index.js";var y=class extends g{constructor(e,t){super(),this.options=t,this.#e=e,this.#s=null,this.#o=d(),this.bindMethods(),this.setOptions(t)}#e;#t=void 0;#n=void 0;#r=void 0;#i;#a;#o;#s;#c;#l;#u;#d;#f;#p;#m=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(this.#t.addObserver(this),x(this.#t,this.options)?this.#h():this.updateResult(),this.#y())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return S(this.#t,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return S(this.#t,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#b(),this.#x(),this.#t.removeObserver(this)}setOptions(e){let t=this.options,n=this.#t;if(this.options=this.#e.defaultQueryOptions(e),this.options.enabled!==void 0&&typeof this.options.enabled!=`boolean`&&typeof this.options.enabled!=`function`&&typeof _(this.options.enabled,this.#t)!=`boolean`)throw Error(`Expected enabled to be a boolean or a callback that returns a boolean`);this.#S(),this.#t.setOptions(this.options),t._defaulted&&!m(this.options,t)&&this.#e.getQueryCache().notify({type:`observerOptionsUpdated`,query:this.#t,observer:this});let r=this.hasListeners();r&&C(this.#t,n,this.options,t)&&this.#h(),this.updateResult(),r&&(this.#t!==n||_(this.options.enabled,this.#t)!==_(t.enabled,this.#t)||s(this.options.staleTime,this.#t)!==s(t.staleTime,this.#t))&&this.#g();let i=this.#_();r&&(this.#t!==n||_(this.options.enabled,this.#t)!==_(t.enabled,this.#t)||i!==this.#p)&&this.#v(i)}getOptimisticResult(e){let t=this.#e.getQueryCache().build(this.#e,e),n=this.createResult(t,e);return T(this,n)&&(this.#r=n,this.#a=this.options,this.#i=this.#t.state),n}getCurrentResult(){return this.#r}trackResult(e,t){return new Proxy(e,{get:(e,n)=>(this.trackProp(n),t?.(n),n===`promise`&&(this.trackProp(`data`),!this.options.experimental_prefetchInRender&&this.#o.status===`pending`&&this.#o.reject(Error(`experimental_prefetchInRender feature flag is not enabled`))),Reflect.get(e,n))})}trackProp(e){this.#m.add(e)}getCurrentQuery(){return this.#t}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){let t=this.#e.defaultQueryOptions(e),n=this.#e.getQueryCache().build(this.#e,t);return n.fetch().then(()=>this.createResult(n,t))}fetch(e){return this.#h({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#r))}#h(e){this.#S();let t=this.#t.fetch(this.options,e);return e?.throwOnError||(t=t.catch(a)),t}#g(){this.#b();let e=s(this.options.staleTime,this.#t);if(o.isServer()||this.#r.isStale||!f(e))return;let t=h(this.#r.dataUpdatedAt,e)+1;this.#d=u.setTimeout(()=>{this.#r.isStale||this.updateResult()},t)}#_(){return(typeof this.options.refetchInterval==`function`?this.options.refetchInterval(this.#t):this.options.refetchInterval)??!1}#v(e){this.#x(),this.#p=e,!(o.isServer()||_(this.options.enabled,this.#t)===!1||!f(this.#p)||this.#p===0)&&(this.#f=u.setInterval(()=>{(this.options.refetchIntervalInBackground||r.isFocused())&&this.#h()},this.#p))}#y(){this.#g(),this.#v(this.#_())}#b(){this.#d!==void 0&&(u.clearTimeout(this.#d),this.#d=void 0)}#x(){this.#f!==void 0&&(u.clearInterval(this.#f),this.#f=void 0)}createResult(e,t){let n=this.#t,r=this.options,i=this.#r,a=this.#i,o=this.#a,s=e===n?this.#n:e.state,{state:l}=e,u={...l},f=!1,p;if(t._optimisticResults){let i=this.hasListeners(),a=!i&&x(e,t),o=i&&C(e,n,t,r);(a||o)&&(u={...u,...v(l.data,e.options)}),t._optimisticResults===`isRestoring`&&(u.fetchStatus=`idle`)}let{error:m,errorUpdatedAt:h,status:g}=u;p=u.data;let y=!1;if(t.placeholderData!==void 0&&p===void 0&&g===`pending`){let e;i?.isPlaceholderData&&t.placeholderData===o?.placeholderData?(e=i.data,y=!0):e=typeof t.placeholderData==`function`?t.placeholderData(this.#u?.state.data,this.#u):t.placeholderData,e!==void 0&&(g=`success`,p=c(i?.data,e,t),f=!0)}if(t.select&&p!==void 0&&!y)if(i&&p===a?.data&&t.select===this.#c)p=this.#l;else try{this.#c=t.select,p=t.select(p),p=c(i?.data,p,t),this.#l=p,this.#s=null}catch(e){this.#s=e}this.#s&&(m=this.#s,p=this.#l,h=Date.now(),g=`error`);let b=u.fetchStatus===`fetching`,S=g===`pending`,T=g===`error`,E=S&&b,D=p!==void 0,O={status:g,fetchStatus:u.fetchStatus,isPending:S,isSuccess:g===`success`,isError:T,isInitialLoading:E,isLoading:E,data:p,dataUpdatedAt:u.dataUpdatedAt,error:m,errorUpdatedAt:h,failureCount:u.fetchFailureCount,failureReason:u.fetchFailureReason,errorUpdateCount:u.errorUpdateCount,isFetched:e.isFetched(),isFetchedAfterMount:u.dataUpdateCount>s.dataUpdateCount||u.errorUpdateCount>s.errorUpdateCount,isFetching:b,isRefetching:b&&!S,isLoadingError:T&&!D,isPaused:u.fetchStatus===`paused`,isPlaceholderData:f,isRefetchError:T&&D,isStale:w(e,t),refetch:this.refetch,promise:this.#o,isEnabled:_(t.enabled,e)!==!1};if(this.options.experimental_prefetchInRender){let t=O.data!==void 0,r=O.status===`error`&&!t,i=e=>{r?e.reject(O.error):t&&e.resolve(O.data)},a=()=>{i(this.#o=O.promise=d())},o=this.#o;switch(o.status){case`pending`:e.queryHash===n.queryHash&&i(o);break;case`fulfilled`:(r||O.data!==o.value)&&a();break;case`rejected`:(!r||O.error!==o.reason)&&a();break}}return O}updateResult(){let e=this.#r,t=this.createResult(this.#t,this.options);this.#i=this.#t.state,this.#a=this.options,this.#i.data!==void 0&&(this.#u=this.#t),!m(t,e)&&(this.#r=t,this.#C({listeners:(()=>{if(!e)return!0;let{notifyOnChangeProps:t}=this.options,n=typeof t==`function`?t():t;if(n===`all`||!n&&!this.#m.size)return!0;let r=new Set(n??this.#m);return this.options.throwOnError&&r.add(`error`),Object.keys(this.#r).some(t=>{let n=t;return this.#r[n]!==e[n]&&r.has(n)})})()}))}#S(){let e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#t)return;let t=this.#t;this.#t=e,this.#n=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#y()}#C(e){i.batch(()=>{e.listeners&&this.listeners.forEach(e=>{e(this.#r)}),this.#e.getQueryCache().notify({query:this.#t,type:`observerResultsUpdated`})})}};function b(e,t){return _(t.enabled,e)!==!1&&e.state.data===void 0&&!(e.state.status===`error`&&_(t.retryOnMount,e)===!1)}function x(e,t){return b(e,t)||e.state.data!==void 0&&S(e,t,t.refetchOnMount)}function S(e,t,n){if(_(t.enabled,e)!==!1&&s(t.staleTime,e)!==`static`){let r=typeof n==`function`?n(e):n;return r===`always`||r!==!1&&w(e,t)}return!1}function C(e,t,n,r){return(e!==t||_(r.enabled,e)===!1)&&(!n.suspense||e.state.status!==`error`)&&w(e,n)}function w(e,t){return _(t.enabled,e)!==!1&&e.isStaleByTime(s(t.staleTime,e))}function T(e,t){return!m(e.getCurrentResult(),t)}e();var E=t(n(),1),D=E.createContext(!1),O=()=>E.useContext(D);D.Provider;function k(){let e=!1;return{clearReset:()=>{e=!1},reset:()=>{e=!0},isReset:()=>e}}var A=E.createContext(k()),j=()=>E.useContext(A),M=(e,t,n)=>{let r=n?.state.error&&typeof e.throwOnError==`function`?p(e.throwOnError,[n.state.error,n]):e.throwOnError;(e.suspense||e.experimental_prefetchInRender||r)&&(t.isReset()||(e.retryOnMount=!1))},N=e=>{E.useEffect(()=>{e.clearReset()},[e])},P=({result:e,errorResetBoundary:t,throwOnError:n,query:r,suspense:i})=>e.isError&&!t.isReset()&&!e.isFetching&&r&&(i&&e.data===void 0||p(n,[e.error,r])),F=(e,t)=>t.state.data===void 0,I=e=>{if(e.suspense){let t=1e3,n=e=>e===`static`?e:Math.max(e??t,t),r=e.staleTime;e.staleTime=typeof r==`function`?(...e)=>n(r(...e)):n(r),typeof e.gcTime==`number`&&(e.gcTime=Math.max(e.gcTime,t))}},L=(e,t)=>e.isLoading&&e.isFetching&&!t,R=(e,t)=>e?.suspense&&t.isPending,z=(e,t,n)=>t.fetchOptimistic(e).catch(()=>{n.clearReset()});function B(e,t,n){let r=O(),s=j(),c=l(n),u=c.defaultQueryOptions(e);c.getDefaultOptions().queries?._experimental_beforeQuery?.(u);let d=c.getQueryCache().get(u.queryHash),f=e.subscribed!==!1;u._optimisticResults=r?`isRestoring`:f?`optimistic`:void 0,I(u),M(u,s,d),N(s);let p=!c.getQueryCache().get(u.queryHash),[m]=E.useState(()=>new t(c,u)),h=m.getOptimisticResult(u),g=!r&&f;if(E.useSyncExternalStore(E.useCallback(e=>{let t=g?m.subscribe(i.batchCalls(e)):a;return m.updateResult(),t},[m,g]),()=>m.getCurrentResult(),()=>m.getCurrentResult()),E.useEffect(()=>{m.setOptions(u)},[u,m]),R(u,h))throw z(u,m,s);if(P({result:h,errorResetBoundary:s,throwOnError:u.throwOnError,query:d,suspense:u.suspense}))throw h.error;return c.getDefaultOptions().queries?._experimental_afterQuery?.(u,h),u.experimental_prefetchInRender&&!o.isServer()&&L(h,r)&&(p?z(u,m,s):d?.promise)?.catch(a).finally(()=>{m.updateResult()}),u.notifyOnChangeProps?h:m.trackResult(h)}export{F as n,y as r,B as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/useCompositeListItem.js b/spinifex/services/spinifexui/frontend/dist/assets/useCompositeListItem.js new file mode 100644 index 000000000..845905f84 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/useCompositeListItem.js @@ -0,0 +1 @@ +import{n as e,o as t,r as n}from"./compiler-runtime.js";import{l as r}from"./dist.js";import{o as i,s as a}from"./button.js";var o=t(n()),s=o.createContext({register:()=>{},unregister:()=>{},subscribeMapChange:()=>()=>{},elementsRef:{current:[]},nextIndexRef:{current:0}});function c(){return o.useContext(s)}var l=e();function u(e){let{children:t,elementsRef:n,labelsRef:c,onMapChange:u}=e,m=a(u),h=o.useRef(0),g=r(f).current,_=r(d).current,[v,y]=o.useState(0),b=o.useRef(v),x=a((e,t)=>{_.set(e,t??null),b.current+=1,y(b.current)}),S=a(e=>{_.delete(e),b.current+=1,y(b.current)}),C=o.useMemo(()=>{let e=new Map;return Array.from(_.keys()).filter(e=>e.isConnected).sort(p).forEach((t,n)=>{let r=_.get(t)??{};e.set(t,{...r,index:n})}),e},[_,v]);i(()=>{if(typeof MutationObserver!=`function`||C.size===0)return;let e=new MutationObserver(e=>{let t=new Set,n=e=>t.has(e)?t.delete(e):t.add(e);e.forEach(e=>{e.removedNodes.forEach(n),e.addedNodes.forEach(n)}),t.size===0&&(b.current+=1,y(b.current))});return C.forEach((t,n)=>{n.parentElement&&e.observe(n.parentElement,{childList:!0})}),()=>{e.disconnect()}},[C]),i(()=>{b.current===v&&(n.current.length!==C.size&&(n.current.length=C.size),c&&c.current.length!==C.size&&(c.current.length=C.size),h.current=C.size),m(C)},[m,C,n,c,v]),i(()=>()=>{n.current=[]},[n]),i(()=>()=>{c&&(c.current=[])},[c]);let w=a(e=>(g.add(e),()=>{g.delete(e)}));i(()=>{g.forEach(e=>e(C))},[g,C]);let T=o.useMemo(()=>({register:x,unregister:S,subscribeMapChange:w,elementsRef:n,labelsRef:c,nextIndexRef:h}),[x,S,w,n,c,h]);return(0,l.jsx)(s.Provider,{value:T,children:t})}function d(){return new Map}function f(){return new Set}function p(e,t){let n=e.compareDocumentPosition(t);return n&Node.DOCUMENT_POSITION_FOLLOWING||n&Node.DOCUMENT_POSITION_CONTAINED_BY?-1:n&Node.DOCUMENT_POSITION_PRECEDING||n&Node.DOCUMENT_POSITION_CONTAINS?1:0}var m=function(e){return e[e.None=0]=`None`,e[e.GuessFromOrder=1]=`GuessFromOrder`,e}({});function h(e={}){let{label:t,metadata:n,textRef:r,indexGuessBehavior:a,index:s}=e,{register:l,unregister:u,subscribeMapChange:d,elementsRef:f,labelsRef:p,nextIndexRef:h}=c(),g=o.useRef(-1),[_,v]=o.useState(s??(a===m.GuessFromOrder?()=>{if(g.current===-1){let e=h.current;h.current+=1,g.current=e}return g.current}:-1)),y=o.useRef(null),b=o.useCallback(e=>{if(y.current=e,_!==-1&&e!==null&&(f.current[_]=e,p)){let n=t!==void 0;p.current[_]=n?t:r?.current?.textContent??e.textContent}},[_,f,p,t,r]);return i(()=>{if(s!=null)return;let e=y.current;if(e)return l(e,n),()=>{u(e)}},[s,l,u,n]),i(()=>{if(s==null)return d(e=>{let t=y.current?e.get(y.current)?.index:null;t!=null&&v(t)})},[s,d,v]),o.useMemo(()=>({ref:b,index:_}),[_,b])}export{h as n,u as r,m as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/useStore.js b/spinifex/services/spinifexui/frontend/dist/assets/useStore.js new file mode 100644 index 000000000..15071f9d2 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/dist/assets/useStore.js @@ -0,0 +1 @@ +import{i as e,n as t,o as n,r}from"./compiler-runtime.js";var i=n(r(),1),a=i.use,o=typeof window<`u`?i.useLayoutEffect:i.useEffect;function s(e){let t=i.useRef({value:e,prev:null}),n=t.current.value;return e!==n&&(t.current={value:e,prev:n}),t.current.prev}function c(e,t,n={},r={}){i.useEffect(()=>{if(!e.current||r.disabled||typeof IntersectionObserver!=`function`)return;let i=new IntersectionObserver(([e])=>{t(e)},n);return i.observe(e.current),()=>{i.disconnect()}},[t,n,r.disabled,e])}function l(e){let t=i.useRef(null);return i.useImperativeHandle(e,()=>t.current,[]),t}function u(e){return e[e.length-1]}function d(e){return typeof e==`function`}function f(e,t){return d(e)?e(t):e}var p=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable;function h(e){for(let t in e)if(p.call(e,t))return!0;return!1}var g=()=>Object.create(null),_=(e,t)=>v(e,t,g);function v(e,t,n=()=>({}),r=0){if(e===t)return e;if(r>500)return t;let i=t,a=S(e)&&S(i);if(!a&&!(b(e)&&b(i)))return i;let o=a?e:y(e);if(!o)return i;let s=a?i:y(i);if(!s)return i;let c=o.length,l=s.length,u=a?Array(l):n(),d=0;for(let t=0;ti||!C(e[o],t[o],n)))return!1;return i===a}return!1}function ee(e){let t,n,r=new Promise((e,r)=>{t=e,n=r});return r.status=`pending`,r.resolve=n=>{r.status=`resolved`,r.value=n,t(n),e?.(n)},r.reject=e=>{r.status=`rejected`,n(e)},r}function te(e){return typeof e?.message==`string`?e.message.startsWith(`Failed to fetch dynamically imported module`)||e.message.startsWith(`error loading dynamically imported module`)||e.message.startsWith(`Importing a module script failed`):!1}function ne(e){return!!(e&&typeof e==`object`&&typeof e.then==`function`)}function re(e){return e.replace(/[\x00-\x1f\x7f]/g,``)}function w(e){let t;try{t=decodeURI(e)}catch{t=e.replaceAll(/%[0-9A-F]{2}/gi,e=>{try{return decodeURI(e)}catch{return e}})}return re(t)}var T=[`http:`,`https:`,`mailto:`,`tel:`];function ie(e,t){if(!e)return!1;try{let n=new URL(e);return!t.has(n.protocol)}catch{return!1}}var ae={"&":`\\u0026`,">":`\\u003e`,"<":`\\u003c`,"\u2028":`\\u2028`,"\u2029":`\\u2029`},oe=/[&><\u2028\u2029]/g;function se(e){return e.replace(oe,e=>ae[e])}function ce(e){if(!e||!/[%\\\x00-\x1f\x7f]/.test(e)&&!e.startsWith(`//`))return{path:e,handledProtocolRelativeURL:!1};let t=/%25|%5C/gi,n=0,r=``,i;for(;(i=t.exec(e))!==null;)r+=w(e.slice(n,i.index))+i[0],n=t.lastIndex;r+=w(n?e.slice(n):e);let a=!1;return r.startsWith(`//`)&&(a=!0,r=`/`+r.replace(/^\/+/,``)),{path:r,handledProtocolRelativeURL:a}}function le(e){return/\s|[^\u0000-\u007F]/.test(e)?e.replace(/\s|[^\u0000-\u007F]/gu,encodeURIComponent):e}function ue(e,t){if(e===t)return!0;if(e.length!==t.length)return!1;for(let n=0;n{e.next&&(e.prev?(e.prev.next=e.next,e.next.prev=e.prev,e.next=void 0,r&&(r.next=e,e.prev=r)):(e.next.prev=void 0,n=e.next,e.next=void 0,r&&(e.prev=r,r.next=e)),r=e)};return{get(e){let n=t.get(e);if(n)return i(n),n.value},set(a,o){if(t.size>=e&&n){let e=n;t.delete(e.key),e.next&&(n=e.next,e.next.prev=void 0),e===r&&(r=void 0)}let s=t.get(a);if(s)s.value=o,i(s);else{let e={key:a,value:o,prev:r};r&&(r.next=e),r=e,n||=e,t.set(a,e)}},clear(){t.clear(),n=void 0,r=void 0}}}var O=4,k=5;function de(e){let t=e.indexOf(`{`);if(t===-1)return null;let n=e.indexOf(`}`,t);return n===-1||t+1>=e.length?null:[t,n]}function A(e,t,n=new Uint16Array(6)){let r=e.indexOf(`/`,t),i=r===-1?e.length:r,a=e.substring(t,i);if(!a||!a.includes(`$`))return n[0]=0,n[1]=t,n[2]=t,n[3]=i,n[4]=i,n[5]=i,n;if(a===`$`){let r=e.length;return n[0]=2,n[1]=t,n[2]=t,n[3]=r,n[4]=r,n[5]=r,n}if(a.charCodeAt(0)===36)return n[0]=1,n[1]=t,n[2]=t+1,n[3]=i,n[4]=i,n[5]=i,n;let o=de(a);if(o){let[r,s]=o,c=a.charCodeAt(r+1);if(c===45){if(r+2!e.parse&&e.caseSensitive===f&&e.prefix===p&&e.suffix===m);if(h)o=h;else{let e=F(1,n.fullPath??n.from,f,p,m);o=e,e.depth=a,e.parent=i,i.dynamic??=[],i.dynamic.push(e)}break}case 3:{let t=r.substring(u,e[1]),s=r.substring(e[4],d),f=c&&!!(t||s),p=t?f?t:t.toLowerCase():void 0,m=s?f?s:s.toLowerCase():void 0,h=!l&&i.optional?.find(e=>!e.parse&&e.caseSensitive===f&&e.prefix===p&&e.suffix===m);if(h)o=h;else{let e=F(3,n.fullPath??n.from,f,p,m);o=e,e.parent=i,e.depth=a,i.optional??=[],i.optional.push(e)}break}case 2:{let t=r.substring(u,e[1]),s=r.substring(e[4],d),l=c&&!!(t||s),f=t?l?t:t.toLowerCase():void 0,p=s?l?s:s.toLowerCase():void 0,m=F(2,n.fullPath??n.from,l,f,p);o=m,m.parent=i,m.depth=a,i.wildcard??=[],i.wildcard.push(m)}}i=o}if(l&&n.children&&!n.isRoot&&n.id&&n.id.charCodeAt(n.id.lastIndexOf(`/`)+1)===95){let e=P(n.fullPath??n.from);e.kind=k,e.parent=i,a++,e.depth=a,i.pathless??=[],i.pathless.push(e),i=e}let u=(n.path||!n.children)&&!n.isRoot;if(u&&r.endsWith(`/`)){let e=P(n.fullPath??n.from);e.kind=O,e.parent=i,a++,e.depth=a,i.index=e,i=e}i.parse=l??null,i.priority=n.options?.params?.priority??0,u&&!i.route&&(i.route=n,i.fullPath=n.fullPath??n.from)}if(n.children)for(let r of n.children)j(e,t,r,s,i,a,o)}function M(e,t){if(e.parse&&!t.parse)return-1;if(!e.parse&&t.parse)return 1;if(e.parse&&t.parse&&(e.priority||t.priority))return t.priority-e.priority;if(e.prefix&&t.prefix&&e.prefix!==t.prefix){if(e.prefix.startsWith(t.prefix))return-1;if(t.prefix.startsWith(e.prefix))return 1}if(e.suffix&&t.suffix&&e.suffix!==t.suffix){if(e.suffix.endsWith(t.suffix))return-1;if(t.suffix.endsWith(e.suffix))return 1}return e.prefix&&!t.prefix?-1:!e.prefix&&t.prefix?1:e.suffix&&!t.suffix?-1:!e.suffix&&t.suffix?1:e.caseSensitive&&!t.caseSensitive?-1:!e.caseSensitive&&t.caseSensitive?1:0}function N(e){if(e.pathless)for(let t of e.pathless)N(t);if(e.static)for(let t of e.static.values())N(t);if(e.staticInsensitive)for(let t of e.staticInsensitive.values())N(t);if(e.dynamic?.length){e.dynamic.sort(M);for(let t of e.dynamic)N(t)}if(e.optional?.length){e.optional.sort(M);for(let t of e.optional)N(t)}if(e.wildcard?.length){e.wildcard.sort(M);for(let t of e.wildcard)N(t)}}function P(e){return{kind:0,depth:0,pathless:null,index:null,static:null,staticInsensitive:null,dynamic:null,optional:null,wildcard:null,route:null,fullPath:e,parent:null,parse:null,priority:0}}function F(e,t,n,r,i){return{kind:e,depth:0,pathless:null,index:null,static:null,staticInsensitive:null,dynamic:null,optional:null,wildcard:null,route:null,fullPath:t,parent:null,parse:null,priority:0,caseSensitive:n,prefix:r,suffix:i}}function fe(e,t){let n=P(`/`),r=new Uint16Array(6);for(let t of e)j(!1,r,t,1,n,0);N(n),t.masksTree=n,t.flatCache=D(1e3)}function pe(e,t){e||=`/`;let n=t.flatCache.get(e);if(n)return n;let r=I(e,t.masksTree);return t.flatCache.set(e,r),r}function me(e,t,n,r,i){e||=`/`,r||=`/`;let a=t?`case\0${e}`:e,o=i.singleCache.get(a);return o||(o=P(`/`),j(t,new Uint16Array(6),{from:e},1,o,0),i.singleCache.set(a,o)),I(r,o,n)}function he(e,t,n=!1){let r=n?e:`nofuzz\0${e}`,i=t.matchCache.get(r);if(i!==void 0)return i;e||=`/`;let a;try{a=I(e,t.segmentTree,n)}catch(e){if(e instanceof URIError)a=null;else throw e}return a&&(a.branch=R(a.route)),t.matchCache.set(r,a),a}function ge(e){return e===`/`?e:e.replace(/\/{1,}$/,``)}function _e(e,t=!1,n){let r=P(e.fullPath),i=new Uint16Array(6),a={},o={},s=0;return j(t,i,e,1,r,0,e=>{if(n?.(e,s),e.id in a&&E(),a[e.id]=e,s!==0&&e.path){let t=ge(e.fullPath);(!o[t]||e.fullPath.endsWith(`/`))&&(o[t]=e)}s++}),N(r),{processedTree:{segmentTree:r,singleCache:D(1e3),matchCache:D(1e3),flatCache:null,masksTree:null},routesById:a,routesByPath:o}}function I(e,t,n=!1){let r=e.split(`/`),i=ye(e,r,t,n);if(!i)return null;let[a]=L(e,r,i);return{route:i.node.route,rawParams:a}}function L(e,t,n){let r=ve(n.node),i=null,a=Object.create(null),o=n.extract?.part??0,s=n.extract?.node??0,c=n.extract?.path??0,l=n.extract?.segment??0;for(;s=0;e--){let n=i.wildcard[e],{prefix:r,suffix:a}=n;if(!(r&&(v||!(n.caseSensitive?y:b??=y.toLowerCase()).startsWith(r)))){if(a){if(v)continue;let e=t.slice(u).join(`/`).slice(-a.length);if((n.caseSensitive?e:e.toLowerCase())!==a)continue}s.push({node:n,index:o,skipped:d,depth:f+1,statics:p,dynamics:m,optionals:h,extract:g,rawParams:_})}}if(i.optional){let e=d|1<=0;n--){let r=i.optional[n];s.push({node:r,index:u,skipped:e,depth:t,statics:p,dynamics:m,optionals:h,extract:g,rawParams:_})}if(!v)for(let e=i.optional.length-1;e>=0;e--){let n=i.optional[e],{prefix:r,suffix:a}=n;if(r||a){let e=n.caseSensitive?y:b??=y.toLowerCase();if(r&&!e.startsWith(r)||a&&!e.endsWith(a))continue}s.push({node:n,index:u+1,skipped:d,depth:t,statics:p,dynamics:m,optionals:h+z(o,u),extract:g,rawParams:_})}}if(!v&&i.dynamic&&y)for(let e=i.dynamic.length-1;e>=0;e--){let t=i.dynamic[e],{prefix:n,suffix:r}=t;if(n||r){let e=t.caseSensitive?y:b??=y.toLowerCase();if(n&&!e.startsWith(n)||r&&!e.endsWith(r))continue}s.push({node:t,index:u+1,skipped:d,depth:f+1,statics:p,dynamics:m+z(o,u),optionals:h,extract:g,rawParams:_})}if(!v&&i.staticInsensitive){let e=i.staticInsensitive.get(b??=y.toLowerCase());e&&s.push({node:e,index:u+1,skipped:d,depth:f+1,statics:p+z(o,u),dynamics:m,optionals:h,extract:g,rawParams:_})}if(!v&&i.static){let e=i.static.get(y);e&&s.push({node:e,index:u+1,skipped:d,depth:f+1,statics:p+z(o,u),dynamics:m,optionals:h,extract:g,rawParams:_})}if(i.pathless){let e=f+1;for(let t=i.pathless.length-1;t>=0;t--){let n=i.pathless[t];s.push({node:n,index:u,skipped:d,depth:e,statics:p,dynamics:m,optionals:h,extract:g,rawParams:_})}}}if(l)return l;if(r&&c){let n=c.index;for(let e=0;ee.statics||t.statics===e.statics&&(t.dynamics>e.dynamics||t.dynamics===e.dynamics&&(t.optionals>e.optionals||t.optionals===e.optionals&&((t.node.kind===O)>(e.node.kind===O)||t.node.kind===O==(e.node.kind===O)&&t.depth>e.depth))):!0}function be(e){return U(e.filter(e=>e!==void 0).join(`/`))}function U(e){return e.replace(/\/{2,}/g,`/`)}function W(e){return e===`/`?e:e.replace(/^\/{1,}/,``)}function G(e){let t=e.length;return t>1&&e[t-1]===`/`?e.replace(/\/{1,}$/,``):e}function xe(e){return G(W(e))}function K(e,t){return e?.endsWith(`/`)&&e!==`/`&&e!==`${t}/`?e.slice(0,-1):e}function Se(e,t,n){return K(e,n)===K(t,n)}function Ce({base:e,to:t,trailingSlash:n=`never`,cache:r}){let i=t.startsWith(`/`),a=!i&&t===`.`,o;if(r){o=i?t:a?e:e+`\0`+t;let n=r.get(o);if(n)return n}let s;if(a)s=e.split(`/`);else if(i)s=t.split(`/`);else{for(s=e.split(`/`);s.length>1&&u(s)===``;)s.pop();let n=t.split(`/`);for(let e=0,t=n.length;e1&&(u(s)===``?n===`never`&&s.pop():n===`always`&&s.push(``));let c=U(s.join(`/`))||`/`;return o&&r&&r.set(o,c),c}function we(e){let t=new Map(e.map(e=>[encodeURIComponent(e),e])),n=Array.from(t.keys()).map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)).join(`|`),r=new RegExp(n,`g`);return e=>e.replace(r,e=>t.get(e)??e)}function q(e,t,n){let r=t[e];return typeof r==`string`?e===`_splat`?/^[a-zA-Z0-9\-._~!/]*$/.test(r)?r:r.split(`/`).map(e=>J(e,n)).join(`/`):J(r,n):r}function Te({path:e,params:t,decoder:n,...r}){let i=!1,a=Object.create(null);if(!e||e===`/`)return{interpolatedPath:`/`,usedParams:a,isMissingParams:i};if(!e.includes(`$`))return{interpolatedPath:e,usedParams:a,isMissingParams:i};let o=e.length,s=0,c,l=``;for(;s!0,()=>!1)}function De(){return()=>{}}var Z=i.createContext(null);function Oe(e){return i.useContext(Z)}var ke=e((e=>{var t=r();function n(e,t){return e===t&&(e!==0||1/e==1/t)||e!==e&&t!==t}var i=typeof Object.is==`function`?Object.is:n,a=t.useState,o=t.useEffect,s=t.useLayoutEffect,c=t.useDebugValue;function l(e,t){var n=t(),r=a({inst:{value:n,getSnapshot:t}}),i=r[0].inst,l=r[1];return s(function(){i.value=n,i.getSnapshot=t,u(i)&&l({inst:i})},[e,n,t]),o(function(){return u(i)&&l({inst:i}),e(function(){u(i)&&l({inst:i})})},[e]),c(n),n}function u(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!i(e,n)}catch{return!0}}function d(e,t){return t()}var f=typeof window>`u`||window.document===void 0||window.document.createElement===void 0?d:l;e.useSyncExternalStore=t.useSyncExternalStore===void 0?f:t.useSyncExternalStore})),Q=e(((e,t)=>{t.exports=ke()})),Ae=e((e=>{var t=r(),n=Q();function i(e,t){return e===t&&(e!==0||1/e==1/t)||e!==e&&t!==t}var a=typeof Object.is==`function`?Object.is:i,o=n.useSyncExternalStore,s=t.useRef,c=t.useEffect,l=t.useMemo,u=t.useDebugValue;e.useSyncExternalStoreWithSelector=function(e,t,n,r,i){var d=s(null);if(d.current===null){var f={hasValue:!1,value:null};d.current=f}else f=d.current;d=l(function(){function e(e){if(!o){if(o=!0,s=e,e=r(e),i!==void 0&&f.hasValue){var t=f.value;if(i(t,e))return c=t}return c=e}if(t=c,a(s,e))return t;var n=r(e);return i!==void 0&&i(t,n)?(s=e,t):(s=e,c=n)}var o=!1,s,c,l=n===void 0?null:n;return[function(){return e(t())},l===null?void 0:function(){return e(l())}]},[t,n,r,i]);var p=o(e,d[0],d[1]);return c(function(){f.hasValue=!0,f.value=p},[p]),u(p),p}})),$=e(((e,t)=>{t.exports=Ae()})),je=$();function Me(e,t){return e===t}function Ne(e,t,n=Me){let r=(0,i.useCallback)(t=>{if(!e)return()=>{};let{unsubscribe:n}=e.subscribe(t);return n},[e]),a=(0,i.useCallback)(()=>e?.get(),[e]);return(0,je.useSyncExternalStoreWithSelector)(r,a,a,t,n)}export{C as A,v as B,_e as C,ue as D,T as E,ie as F,s as G,l as H,te as I,ne as L,se as M,f as N,ee as O,h as P,u as R,fe as S,E as T,c as U,a as V,o as W,G as _,Z as a,he as b,U as c,Te as d,be as f,W as g,xe as h,Oe as i,le as j,ce as k,we as l,Ce as m,$ as n,Ee as o,K as p,Q as r,X as s,Ne as t,Se as u,R as v,D as w,me as x,pe as y,_ as z}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/assets/zod.js b/spinifex/services/spinifexui/frontend/dist/assets/zod.js index 057871ee1..4130f0338 100644 --- a/spinifex/services/spinifexui/frontend/dist/assets/zod.js +++ b/spinifex/services/spinifexui/frontend/dist/assets/zod.js @@ -1 +1 @@ -import{Gr as e,Kr as t,Uo as n,Wr as r,cs as i}from"./index.js";var a=i(n(),1),o=e=>e.type===`checkbox`,s=e=>e instanceof Date,c=e=>e==null,l=e=>typeof e==`object`,u=e=>!c(e)&&!Array.isArray(e)&&l(e)&&!s(e),d=e=>u(e)&&e.target?o(e.target)?e.target.checked:e.target.value:e,f=(e,t)=>{let n=t.split(`.`),r=[],i=n[0];for(let t=1;t{let t=e.constructor&&e.constructor.prototype;return u(t)&&t.hasOwnProperty(`isPrototypeOf`)},m=typeof window<`u`&&window.HTMLElement!==void 0&&typeof document<`u`;function h(e){if(e instanceof Date)return new Date(e);let t=typeof FileList<`u`&&e instanceof FileList;if(m&&(e instanceof Blob||t))return e;let n=Array.isArray(e);if(!n&&!(u(e)&&p(e)))return e;let r=n?[]:Object.create(Object.getPrototypeOf(e));for(let t in e)Object.prototype.hasOwnProperty.call(e,t)&&(r[t]=h(e[t]));return r}var g=e=>/^\w*$/.test(e),_=e=>e===void 0,v=e=>Array.isArray(e)?e.filter(Boolean):[],y=e=>v(e.replace(/["|']|\]/g,``).split(/\.|\[/)),b=(e,t,n)=>{if(!t||!u(e))return n;let r=(g(t)?[t]:y(t)).reduce((e,t)=>c(e)?void 0:e[t],e);return _(r)||r===e?_(e[t])?n:e[t]:r},x=e=>typeof e==`boolean`,S=e=>typeof e==`function`,C=(e,t,n)=>{let r=-1,i=g(t)?[t]:y(t),a=i.length,o=a-1;for(;++ra.useContext(k),j=(e,t,n,r=!0)=>{let i={};for(let a in e)Object.defineProperty(i,a,{get:()=>{let i=a;return t._proxyFormState[i]!==T.all&&(t._proxyFormState[i]=!r||T.all),n&&(n[i]=!0),e[i]}});return i},M=typeof window<`u`?a.useLayoutEffect:a.useEffect;function N(e){let t=A(),{control:n=t,disabled:r,name:i,exact:o}=e||{},[s,c]=a.useState(()=>({...n._formState,defaultValues:n._defaultValues})),l=a.useRef({isDirty:!1,isLoading:!1,dirtyFields:!1,touchedFields:!1,validatingFields:!1,isValidating:!1,isValid:!1,errors:!1});return M(()=>n._subscribe({name:i,formState:l.current,exact:o,callback:e=>{!r&&c({...n._formState,...e,defaultValues:n._defaultValues})}}),[i,r,o]),a.useEffect(()=>{l.current.isValid&&n._setValid(!0)},[n]),a.useMemo(()=>j(s,n,l.current,!1),[s,n])}var P=e=>typeof e==`string`,F=(e,t,n,r,i)=>P(e)?(r&&t.watch.add(e),b(n,e,i)):Array.isArray(e)?e.map(e=>(r&&t.watch.add(e),b(n,e))):(r&&(t.watchAll=!0),n),I=e=>c(e)||!l(e);function L(e,t,n=new WeakSet){if(e===t)return!0;if(I(e)||I(t))return Object.is(e,t);if(s(e)&&s(t))return Object.is(e.getTime(),t.getTime());let r=Object.keys(e),i=Object.keys(t);if(r.length!==i.length)return!1;if(n.has(e)||n.has(t))return!0;n.add(e),n.add(t);for(let i of r){let r=e[i];if(!(i in t))return!1;if(i!==`ref`){let e=t[i];if(s(r)&&s(e)||(u(r)||Array.isArray(r))&&(u(e)||Array.isArray(e))?!L(r,e,n):!Object.is(r,e))return!1}}return!0}function ee(e){let t=A(),{control:n=t,name:r,defaultValue:i,disabled:o,exact:s,compute:c}=e||{},l=a.useRef(i),u=a.useRef(c),d=a.useRef(void 0),f=a.useRef(n),p=a.useRef(r);u.current=c;let[m,h]=a.useState(()=>{let e=n._getWatch(r,l.current);return u.current?u.current(e):e}),g=a.useCallback(e=>{let t=F(r,n._names,e||n._formValues,!1,l.current);return u.current?u.current(t):t},[n._formValues,n._names,r]),_=a.useCallback(e=>{if(!o){let t=F(r,n._names,e||n._formValues,!1,l.current);if(u.current){let e=u.current(t);L(e,d.current)||(h(e),d.current=e)}else h(t)}},[n._formValues,n._names,o,r]);M(()=>((f.current!==n||!L(p.current,r))&&(f.current=n,p.current=r,_()),n._subscribe({name:r,formState:{values:!0},exact:s,callback:e=>{_(e.values)}})),[n,s,r,_]),a.useEffect(()=>n._removeUnmounted());let v=f.current!==n,y=p.current,b=a.useMemo(()=>{if(o)return null;let e=!v&&!L(y,r);return v||e?g():null},[o,v,r,y,g]);return b===null?m:b}function te(e){let t=A(),{name:n,disabled:r,control:i=t,shouldUnregister:o,defaultValue:s,exact:c=!0}=e,l=!!f(i._names.array,n).length,u=ee({control:i,name:n,defaultValue:a.useMemo(()=>b(i._formValues,n,b(i._defaultValues,n,s)),[i,n,s]),exact:c}),p=N({control:i,name:n,exact:c}),m=a.useRef(e),g=a.useRef(i.register(n,{...e.rules,value:u,...x(e.disabled)?{disabled:e.disabled}:{}}));m.current=e;let v=a.useMemo(()=>Object.defineProperties({},{invalid:{enumerable:!0,get:()=>!!b(p.errors,n)},isDirty:{enumerable:!0,get:()=>!!b(p.dirtyFields,n)},isTouched:{enumerable:!0,get:()=>!!b(p.touchedFields,n)},isValidating:{enumerable:!0,get:()=>!!b(p.validatingFields,n)},error:{enumerable:!0,get:()=>b(p.errors,n)}}),[p,n]),y=a.useCallback(e=>g.current.onChange({target:{value:d(e),name:n},type:w.CHANGE}),[n]),T=a.useCallback(()=>g.current.onBlur({target:{value:b(i._formValues,n),name:n},type:w.BLUR}),[n,i._formValues]),E=a.useCallback(e=>{let t=b(i._fields,n);t&&t._f&&e&&(t._f.ref={focus:()=>S(e.focus)&&e.focus(),select:()=>S(e.select)&&e.select(),setCustomValidity:t=>S(e.setCustomValidity)&&e.setCustomValidity(t),reportValidity:()=>S(e.reportValidity)&&e.reportValidity()})},[i._fields,n]),D=a.useMemo(()=>({name:n,value:u,...x(r)||p.disabled?{disabled:p.disabled||r}:{},onChange:y,onBlur:T,ref:E}),[n,r,p.disabled,y,T,E,u]);return a.useEffect(()=>{let e=i._options.shouldUnregister||o;i.register(n,{...m.current.rules,...x(m.current.disabled)?{disabled:m.current.disabled}:{}});let t=(e,t)=>{let n=b(i._fields,e);n&&n._f&&(n._f.mount=t)};if(t(n,!0),e){let e=h(b(i._defaultValues,n,b(i._options.defaultValues,n,m.current.defaultValue)));C(i._defaultValues,n,e),_(b(i._formValues,n))&&C(i._formValues,n,e)}return!l&&i.register(n),()=>{(l?e&&!i._state.action:e)?i.unregister(n):t(n,!1)}},[n,i,l,o]),a.useEffect(()=>{i._setDisabledField({disabled:r,name:n})},[r,n,i]),a.useMemo(()=>({field:D,formState:p,fieldState:v}),[D,p,v])}var R=e=>e.render(te(e)),z=a.createContext(null);z.displayName=`HookFormContext`;var ne=(e,t,n,r,i)=>t?{...n[e],types:{...n[e]&&n[e].types?n[e].types:{},[r]:i||!0}}:{},B=e=>Array.isArray(e)?e:[e],re=()=>{let e=[];return{get observers(){return e},next:t=>{for(let n of e)n.next&&n.next(t)},subscribe:t=>(e.push(t),{unsubscribe:()=>{e=e.filter(e=>e!==t)}}),unsubscribe:()=>{e=[]}}};function ie(e,t){let n={};for(let r in e)if(e.hasOwnProperty(r)){let i=e[r],a=t[r];if(i&&u(i)&&a){let e=ie(i,a);u(e)&&(n[r]=e)}else e[r]&&(n[r]=a)}return n}var V=e=>u(e)&&!Object.keys(e).length,ae=e=>e.type===`file`,oe=e=>{if(!m)return!1;let t=e?e.ownerDocument:0;return e instanceof(t&&t.defaultView?t.defaultView.HTMLElement:HTMLElement)},se=e=>e.type===`select-multiple`,ce=e=>e.type===`radio`,le=e=>ce(e)||o(e),ue=e=>oe(e)&&e.isConnected;function de(e,t){let n=t.slice(0,-1).length,r=0;for(;r{for(let t in e)if(S(e[t]))return!0;return!1};function me(e){return Array.isArray(e)||u(e)&&!pe(e)}function he(e,t={}){for(let n in e){let r=e[n];me(r)?(t[n]=Array.isArray(r)?[]:{},he(r,t[n])):_(r)||(t[n]=!0)}return t}function ge(e){if(e!==!1){if(e===!0)return!0;if(Array.isArray(e)){let t=e.map(e=>ge(e));return t.some(e=>e!==void 0)?t:void 0}if(u(e)){let t={};for(let n in e){let r=ge(e[n]);_(r)||(t[n]=r)}return Object.keys(t).length?t:void 0}}}function U(e,t,n){n||=he(t);for(let r in e){let i=e[r];if(me(i))_(t)||I(n[r])?n[r]=he(i,Array.isArray(i)?[]:{}):U(i,c(t)?{}:t[r],n[r]);else{let e=t[r];n[r]=!L(i,e)}}return ge(n)||{}}var W={value:!1,isValid:!1},_e={value:!0,isValid:!0},ve=e=>{if(Array.isArray(e)){if(e.length>1){let t=e.filter(e=>e&&e.checked&&!e.disabled).map(e=>e.value);return{value:t,isValid:!!t.length}}return e[0].checked&&!e[0].disabled?e[0].attributes&&!_(e[0].attributes.value)?_(e[0].value)||e[0].value===``?_e:{value:e[0].value,isValid:!0}:_e:W}return W},ye=(e,{valueAsNumber:t,valueAsDate:n,setValueAs:r})=>_(e)?e:t?e===``?NaN:e&&+e:n&&P(e)?new Date(e):r?r(e):e,G={isValid:!1,value:null},be=e=>Array.isArray(e)?e.reduce((e,t)=>t&&t.checked&&!t.disabled?{isValid:!0,value:t.value}:e,G):G;function xe(e){let t=e.ref;return ae(t)?t.files:ce(t)?be(e.refs).value:se(t)?[...t.selectedOptions].map(({value:e})=>e):o(t)?ve(e.refs).value:ye(_(t.value)?e.ref.value:t.value,e)}var Se=(e,t,n,r)=>{let i={};for(let n of e){let e=b(t,n);e&&C(i,n,e._f)}return{criteriaMode:n,names:[...e],fields:i,shouldUseNativeValidation:r}},K=e=>e instanceof RegExp,Ce=e=>_(e)?e:K(e)?e.source:u(e)?K(e.value)?e.value.source:e.value:e,q=e=>({isOnSubmit:!e||e===T.onSubmit,isOnBlur:e===T.onBlur,isOnChange:e===T.onChange,isOnAll:e===T.all,isOnTouch:e===T.onTouched}),we=`AsyncFunction`,Te=e=>!!e&&!!e.validate&&!!(S(e.validate)&&e.validate.constructor.name===we||u(e.validate)&&Object.values(e.validate).find(e=>e.constructor.name===we)),Ee=e=>e.mount&&(e.required||e.min||e.max||e.maxLength||e.minLength||e.pattern||e.validate),De=(e,t,n)=>!n&&(t.watchAll||t.watch.has(e)||[...t.watch].some(t=>e.startsWith(t)&&/^\.\w+/.test(e.slice(t.length)))),J=(e,t,n,r)=>{for(let i of n||Object.keys(e)){let n=b(e,i);if(n){let{_f:e,...a}=n;if(e){if(e.refs&&e.refs[0]&&t(e.refs[0],i)&&!r||e.ref&&t(e.ref,e.name)&&!r)return!0;if(J(a,t))break}else if(u(a)&&J(a,t))break}}};function Oe(e,t,n){let r=b(e,n);if(r||g(n))return{error:r,name:n};let i=n.split(`.`);for(;i.length;){let r=i.join(`.`),a=b(t,r),o=b(e,r);if(a&&!Array.isArray(a)&&n!==r)return{name:n};if(o&&o.type)return{name:r,error:o};if(o&&o.root&&o.root.type)return{name:`${r}.root`,error:o.root};i.pop()}return{name:n}}var ke=(e,t,n,r)=>{n(e);let{name:i,...a}=e;return V(a)||r&&Object.keys(a).length>=Object.keys(t).length||Object.keys(a).find(e=>t[e]===(!r||T.all))},Ae=(e,t,n)=>!e||!t||e===t||B(e).some(e=>e&&(n?e===t:e.startsWith(t)||t.startsWith(e))),je=(e,t,n,r,i)=>i.isOnAll?!1:!n&&i.isOnTouch?!(t||e):(n?r.isOnBlur:i.isOnBlur)?!e:(n?r.isOnChange:i.isOnChange)?e:!0,Me=(e,t)=>!v(b(e,t)).length&&H(e,t),Ne=(e,t,n)=>{let r=B(b(e,n));return C(r,O,t[n]),C(e,n,r),e};function Pe(e,t,n=`validate`){if(P(e)||Array.isArray(e)&&e.every(P)||x(e)&&!e)return{type:n,message:P(e)?e:``,ref:t}}var Y=e=>u(e)&&!K(e)?e:{value:e,message:``},Fe=async(e,t,n,r,i,a)=>{let{ref:s,refs:l,required:d,maxLength:f,minLength:p,min:m,max:h,pattern:g,validate:v,name:y,valueAsNumber:C,mount:w}=e._f,T=b(n,y);if(!w||t.has(y))return{};let D=l?l[0]:s,O=e=>{i&&D.reportValidity&&(D.setCustomValidity(x(e)?``:e||``),D.reportValidity())},k={},A=ce(s),j=o(s),M=A||j,N=(C||ae(s))&&_(s.value)&&_(T)||oe(s)&&s.value===``||T===``||Array.isArray(T)&&!T.length||C&&typeof T==`number`&&isNaN(T),F=ne.bind(null,y,r,k),I=(e,t,n,r=E.maxLength,i=E.minLength)=>{let a=e?t:n;k[y]={type:e?r:i,message:a,ref:s,...F(e?r:i,a)}};if(a?!Array.isArray(T)||!T.length:d&&(!M&&(N||c(T))||x(T)&&!T||j&&!ve(l).isValid||A&&!be(l).isValid)){let{value:e,message:t}=P(d)?{value:!!d,message:d}:Y(d);if(e&&(k[y]={type:E.required,message:t,ref:D,...F(E.required,t)},!r))return O(t),k}if(!N&&(!c(m)||!c(h))){let e,t,n=Y(h),i=Y(m);if(!c(T)&&!isNaN(T)){let r=s.valueAsNumber||T&&+T;c(n.value)||(e=r>n.value),c(i.value)||(t=rnew Date(new Date().toDateString()+` `+e),o=s.type==`time`,c=s.type==`week`;P(n.value)&&T&&(e=o?a(T)>a(n.value):c?T>n.value:r>new Date(n.value)),P(i.value)&&T&&(t=o?a(T)+e.value,i=!c(t.value)&&T.length<+t.value;if((n||i)&&(I(n,e.message,t.message),!r))return O(k[y].message),k}if(g&&!N&&P(T)){let{value:e,message:t}=Y(g);if(K(e)&&!T.match(e)&&(k[y]={type:E.pattern,message:t,ref:s,...F(E.pattern,t)},!r))return O(t),k}if(v){if(S(v)){let e=Pe(await v(T,n),D);if(e&&(k[y]={...e,...F(E.validate,e.message)},!r))return O(e.message),k}else if(u(v)){let e={};for(let t in v){if(!V(e)&&!r)break;let i=Pe(await v[t](T,n),D,t);i&&(e={...i,...F(t,i.message)},O(i.message),r&&(k[y]=e))}if(!V(e)&&(k[y]={ref:D,...e},!r))return k}}return O(!0),k},Ie={mode:T.onSubmit,reValidateMode:T.onChange,shouldFocusError:!0},Le={submitCount:0,isDirty:!1,isReady:!1,isValidating:!1,isSubmitted:!1,isSubmitting:!1,isSubmitSuccessful:!1,isValid:!1,touchedFields:{},dirtyFields:{},validatingFields:{}};function Re(e={}){let t={...Ie,...e},n={...h(Le),isLoading:S(t.defaultValues),errors:t.errors||{},disabled:t.disabled||!1},r={},i=(u(t.defaultValues)||u(t.values))&&h(t.defaultValues||t.values)||{},a=t.shouldUnregister?{}:h(i),l={action:!1,mount:!1,watch:!1,keepIsValid:!1},p={mount:new Set,disabled:new Set,unMount:new Set,array:new Set,watch:new Set,registerName:new Set},k,A=0,j={isDirty:!1,dirtyFields:!1,validatingFields:!1,touchedFields:!1,isValidating:!1,isValid:!1,errors:!1},M={...j},N={...M},I={array:re(),state:re()},ee=t.criteriaMode===T.all,te=e=>t=>{clearTimeout(A),A=setTimeout(e,t)},R=async e=>{if(!l.keepIsValid&&!t.disabled&&(M.isValid||N.isValid||e)){let e;t.resolver?(e=V((await W()).errors),z()):e=await G({fields:r,onlyCheckValid:!0,eventType:w.VALID}),e!==n.isValid&&I.state.next({isValid:e})}},z=(e,r)=>{!t.disabled&&(M.isValidating||M.validatingFields||N.isValidating||N.validatingFields)&&((e||Array.from(p.mount)).forEach(e=>{e&&(r?C(n.validatingFields,e,r):H(n.validatingFields,e))}),I.state.next({validatingFields:n.validatingFields,isValidating:!V(n.validatingFields)}))},ne=()=>{n.dirtyFields=U(i,a)},ce=(e,i=[],o,s,c=!0,u=!0)=>{if(s&&o&&!t.disabled){if(l.action=!0,u&&Array.isArray(b(r,e))){let t=o(b(r,e),s.argA,s.argB);c&&C(r,e,t)}if(u&&Array.isArray(b(n.errors,e))){let t=o(b(n.errors,e),s.argA,s.argB);c&&C(n.errors,e,t),Me(n.errors,e)}if((M.touchedFields||N.touchedFields)&&u&&Array.isArray(b(n.touchedFields,e))){let t=o(b(n.touchedFields,e),s.argA,s.argB);c&&C(n.touchedFields,e,t)}(M.dirtyFields||N.dirtyFields)&&ne(),I.state.next({name:e,isDirty:K(e,i),dirtyFields:n.dirtyFields,errors:n.errors,isValid:n.isValid})}else C(a,e,i)},de=(e,t)=>{C(n.errors,e,t),I.state.next({errors:n.errors})},fe=e=>{n.errors=e,I.state.next({errors:n.errors,isValid:!1})},pe=e=>{let t=g(e)?[e]:y(e),n=a,r=i;for(let e=0;e{let c=b(r,e);if(c){if(pe(e))return;let r=_(b(a,e)),u=b(a,e,_(o)?b(i,e):o);_(u)||s&&s.defaultChecked||t?C(a,e,t?u:xe(c._f)):Y(e,u),l.mount&&!l.action&&(R(),r&&n.isDirty&&(M.isDirty||N.isDirty)&&(K()||(n.isDirty=!1,I.state.next({...n}))))}},he=(e,r,o,s,c)=>{let l=!1,u=!1,d={name:e};if(!t.disabled){if(!o||s){(M.isDirty||N.isDirty)&&(u=n.isDirty,n.isDirty=d.isDirty=K(),l=u!==d.isDirty);let t=L(b(i,e),r);u=!!b(n.dirtyFields,e),t===n.isDirty?t?H(n.dirtyFields,e):C(n.dirtyFields,e,!0):n.dirtyFields=U(i,a),d.dirtyFields=n.dirtyFields,l||=(M.dirtyFields||N.dirtyFields)&&u!==!t}if(o){let t=b(n.touchedFields,e);t||(C(n.touchedFields,e,o),d.touchedFields=n.touchedFields,l||=(M.touchedFields||N.touchedFields)&&t!==o)}l&&c&&I.state.next(d)}return l?d:{}},ge=(e,r,i,a)=>{let o=b(n.errors,e),s=(M.isValid||N.isValid)&&x(r)&&n.isValid!==r;if(t.delayError&&i?(k=te(()=>de(e,i)),k(t.delayError)):(clearTimeout(A),k=null,i?C(n.errors,e,i):H(n.errors,e)),(i?!L(o,i):o)||!V(a)||s){let t={...a,...s&&x(r)?{isValid:r}:{},errors:n.errors,name:e};n={...n,...t},I.state.next(t)}},W=async e=>(z(e,!0),await t.resolver(a,t.context,Se(e||p.mount,r,t.criteriaMode,t.shouldUseNativeValidation))),_e=async e=>{let{errors:t}=await W(e);if(z(e),e)for(let r of e){let e=b(t,r);e?p.array.has(r)&&u(e)?Ne(n.errors,{[r]:e},r):C(n.errors,r,e):H(n.errors,r)}else n.errors=t;return t},ve=async({name:t,eventType:r})=>{if(e.validate){let i=await e.validate({formValues:a,formState:n,name:t,eventType:r});if(u(i))for(let e in i){let t=i[e];t&&Q(`${D}.${e}`,{message:P(t.message)?t.message:``,type:t.type||E.validate})}else P(i)||!i?Q(D,{message:i||``,type:E.validate}):We(D);return i}return!0},G=async({fields:r,onlyCheckValid:i,name:o,eventType:s,context:c={valid:!0,runRootValidation:!1}})=>{if(e.validate&&(c.runRootValidation=!0,!await ve({name:o,eventType:s})&&(c.valid=!1,i)))return c.valid;for(let o in r){let l=r[o];if(l){let{_f:r,...u}=l;if(r){let o=p.array.has(r.name),s=l._f&&Te(l._f),u=M.validatingFields||M.isValidating||N.validatingFields||N.isValidating;s&&u&&z([r.name],!0);let d=await Fe(l,p.disabled,a,ee,t.shouldUseNativeValidation&&!i,o);if(s&&u&&z([r.name]),d[r.name]&&(c.valid=!1,i)||(!i&&(b(d,r.name)?o?Ne(n.errors,d,r.name):C(n.errors,r.name,d[r.name]):H(n.errors,r.name)),e.shouldUseNativeValidation&&d[r.name]))break}!V(u)&&await G({context:c,onlyCheckValid:i,fields:u,name:o,eventType:s})}}return c.valid},be=()=>{for(let e of p.unMount){let t=b(r,e);t&&(t._f.refs?t._f.refs.every(e=>!ue(e)):!ue(t._f.ref))&&Je(e)}p.unMount=new Set},K=(e,n)=>!t.disabled&&(e&&n&&C(a,e,n),!L(He(),i)),we=(e,t,n)=>F(e,p,{...l.mount?a:_(t)?i:P(e)?{[e]:t}:t},n,t),Pe=e=>v(b(l.mount?a:i,e,t.shouldUnregister?b(i,e,[]):[])),Y=(e,t,n={})=>{let i=b(r,e),s=t;if(i){let n=i._f;n&&(!n.disabled&&C(a,e,ye(t,n)),s=oe(n.ref)&&c(t)?``:t,se(n.ref)?[...n.ref.options].forEach(e=>e.selected=s.includes(e.value)):n.refs?o(n.ref)?n.refs.forEach(e=>{(!e.defaultChecked||!e.disabled)&&(Array.isArray(s)?e.checked=!!s.find(t=>t===e.value):e.checked=s===e.value||!!s)}):n.refs.forEach(e=>e.checked=e.value===s):ae(n.ref)?n.ref.value=``:(n.ref.value=s,n.ref.type||I.state.next({name:e,values:h(a)})))}(n.shouldDirty||n.shouldTouch)&&he(e,s,n.shouldTouch,n.shouldDirty,!0),n.shouldValidate&&Z(e)},Re=(e,t,n)=>{for(let i in t){if(!t.hasOwnProperty(i))return;let a=t[i],o=e+`.`+i,c=b(r,o);(p.array.has(e)||u(a)||c&&!c._f)&&!s(a)?Re(o,a,n):Y(o,a,n)}},X=(e,t,i={})=>{let o=b(r,e),s=p.array.has(e),u=h(t),d=L(b(a,e),u);if(d||C(a,e,u),s)I.array.next({name:e,values:h(a)}),(M.isDirty||M.dirtyFields||N.isDirty||N.dirtyFields)&&i.shouldDirty&&(ne(),I.state.next({name:e,dirtyFields:n.dirtyFields,isDirty:K(e,u)}));else{let t=Array.isArray(u)&&!u.length||V(u);!o||o._f||c(u)||t?Y(e,u,i):Re(e,u,i)}if(!d){let t=De(e,p),r=h(a);if(!s)for(let t of f(p.array,e))I.array.next({name:t,values:r});I.state.next({...t&&n,name:l.mount||t?e:void 0,values:r})}},ze=e=>{let t=S(e)?e(a):e;if(!L(a,t)){a={...a,...t};for(let e of p.mount)X(e,b(t,e));I.state.next({...n,values:a})}},Be=async i=>{l.mount=!0;let o=i.target,c=o.name,u=!0,f=b(r,c),m=e=>{u=Number.isNaN(e)||s(e)&&isNaN(e.getTime())||L(e,b(a,c,e))},g=q(t.mode),_=q(t.reValidateMode);if(f){let s,l,v=o.type?xe(f._f):d(i),y=i.type===w.BLUR||i.type===w.FOCUS_OUT,x=!Ee(f._f)&&!e.validate&&!t.resolver&&!b(n.errors,c)&&!f._f.deps||je(y,b(n.touchedFields,c),n.isSubmitted,_,g),S=De(c,p,y);C(a,c,v),y?(!o||!o.readOnly)&&(f._f.onBlur&&f._f.onBlur(i),k&&k(0)):f._f.onChange&&f._f.onChange(i);let T=he(c,v,y),E=!V(T)||S;if(!y&&I.state.next({name:c,type:i.type,values:h(a)}),x)return(M.isValid||N.isValid)&&(t.mode===`onBlur`?y&&R():y||R()),E&&I.state.next({name:c,...S?{}:T});if(!t.resolver&&e.validate&&await ve({name:c,eventType:i.type}),!y&&S&&I.state.next({...n}),t.resolver){let{errors:e}=await W([c]);if(z([c]),m(v),u){let t=Oe(n.errors,r,c),i=Oe(e,r,t.name||c);s=i.error,c=i.name,l=V(e)}}else z([c],!0),s=(await Fe(f,p.disabled,a,ee,t.shouldUseNativeValidation))[c],z([c]),m(v),u&&(s?l=!1:(M.isValid||N.isValid)&&(l=await G({fields:r,onlyCheckValid:!0,name:c,eventType:i.type})));u&&(f._f.deps&&(!Array.isArray(f._f.deps)||f._f.deps.length>0)&&Z(f._f.deps),ge(c,l,s,T))}},Ve=(e,t)=>{if(b(n.errors,t)&&e.focus)return e.focus(),1},Z=async(e,i={})=>{let a,o,s=B(e);if(t.resolver){let t=await _e(_(e)?e:s);a=V(t),o=e?!s.some(e=>b(t,e)):a}else e?(o=(await Promise.all(s.map(async e=>{let t=b(r,e);return await G({fields:t&&t._f?{[e]:t}:t,eventType:w.TRIGGER})}))).every(Boolean),!(!o&&!n.isValid)&&R()):o=a=await G({fields:r,name:e,eventType:w.TRIGGER});return I.state.next({...!P(e)||(M.isValid||N.isValid)&&a!==n.isValid?{}:{name:e},...t.resolver||!e?{isValid:a}:{},errors:n.errors}),i.shouldFocus&&!o&&J(r,Ve,e?s:p.mount),o},He=(e,t)=>{let r={...l.mount?a:i};return t&&(r=ie(t.dirtyFields?n.dirtyFields:n.touchedFields,r)),_(e)?r:P(e)?b(r,e):e.map(e=>b(r,e))},Ue=(e,t)=>({invalid:!!b((t||n).errors,e),isDirty:!!b((t||n).dirtyFields,e),error:b((t||n).errors,e),isValidating:!!b(n.validatingFields,e),isTouched:!!b((t||n).touchedFields,e)}),We=e=>{let t=e?B(e):void 0;t?.forEach(e=>H(n.errors,e)),t?t.forEach(e=>{I.state.next({name:e,errors:n.errors})}):I.state.next({errors:{}})},Q=(e,t,i)=>{let a=(b(r,e,{_f:{}})._f||{}).ref,{ref:o,message:s,type:c,...l}=b(n.errors,e)||{};C(n.errors,e,{...l,...t,ref:a}),I.state.next({name:e,errors:n.errors,isValid:!1}),i&&i.shouldFocus&&a&&a.focus&&a.focus()},Ge=(e,t)=>S(e)?I.state.subscribe({next:n=>`values`in n&&e(n.values||we(void 0,t),n)}):we(e,t,!0),Ke=e=>I.state.subscribe({next:t=>{if(Ae(e.name,t.name,e.exact)&&ke(t,e.formState||M,rt,e.reRenderRoot)){let r={...a};e.callback({values:r,...n,...t,defaultValues:i})}}}).unsubscribe,qe=e=>(l.mount=!0,N={...N,...e.formState},Ke({...e,formState:{...j,...e.formState}})),Je=(e,o={})=>{for(let s of e?B(e):p.mount)p.mount.delete(s),p.array.delete(s),o.keepValue||(H(r,s),H(a,s)),!o.keepError&&H(n.errors,s),!o.keepDirty&&H(n.dirtyFields,s),!o.keepTouched&&H(n.touchedFields,s),!o.keepIsValidating&&H(n.validatingFields,s),!t.shouldUnregister&&!o.keepDefaultValue&&H(i,s);I.state.next({values:h(a)}),I.state.next({...n,...o.keepDirty?{isDirty:K()}:{}}),!o.keepIsValid&&R()},Ye=({disabled:e,name:t})=>{if(x(e)&&l.mount||e||p.disabled.has(t)){let n=p.disabled.has(t)!==!!e;e?p.disabled.add(t):p.disabled.delete(t),n&&l.mount&&!l.action&&R()}},$=(e,n={})=>{let a=b(r,e),o=x(n.disabled)||x(t.disabled),s=!p.registerName.has(e)&&a&&a._f&&!a._f.mount;return C(r,e,{...a||{},_f:{...a&&a._f?a._f:{ref:{name:e}},name:e,mount:!0,...n}}),p.mount.add(e),a&&!s?Ye({disabled:x(n.disabled)?n.disabled:t.disabled,name:e}):me(e,!0,n.value),{...o?{disabled:n.disabled||t.disabled}:{},...t.progressive?{required:!!n.required,min:Ce(n.min),max:Ce(n.max),minLength:Ce(n.minLength),maxLength:Ce(n.maxLength),pattern:Ce(n.pattern)}:{},name:e,onChange:Be,onBlur:Be,ref:o=>{if(o){p.registerName.add(e),$(e,n),p.registerName.delete(e),a=b(r,e);let t=_(o.value)&&o.querySelectorAll&&o.querySelectorAll(`input,select,textarea`)[0]||o,s=le(t),c=a._f.refs||[];if(s?c.find(e=>e===t):t===a._f.ref)return;C(r,e,{_f:{...a._f,...s?{refs:[...c.filter(ue),t,...Array.isArray(b(i,e))?[{}]:[]],ref:{type:t.type,name:e}}:{ref:t}}}),me(e,!1,void 0,t)}else a=b(r,e,{}),a._f&&(a._f.mount=!1),(t.shouldUnregister||n.shouldUnregister)&&!(f(p.array,e).length&&l.action)&&p.unMount.add(e)}}},Xe=()=>t.shouldFocusError&&!t.shouldUseNativeValidation&&J(r,Ve,p.mount),Ze=e=>{x(e)&&(I.state.next({disabled:e}),J(r,(t,n)=>{let i=b(r,n);i&&(t.disabled=i._f.disabled||e,Array.isArray(i._f.refs)&&i._f.refs.forEach(t=>{t.disabled=i._f.disabled||e}))},0,!1))},Qe=(e,i)=>async o=>{let s;o&&(o.preventDefault&&o.preventDefault(),o.persist&&o.persist());let c=h(a);if(I.state.next({isSubmitting:!0}),t.resolver){let{errors:e,values:t}=await W();z(),n.errors=e,c=h(t)}else await G({fields:r,eventType:w.SUBMIT});if(p.disabled.size)for(let e of p.disabled)H(c,e);if(H(n.errors,O),V(n.errors)){I.state.next({errors:{}});try{await e(c,o)}catch(e){s=e}}else i&&await i({...n.errors},o),Xe(),setTimeout(Xe);if(I.state.next({isSubmitted:!0,isSubmitting:!1,isSubmitSuccessful:V(n.errors)&&!s,submitCount:n.submitCount+1,errors:n.errors}),s)throw s},$e=(e,t={})=>{b(r,e)&&(_(t.defaultValue)?X(e,h(b(i,e))):(X(e,t.defaultValue),C(i,e,h(t.defaultValue))),t.keepTouched||H(n.touchedFields,e),t.keepDirty||(H(n.dirtyFields,e),n.isDirty=t.defaultValue?K(e,h(b(i,e))):K()),t.keepError||(H(n.errors,e),M.isValid&&R()),I.state.next({...n}))},et=(e,o={})=>{let s=e?h(e):i,c=h(s),u=V(e),d=u?i:c;if(o.keepDefaultValues||(i=s),!o.keepValues){if(o.keepDirtyValues){let e=new Set([...p.mount,...Object.keys(U(i,a))]);for(let t of Array.from(e)){let e=b(n.dirtyFields,t),r=b(a,t),i=b(d,t);e&&!_(r)?C(d,t,r):!e&&!_(i)&&X(t,i)}}else{if(m&&_(e))for(let e of p.mount){let t=b(r,e);if(t&&t._f){let e=Array.isArray(t._f.refs)?t._f.refs[0]:t._f.ref;if(oe(e)){let t=e.closest(`form`);if(t){t.reset();break}}}}if(o.keepFieldsRef)for(let e of p.mount)X(e,b(d,e));else r={}}a=t.shouldUnregister?o.keepDefaultValues?h(i):{}:h(d),I.array.next({values:{...d}}),I.state.next({values:{...d}})}p={mount:o.keepDirtyValues?p.mount:new Set,unMount:new Set,array:new Set,registerName:new Set,disabled:new Set,watch:new Set,watchAll:!1,focus:``},l.mount=!M.isValid||!!o.keepIsValid||!!o.keepDirtyValues||!t.shouldUnregister&&!V(d),l.watch=!!t.shouldUnregister,l.keepIsValid=!!o.keepIsValid,l.action=!1,o.keepErrors||(n.errors={}),I.state.next({submitCount:o.keepSubmitCount?n.submitCount:0,isDirty:u?!1:o.keepDirty?n.isDirty:!!(o.keepDefaultValues&&!L(e,i)),isSubmitted:o.keepIsSubmitted?n.isSubmitted:!1,dirtyFields:u?{}:o.keepDirtyValues?o.keepDefaultValues&&a?U(i,a):n.dirtyFields:o.keepDefaultValues&&e?U(i,e):o.keepDirty?n.dirtyFields:{},touchedFields:o.keepTouched?n.touchedFields:{},errors:o.keepErrors?n.errors:{},isSubmitSuccessful:o.keepIsSubmitSuccessful?n.isSubmitSuccessful:!1,isSubmitting:!1,defaultValues:i})},tt=(e,n)=>et(S(e)?e(a):e,{...t.resetOptions,...n}),nt=(e,t={})=>{let n=b(r,e),i=n&&n._f;if(i){let e=i.refs?i.refs[0]:i.ref;e.focus&&setTimeout(()=>{e.focus(),t.shouldSelect&&S(e.select)&&e.select()})}},rt=e=>{n={...n,...e}},it={control:{register:$,unregister:Je,getFieldState:Ue,handleSubmit:Qe,setError:Q,_subscribe:Ke,_runSchema:W,_updateIsValidating:z,_focusError:Xe,_getWatch:we,_getDirty:K,_setValid:R,_setFieldArray:ce,_setDisabledField:Ye,_setErrors:fe,_getFieldArray:Pe,_reset:et,_resetDefaultValues:()=>S(t.defaultValues)&&t.defaultValues().then(e=>{tt(e,t.resetOptions),I.state.next({isLoading:!1})}),_removeUnmounted:be,_disableForm:Ze,_subjects:I,_proxyFormState:M,get _fields(){return r},get _formValues(){return a},get _state(){return l},set _state(e){l=e},get _defaultValues(){return i},get _names(){return p},set _names(e){p=e},get _formState(){return n},get _options(){return t},set _options(e){t={...t,...e}}},subscribe:qe,trigger:Z,register:$,handleSubmit:Qe,watch:Ge,setValue:X,setValues:ze,getValues:He,reset:tt,resetField:$e,clearErrors:We,unregister:Je,setError:Q,setFocus:nt,getFieldState:Ue};return{...it,formControl:it}}var X=()=>{if(typeof crypto<`u`&&crypto.randomUUID)return crypto.randomUUID();let e=typeof performance>`u`?Date.now():performance.now()*1e3;return`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,t=>{let n=(Math.random()*16+e)%16|0;return(t==`x`?n:n&3|8).toString(16)})},ze=(e,t,n={})=>n.shouldFocus||_(n.shouldFocus)?n.focusName||`${e}.${_(n.focusIndex)?t:n.focusIndex}.`:``,Be=(e,t)=>[...e,...B(t)],Ve=e=>Array.isArray(e)?e.map(()=>void 0):void 0;function Z(e,t,n){return[...e.slice(0,t),...B(n),...e.slice(t)]}var He=(e,t,n)=>Array.isArray(e)?(_(e[n])&&(e[n]=void 0),e.splice(n,0,e.splice(t,1)[0]),e):[],Ue=(e,t)=>[...B(t),...B(e)];function We(e,t){let n=0,r=[...e];for(let e of t)r.splice(e-n,1),n++;return v(r).length?r:[]}var Q=(e,t)=>_(t)?[]:We(e,B(t).sort((e,t)=>e-t)),Ge=(e,t,n)=>{[e[t],e[n]]=[e[n],e[t]]},Ke=(e,t,n)=>(e[t]=n,e);function qe(e){let t=A(),{control:n=t,name:r,keyName:i=`id`,shouldUnregister:o,rules:s}=e,[c,l]=a.useState(n._getFieldArray(r)),u=a.useRef(n._getFieldArray(r).map(X)),d=a.useRef(!1);n._names.array.add(r),a.useMemo(()=>s&&c.length>=0&&n.register(r,s),[n,r,c.length,s]),M(()=>n._subjects.array.subscribe({next:({values:e,name:t})=>{if(t===r||!t){let t=b(e,r);Array.isArray(t)&&(l(t),u.current=t.map(X))}}}).unsubscribe,[n,r]);let f=a.useCallback(e=>{d.current=!0,n._setFieldArray(r,e)},[n,r]);return a.useEffect(()=>{n._state.action=!1,De(r,n._names)&&n._subjects.state.next({...n._formState});let e=q(n._options.mode);if(d.current&&(!e.isOnSubmit||n._formState.isSubmitted)&&!q(n._options.reValidateMode).isOnSubmit&&!e.isOnBlur)if(n._options.resolver)n._runSchema([r]).then(e=>{n._updateIsValidating([r]);let t=b(e.errors,r),i=b(n._formState.errors,r);(i?!t&&i.type||t&&(i.type!==t.type||i.message!==t.message):t&&t.type)&&(t?C(n._formState.errors,r,t):H(n._formState.errors,r),n._subjects.state.next({errors:n._formState.errors}))});else{let e=b(n._fields,r);e&&e._f&&!(q(n._options.reValidateMode).isOnSubmit&&q(n._options.mode).isOnSubmit)&&Fe(e,n._names.disabled,n._formValues,n._options.criteriaMode===T.all,n._options.shouldUseNativeValidation,!0).then(e=>!V(e)&&n._subjects.state.next({errors:Ne(n._formState.errors,e,r)}))}n._subjects.state.next({name:r,values:h(n._formValues)}),n._names.focus&&J(n._fields,(e,t)=>{if(n._names.focus&&t.startsWith(n._names.focus)&&e.focus)return e.focus(),1}),n._names.focus=``,n._setValid(),d.current=!1},[c,r,n]),a.useEffect(()=>(!b(n._formValues,r)&&n._setFieldArray(r),()=>{let e=!(n._options.shouldUnregister||o);d.current&&e&&n._subjects.state.next({name:r,values:h(n._formValues)}),e?((e,t)=>{let r=b(n._fields,e);r&&r._f&&(r._f.mount=t)})(r,!1):n.unregister(r)}),[r,n,i,o]),{swap:a.useCallback((e,t)=>{let i=n._getFieldArray(r);Ge(i,e,t),Ge(u.current,e,t),f(i),l(i),n._setFieldArray(r,i,Ge,{argA:e,argB:t},!1)},[f,r,n]),move:a.useCallback((e,t)=>{let i=n._getFieldArray(r);He(i,e,t),He(u.current,e,t),f(i),l(i),n._setFieldArray(r,i,He,{argA:e,argB:t},!1)},[f,r,n]),prepend:a.useCallback((e,t)=>{let i=B(h(e)),a=Ue(n._getFieldArray(r),i);n._names.focus=ze(r,0,t),u.current=Ue(u.current,i.map(X)),f(a),l(a),n._setFieldArray(r,a,Ue,{argA:Ve(e)})},[f,r,n]),append:a.useCallback((e,t)=>{let i=B(h(e)),a=Be(n._getFieldArray(r),i);n._names.focus=ze(r,a.length-1,t),u.current=Be(u.current,i.map(X)),f(a),l(a),n._setFieldArray(r,a,Be,{argA:Ve(e)})},[f,r,n]),remove:a.useCallback(e=>{let t=Q(n._getFieldArray(r),e);u.current=Q(u.current,e),f(t),l(t),!Array.isArray(b(n._fields,r))&&C(n._fields,r,void 0),n._setFieldArray(r,t,Q,{argA:e})},[f,r,n]),insert:a.useCallback((e,t,i)=>{let a=B(h(t)),o=Z(n._getFieldArray(r),e,a);n._names.focus=ze(r,e,i),u.current=Z(u.current,e,a.map(X)),f(o),l(o),n._setFieldArray(r,o,Z,{argA:e,argB:Ve(t)})},[f,r,n]),update:a.useCallback((e,t)=>{let i=h(t),a=Ke(n._getFieldArray(r),e,i);u.current=[...a].map((t,n)=>!t||n===e?X():u.current[n]),f(a),l([...a]),n._setFieldArray(r,a,Ke,{argA:e,argB:i},!0,!1)},[f,r,n]),replace:a.useCallback(e=>{let t=B(h(e));u.current=t.map(X),f([...t]),l([...t]),n._setFieldArray(r,[...t],e=>e,{},!0,!1)},[f,r,n]),fields:a.useMemo(()=>c.map((e,t)=>({...e,[i]:u.current[t]||X()})),[c,i])}}function Je(e={}){let t=a.useRef(void 0),n=a.useRef(void 0),[r,i]=a.useState(()=>({...h(Le),isLoading:S(e.defaultValues),errors:e.errors||{},disabled:e.disabled||!1,defaultValues:S(e.defaultValues)?void 0:e.defaultValues}));if(!t.current)if(e.formControl)t.current={...e.formControl,formState:r},e.defaultValues&&!S(e.defaultValues)&&e.formControl.reset(e.defaultValues,e.resetOptions);else{let{formControl:n,...i}=Re(e);t.current={...i,formState:r}}let o=t.current.control;return o._options=e,M(()=>{let e=o._subscribe({formState:o._proxyFormState,callback:()=>i({...o._formState,defaultValues:o._defaultValues}),reRenderRoot:!0});return i(e=>({...e,isReady:!0})),o._formState.isReady=!0,e},[o]),a.useEffect(()=>o._disableForm(e.disabled),[o,e.disabled]),a.useEffect(()=>{e.mode&&(o._options.mode=e.mode),e.reValidateMode&&(o._options.reValidateMode=e.reValidateMode)},[o,e.mode,e.reValidateMode]),a.useEffect(()=>{e.errors&&(o._setErrors(e.errors),o._focusError())},[o,e.errors]),a.useEffect(()=>{e.shouldUnregister&&o._subjects.state.next({values:o._getWatch()})},[o,e.shouldUnregister]),a.useEffect(()=>{if(o._proxyFormState.isDirty){let e=o._getDirty();e!==r.isDirty&&o._subjects.state.next({isDirty:e})}},[o,r.isDirty]),a.useEffect(()=>{e.values&&!L(e.values,n.current)?(o._reset(e.values,{keepFieldsRef:!0,...o._options.resetOptions}),o._options.resetOptions?.keepIsValid||o._setValid(),n.current=e.values,i(e=>({...e}))):o._resetDefaultValues()},[o,e.values]),a.useEffect(()=>{o._state.mount||(o._setValid(),o._state.mount=!0),o._state.watch&&(o._state.watch=!1,o._subjects.state.next({...o._formState})),o._removeUnmounted()}),t.current.formState=a.useMemo(()=>j(r,o),[o,r]),t.current}var Ye=(e,t,n)=>{if(e&&`reportValidity`in e){let r=b(n,t);e.setCustomValidity(r&&r.message||``),e.reportValidity()}},$=(e,t)=>{for(let n in t.fields){let r=t.fields[n];r&&r.ref&&`reportValidity`in r.ref?Ye(r.ref,n,e):r&&r.refs&&r.refs.forEach(t=>Ye(t,n,e))}},Xe=(e,t)=>{t.shouldUseNativeValidation&&$(e,t);let n={};for(let r in e){let i=b(t.fields,r),a=Object.assign(e[r]||{},{ref:i&&i.ref});if(Ze(t.names||Object.keys(e),r)){let e=Object.assign({},b(n,r));C(e,`root`,a),C(n,r,e)}else C(n,r,a)}return n},Ze=(e,t)=>{let n=Qe(t);return e.some(e=>Qe(e).match(`^${n}\\.\\d+`))};function Qe(e){return e.replace(/\]|\[/g,``)}function $e(e,t){try{var n=e()}catch(e){return t(e)}return n&&n.then?n.then(void 0,t):n}function et(e,t){for(var n={};e.length;){var r=e[0],i=r.code,a=r.message,o=r.path.join(`.`);if(!n[o])if(`unionErrors`in r){var s=r.unionErrors[0].errors[0];n[o]={message:s.message,type:s.code}}else n[o]={message:a,type:i};if(`unionErrors`in r&&r.unionErrors.forEach(function(t){return t.errors.forEach(function(t){return e.push(t)})}),t){var c=n[o].types,l=c&&c[r.code];n[o]=ne(o,t,n,i,l?[].concat(l,r.message):r.message)}e.shift()}return n}function tt(e,t){for(var n={};e.length;){var r=e[0],i=r.code,a=r.message,o=r.path.join(`.`);if(!n[o])if(r.code===`invalid_union`&&r.errors.length>0){var s=r.errors[0][0];n[o]={message:s.message,type:s.code}}else n[o]={message:a,type:i};if(r.code===`invalid_union`&&r.errors.forEach(function(t){return t.forEach(function(t){return e.push(t)})}),t){var c=n[o].types,l=c&&c[r.code];n[o]=ne(o,t,n,i,l?[].concat(l,r.message):r.message)}e.shift()}return n}function nt(n,i,a){if(a===void 0&&(a={}),function(e){return`_def`in e&&typeof e._def==`object`&&`typeName`in e._def}(n))return function(e,t,r){try{return Promise.resolve($e(function(){return Promise.resolve(n[a.mode===`sync`?`parse`:`parseAsync`](e,i)).then(function(t){return r.shouldUseNativeValidation&&$({},r),{errors:{},values:a.raw?Object.assign({},e):t}})},function(e){if(function(e){return Array.isArray(e?.issues)}(e))return{values:{},errors:Xe(et(e.errors,!r.shouldUseNativeValidation&&r.criteriaMode===`all`),r)};throw e}))}catch(e){return Promise.reject(e)}};if(function(e){return`_zod`in e&&typeof e._zod==`object`}(n))return function(o,s,c){try{return Promise.resolve($e(function(){return Promise.resolve((a.mode===`sync`?r:e)(n,o,i)).then(function(e){return c.shouldUseNativeValidation&&$({},c),{errors:{},values:a.raw?Object.assign({},o):e}})},function(e){if(function(e){return e instanceof t}(e))return{values:{},errors:Xe(tt(e.issues,!c.shouldUseNativeValidation&&c.criteriaMode===`all`),c)};throw e}))}catch(e){return Promise.reject(e)}};throw Error(`Invalid input: not a Zod schema`)}export{ee as a,Je as i,R as n,qe as r,nt as t}; \ No newline at end of file +import{o as e,r as t}from"./compiler-runtime.js";import{S as n,b as r,x as i}from"./SignatureV4.js";var a=e(t(),1),o=e=>e.type===`checkbox`,s=e=>e instanceof Date,c=e=>e==null,l=e=>typeof e==`object`,u=e=>!c(e)&&!Array.isArray(e)&&l(e)&&!s(e),d=e=>u(e)&&e.target?o(e.target)?e.target.checked:e.target.value:e,f=(e,t)=>t.split(`.`).some((t,n,r)=>!isNaN(Number(t))&&e.has(r.slice(0,n).join(`.`))),p=e=>{let t=e.constructor&&e.constructor.prototype;return u(t)&&t.hasOwnProperty(`isPrototypeOf`)},m=typeof window<`u`&&window.HTMLElement!==void 0&&typeof document<`u`;function h(e){if(e instanceof Date)return new Date(e);let t=typeof FileList<`u`&&e instanceof FileList;if(m&&(e instanceof Blob||t))return e;let n=Array.isArray(e);if(!n&&!(u(e)&&p(e)))return e;let r=n?[]:Object.create(Object.getPrototypeOf(e));for(let t in e)Object.prototype.hasOwnProperty.call(e,t)&&(r[t]=h(e[t]));return r}var g={BLUR:`blur`,FOCUS_OUT:`focusout`,CHANGE:`change`,SUBMIT:`submit`,TRIGGER:`trigger`,VALID:`valid`},_={onBlur:`onBlur`,onChange:`onChange`,onSubmit:`onSubmit`,onTouched:`onTouched`,all:`all`},v={max:`max`,min:`min`,maxLength:`maxLength`,minLength:`minLength`,pattern:`pattern`,required:`required`,validate:`validate`},y=`form`,b=`root`,x=[`__proto__`,`constructor`,`prototype`],S=e=>/^\w*$/.test(e),C=e=>e===void 0,w=e=>e.split(/[.[\]'"]/g).filter(Boolean),T=(e,t,n)=>{if(!t||!u(e))return n;let r=S(t)?[t]:w(t);if(r.some(e=>x.includes(e)))return n;let i=r.reduce((e,t)=>c(e)?void 0:e[t],e);return C(i)||i===e?C(e[t])?n:e[t]:i},E=e=>typeof e==`boolean`,D=e=>typeof e==`function`,O=(e,t,n)=>{let r=-1,i=S(t)?[t]:w(t),a=i.length,o=a-1;for(;++ra.useContext(k),j=(e,t,n,r=!0)=>{let i={};for(let a in e)Object.defineProperty(i,a,{get:()=>{let i=a;return t._proxyFormState[i]!==_.all&&(t._proxyFormState[i]=!r||_.all),n&&(n[i]=!0),e[i]}});return i},M=m?a.useLayoutEffect:a.useEffect;function N(e){let t=A(),{control:n=t,disabled:r,name:i,exact:o}=e||{},[s,c]=a.useState(()=>({...n._formState,defaultValues:n._defaultValues})),l=a.useRef({isDirty:!1,isLoading:!1,dirtyFields:!1,touchedFields:!1,validatingFields:!1,isValidating:!1,isValid:!1,errors:!1});return M(()=>n._subscribe({name:i,formState:l.current,exact:o,callback:e=>{!r&&c({...n._formState,...e,defaultValues:n._defaultValues})}}),[i,r,o]),a.useEffect(()=>{l.current.isValid&&n._setValid(!0)},[n]),a.useMemo(()=>j(s,n,l.current,!1),[s,n])}var P=e=>typeof e==`string`,F=(e,t,n,r,i)=>P(e)?(r&&t.watch.add(e),T(n,e,i)):Array.isArray(e)?e.map(e=>(r&&t.watch.add(e),T(n,e))):(r&&(t.watchAll=!0),n),I=e=>c(e)||!l(e);function L(e,t,n=new WeakSet){if(e===t)return!0;if(I(e)||I(t))return Object.is(e,t);if(s(e)&&s(t))return Object.is(e.getTime(),t.getTime());let r=Object.keys(e),i=Object.keys(t);if(r.length!==i.length)return!1;if(n.has(e)||n.has(t))return!0;n.add(e),n.add(t);for(let i of r){let r=e[i];if(!(i in t))return!1;if(i!==`ref`){let e=t[i];if(s(r)&&s(e)||(u(r)||Array.isArray(r))&&(u(e)||Array.isArray(e))?!L(r,e,n):!Object.is(r,e))return!1}}return!0}function ee(e){let t=A(),{control:n=t,name:r,defaultValue:i,disabled:o,exact:s,compute:c}=e||{},l=a.useRef(i),u=a.useRef(c),d=a.useRef(void 0),f=a.useRef(n),p=a.useRef(r);u.current=c;let[m,h]=a.useState(()=>{let e=n._getWatch(r,l.current);return u.current?u.current(e):e}),g=a.useCallback(e=>{let t=F(r,n._names,e||n._formValues,!1,l.current);return u.current?u.current(t):t},[n._formValues,n._names,r]),_=a.useCallback(e=>{if(!o){let t=F(r,n._names,e||n._formValues,!1,l.current);if(u.current){let e=u.current(t);L(e,d.current)||(h(e),d.current=e)}else h(t)}},[n._formValues,n._names,o,r]);M(()=>((f.current!==n||!L(p.current,r))&&(f.current=n,p.current=r,_()),n._subscribe({name:r,formState:{values:!0},exact:s,callback:e=>{_(e.values)}})),[n,s,r,_]),a.useEffect(()=>n._removeUnmounted());let v=f.current!==n,y=p.current,b=a.useMemo(()=>{if(o)return null;let e=!v&&!L(y,r);return v||e?g():null},[o,v,r,y,g]);return b===null?m:b}function R(e){let t=A(),{name:n,disabled:r,control:i=t,shouldUnregister:o,defaultValue:s,exact:c=!0}=e,l=f(i._names.array,n),u=ee({control:i,name:n,defaultValue:a.useMemo(()=>T(i._formValues,n,T(i._defaultValues,n,s)),[i,n,s]),exact:c}),p=N({control:i,name:n,exact:c}),m=a.useRef(e),_=a.useRef(i.register(n,{...e.rules,value:u,...E(e.disabled)?{disabled:e.disabled}:{}}));m.current=e;let v=a.useMemo(()=>Object.defineProperties({},{invalid:{enumerable:!0,get:()=>!!T(p.errors,n)},isDirty:{enumerable:!0,get:()=>!!T(p.dirtyFields,n)},isTouched:{enumerable:!0,get:()=>!!T(p.touchedFields,n)},isValidating:{enumerable:!0,get:()=>!!T(p.validatingFields,n)},error:{enumerable:!0,get:()=>T(p.errors,n)}}),[p,n]),y=a.useCallback(e=>_.current.onChange({target:{value:d(e),name:n},type:g.CHANGE}),[n]),b=a.useCallback(()=>_.current.onBlur({target:{value:T(i._formValues,n),name:n},type:g.BLUR}),[n,i._formValues]),x=a.useCallback(e=>{let t=T(i._fields,n);t&&t._f&&e&&(t._f.ref={focus:()=>D(e.focus)&&e.focus(),select:()=>D(e.select)&&e.select(),setCustomValidity:t=>D(e.setCustomValidity)&&e.setCustomValidity(t),reportValidity:()=>D(e.reportValidity)&&e.reportValidity()})},[i._fields,n]),S=a.useMemo(()=>({name:n,value:u,...E(r)||p.disabled?{disabled:p.disabled||r}:{},onChange:y,onBlur:b,ref:x}),[n,r,p.disabled,y,b,x,u]);return a.useEffect(()=>{let e=i._options.shouldUnregister||o;i.register(n,{...m.current.rules,...E(m.current.disabled)?{disabled:m.current.disabled}:{}});let t=(e,t)=>{let n=T(i._fields,e);n&&n._f&&(n._f.mount=t)};if(t(n,!0),e){let e=h(T(i._defaultValues,n,T(i._options.defaultValues,n,m.current.defaultValue)));O(i._defaultValues,n,e),C(T(i._formValues,n))&&O(i._formValues,n,e)}return!l&&i.register(n),()=>{(l?e&&!i._state.action:e)?i.unregister(n):t(n,!1)}},[n,i,l,o]),a.useEffect(()=>{i._setDisabledField({disabled:r,name:n})},[r,n,i]),a.useMemo(()=>({field:S,formState:p,fieldState:v}),[S,p,v])}var z=e=>e.render(R(e)),te=a.createContext(null);te.displayName=`HookFormContext`;var ne=(e,t,n,r,i)=>t?{...n[e],types:{...n[e]&&n[e].types?n[e].types:{},[r]:i||!0}}:{},re=e=>Array.isArray(e)?e.filter(Boolean):[],B=e=>Array.isArray(e)?e:[e],ie=()=>{let e=[];return{get observers(){return e},next:t=>{for(let n of e)n.next&&n.next(t)},subscribe:t=>(e.push(t),{unsubscribe:()=>{e=e.filter(e=>e!==t)}}),unsubscribe:()=>{e=[]}}};function ae(e,t){let n={};for(let r in e)if(e.hasOwnProperty(r)){let i=e[r],a=t[r];if(i&&u(i)&&a){let e=ae(i,a);u(e)&&(n[r]=e)}else e[r]&&(n[r]=a)}return n}var V=e=>u(e)&&!Object.keys(e).length,oe=e=>e.type===`file`,se=e=>{if(!m)return!1;let t=e?e.ownerDocument:0;return e instanceof(t&&t.defaultView?t.defaultView.HTMLElement:HTMLElement)},ce=e=>e.type===`select-multiple`,le=e=>e.type===`radio`,ue=e=>le(e)||o(e),de=e=>se(e)&&e.isConnected;function fe(e,t){let n=t.slice(0,-1).length,r=0;for(;r{for(let t in e)if(D(e[t]))return!0;return!1};function he(e){return Array.isArray(e)||u(e)&&!me(e)}function ge(e,t={}){for(let n in e){let r=e[n];he(r)?(t[n]=Array.isArray(r)?[]:{},ge(r,t[n])):C(r)||(t[n]=!0)}return t}function U(e){if(e!==!1){if(e===!0)return!0;if(Array.isArray(e)){let t=e.map(e=>U(e));return t.some(e=>e!==void 0)?t:void 0}if(u(e)){let t={};for(let n in e){let r=U(e[n]);C(r)||(t[n]=r)}return Object.keys(t).length?t:void 0}}}function W(e,t,n){n||=ge(t);for(let r in e){let i=e[r];if(he(i))C(t)||I(n[r])?n[r]=ge(i,Array.isArray(i)?[]:{}):W(i,c(t)?{}:t[r],n[r]);else{let e=t[r];n[r]=!L(i,e)}}return U(n)||{}}var _e={value:!1,isValid:!1},ve={value:!0,isValid:!0},G=e=>{if(Array.isArray(e)){if(e.length>1){let t=e.filter(e=>e&&e.checked&&!e.disabled).map(e=>e.value);return{value:t,isValid:!!t.length}}return e[0].checked&&!e[0].disabled?e[0].attributes&&!C(e[0].attributes.value)?C(e[0].value)||e[0].value===``?ve:{value:e[0].value,isValid:!0}:ve:_e}return _e},ye=(e,{valueAsNumber:t,valueAsDate:n,setValueAs:r})=>C(e)?e:t?e===``?NaN:e&&+e:n&&P(e)?new Date(e):r?r(e):e,be={isValid:!1,value:null},K=e=>Array.isArray(e)?e.reduce((e,t)=>t&&t.checked&&!t.disabled?{isValid:!0,value:t.value}:e,be):be;function xe(e){let t=e.ref;return oe(t)?t.files:le(t)?K(e.refs).value:ce(t)?[...t.selectedOptions].map(({value:e})=>e):o(t)?G(e.refs).value:ye(C(t.value)?e.ref.value:t.value,e)}var Se=(e,t,n,r)=>{let i={};for(let n of e){let e=T(t,n);e&&O(i,n,e._f)}return{criteriaMode:n,names:[...e],fields:i,shouldUseNativeValidation:r}},q=e=>e instanceof RegExp,Ce=e=>C(e)?e:q(e)?e.source:u(e)?q(e.value)?e.value.source:e.value:e,J=e=>({isOnSubmit:!e||e===_.onSubmit,isOnBlur:e===_.onBlur,isOnChange:e===_.onChange,isOnAll:e===_.all,isOnTouch:e===_.onTouched}),we=`AsyncFunction`,Te=e=>!!e&&!!e.validate&&!!(D(e.validate)&&e.validate.constructor.name===we||u(e.validate)&&Object.values(e.validate).find(e=>e.constructor.name===we)),Ee=e=>e.mount&&(e.required||e.min||e.max||e.maxLength||e.minLength||e.pattern||e.validate),De=(e,t,n)=>!n&&(t.watchAll||t.watch.has(e)||[...t.watch].some(t=>e.startsWith(`${t}.`))),Y=(e,t,n,r)=>{for(let i of n||Object.keys(e)){let n=T(e,i);if(n){let{_f:e,...a}=n;if(e){if(e.refs&&e.refs[0]&&t(e.refs[0],i)&&!r||e.ref&&t(e.ref,e.name)&&!r)return!0;if(Y(a,t))break}else if(u(a)&&Y(a,t))break}}};function Oe(e,t,n){let r=T(e,n);if(r||S(n))return{error:r,name:n};let i=n.split(`.`);for(;i.length;){let r=i.join(`.`),a=T(t,r),o=T(e,r);if(a&&!Array.isArray(a)&&n!==r)return{name:n};if(o&&o.type)return{name:r,error:o};if(o&&o.root&&o.root.type)return{name:`${r}.root`,error:o.root};i.pop()}return{name:n}}var ke=(e,t,n,r)=>{n(e);let{name:i,...a}=e;return V(a)||r&&Object.keys(a).length>=Object.keys(t).length||Object.keys(a).find(e=>t[e]===(!r||_.all))},Ae=(e,t,n)=>!e||!t||e===t||B(e).some(e=>e&&(n?e===t:e.startsWith(t)||t.startsWith(e))),je=(e,t,n,r,i)=>i.isOnAll?!1:!n&&i.isOnTouch?!(t||e):(n?r.isOnBlur:i.isOnBlur)?!e:(n?r.isOnChange:i.isOnChange)?e:!0,Me=(e,t)=>!re(T(e,t)).length&&H(e,t),Ne=(e,t,n)=>{let r=T(e,n),i=Array.isArray(r)?r:[];return O(i,b,t[n]),O(e,n,i),e};function Pe(e,t,n=`validate`){if(P(e)||Array.isArray(e)&&e.every(P)||E(e)&&!e)return{type:n,message:P(e)?e:``,ref:t}}var X=e=>u(e)&&!q(e)?e:{value:e,message:``},Fe=async(e,t,n,r,i,a)=>{let{ref:s,refs:l,required:d,maxLength:f,minLength:p,min:m,max:h,pattern:g,validate:_,name:y,valueAsNumber:b,mount:x}=e._f,S=T(n,y);if(!x||t.has(y))return{};let w=l?l[0]:s,O=e=>{i&&w.reportValidity&&(w.setCustomValidity(E(e)?``:e||``),w.reportValidity())},k={},A=le(s),j=o(s),M=A||j,N=(b||oe(s))&&C(s.value)&&C(S)||se(s)&&s.value===``||S===``||Array.isArray(S)&&!S.length,F=ne.bind(null,y,r,k),I=(e,t,n,r=v.maxLength,i=v.minLength)=>{let a=e?t:n;k[y]={type:e?r:i,message:a,ref:s,...F(e?r:i,a)}};if(a?!Array.isArray(S)||!S.length:d&&(!M&&(N||c(S))||E(S)&&!S||j&&!G(l).isValid||A&&!K(l).isValid)){let{value:e,message:t}=P(d)?{value:!!d,message:d}:X(d);if(e&&(k[y]={type:v.required,message:t,ref:w,...F(v.required,t)},!r))return O(t),k}if(!N&&(!c(m)||!c(h))){let e,t,n=X(h),i=X(m);if(!c(S)&&!isNaN(S)){let r=s.valueAsNumber||S&&+S;c(n.value)||(e=r>n.value),c(i.value)||(t=rnew Date(new Date().toDateString()+` `+e),o=s.type==`time`,c=s.type==`week`;P(n.value)&&S&&(e=o?a(S)>a(n.value):c?S>n.value:r>new Date(n.value)),P(i.value)&&S&&(t=o?a(S)+e.value,i=!c(t.value)&&S.length<+t.value;if((n||i)&&(I(n,e.message,t.message),!r))return O(k[y].message),k}if(g&&!N&&P(S)){let{value:e,message:t}=X(g);if(q(e)&&!S.match(e)&&(k[y]={type:v.pattern,message:t,ref:s,...F(v.pattern,t)},!r))return O(t),k}if(_){if(D(_)){let e=Pe(await _(S,n),w);if(e&&(k[y]={...e,...F(v.validate,e.message)},!r))return O(e.message),k}else if(u(_)){let e={};for(let t in _){if(!V(e)&&!r)break;let i=Pe(await _[t](S,n),w,t);i&&(e={...i,...F(t,i.message)},O(i.message),r&&(k[y]=e))}if(!V(e)&&(k[y]={ref:w,...e},!r))return k}}return O(!0),k},Ie={mode:_.onSubmit,reValidateMode:_.onChange,shouldFocusError:!0},Le={submitCount:0,isDirty:!1,isReady:!1,isValidating:!1,isSubmitted:!1,isSubmitting:!1,isSubmitSuccessful:!1,isValid:!1,touchedFields:{},dirtyFields:{},validatingFields:{}};function Re(e={}){let t={...Ie,...e},n={...h(Le),isLoading:D(t.defaultValues),errors:t.errors||{},disabled:t.disabled||!1},r={},i=(u(t.defaultValues)||u(t.values))&&h(t.defaultValues||t.values)||{},a=t.shouldUnregister?{}:h(i),l={action:!1,mount:!1,watch:!1,keepIsValid:!1},p={mount:new Set,disabled:new Set,unMount:new Set,array:new Set,watch:new Set,registerName:new Set},x,k=0,A={isDirty:!1,dirtyFields:!1,validatingFields:!1,touchedFields:!1,isValidating:!1,isValid:!1,errors:!1},j={...A},M={...j},N={array:ie(),state:ie()},I=t.criteriaMode===_.all,ee=e=>t=>{clearTimeout(k),k=setTimeout(e,t)},R=async e=>{if(!l.keepIsValid&&!t.disabled&&(j.isValid||M.isValid||e)){let e;t.resolver?(e=V((await U()).errors),z()):e=await G({fields:r,onlyCheckValid:!0,eventType:g.VALID}),e!==n.isValid&&N.state.next({isValid:e})}},z=(e,r)=>{!t.disabled&&(j.isValidating||j.validatingFields||M.isValidating||M.validatingFields)&&((e||Array.from(p.mount)).forEach(e=>{e&&(r?O(n.validatingFields,e,r):H(n.validatingFields,e))}),N.state.next({validatingFields:n.validatingFields,isValidating:!V(n.validatingFields)}))},te=()=>{n.dirtyFields=W(i,a)},ne=(e,i=[],o,s,c=!0,u=!0)=>{if(s&&o&&!t.disabled){if(l.action=!0,u&&Array.isArray(T(r,e))){let t=o(T(r,e),s.argA,s.argB);c&&O(r,e,t)}if(u&&Array.isArray(T(n.errors,e))){let t=o(T(n.errors,e),s.argA,s.argB);c&&O(n.errors,e,t),Me(n.errors,e)}if((j.touchedFields||M.touchedFields)&&u&&Array.isArray(T(n.touchedFields,e))){let t=o(T(n.touchedFields,e),s.argA,s.argB);c&&O(n.touchedFields,e,t)}(j.dirtyFields||M.dirtyFields)&&te(),N.state.next({name:e,isDirty:K(e,i),dirtyFields:n.dirtyFields,errors:n.errors,isValid:n.isValid})}else O(a,e,i)},le=(e,t)=>{O(n.errors,e,t),N.state.next({errors:n.errors})},fe=e=>{n.errors=e,N.state.next({errors:n.errors,isValid:!1})},pe=e=>{let t=S(e)?[e]:w(e),n=a,r=i;for(let e=0;e{let c=T(r,e);if(c){if(pe(e))return;let r=C(T(a,e)),u=T(a,e,C(o)?T(i,e):o);C(u)||s&&s.defaultChecked||t?O(a,e,t?u:xe(c._f)):Pe(e,u),l.mount&&!l.action&&(R(),r&&n.isDirty&&(j.isDirty||M.isDirty)&&(K()||(n.isDirty=!1,N.state.next({...n}))))}},he=(e,r,o,s,c)=>{let l=!1,u=!1,d={name:e};if(!t.disabled){if(!o||s){(j.isDirty||M.isDirty)&&(u=n.isDirty,n.isDirty=d.isDirty=K(),l=u!==d.isDirty);let t=L(T(i,e),r);u=!!T(n.dirtyFields,e),t===n.isDirty?t?H(n.dirtyFields,e):O(n.dirtyFields,e,!0):n.dirtyFields=W(i,a),d.dirtyFields=n.dirtyFields,l||=(j.dirtyFields||M.dirtyFields)&&u!==!t}if(o){let t=T(n.touchedFields,e);t||(O(n.touchedFields,e,o),d.touchedFields=n.touchedFields,l||=(j.touchedFields||M.touchedFields)&&t!==o)}l&&c&&N.state.next(d)}return l?d:{}},ge=(e,r,i,a)=>{let o=T(n.errors,e),s=(j.isValid||M.isValid)&&E(r)&&n.isValid!==r;if(t.delayError&&i?(x=ee(()=>le(e,i)),x(t.delayError)):(clearTimeout(k),x=null,i?O(n.errors,e,i):H(n.errors,e)),(i?!L(o,i):o)||!V(a)||s){let t={...a,...s&&E(r)?{isValid:r}:{},errors:n.errors,name:e};n={...n,...t},N.state.next(t)}},U=async e=>(z(e,!0),await t.resolver(a,t.context,Se(e||p.mount,r,t.criteriaMode,t.shouldUseNativeValidation))),_e=async e=>{let{errors:t}=await U(e);if(z(e),e)for(let r of e){let e=T(t,r);e?p.array.has(r)&&u(e)&&!Object.keys(e).some(e=>!Number.isNaN(Number(e)))?Ne(n.errors,{[r]:e},r):O(n.errors,r,e):H(n.errors,r)}else n.errors=t;return t},ve=async({name:t,eventType:r})=>{if(e.validate){let i=await e.validate({formValues:a,formState:n,name:t,eventType:r});if(u(i))for(let e in i){let t=i[e];t&&Q(`${y}.${e}`,{message:P(t.message)?t.message:``,type:t.type||v.validate})}else P(i)||!i?Q(y,{message:i||``,type:v.validate}):Ge(y);return i}return!0},G=async({fields:r,onlyCheckValid:i,name:o,eventType:s,context:c={valid:!0,runRootValidation:!1}})=>{if(e.validate&&(c.runRootValidation=!0,!await ve({name:o,eventType:s})&&(c.valid=!1,i)))return c.valid;for(let o in r){let l=r[o];if(l){let{_f:r,...u}=l;if(r){let o=p.array.has(r.name),s=l._f&&Te(l._f),u=j.validatingFields||j.isValidating||M.validatingFields||M.isValidating;s&&u&&z([r.name],!0);let d=await Fe(l,p.disabled,a,I,t.shouldUseNativeValidation&&!i,o);if(s&&u&&z([r.name]),d[r.name]&&(c.valid=!1,i)||(!i&&(T(d,r.name)?o?Ne(n.errors,d,r.name):O(n.errors,r.name,d[r.name]):H(n.errors,r.name)),e.shouldUseNativeValidation&&d[r.name]))break}!V(u)&&await G({context:c,onlyCheckValid:i,fields:u,name:o,eventType:s})}}return c.valid},be=()=>{for(let e of p.unMount){let t=T(r,e);t&&(t._f.refs?t._f.refs.every(e=>!de(e)):!de(t._f.ref))&&Ye(e)}p.unMount=new Set},K=(e,n)=>!t.disabled&&(e&&n&&O(a,e,n),!L(Ue(),i)),q=(e,t,n)=>F(e,p,{...l.mount?a:C(t)?i:P(e)?{[e]:t}:t},n,t),we=e=>re(T(l.mount?a:i,e,t.shouldUnregister?T(i,e,[]):[])),Pe=(e,t,n={},i=!1)=>{let s=T(r,e),l=t;if(s){let n=s._f;n&&(!n.disabled&&O(a,e,ye(t,n)),l=se(n.ref)&&c(t)?``:t,ce(n.ref)?[...n.ref.options].forEach(e=>e.selected=l.includes(e.value)):n.refs?o(n.ref)?n.refs.forEach(e=>{(!e.defaultChecked||!e.disabled)&&(Array.isArray(l)?e.checked=!!l.find(t=>t===e.value):e.checked=l===e.value||!!l)}):n.refs.forEach(e=>e.checked=e.value===l):oe(n.ref)?n.ref.value=``:(n.ref.value=l,n.ref.type||N.state.next({name:e,values:i?a:h(a)})))}(n.shouldDirty||n.shouldTouch)&&he(e,l,n.shouldTouch,n.shouldDirty,!0),n.shouldValidate&&He(e)},X=(e,t,n,i=!1)=>{for(let a in t){if(!t.hasOwnProperty(a))return;let o=t[a],c=e+`.`+a,l=T(r,c);(p.array.has(e)||u(o)||l&&!l._f)&&!s(o)?X(c,o,n,i):Pe(c,o,n,i)}},Re=(e,t,i,o)=>{let s=T(r,e),u=p.array.has(e),d=o?t:h(t),f=L(T(a,e),d);if(f||O(a,e,d),u)N.array.next({name:e,values:o?a:h(a)}),(j.isDirty||j.dirtyFields||M.isDirty||M.dirtyFields)&&i.shouldDirty&&(te(),N.state.next({name:e,dirtyFields:n.dirtyFields,isDirty:K(e,d)}));else{let t=Array.isArray(d)&&!d.length||V(d);!s||s._f||c(d)||t?Pe(e,d,i,o):X(e,d,i,o)}if(!f){let t=De(e,p),r=o?a:h(a);N.state.next({...t&&n,name:l.mount||t?e:void 0,values:r})}},Z=(e,t,n={})=>Re(e,t,n,!1),ze=(e,t={})=>{let r=D(e)?e(a):e;if(!L(a,r)){a={...a,...r};for(let e of p.mount)Re(e,T(r,e),t,!0);N.state.next({...n,name:void 0,type:void 0,values:a}),t.shouldValidate&&R()}},Be=async i=>{l.mount=!0;let o=i.target,c=o.name,u=!0,f=T(r,c),m=e=>{u=Number.isNaN(e)||s(e)&&isNaN(e.getTime())||L(e,T(a,c,e))},_=J(t.mode),v=J(t.reValidateMode);if(f){let s,l,y=o.type?xe(f._f):d(i),b=i.type===g.BLUR||i.type===g.FOCUS_OUT,S=!Ee(f._f)&&!e.validate&&!t.resolver&&!T(n.errors,c)&&!f._f.deps||je(b,T(n.touchedFields,c),n.isSubmitted,v,_),C=De(c,p,b);O(a,c,y),b?(!o||!o.readOnly)&&(f._f.onBlur&&f._f.onBlur(i),x&&x(0)):f._f.onChange&&f._f.onChange(i);let w=he(c,y,b),E=!V(w)||C;if(!b&&N.state.next({name:c,type:i.type,values:h(a)}),S)return(j.isValid||M.isValid)&&(t.mode===`onBlur`?b&&R():b||R()),E&&N.state.next({name:c,...C?{}:w});if(!t.resolver&&e.validate&&await ve({name:c,eventType:i.type}),!b&&C&&N.state.next({...n}),t.resolver){let{errors:e}=await U([c]);if(z([c]),m(y),u){let t=Oe(n.errors,r,c),i=Oe(e,r,t.name||c);s=i.error,c=i.name,l=V(e)}}else z([c],!0),s=(await Fe(f,p.disabled,a,I,t.shouldUseNativeValidation))[c],z([c]),m(y),u&&(s?l=!1:(j.isValid||M.isValid)&&(l=await G({fields:r,onlyCheckValid:!0,name:c,eventType:i.type})));u&&(f._f.deps&&(!Array.isArray(f._f.deps)||f._f.deps.length>0)&&He(f._f.deps),ge(c,l,s,w))}},Ve=(e,t)=>{if(T(n.errors,t)&&e.focus)return e.focus(),1},He=async(e,i={})=>{let a,o,s=B(e);if(t.resolver){let t=await _e(C(e)?e:s);a=V(t),o=e?!s.some(e=>T(t,e)):a}else e?(o=(await Promise.all(s.map(async e=>{let t=T(r,e);return await G({fields:t&&t._f?{[e]:t}:t,eventType:g.TRIGGER})}))).every(Boolean),!(!o&&!n.isValid)&&R()):o=a=await G({fields:r,name:e,eventType:g.TRIGGER});return N.state.next({...!P(e)||(j.isValid||M.isValid)&&a!==n.isValid?{}:{name:e},...t.resolver||!e?{isValid:a}:{},errors:n.errors}),i.shouldFocus&&!o&&Y(r,Ve,e?s:p.mount),o},Ue=(e,t)=>{let r={...l.mount?a:i};return t&&(r=ae(t.dirtyFields?n.dirtyFields:n.touchedFields,r)),C(e)?r:P(e)?T(r,e):e.map(e=>T(r,e))},We=(e,t)=>({invalid:!!T((t||n).errors,e),isDirty:!!T((t||n).dirtyFields,e),error:T((t||n).errors,e),isValidating:!!T(n.validatingFields,e),isTouched:!!T((t||n).touchedFields,e)}),Ge=e=>{let t=e?B(e):void 0;t?.forEach(e=>H(n.errors,e)),t?t.forEach(e=>{N.state.next({name:e,errors:n.errors})}):N.state.next({errors:{}})},Q=(e,t,i)=>{let a=(T(r,e,{_f:{}})._f||{}).ref,{ref:o,message:s,type:c,...l}=T(n.errors,e)||{};O(n.errors,e,{...l,...t,ref:a}),N.state.next({name:e,errors:n.errors,isValid:!1}),i&&i.shouldFocus&&a&&a.focus&&a.focus()},Ke=(e,t)=>D(e)?N.state.subscribe({next:n=>`values`in n&&e(n.values||q(void 0,t),n)}):q(e,t,!0),qe=e=>N.state.subscribe({next:t=>{if(Ae(e.name,t.name,e.exact)&&ke(t,e.formState||j,it,e.reRenderRoot)){let r={...a};e.callback({values:r,...n,...t,defaultValues:i})}}}).unsubscribe,Je=e=>(l.mount=!0,M={...M,...e.formState},qe({...e,formState:{...A,...e.formState}})),Ye=(e,o={})=>{for(let s of e?B(e):p.mount)p.mount.delete(s),p.array.delete(s),o.keepValue||(H(r,s),H(a,s)),!o.keepError&&H(n.errors,s),!o.keepDirty&&H(n.dirtyFields,s),!o.keepTouched&&H(n.touchedFields,s),!o.keepIsValidating&&H(n.validatingFields,s),!t.shouldUnregister&&!o.keepDefaultValue&&H(i,s);N.state.next({values:h(a)}),N.state.next({...n,...o.keepDirty?{isDirty:K()}:{}}),!o.keepIsValid&&R()},Xe=({disabled:e,name:t})=>{if(E(e)&&l.mount||e||p.disabled.has(t)){let n=p.disabled.has(t)!==!!e;e?p.disabled.add(t):p.disabled.delete(t),n&&l.mount&&!l.action&&R()}},$=(e,n={})=>{let a=T(r,e),o=E(n.disabled)||E(t.disabled),s=!p.registerName.has(e)&&a&&a._f&&!a._f.mount;return O(r,e,{...a||{},_f:{...a&&a._f?a._f:{ref:{name:e}},name:e,mount:!0,...n}}),p.mount.add(e),a&&!s?Xe({disabled:E(n.disabled)?n.disabled:t.disabled,name:e}):me(e,!0,n.value),{...o?{disabled:n.disabled||t.disabled}:{},...t.progressive?{required:!!n.required,min:Ce(n.min),max:Ce(n.max),minLength:Ce(n.minLength),maxLength:Ce(n.maxLength),pattern:Ce(n.pattern)}:{},name:e,onChange:Be,onBlur:Be,ref:o=>{if(o){p.registerName.add(e),$(e,n),p.registerName.delete(e),a=T(r,e);let t=C(o.value)&&o.querySelectorAll&&o.querySelectorAll(`input,select,textarea`)[0]||o,s=ue(t),c=a._f.refs||[];if(s?c.find(e=>e===t):t===a._f.ref)return;O(r,e,{_f:{...a._f,...s?{refs:[...c.filter(de),t,...Array.isArray(T(i,e))?[{}]:[]],ref:{type:t.type,name:e}}:{ref:t}}}),me(e,!1,void 0,t)}else a=T(r,e,{}),a._f&&(a._f.mount=!1),(t.shouldUnregister||n.shouldUnregister)&&!(f(p.array,e)&&l.action)&&p.unMount.add(e)}}},Ze=()=>t.shouldFocusError&&!t.shouldUseNativeValidation&&Y(r,Ve,p.mount),Qe=e=>{E(e)&&(N.state.next({disabled:e}),Y(r,(t,n)=>{let i=T(r,n);i&&(t.disabled=i._f.disabled||e,Array.isArray(i._f.refs)&&i._f.refs.forEach(t=>{t.disabled=i._f.disabled||e}))},0,!1))},$e=(e,i)=>async o=>{let s;o&&(o.preventDefault&&o.preventDefault(),o.persist&&o.persist());let c=h(a);if(N.state.next({isSubmitting:!0}),t.resolver){let{errors:e,values:t}=await U();z(),n.errors=e,c=h(t)}else await G({fields:r,eventType:g.SUBMIT});if(p.disabled.size)for(let e of p.disabled)H(c,e);if(H(n.errors,b),V(n.errors)){N.state.next({errors:{}});try{await e(c,o)}catch(e){s=e}}else i&&await i({...n.errors},o),Ze(),setTimeout(Ze);if(N.state.next({isSubmitted:!0,isSubmitting:!1,isSubmitSuccessful:V(n.errors)&&!s,submitCount:n.submitCount+1,errors:n.errors}),s)throw s},et=(e,t={})=>{T(r,e)&&(C(t.defaultValue)?Z(e,h(T(i,e))):(Z(e,t.defaultValue),O(i,e,h(t.defaultValue))),t.keepTouched||H(n.touchedFields,e),t.keepDirty||(H(n.dirtyFields,e),n.isDirty=t.defaultValue?K(e,h(T(i,e))):K()),t.keepError||(H(n.errors,e),j.isValid&&R()),N.state.next({...n}))},tt=(e,o={})=>{let s=e?h(e):i,c=h(s),u=V(e),d=c;if(o.keepDefaultValues||(i=s),!o.keepValues){if(o.keepDirtyValues){let e=new Set([...p.mount,...Object.keys(W(i,a))]);for(let t of Array.from(e)){let e=T(n.dirtyFields,t),r=T(a,t),i=T(d,t);e&&!C(r)?O(d,t,r):!e&&!C(i)&&Z(t,i)}}else{if(m&&C(e))for(let e of p.mount){let t=T(r,e);if(t&&t._f){let e=Array.isArray(t._f.refs)?t._f.refs[0]:t._f.ref;if(se(e)){let t=e.closest(`form`);if(t){t.reset();break}}}}if(o.keepFieldsRef)for(let e of p.mount)Z(e,T(d,e));else r={}}if(t.shouldUnregister){if(a=o.keepDefaultValues?h(i):{},o.keepFieldsRef)for(let e of p.mount)O(a,e,T(d,e))}else a=h(d);N.array.next({values:{...d}}),N.state.next({values:{...d}})}p={mount:o.keepDirtyValues?p.mount:new Set,unMount:new Set,array:new Set,registerName:new Set,disabled:new Set,watch:new Set,watchAll:!1,focus:``},l.mount=!j.isValid||!!o.keepIsValid||!!o.keepDirtyValues||!t.shouldUnregister&&!V(d),l.watch=!!t.shouldUnregister,l.keepIsValid=!!o.keepIsValid,l.action=!1,o.keepErrors||(n.errors={}),N.state.next({submitCount:o.keepSubmitCount?n.submitCount:0,isDirty:u?!1:o.keepDirty?n.isDirty:o.keepValues?K():!!(o.keepDefaultValues&&!L(e,i)),isSubmitted:o.keepIsSubmitted?n.isSubmitted:!1,dirtyFields:u?{}:o.keepDirtyValues?o.keepDefaultValues&&a?W(i,a):n.dirtyFields:o.keepDefaultValues&&e?W(i,e):o.keepDirty?n.dirtyFields:{},touchedFields:o.keepTouched?n.touchedFields:{},errors:o.keepErrors?n.errors:{},isSubmitSuccessful:o.keepIsSubmitSuccessful?n.isSubmitSuccessful:!1,isSubmitting:!1,defaultValues:i})},nt=(e,n)=>tt(D(e)?e(a):e,{...t.resetOptions,...n}),rt=(e,t={})=>{let n=T(r,e),i=n&&n._f;if(i){let e=i.refs?i.refs[0]:i.ref;e.focus&&setTimeout(()=>{e.focus(),t.shouldSelect&&D(e.select)&&e.select()})}},it=e=>{n={...n,...e}},at={control:{register:$,unregister:Ye,getFieldState:We,handleSubmit:$e,setError:Q,_subscribe:qe,_runSchema:U,_updateIsValidating:z,_focusError:Ze,_getWatch:q,_getDirty:K,_setValid:R,_setFieldArray:ne,_setDisabledField:Xe,_setErrors:fe,_getFieldArray:we,_reset:tt,_resetDefaultValues:()=>D(t.defaultValues)&&t.defaultValues().then(e=>{nt(e,t.resetOptions),N.state.next({isLoading:!1})}),_removeUnmounted:be,_disableForm:Qe,_subjects:N,_proxyFormState:j,get _fields(){return r},get _formValues(){return a},get _state(){return l},set _state(e){l=e},get _defaultValues(){return i},get _names(){return p},set _names(e){p=e},get _formState(){return n},get _options(){return t},set _options(e){t={...t,...e}}},subscribe:Je,trigger:He,register:$,handleSubmit:$e,watch:Ke,setValue:Z,setValues:ze,getValues:Ue,reset:nt,resetField:et,resetDefaultValues:(e,t={})=>{if(i=h(e),!t.keepDirty){let e=W(i,a);n.dirtyFields=e,n.isDirty=!V(e)}t.keepIsValid||R(),N.state.next({...n,defaultValues:i})},clearErrors:Ge,unregister:Ye,setError:Q,setFocus:rt,getFieldState:We};return{...at,formControl:at}}var Z=()=>{if(typeof crypto<`u`&&crypto.randomUUID)return crypto.randomUUID();let e=typeof performance>`u`?Date.now():performance.now()*1e3;return`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,t=>{let n=(Math.random()*16+e)%16|0;return(t==`x`?n:n&3|8).toString(16)})},ze=(e,t,n={})=>n.shouldFocus||C(n.shouldFocus)?n.focusName||`${e}.${C(n.focusIndex)?t:n.focusIndex}.`:``,Be=(e,t)=>[...e,...B(t)],Ve=e=>Array.isArray(e)?e.map(()=>void 0):void 0;function He(e,t,n){return[...e.slice(0,t),...B(n),...e.slice(t)]}var Ue=(e,t,n)=>Array.isArray(e)?(C(e[n])&&(e[n]=void 0),e.splice(n,0,e.splice(t,1)[0]),e):[],We=(e,t)=>[...B(t),...B(e)];function Ge(e,t){let n=0,r=[...e];for(let e of t)r.splice(e-n,1),n++;return re(r).length?r:[]}var Q=(e,t)=>C(t)?[]:Ge(e,B(t).sort((e,t)=>e-t)),Ke=(e,t,n)=>{[e[t],e[n]]=[e[n],e[t]]},qe=(e,t,n)=>(e[t]=n,e);function Je(e){let t=A(),{control:n=t,name:r,keyName:i=`id`,shouldUnregister:o,rules:s}=e,[c,l]=a.useState(n._getFieldArray(r)),u=a.useRef(n._getFieldArray(r).map(Z)),d=a.useRef(!1);n._names.array.add(r),a.useMemo(()=>s&&c.length>=0&&n.register(r,s),[n,r,c.length,s]),M(()=>n._subjects.array.subscribe({next:({values:e,name:t})=>{if(t===r||!t){let n=T(e,r);Array.isArray(n)?(l(n),u.current=n.map(Z)):t||(l([]),u.current=[])}}}).unsubscribe,[n,r]);let f=a.useCallback(e=>{d.current=!0,n._setFieldArray(r,e)},[n,r]);return a.useEffect(()=>{n._state.action=!1,De(r,n._names)&&n._subjects.state.next({...n._formState});let e=J(n._options.mode);if(d.current&&(!e.isOnSubmit||n._formState.isSubmitted)&&!J(n._options.reValidateMode).isOnSubmit&&!e.isOnBlur)if(n._options.resolver)n._runSchema([r]).then(e=>{n._updateIsValidating([r]);let t=T(e.errors,r),i=T(n._formState.errors,r);(i?!t&&i.type||t&&(i.type!==t.type||i.message!==t.message):t&&t.type)&&(t?O(n._formState.errors,r,t):H(n._formState.errors,r),n._subjects.state.next({errors:n._formState.errors}))});else{let e=T(n._fields,r);e&&e._f&&!(J(n._options.reValidateMode).isOnSubmit&&J(n._options.mode).isOnSubmit)&&Fe(e,n._names.disabled,n._formValues,n._options.criteriaMode===_.all,n._options.shouldUseNativeValidation,!0).then(e=>!V(e)&&n._subjects.state.next({errors:Ne(n._formState.errors,e,r)}))}n._subjects.state.next({name:r,values:h(n._formValues)}),n._names.focus&&Y(n._fields,(e,t)=>{if(n._names.focus&&t.startsWith(n._names.focus)&&e.focus)return e.focus(),1}),n._names.focus=``,n._setValid(),d.current=!1},[c,r,n]),a.useEffect(()=>(!T(n._formValues,r)&&n._setFieldArray(r),()=>{let e=!(n._options.shouldUnregister||o);d.current&&e&&n._subjects.state.next({name:r,values:h(n._formValues)}),e?((e,t)=>{let r=T(n._fields,e);r&&r._f&&(r._f.mount=t)})(r,!1):n.unregister(r)}),[r,n,i,o]),{swap:a.useCallback((e,t)=>{let i=n._getFieldArray(r);Ke(i,e,t),Ke(u.current,e,t),f(i),l(i),n._setFieldArray(r,i,Ke,{argA:e,argB:t},!1)},[f,r,n]),move:a.useCallback((e,t)=>{let i=n._getFieldArray(r);Ue(i,e,t),Ue(u.current,e,t),f(i),l(i),n._setFieldArray(r,i,Ue,{argA:e,argB:t},!1)},[f,r,n]),prepend:a.useCallback((e,t)=>{let i=B(h(e)),a=We(n._getFieldArray(r),i);n._names.focus=ze(r,0,t),u.current=We(u.current,i.map(Z)),f(a),l(a),n._setFieldArray(r,a,We,{argA:Ve(e)})},[f,r,n]),append:a.useCallback((e,t)=>{let i=B(h(e)),a=Be(n._getFieldArray(r),i);n._names.focus=ze(r,a.length-1,t),u.current=Be(u.current,i.map(Z)),f(a),l(a),n._setFieldArray(r,a,Be,{argA:Ve(e)})},[f,r,n]),remove:a.useCallback(e=>{let t=Q(n._getFieldArray(r),e);u.current=Q(u.current,e),f(t),l(t),!Array.isArray(T(n._fields,r))&&O(n._fields,r,void 0),n._setFieldArray(r,t,Q,{argA:e})},[f,r,n]),insert:a.useCallback((e,t,i)=>{let a=B(h(t)),o=He(n._getFieldArray(r),e,a);n._names.focus=ze(r,e,i),u.current=He(u.current,e,a.map(Z)),f(o),l(o),n._setFieldArray(r,o,He,{argA:e,argB:Ve(t)})},[f,r,n]),update:a.useCallback((e,t)=>{let i=h(t),a=qe(n._getFieldArray(r),e,i);u.current=[...a].map((t,n)=>!t||n===e?Z():u.current[n]),f(a),l([...a]),n._setFieldArray(r,a,qe,{argA:e,argB:i},!0,!1)},[f,r,n]),replace:a.useCallback(e=>{let t=B(h(e));u.current=t.map(Z),f([...t]),l([...t]),n._setFieldArray(r,[...t],e=>e,{},!0,!1)},[f,r,n]),fields:a.useMemo(()=>c.map((e,t)=>({...e,[i]:u.current[t]||Z()})),[c,i])}}function Ye(e={}){let t=a.useRef(void 0),n=a.useRef(void 0),[r,i]=a.useState(()=>({...h(Le),isLoading:D(e.defaultValues),errors:e.errors||{},disabled:e.disabled||!1,defaultValues:D(e.defaultValues)?void 0:e.defaultValues}));if(!t.current)if(e.formControl)t.current={...e.formControl,formState:r},e.defaultValues&&!D(e.defaultValues)&&e.formControl.reset(e.defaultValues,e.resetOptions);else{let{formControl:n,...i}=Re(e);t.current={...i,formState:r}}let o=t.current.control;return o._options=e,M(()=>{let e=o._subscribe({formState:o._proxyFormState,callback:()=>i({...o._formState,defaultValues:o._defaultValues}),reRenderRoot:!0});return i(e=>({...e,isReady:!0})),o._formState.isReady=!0,e},[o]),a.useEffect(()=>o._disableForm(e.disabled),[o,e.disabled]),a.useEffect(()=>{e.mode&&(o._options.mode=e.mode),e.reValidateMode&&(o._options.reValidateMode=e.reValidateMode)},[o,e.mode,e.reValidateMode]),a.useEffect(()=>{e.errors&&(o._setErrors(e.errors),o._focusError())},[o,e.errors]),a.useEffect(()=>{e.shouldUnregister&&o._subjects.state.next({values:o._getWatch()})},[o,e.shouldUnregister]),a.useEffect(()=>{if(o._proxyFormState.isDirty){let e=o._getDirty();e!==r.isDirty&&o._subjects.state.next({isDirty:e})}},[o,r.isDirty]),a.useEffect(()=>{e.values&&!L(e.values,n.current)?(o._reset(e.values,{keepFieldsRef:!0,...o._options.resetOptions}),o._options.resetOptions?.keepIsValid||o._setValid(),n.current=e.values,i(e=>({...e}))):o._resetDefaultValues()},[o,e.values]),a.useEffect(()=>{o._state.mount||(o._setValid(),o._state.mount=!0),o._state.watch&&(o._state.watch=!1,o._subjects.state.next({...o._formState})),o._removeUnmounted()}),t.current.formState=a.useMemo(()=>j(r,o),[o,r]),t.current}var Xe=(e,t,n)=>{if(e&&`reportValidity`in e){let r=T(n,t);e.setCustomValidity(r&&r.message||``),e.reportValidity()}},$=(e,t)=>{for(let n in t.fields){let r=t.fields[n];r&&r.ref&&`reportValidity`in r.ref?Xe(r.ref,n,e):r&&r.refs&&r.refs.forEach(t=>Xe(t,n,e))}},Ze=(e,t)=>{t.shouldUseNativeValidation&&$(e,t);let n={};for(let r in e){let i=T(t.fields,r),a=Object.assign(e[r]||{},{ref:i&&i.ref});if(Qe(t.names||Object.keys(e),r)){let e=Object.assign({},T(n,r));O(e,`root`,a),O(n,r,e)}else O(n,r,a)}return n},Qe=(e,t)=>{let n=$e(t).replace(/[.*+?^${}()|\\]/g,`\\$&`);return e.some(e=>$e(e).match(`^${n}\\.\\d+`))};function $e(e){return e.replace(/[\[\]]/g,``)}function et(){return et=Object.assign?Object.assign.bind():function(e){for(var t=1;t0){var s=r.errors[0][0];n[o]={message:s.message,type:s.code}}else n[o]={message:a,type:i};if(r.code===`invalid_union`&&r.errors.forEach(function(t){return t.forEach(function(t){return e.push(et({},t,{path:[].concat(r.path,t.path)}))})}),t){var c=n[o].types,l=c&&c[r.code];n[o]=ne(o,t,n,i,l?[].concat(l,r.message):r.message)}e.shift()};e.length;)r();return n}function it(e,t,a){if(a===void 0&&(a={}),function(e){return`_def`in e&&typeof e._def==`object`&&`typeName`in e._def}(e))return function(n,r,i){try{return Promise.resolve(tt(function(){return Promise.resolve(e[a.mode===`sync`?`parse`:`parseAsync`](n,t)).then(function(e){return i.shouldUseNativeValidation&&$({},i),{errors:{},values:a.raw?Object.assign({},n):e}})},function(e){if(function(e){return Array.isArray(e?.issues)}(e))return{values:{},errors:Ze(nt(e.errors,!i.shouldUseNativeValidation&&i.criteriaMode===`all`),i)};throw e}))}catch(e){return Promise.reject(e)}};if(function(e){return`_zod`in e&&typeof e._zod==`object`}(e))return function(o,s,c){try{return Promise.resolve(tt(function(){return Promise.resolve((a.mode===`sync`?r:i)(e,o,t)).then(function(e){return c.shouldUseNativeValidation&&$({},c),{errors:{},values:a.raw?Object.assign({},o):e}})},function(e){if(function(e){return e instanceof n}(e))return{values:{},errors:Ze(rt(e.issues,!c.shouldUseNativeValidation&&c.criteriaMode===`all`),c)};throw e}))}catch(e){return Promise.reject(e)}};throw Error(`Invalid input: not a Zod schema`)}export{ee as a,Ye as i,z as n,Je as r,it as t}; \ No newline at end of file diff --git a/spinifex/services/spinifexui/frontend/dist/index.html b/spinifex/services/spinifexui/frontend/dist/index.html index 201fbf542..d70382476 100644 --- a/spinifex/services/spinifexui/frontend/dist/index.html +++ b/spinifex/services/spinifexui/frontend/dist/index.html @@ -8,6 +8,18 @@ + + + + + + + + + + + + diff --git a/spinifex/services/spinifexui/frontend/oxlint.config.ts b/spinifex/services/spinifexui/frontend/oxlint.config.ts index 7d27664b4..99edda793 100644 --- a/spinifex/services/spinifexui/frontend/oxlint.config.ts +++ b/spinifex/services/spinifexui/frontend/oxlint.config.ts @@ -1,11 +1,11 @@ import { defineConfig } from "oxlint" import core from "ultracite/oxlint/core" import react from "ultracite/oxlint/react" -import remix from "ultracite/oxlint/remix" +import tanstack from "ultracite/oxlint/tanstack" import vitest from "ultracite/oxlint/vitest" export default defineConfig({ - extends: [core, react, remix, vitest], + extends: [core, react, vitest, tanstack], jsPlugins: [ "@tanstack/eslint-plugin-query", "@tanstack/eslint-plugin-router", @@ -25,6 +25,7 @@ export default defineConfig({ "@tanstack/router/create-route-property-order": "error", "@tanstack/router/route-param-names": "error", "react/rules-of-hooks": "error", + "react/no-object-type-as-default-prop": "off", "react/no-array-index-key": "error", "react/jsx-no-constructed-context-values": "error", "react/jsx-no-comment-textnodes": "error", @@ -107,5 +108,5 @@ export default defineConfig({ }, }, ], - ignorePatterns: ["dist", "src/routeTree.gen.ts", "*.config.ts"], + ignorePatterns: core.ignorePatterns, }) diff --git a/spinifex/services/spinifexui/frontend/package.json b/spinifex/services/spinifexui/frontend/package.json index 551e9c3c6..0baa35ffe 100644 --- a/spinifex/services/spinifexui/frontend/package.json +++ b/spinifex/services/spinifexui/frontend/package.json @@ -14,25 +14,27 @@ }, "dependencies": { "@aws-crypto/sha256-browser": "^5.2.0", - "@aws-sdk/client-ec2": "^3.1045.0", - "@aws-sdk/client-elastic-load-balancing-v2": "^3.1045.0", - "@aws-sdk/client-iam": "^3.1045.0", - "@aws-sdk/client-s3": "^3.1045.0", - "@aws-sdk/lib-storage": "^3.1045.0", - "@base-ui/react": "^1.4.1", - "@fontsource-variable/geist": "^5.2.8", + "@aws-sdk/client-acm": "^3.1062.0", + "@aws-sdk/client-ec2": "^3.1062.0", + "@aws-sdk/client-eks": "^3.1062.0", + "@aws-sdk/client-elastic-load-balancing-v2": "^3.1062.0", + "@aws-sdk/client-iam": "^3.1062.0", + "@aws-sdk/client-s3": "^3.1062.0", + "@aws-sdk/lib-storage": "^3.1062.0", + "@base-ui/react": "^1.5.0", + "@fontsource-variable/geist": "^5.2.9", "@fontsource-variable/jetbrains-mono": "^5.2.8", - "@hookform/resolvers": "^5.2.2", - "@smithy/protocol-http": "^5.3.14", - "@smithy/signature-v4": "^5.3.14", - "@tanstack/react-query": "^5.100.10", - "@tanstack/react-router": "^1.170.1", + "@hookform/resolvers": "^5.4.0", + "@smithy/protocol-http": "^5.4.6", + "@smithy/signature-v4": "^5.4.6", + "@tanstack/react-query": "^5.101.0", + "@tanstack/react-router": "^1.170.13", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "lucide-react": "^1.16.0", - "react": "^19.2.6", - "react-dom": "^19.2.6", - "react-hook-form": "^7.76.0", + "lucide-react": "^1.17.0", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-hook-form": "^7.77.0", "tailwind-merge": "^3.6.0", "tailwindcss": "^4.3.0", "zod": "^4.4.3" @@ -40,28 +42,28 @@ "devDependencies": { "@rolldown/plugin-babel": "^0.2.3", "@tailwindcss/vite": "^4.3.0", - "@tanstack/eslint-plugin-query": "^5.100.10", + "@tanstack/eslint-plugin-query": "^5.101.0", "@tanstack/eslint-plugin-router": "^1.162.0", - "@tanstack/router-plugin": "^1.168.1", + "@tanstack/router-plugin": "^1.168.16", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/node": "24.12.3", - "@types/react": "^19.2.14", + "@types/node": "24.13.1", + "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", - "@typescript/native-preview": "7.0.0-dev.20260515.1", + "@typescript/native-preview": "7.0.0-dev.20260605.1", "@vitejs/plugin-basic-ssl": "^2.3.0", "@vitejs/plugin-react": "^6.0.2", - "@vitest/coverage-v8": "^4.1.6", + "@vitest/coverage-v8": "^4.1.8", "babel-plugin-react-compiler": "^1.0.0", - "happy-dom": "^20.9.0", - "oxfmt": "^0.50.0", - "oxlint": "^1.65.0", - "oxlint-tsgolint": "^0.22.1", + "happy-dom": "^20.10.1", + "oxfmt": "^0.53.0", + "oxlint": "^1.68.0", + "oxlint-tsgolint": "^0.23.0", "typescript": "^6.0.3", - "ultracite": "7.7.0", - "vite": "^8.0.13", - "vitest": "^4.1.6" + "ultracite": "7.8.1", + "vite": "^8.0.16", + "vitest": "^4.1.8" }, - "packageManager": "pnpm@11.1.0+sha512.0c44e842e5686b2c061a81adda8b2258bd8818e9704b2cf2c63d56b931a7b2e910092e085027003b96ca3911ab56a07f6df5abaed2be9925034cdd686a535b14" + "packageManager": "pnpm@11.5.2+sha512.71c631e382066efc25625d5cf029075de07b61b37f6e27350fbd84b1bda5864c8c1967adc280776b45c30a715c0359a3be08fef42d5bb09e2b99029979692916" } diff --git a/spinifex/services/spinifexui/frontend/pnpm-lock.yaml b/spinifex/services/spinifexui/frontend/pnpm-lock.yaml index 36755985c..1a49cd47a 100644 --- a/spinifex/services/spinifexui/frontend/pnpm-lock.yaml +++ b/spinifex/services/spinifexui/frontend/pnpm-lock.yaml @@ -11,45 +11,51 @@ importers: '@aws-crypto/sha256-browser': specifier: ^5.2.0 version: 5.2.0 + '@aws-sdk/client-acm': + specifier: ^3.1062.0 + version: 3.1063.0 '@aws-sdk/client-ec2': - specifier: ^3.1045.0 - version: 3.1045.0 + specifier: ^3.1062.0 + version: 3.1063.0 + '@aws-sdk/client-eks': + specifier: ^3.1062.0 + version: 3.1063.0 '@aws-sdk/client-elastic-load-balancing-v2': - specifier: ^3.1045.0 - version: 3.1045.0 + specifier: ^3.1062.0 + version: 3.1063.0 '@aws-sdk/client-iam': - specifier: ^3.1045.0 - version: 3.1045.0 + specifier: ^3.1062.0 + version: 3.1063.0 '@aws-sdk/client-s3': - specifier: ^3.1045.0 - version: 3.1045.0 + specifier: ^3.1062.0 + version: 3.1063.0 '@aws-sdk/lib-storage': - specifier: ^3.1045.0 - version: 3.1045.0(@aws-sdk/client-s3@3.1045.0) + specifier: ^3.1062.0 + version: 3.1063.0(@aws-sdk/client-s3@3.1063.0) '@base-ui/react': - specifier: ^1.4.1 - version: 1.4.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^1.5.0 + version: 1.5.0(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@fontsource-variable/geist': - specifier: ^5.2.8 - version: 5.2.8 + specifier: ^5.2.9 + version: 5.2.9 '@fontsource-variable/jetbrains-mono': specifier: ^5.2.8 version: 5.2.8 '@hookform/resolvers': - specifier: ^5.2.2 - version: 5.2.2(react-hook-form@7.76.0(react@19.2.6)) + specifier: ^5.4.0 + version: 5.4.0(react-hook-form@7.77.0(react@19.2.7)) '@smithy/protocol-http': - specifier: ^5.3.14 - version: 5.3.14 + specifier: ^5.4.6 + version: 5.4.6 '@smithy/signature-v4': - specifier: ^5.3.14 - version: 5.3.14 + specifier: ^5.4.6 + version: 5.4.6 '@tanstack/react-query': - specifier: ^5.100.10 - version: 5.100.10(react@19.2.6) + specifier: ^5.101.0 + version: 5.101.0(react@19.2.7) '@tanstack/react-router': - specifier: ^1.170.1 - version: 1.170.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^1.170.13 + version: 1.170.13(react-dom@19.2.7(react@19.2.7))(react@19.2.7) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -57,17 +63,17 @@ importers: specifier: ^2.1.1 version: 2.1.1 lucide-react: - specifier: ^1.16.0 - version: 1.16.0(react@19.2.6) + specifier: ^1.17.0 + version: 1.17.0(react@19.2.7) react: - specifier: ^19.2.6 - version: 19.2.6 + specifier: ^19.2.7 + version: 19.2.7 react-dom: - specifier: ^19.2.6 - version: 19.2.6(react@19.2.6) + specifier: ^19.2.7 + version: 19.2.7(react@19.2.7) react-hook-form: - specifier: ^7.76.0 - version: 7.76.0(react@19.2.6) + specifier: ^7.77.0 + version: 7.77.0(react@19.2.7) tailwind-merge: specifier: ^3.6.0 version: 3.6.0 @@ -80,76 +86,76 @@ importers: devDependencies: '@rolldown/plugin-babel': specifier: ^0.2.3 - version: 0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.1)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) + version: 0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) '@tailwindcss/vite': specifier: ^4.3.0 - version: 4.3.0(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) '@tanstack/eslint-plugin-query': - specifier: ^5.100.10 - version: 5.100.10(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + specifier: ^5.101.0 + version: 5.101.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) '@tanstack/eslint-plugin-router': specifier: ^1.162.0 version: 1.162.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) '@tanstack/router-plugin': - specifier: ^1.168.1 - version: 1.168.1(@tanstack/react-router@1.170.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) + specifier: ^1.168.16 + version: 1.168.16(@tanstack/react-router@1.170.13(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) '@types/node': - specifier: 24.12.3 - version: 24.12.3 + specifier: 24.13.1 + version: 24.13.1 '@types/react': - specifier: ^19.2.14 - version: 19.2.14 + specifier: ^19.2.17 + version: 19.2.17 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.17) '@typescript/native-preview': - specifier: 7.0.0-dev.20260515.1 - version: 7.0.0-dev.20260515.1 + specifier: 7.0.0-dev.20260605.1 + version: 7.0.0-dev.20260605.1 '@vitejs/plugin-basic-ssl': specifier: ^2.3.0 - version: 2.3.0(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) + version: 2.3.0(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) '@vitejs/plugin-react': specifier: ^6.0.2 - version: 6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.1)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) + version: 6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) '@vitest/coverage-v8': - specifier: ^4.1.6 - version: 4.1.6(vitest@4.1.6) + specifier: ^4.1.8 + version: 4.1.8(vitest@4.1.8) babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 happy-dom: - specifier: ^20.9.0 - version: 20.9.0 + specifier: ^20.10.1 + version: 20.10.1 oxfmt: - specifier: ^0.50.0 - version: 0.50.0 + specifier: ^0.53.0 + version: 0.53.0 oxlint: - specifier: ^1.65.0 - version: 1.65.0(oxlint-tsgolint@0.22.1) + specifier: ^1.68.0 + version: 1.68.0(oxlint-tsgolint@0.23.0) oxlint-tsgolint: - specifier: ^0.22.1 - version: 0.22.1 + specifier: ^0.23.0 + version: 0.23.0 typescript: specifier: ^6.0.3 version: 6.0.3 ultracite: - specifier: 7.7.0 - version: 7.7.0(oxfmt@0.50.0)(oxlint@1.65.0(oxlint-tsgolint@0.22.1)) + specifier: 7.8.1 + version: 7.8.1(oxfmt@0.53.0)(oxlint@1.68.0(oxlint-tsgolint@0.23.0)) vite: - specifier: ^8.0.13 - version: 8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0) + specifier: ^8.0.16 + version: 8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0) vitest: - specifier: ^4.1.6 - version: 4.1.6(@types/node@24.12.3)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) + specifier: ^4.1.8 + version: 4.1.8(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.10.1)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) packages: @@ -179,261 +185,209 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-ec2@3.1045.0': - resolution: {integrity: sha512-/ShpsuHZbpwiNBSRc6rupxFRh/NqKTaxAZv34w58tPFOgghpJ8vPX2VV+FZiZJWHqrdQfwk7PhStxRpOz+ru+w==} + '@aws-sdk/checksums@3.1000.2': + resolution: {integrity: sha512-PIha+kauTbp6IRmOpYktPTrlfrrSqDVixvhO/EUOFOf62DPX81CaJoHJreuA1m9HYpSKyXf99BKjU1dvJPeUfw==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-elastic-load-balancing-v2@3.1045.0': - resolution: {integrity: sha512-tyhEmOUj6xEkqfWHooJs0IlOD5Ltry8fnY/ePyIU9d2k6zj0bTSROLAb2pXGxWeDYyLZJYnQvccowbz1LCj0wQ==} + '@aws-sdk/client-acm@3.1063.0': + resolution: {integrity: sha512-tvFj8eQmZWxQB2ZUZBKXNr4t+OI5DM2ZDgSA26f1fFblhRswkig20GzoSHQq8hMbnLnRX836iSmYXw+7Z9FOpg==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-iam@3.1045.0': - resolution: {integrity: sha512-1kCDjvkOUyZ7MxQu2ybgXxYDW+8PG+cHjVCk0IXZwYy5R2dhAeHLLlDGJB9jA/KeSh6+XcN5uMlApqJGP1zbUg==} + '@aws-sdk/client-ec2@3.1063.0': + resolution: {integrity: sha512-RVVqRR6SBQwTHnttHXj/QbUwagA1a86tqJhyDik9/REviSfu7XGJ61RlXmDfyeU76bhv3NJ4mW2EZrhWO2/0hQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-s3@3.1045.0': - resolution: {integrity: sha512-fsuO3Y6t+3Ro9Bsg41DKj4Sfy53CGSrhnMldNplWmG8Tx0UbYk+YDa4RD1hVlJpERw4JBmPkl0+J9qlxMh1pcA==} + '@aws-sdk/client-eks@3.1063.0': + resolution: {integrity: sha512-+s5ZAxXW4A6tmfE/hF23woIsoCA3zogPCO+T4hj45CF0O6MRpwDVjJkMhjfDWnCzKvhnrJwabfRXk5LGyeIGnQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.974.8': - resolution: {integrity: sha512-njR2qoG6ZuB0kvAS2FyICsFZJ6gmCcf2X/7JcD14sUvGDm26wiZ5BrA6LOiUxKFEF+IVe7kdroxyE00YlkiYsw==} + '@aws-sdk/client-elastic-load-balancing-v2@3.1063.0': + resolution: {integrity: sha512-MF5OmHwWXKEQNsmXTbOvjuRHxs5uGUaMjFcqvszNnx52AGOeFdx/ph7p45XHa6YhSSVsR7yt5YlaJzYFSyAb8w==} engines: {node: '>=20.0.0'} - '@aws-sdk/crc64-nvme@3.972.7': - resolution: {integrity: sha512-QUagVVBbC8gODCF6e1aV0mE2TXWB9Opz4k8EJFdNrujUVQm5R4AjJa1mpOqzwOuROBzqJU9zawzig7M96L8Ejg==} + '@aws-sdk/client-iam@3.1063.0': + resolution: {integrity: sha512-5BPya0CvFQvgI4Ru2rzoQ/GQLZ8LevVNXHPNT/q0otms1Yg4rRi2G4/yoN6zmsU3Og4CHSOSuS287zwFVNTlQA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.34': - resolution: {integrity: sha512-XT0jtf8Fw9JE6ppsQeoNnZRiG+jqRixMT1v1ZR17G60UvVdsQmTG8nbEyHuEPfMxDXEhfdARaM/XiEhca4lGHQ==} + '@aws-sdk/client-s3@3.1063.0': + resolution: {integrity: sha512-ETn+vvmZVK1MmOZwVBXmWANpmD5iTbzojIqyEIoZ86qo+8oWy35S8QyQNE/ZDI+WHgMU1dS+VSYbpRl1QkEySg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.36': - resolution: {integrity: sha512-DPoGWfy7J7RKxvbf5kOKIGQkD2ek3dbKgzKIGrnLuvZBz5myU+Im/H6pmc14QcnFbqHMqxvtWSgRDSJW3qXLQg==} + '@aws-sdk/core@3.974.18': + resolution: {integrity: sha512-JDYCPI0j7zGrzXTDFsLB346cxss7J/AxH7+O0MzWlqppJBEyB9Qe6TQXRL6iwLUo/xZkNv9KFmBL2hqElmwW0g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.38': - resolution: {integrity: sha512-oDzUBu2MGJFgoar05sPMCwSrhw44ASyccrHzj66vO69OZqi7I6hZZxXfuPLC8OCzW7C+sU+bI73XHij41yekgQ==} + '@aws-sdk/credential-provider-env@3.972.44': + resolution: {integrity: sha512-3hKJVrZ7bqXzDAXCQp+OaQ1ASN+vWstaNuEH418wQVl//cRZhqhfR9Bjk1qIWmgUGe8/D3gdO73PgidRj378EQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.38': - resolution: {integrity: sha512-g1NosS8qe4OF++G2UFCM5ovSkgipC7YYor5KCWatG0UoMSO5YFj9C8muePlyVmOBV/WTI16Jo3/s1NUo/o1Bww==} + '@aws-sdk/credential-provider-http@3.972.46': + resolution: {integrity: sha512-VhwC9pGAZHhiQ2xSViyOPDFqvr9aRxGCAXZtADsUhU3R65nad7y//CwynE6mQnWNR+suRlqE79W36IVayL+m1g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.39': - resolution: {integrity: sha512-HEswDQyxUtadoZ/bJsPPENHg7R0Lzym5LuMksJeHvqhCOpP+rtkDLKI4/ZChH4w3cf5kG8n6bZuI8PzajoiqMg==} + '@aws-sdk/credential-provider-ini@3.972.50': + resolution: {integrity: sha512-09Xi6ovxiK42+De/qBGF71sT5F2bWgYM+1fFyDwSOpy1xpsQ5R/naIu7MVDpH6Dic36QNc8dAv4KADtMGK2JYg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.34': - resolution: {integrity: sha512-T3IFs4EVmVi1dVN5RciFnklCANSzvrQd/VuHY9ThHSQmYkTogjcGkoJEr+oNUPQZnso52183088NqysMPji1/Q==} + '@aws-sdk/credential-provider-login@3.972.49': + resolution: {integrity: sha512-EfJF/1Fh9mI4pZyoheU2RY9xUhTcugIZNkD63+orXMkYj/QXacJNbKVDUK90Yv5hE+aX+rt9J/EZ9Qr3vKOa7g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.38': - resolution: {integrity: sha512-5ZxG+t0+3Q3QPh8KEjX6syskhgNf7I0MN7oGioTf6Lm1NTjfP7sIcYGNsthXC2qR8vcD3edNZwCr2ovfSSWuRA==} + '@aws-sdk/credential-provider-node@3.972.52': + resolution: {integrity: sha512-7QX+PbyiWBEOVipJq8Nke/TqXT6lAPLE7fvTaopa39/IVWuLfS+Fzdy71sZJONf/mLGgmtj6aU17+REw3+aRrw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.38': - resolution: {integrity: sha512-lYHFF30DGI20jZcYX8cm6Ns0V7f1dDN6g/MBDLTyD/5iw+bXs3yBr2iAiHDkx4RFU5JgsnZvCHYKiRVPRdmOgw==} + '@aws-sdk/credential-provider-process@3.972.44': + resolution: {integrity: sha512-V+UUhZpRP7QDRhi+qgBDisM9tUBnYmMje8Bk77A6MZsfeGeGdMsQXmaHP1CDYFcept0o/Rz5g2Y0TMeVlG9dzg==} engines: {node: '>=20.0.0'} - '@aws-sdk/lib-storage@3.1045.0': - resolution: {integrity: sha512-F7dp2ST/83Iz6JTMMmUYEXxg7R1JDewfvzJeWWiDTwe0vLsg67JTN7OsBe6G8DWYbLQ+EyPWkyc3oFnxOjCVfg==} + '@aws-sdk/credential-provider-sso@3.972.49': + resolution: {integrity: sha512-9QqOYGuh5tZ76OzaT68kwI78AH+5lS/uZGGvkfxb3fc8FzRrIz2jOufNTliEBEeSAwmgK2rWLNsK+IB3zbtNPA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.49': + resolution: {integrity: sha512-IYx1lN38MnnPXv+NBLpuATu0cZakbZ321TAfjW+aVkw7HIJF38YnEwdeEO55MSl3pl7hIX1IvvnD6EmnAzmAJw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/lib-storage@3.1063.0': + resolution: {integrity: sha512-WfDUIna6HicL8oTs26etBIr7yGwHIkadr8ecp/SjQAhX3t5xM/PLJCl0ZJ+bcLtYyjmWrO3Ydyf/EUyraEz8Qw==} engines: {node: '>=20.0.0'} peerDependencies: - '@aws-sdk/client-s3': ^3.1045.0 + '@aws-sdk/client-s3': ^3.1063.0 - '@aws-sdk/middleware-bucket-endpoint@3.972.10': - resolution: {integrity: sha512-Vbc2frZH7wXlMNd+ZZSXUEs/l1Sv8Jj4zUnIfwrYF5lwaLdXHZ9xx4U3rjUcaye3HRhFVc+E5DbBxpRAbB16BA==} + '@aws-sdk/middleware-flexible-checksums@3.974.27': + resolution: {integrity: sha512-bZqezPLdllFC4VAeV/f+EIc/hz56ab3TD/+4zNCgOgmG5ZHAE5dMHrX1gtTwdcQXbPr3KR7x3zTC3zuCTE6+ng==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-expect-continue@3.972.10': - resolution: {integrity: sha512-2Yn0f1Qiq/DjxYR3wfI3LokXnjOhFM7Ssn4LTdFDIxRMCE6I32MAsVnhPX1cUZsuVA9tiZtwwhlSLAtFGxAZlQ==} + '@aws-sdk/middleware-sdk-ec2@3.972.32': + resolution: {integrity: sha512-pYIQfl8jN0HVuz6iDisD4wxA59MgLLp2mAs6ziPPA4OMGXkQ5eDQgZRU1K7d5b6tFcdTmQTYD+pcqqUpeG5Plw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.974.16': - resolution: {integrity: sha512-6ru8doI0/XzszqLIPXf0E/V7HhAw1Pu94010XCKYtBUfD0LxF0BuOzrUf8OQGR6j2o6wgKTHUniOmndQycHwCA==} + '@aws-sdk/middleware-sdk-s3@3.972.48': + resolution: {integrity: sha512-MRTqx8wD/T3REt6LTT3/yN8rrp6+xIHrbUekkDYJTYWVch70mwtdJBovR4qKJz1jIPlbN+9R/Sn6R04BfsglzA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.972.10': - resolution: {integrity: sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==} + '@aws-sdk/nested-clients@3.997.17': + resolution: {integrity: sha512-lDRgraoTfKRawUyc176Ow93mrNrOho/x+EoK4C+lKU+vKkHWhNhzvSMVAx0WEJUJoeQxxDN5ZdKMfiGEyNejig==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-location-constraint@3.972.10': - resolution: {integrity: sha512-rI3NZvJcEvjoD0+0PI0iUAwlPw2IlSlhyvgBK/3WkKJQE/YiKFedd9dMN2lVacdNxPNhxL/jzQaKQdrGtQagjQ==} + '@aws-sdk/signature-v4-multi-region@3.996.32': + resolution: {integrity: sha512-llvApLcsWtmRFhG2wT3WIp1CmDeRaIYutqty1ZZXoMzK7TiJ6MOLOimk9eXUS8PwgG4ew4pa4QAbt0lfhn++1w==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.972.10': - resolution: {integrity: sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==} + '@aws-sdk/token-providers@3.1063.0': + resolution: {integrity: sha512-nYDaWWdzjKiDP5xj8k4oUgcYd4WPgzfAOgdU5vJsaqH/07Dfvm7ffisHCFJ+NEl7kUC9JEIUxh0kznvenbo3NQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.972.11': - resolution: {integrity: sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-sdk-ec2@3.972.22': - resolution: {integrity: sha512-i9BeGH8OIPXmDuu5VZEvq3QVzP2Upt0QJsW/0ziS873CJ+zFiCyobiqQ3QTgJpxIsBBXBswsRQajEG+PvuKxYg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-sdk-s3@3.972.37': - resolution: {integrity: sha512-Km7M+i8DrLArVzrid1gfxeGhYHBd3uxvE77g0s5a52zPSVosxzQBnJ0gwWb6NIp/DOk8gsBMhi7V+cpJG0ndTA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-ssec@3.972.10': - resolution: {integrity: sha512-Gli9A0u8EVVb+5bFDGS/QbSVg28w/wpEidg1ggVcSj65BDTdGR6punsOcVjqdiu1i42WHWo51MCvARPIIz9juw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-user-agent@3.972.38': - resolution: {integrity: sha512-iz+B29TXcAZsJpwB+AwG/TTGA5l/VnmMZ2UxtiySOZjI6gCdmviXPwdgzcmuazMy16rXoPY4mYCGe7zdNKfx5A==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/nested-clients@3.997.6': - resolution: {integrity: sha512-WBDnqatJl+kGObpfmfSxqnXeYTu3Me8wx8WCtvoxX3pfWrrTv8I4WTMSSs7PZqcRcVh8WeUKMgGFjMG+52SR1w==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/region-config-resolver@3.972.13': - resolution: {integrity: sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/signature-v4-multi-region@3.996.25': - resolution: {integrity: sha512-+CMIt3e1VzlklAECmG+DtP1sV8iKq25FuA0OKpnJ4KA0kxUtd7CgClY7/RU6VzJBQwbN4EJ9Ue6plvqx1qGadw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/token-providers@3.1041.0': - resolution: {integrity: sha512-Th7kPI6YPtvJUcdznooXJMy+9rQWjmEF81LxaJssngBzuysK4a/x+l8kjm1zb7nYsUPbndnBdUnwng/3PLvtGw==} + '@aws-sdk/types@3.973.11': + resolution: {integrity: sha512-YjS0qFuECClRh4qhEyW8XagW0fwEPBeZ1cfsW/gU73Kh/ExFILxbzxOfPCmzF/2DwEvhvsHYt0b0qnvStwKYrg==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.6': resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.8': - resolution: {integrity: sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-arn-parser@3.972.3': - resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-endpoints@3.996.8': - resolution: {integrity: sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-format-url@3.972.10': - resolution: {integrity: sha512-DEKiHNJVtNxdyTeQspzY+15Po/kHm6sF0Cs4HV9Q2+lplB63+DrvdeiSoOSdWEWAoO2RcY1veoXVDz2tWxWCgQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/util-locate-window@3.965.5': resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-browser@3.972.10': - resolution: {integrity: sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==} - - '@aws-sdk/util-user-agent-node@3.973.24': - resolution: {integrity: sha512-ZWwlkjcIp7cEL8ZfTpTAPNkwx25p7xol0xlKoWVVf22+nsjwmLcHYtTPjIV1cSpmB/b6DaK4cb1fSkvCXHgRdw==} - engines: {node: '>=20.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - - '@aws-sdk/xml-builder@3.972.22': - resolution: {integrity: sha512-PMYKKtJd70IsSG0yHrdAbxBr+ZWBKLvzFZfD3/urxgf6hXVMzuU5M+3MJ5G67RpOmLBu1fAUN65SbWuKUCOlAA==} + '@aws-sdk/xml-builder@3.972.28': + resolution: {integrity: sha512-lI/l3c/vPvsxmspzV63NfS3x9q4CkMmdhJy4QiM+NThAufVkDvi/PZZQ6xETnICL0UD7jI808pY83gllf86RFg==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.4': resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} engines: {node: '>=18.0.0'} - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.3': - resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.29.2': - resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.3': - resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/runtime@7.29.2': resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} '@babel/types@7.29.0': resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@base-ui/react@1.4.1': - resolution: {integrity: sha512-Ab5/LIhcmL8BQcsBUYiOfkSDRdLpvgUBzMK30cu684JPcLclYlztharvCZyNNgzJtbAiREzI9q0pI5erHCMgCw==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@base-ui/react@1.5.0': + resolution: {integrity: sha512-z1gSAlced1yY+iM+mHDEtIkD8UI3Ebs52MuBPxvV6f5hRutk+xvCH/wuB7hDqDzK9JG5FoMz5nhrqtSs1wjt1A==} engines: {node: '>=14.0.0'} peerDependencies: '@date-fns/tz': ^1.2.0 @@ -449,8 +403,8 @@ packages: date-fns: optional: true - '@base-ui/utils@0.2.8': - resolution: {integrity: sha512-jvOi+c+ftGlGotNcKnzPVg2IhCaDTB6/6R3JeqdjdXktuAJi3wKH9T7+svuaKh1mmfVU11UWzUZVH74JDfi/wQ==} + '@base-ui/utils@0.2.9': + resolution: {integrity: sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw==} peerDependencies: '@types/react': ^17 || ^18 || ^19 react: ^17 || ^18 || ^19 @@ -463,12 +417,12 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@clack/core@1.3.1': - resolution: {integrity: sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA==} + '@clack/core@1.4.1': + resolution: {integrity: sha512-FILJa1gGKEFTGZAJE9RpVhrjKz3c3h4ar60dSv6cGuDqufQ84YEIS3GAGvZiN+H6yaLbbvTFNejjCC4tXpZEuw==} engines: {node: '>= 20.12.0'} - '@clack/prompts@1.4.0': - resolution: {integrity: sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA==} + '@clack/prompts@1.5.1': + resolution: {integrity: sha512-zccHj2z2oCCO4yrDiRSlFOxWerGqRiysP7a5jPK6uoI9URKAquwY42Dd/iUP8JWHxEzdRe4TlbvZCo8z1/mhrw==} engines: {node: '>= 20.12.0'} '@emnapi/core@1.10.0': @@ -533,14 +487,14 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@fontsource-variable/geist@5.2.8': - resolution: {integrity: sha512-cJ6m9e+8MQ5dCYJsLylfZrgBh6KkG4bOLckB35Tr9J/EqdkEM6QllH5PxqP1dhTvFup+HtMRPuz9xOjxXJggxw==} + '@fontsource-variable/geist@5.2.9': + resolution: {integrity: sha512-TP+QSBG3wxKGPE33CbMy/L0Nu3qvJ6Fy81Yc4LnQ95xH+i+cfEp8fyU8/kfV14YwszxIFPhnoMTbjL71waVpyQ==} '@fontsource-variable/jetbrains-mono@5.2.8': resolution: {integrity: sha512-WBA9elru6Jdp5df2mES55wuOO0WIrn3kpXnI4+W2ek5u3ZgLS9XS4gmIlcQhiZOWEKl95meYdvK7xI+ETLCq/Q==} - '@hookform/resolvers@5.2.2': - resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} + '@hookform/resolvers@5.4.0': + resolution: {integrity: sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==} peerDependencies: react-hook-form: ^7.55.0 @@ -589,374 +543,374 @@ packages: '@nodable/entities@2.1.0': resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} - '@oxc-project/types@0.130.0': - resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} - '@oxfmt/binding-android-arm-eabi@0.50.0': - resolution: {integrity: sha512-ICXQVKrDvsWUtfx6EiVJxfWrajKTwTfRV8vz2XiMkxZeuCKJLgD4YAj6dE3BWvpqDlkVkie4VSTAtMUWO9LDXg==} + '@oxfmt/binding-android-arm-eabi@0.53.0': + resolution: {integrity: sha512-XfVM8AmIovBTKXCt14Op5wbfcoM8418nttd+nhMgM3RAVaJg1MtJc73FyWfUt0oxLyBGVwfniNVUsbV/b3VmPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.50.0': - resolution: {integrity: sha512-quwjLQFkuW6OwLHeDeIXsTzOmipQFQbqsYN9HLk2B5I01IlAQZHP1UiLIg0O7pP+dUgPD2AD7SCYA3gs6NH5/g==} + '@oxfmt/binding-android-arm64@0.53.0': + resolution: {integrity: sha512-btHDfXckwdf9zgyAVznfZkf+GVyB0I1m1hlvaOMRx2xoyz3hphfPX97s89J3wfCN8QBETLtk4lQUaeOkrMuQOg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.50.0': - resolution: {integrity: sha512-ikU5umElcMi78/TNI334wtjr5WZ5F4nWa1aIDseAKKGL0W3ygxeYKkrIJ0fggWa8MOon66BmG3xCqmX1m9YAOw==} + '@oxfmt/binding-darwin-arm64@0.53.0': + resolution: {integrity: sha512-k2RjMcSTkHjoOlsVGbL35JVzXL+oQco3GHPl/5kjebVF4oHNfE24In8F5isqBh9LBJucycWHKDXdGrCchdWcHQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.50.0': - resolution: {integrity: sha512-WT4MOYG4mv9IXrH0m60vHsJh+rRMPSOKTQmwDpwmgQ+DuW/i5dU4pqc0HDO5uclO5vjz5IFX5z/taW86LSVe/g==} + '@oxfmt/binding-darwin-x64@0.53.0': + resolution: {integrity: sha512-65jIBE2H1l5SSs16fmv6/7b6sAx/WpvnsgDhVWK9qSjNFDUro7MPQ6q5UhpY7kl46yltfR046iAnxy/Bzqbiew==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.50.0': - resolution: {integrity: sha512-gH0rycVXqV4juWkvLs2uPMtTyppDc7qEUVzXAxnQ7FpcSZNXqKowUgtjH8q67ngj416r8+4NnAlyR/D35zwwhQ==} + '@oxfmt/binding-freebsd-x64@0.53.0': + resolution: {integrity: sha512-oYe1gkz7U49PCYrS9147d2fJZj8mDI4Di6AvlsU5fu9p+Tq8S7qqOMSZjUiVTLX8bXuSA9Lk/tIxuegVjkNYRA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.50.0': - resolution: {integrity: sha512-wL/k+o0hiTeRvi/gPzeC1L/yTHTXIeHDKWU09s2zTBmv7ma59wTm+fADNSGYxhJQDxyavQbwTf1QpW3Zj924tQ==} + '@oxfmt/binding-linux-arm-gnueabihf@0.53.0': + resolution: {integrity: sha512-ailB2vLzGi629tymdAb2VYJyEHref7oqGxP+tRBrtRBxQrb6NV55JMT7xtGZ8uTeG2+Y9zojqW4LhJYxQnz9Pg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.50.0': - resolution: {integrity: sha512-Y59FKqoUM3Gf00E395b4ixfWyJGwO2GzaZawF5MZoVWcb3f6CkWUXyao0jyOvoIxDMzMybcVRuXyG7ih/Nxweg==} + '@oxfmt/binding-linux-arm-musleabihf@0.53.0': + resolution: {integrity: sha512-abh4mWBvOvD966sobqF7r103y2yYx7Rb4WGHLOS4+5igGqLbbPxS9aK5+45D6iUY7dWMsk3Muz9a8gUtufvqJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.50.0': - resolution: {integrity: sha512-OvXbfTjMignXWyJXg/NOFsiy996vFe8wb9tkxJaUq8ylq0XrzJg3ttavC5Tcmm6F8/GUs2r3XFJWWu9q/27uYw==} + '@oxfmt/binding-linux-arm64-gnu@0.53.0': + resolution: {integrity: sha512-z73PvuhJ8qA+cDbaiqbtopHglA91U4+y5wn2sTJJrnpB957d5P33FEuyP3DQIFd7ofljmDmfVT4G0CVGHZaJWg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.50.0': - resolution: {integrity: sha512-rqmvHZm7vMa3NLYa0khwkhReCmp9tqKnF23TFZ7S5cYJLvIE4b0k8famWE7kO897/DXznJe675n5SohFBggbxA==} + '@oxfmt/binding-linux-arm64-musl@0.53.0': + resolution: {integrity: sha512-I6bhOTroqc3ThrwZ89l2k3ivKuELhdPLbAcJhRNyjWvlgwb0vjRgEnVL1XLx5Jud04/ypNRZBykAWrSk6l/D+g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.50.0': - resolution: {integrity: sha512-49bAdYbMSde42tzPDtuHnBWzOgmoS0PT9THCjvMnDVYMQYiHzPc2Mv5rkpBHVQOXM+PHfafJlxgK0anXSWBVvw==} + '@oxfmt/binding-linux-ppc64-gnu@0.53.0': + resolution: {integrity: sha512-w0p3JzB/PkkQjXALMJMqP9YfP3yq4w6zGsu5kezQmUnxRkN3b/Theg2l/nDgBsOcczxS3gL6Gam5XNAVrO6QJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.50.0': - resolution: {integrity: sha512-VFT25/6kckkIM62KeWB2bi+xCEmC/zC+DcMaIpEfaio8ulkGDLSiTz11TyK0eqgTl3x5OklYEGDWohvAgOr8Bw==} + '@oxfmt/binding-linux-riscv64-gnu@0.53.0': + resolution: {integrity: sha512-mzBhF6k1Yq1K/dqDmVe/AAafnlJfEpx7yfUiksyeWXJk5iSzZqBSxcsa02zIytYgQFRZ7h6WPZfwHg/DoOE1Kw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.50.0': - resolution: {integrity: sha512-BBJMuNy6jjkXjUUINF5UTQqb/nvjmtJad43Gp7bab0AAURAdthhJvduR7rHpWInpWYiaMzYsdrmURNcrmpxdZA==} + '@oxfmt/binding-linux-riscv64-musl@0.53.0': + resolution: {integrity: sha512-AlFCpnRQhogQFzZXWbO6xB6/Udy745L+eQNmDPGg7G/OeWsYmJc4jZYfUN5pQg0reOPWSED2mOQqKZOJM1U8cA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.50.0': - resolution: {integrity: sha512-Xd4y+yjAYHKmryXhyUUwbyRD01iKfcvI74iE01L6p4F8SwjhZQXDshK+T8PcrPZLiFqH263P5xqJk94amjkjzQ==} + '@oxfmt/binding-linux-s390x-gnu@0.53.0': + resolution: {integrity: sha512-XD4ulY4f1DWbuuZXAqxhVn+gdPmrhnmojWtFN78ctVoupmS845fGhsUrk1HZXKQI+iymbaiz9vAjPsghHNQ7Ag==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.50.0': - resolution: {integrity: sha512-Qp96rYJru7l++7mk4R+eh8qq9GFfFAMdmoN6VGoRHI8AA1XMnUIzH4u+zOcKZZwY+irHdsaBldDearwB4nOH7A==} + '@oxfmt/binding-linux-x64-gnu@0.53.0': + resolution: {integrity: sha512-xg8KWX0QnxmYWRe60CgHYWXI0ZOtBbqTsXvWiWrcl2XUHJ3fht2QerOk2iWvylzX3zNT2GpvBRxGoR4d3sxPRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.50.0': - resolution: {integrity: sha512-5XLGp+yd5w2Key5LMqJO+X3XVsJKgeeUKljy32+MBF/J/JZ5m8WHl6dI5eOQOr3ixopxPiXIyDAxn3slI3UXiQ==} + '@oxfmt/binding-linux-x64-musl@0.53.0': + resolution: {integrity: sha512-MWExpYBGvl+pIvVB/gj/CcWlN2al8AizT7rUbtaYaWNoQkhWARM6W3qpgoCr72CYSN9PborzPmM5MIRe2BrNdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.50.0': - resolution: {integrity: sha512-QAxwzh7+GHugCD7WuERolVs8TKQwXNIAZXAHHTecbKVc9oWBkWzOiLauQuezXS57tVcof5zhi1IjZ8tOV0htTg==} + '@oxfmt/binding-openharmony-arm64@0.53.0': + resolution: {integrity: sha512-u4sajgO4nxgmJIgc/y2AqPhkdbOkQH8WugXpA1+pW0ESQhvGZ1oGq61Q4xMbJHJU1hFgtO18QNrcFYDPYH0gwQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.50.0': - resolution: {integrity: sha512-3nKN/kqClm9iCFWTwtJ9UpR5SGyExp5l3nw6uIiBt+3XitQtszin+vjHrL7JHfDksZ7Svigdaow2zqz/IKCfqw==} + '@oxfmt/binding-win32-arm64-msvc@0.53.0': + resolution: {integrity: sha512-Yq9sOZoIOJ5xPjO0qOyHJS4CiPuTkB2en9auxZz7Ar2p5RaC7BzLyVVmAA7zz9/L9YnjjY1DwNxN+ivKXimN/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.50.0': - resolution: {integrity: sha512-3r6XZ8+X6qlLbXaPW2NygfiAWSpKbkE36pAVzS83mY+cYY+pSMalJ+qnCgkr92tr+Iqv988XKQ1CpARTg9ITbQ==} + '@oxfmt/binding-win32-ia32-msvc@0.53.0': + resolution: {integrity: sha512-es1fVNZEkBqEcQtBpn19SYFgZF7FawlkCjkT/iImfEAus4gun8fBwB1E9hpV5LcR9B0DBNvRIXhW8BQk3JaE+Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.50.0': - resolution: {integrity: sha512-BSE8D8KsvquMG9vU+Qt4qGuoOcZ36rxU5S6ZkHNguj+MlWkXWCBETnno3yJ9CfWvfCrbmieaN9LK6hdcdHNZ/w==} + '@oxfmt/binding-win32-x64-msvc@0.53.0': + resolution: {integrity: sha512-QFmJs2bEu9AO4O6qsmEaZNGi6dFq8N+rT8EHAAnZIq/B9SeJDUbc4DzVxQ48MfDsL7D3sCZzo37zuTuspcURgg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint-tsgolint/darwin-arm64@0.22.1': - resolution: {integrity: sha512-4150Lpgc1YM09GcjA6GSrra1JoPjC7aOpfywLjWEY4vW0Sd1qKzqHF1WRaiw0/qUZ40OATYdv3aRd7ipPkWQbw==} + '@oxlint-tsgolint/darwin-arm64@0.23.0': + resolution: {integrity: sha512-gOs9PVr2wEg4ox9z0aJo+RKhhImW86YL5N6yav8BK/rgPsIrwN/igSZ+pbRr723NFvUNKde9fgMhRA6JrXAOZw==} cpu: [arm64] os: [darwin] - '@oxlint-tsgolint/darwin-x64@0.22.1': - resolution: {integrity: sha512-vFWcPWYOgZs4HWcgS1EjUZg33NLcNfEYU49KGImmCfZWkflENrmBYV4HN/C0YeAPum6ZZ/goPSvQrB/cOD+NfA==} + '@oxlint-tsgolint/darwin-x64@0.23.0': + resolution: {integrity: sha512-kjJ8B+7n4tB9VJdxS5A9GdJt6/bYpzbu4lXp2uO1S3sRmCB5gDEABlGoiePNApRWaW+xqL4b4xgiE727jSLhuA==} cpu: [x64] os: [darwin] - '@oxlint-tsgolint/linux-arm64@0.22.1': - resolution: {integrity: sha512-6LiUpP0Zir3+29FvBm7Y28q/dBjSHqTZ5MhG1Ckw4fGhI4cAvbcwXaKvbjx1TP7rRmBNOoq/M5xdpHjTb+GAew==} + '@oxlint-tsgolint/linux-arm64@0.23.0': + resolution: {integrity: sha512-6dCZuKNu135seMXilkRk9SpCx6i1XgmiipYGalLij5WVRX6ZYS8c4xI7preN/zv9fCXhsQclTIMDu2Y/cytTjw==} cpu: [arm64] os: [linux] - '@oxlint-tsgolint/linux-x64@0.22.1': - resolution: {integrity: sha512-fuX1hEQfpHauUbXADsfqVhRzrUrGabzGXbj5wsp2vKhV5uk/Rze8Mba9GdjFGECzvXudMGqHqxB4r6jGRdhxVA==} + '@oxlint-tsgolint/linux-x64@0.23.0': + resolution: {integrity: sha512-3bdilnyA7kmSTjK27rvjIjSxL5SIg3wt7vwNiRkouWB83ytssyKnuGvxSYJxgMEmFpSutzaBzcCUM2jDtPGcgA==} cpu: [x64] os: [linux] - '@oxlint-tsgolint/win32-arm64@0.22.1': - resolution: {integrity: sha512-8SZidAj+jrbZf9ZjBEYW0tiNZ+KasqB2zgW26qdiPpQSF/DzURnPmXz651IeA9YsmbVdHGIooEHUmev6QJdquA==} + '@oxlint-tsgolint/win32-arm64@0.23.0': + resolution: {integrity: sha512-j+OEp44SVYiQ+ZD+uttsX7u6L9SvmbbQ77SO1pSFCcJlsVMeCk8qZsjhKfGKuT/jIA+ipOJMVs/+pqUfObBWNw==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@0.22.1': - resolution: {integrity: sha512-QweSk9H5lFh5Y+WUf2Kq/OAN88V6+62ZwGhP38gqdRotI90luXSMkruFTj7Q2rYrzH4ZVNaSqx7NY8JpSfIzqg==} + '@oxlint-tsgolint/win32-x64@0.23.0': + resolution: {integrity: sha512-5MyjFuqf+g8OUPJBSGWHJtmoWnzFJYyOg4To9WMQshZYEWig/vtu7JtJ03VWnzHv9LJkAUeApY0gVCOywFR/iQ==} cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.65.0': - resolution: {integrity: sha512-jDVaGNURT5pEA9qcabh6WusIoBNybOMMDPCx+EFt+gxo6rVvoUf0+73Xy5x81+ZrxU+ewk5uRBYifjy5pgkcnA==} + '@oxlint/binding-android-arm-eabi@1.68.0': + resolution: {integrity: sha512-wEdsIspexXLLMCPAEOcCuFLMt6aE3AzTuA/nQKLPRnoJ+EQTturmGheDkhHuuVHx0GbutjQ3JKmEn+Gz6Ag28Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.65.0': - resolution: {integrity: sha512-v0z80IWNA7c9RhUydq9YprBxCVZrQ6Ixls2tdxUC1F/1FFqSfa7xTX+EJf0mj6+BKRg2zWXqWfcbJUnETlLlIw==} + '@oxlint/binding-android-arm64@1.68.0': + resolution: {integrity: sha512-6aZRNNXQTsYtgaus8HTb9nuCcsrQTlKXGnktwvwW0n/SooRWNxNb3925grDkC63aEYZuCIyOVLV16IdYIoC2aQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.65.0': - resolution: {integrity: sha512-pL/mG/5gMzBwp1gdc5+Cwi87F9j3XRnPxHGyVj5Zd+dCEV5YkKt0L70PB3EGmEEHxgn4H+jnMS3xLuXs6mZW/Q==} + '@oxlint/binding-darwin-arm64@1.68.0': + resolution: {integrity: sha512-lVTbsE3kO4bLpZELgjRZuAJc8kP98wb83yMXWH8gaPaFZ+cM2IDeZto4ByoUAYj0Mxv2rvw+A1ssZequSepVSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.65.0': - resolution: {integrity: sha512-jVTneaeuHtqTrKYnhrdH1buhnSorinvpy1sv43ayclfWx/e/DfdRWv+h1fopJcHQbYr5WMcZMmDvnfEBkPZ+1A==} + '@oxlint/binding-darwin-x64@1.68.0': + resolution: {integrity: sha512-nCmw2XrmQskjBUh/sfP5yKs93V68LijQgjd1cuuZ/q4SCARngLYs60/qqyzuMsg8QQ9KArDI98hxs/RDGE4KRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.65.0': - resolution: {integrity: sha512-8lJQ7B6RloYDUhwVdbSpwT2eKsCN5KP1Scn18ly1tytCuhXhbs0nkfKHT4jWWZBJqmynWuzd+78bF7wILrj6pw==} + '@oxlint/binding-freebsd-x64@1.68.0': + resolution: {integrity: sha512-TI4ovQJliYE9V6e06cEv+qEI9uj7Ao65fmif4er4HD+aouyYyh0P31q2jh3KtqsOHHcQqv2PZ61TjJFLpBDGWQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.65.0': - resolution: {integrity: sha512-EgmZY+DeWhLLEnNl70/49j3ltA8I6X9kxMfexupWi2Vwfp6RonGsBaHtGoedLolaU37ne7eDUgoxa3CFB95GZA==} + '@oxlint/binding-linux-arm-gnueabihf@1.68.0': + resolution: {integrity: sha512-LcNnEi9g71Cmry5ZpLbKT+oVv+/zYG3hYVAbBBB5X85nOQZSk8l92CnDkxJMcxUg0NCnMCOFZuaVDlMyv4tYJw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.65.0': - resolution: {integrity: sha512-OJMWmAYRVBCPPxnYr3j5sXRwHPh1bAuMlTStGco1Z8q3HkvSH4h+A10E9MiRNYmLhUuli5a2P5wmfj8cagiF5Q==} + '@oxlint/binding-linux-arm-musleabihf@1.68.0': + resolution: {integrity: sha512-OovHahL3FX4UaK+hgSf11llUx2vszqjSdQQ61Ck9InOEI/ptZoC4XSQJurITqItVvd53JSlmkLMeaNjM1PoQew==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.65.0': - resolution: {integrity: sha512-D8uNi50LsYKgS0vGARZDRx05TBZeSxAVdLGddSEqQLSU7xsiqdImHPEw55xq8sKA5rCc/4au/5uS7FQALWdLCg==} + '@oxlint/binding-linux-arm64-gnu@1.68.0': + resolution: {integrity: sha512-YbzTglnHLzzi9zv5or8Ztz5fykAoZE8W9iM42/bOrF4HBSB6rJTqdLQWuoP76EHQw9DuKl76K1QmFlG29sPJXQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.65.0': - resolution: {integrity: sha512-IpbA8QGbwFehQhO+YaHwmoI81f93xvywpspf8HrdPCWOIeKwYfM1dhVhO4YKfZewTRRQEPY/JFjTOXTgkwhKrA==} + '@oxlint/binding-linux-arm64-musl@1.68.0': + resolution: {integrity: sha512-qVKtCZNic+OoNnOr/hCQAu22HSQzflI7Fsq/Blzkw02SnLuv163k3kfmrVpZjSBlUHgsRKj6WgQiw30d3SX02Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.65.0': - resolution: {integrity: sha512-ZSe8HgaZdgyHSv2+/pTG68z10+OarB18CkFKQOhRs3lmmP/p2vuigedK2e9d0ztoG2DU/duJzhxXBSjy/492HQ==} + '@oxlint/binding-linux-ppc64-gnu@1.68.0': + resolution: {integrity: sha512-zExyZ8ZOUuAyQ0y9jpTcyjKUz62YY9JhKPyVxzvjTpXzZ3ujdqiVwfPWDdnA1SsIOrxdtxHn7KErDHLWskFjXg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.65.0': - resolution: {integrity: sha512-DcTERf++v6HyPHukKAr0JFTRqB+YeDEvqzRgNDMaz7jITPf+tlJIwRxodlAqoXMYhNVEZhXdQM5RAAYH8/oPuw==} + '@oxlint/binding-linux-riscv64-gnu@1.68.0': + resolution: {integrity: sha512-6C4MPuwewyDavA7sxM14wzgRi5GGL68HPIxRCdVyS75U4MDbpFVYzKO9WNR6KLKTMPq2pcz3THwo1sK2uiqngw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.65.0': - resolution: {integrity: sha512-xjhMwuFJwRh40NOBzol4gM5gqAa0xPCJU+GQLM6BydV8TbfkIA7JeyCFNhyfbE9Q/5EWcKYTx62R0cRcjP7DAA==} + '@oxlint/binding-linux-riscv64-musl@1.68.0': + resolution: {integrity: sha512-bnZooVeHAcvA+dH0EDLgx+7HY/DRi6e0hFszg3P+OBatuUjV6EvfIyNIzWOusmqAVh4L6r21GGTZtiKE4iqM4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.65.0': - resolution: {integrity: sha512-lrWSXb8JzboPWYBG6Kunt/eemvjo2oCFXktShsm3yMToY7HjzKLjxh7CljSvGnnZH9oohNFHOKc9xYpGKCPm6w==} + '@oxlint/binding-linux-s390x-gnu@1.68.0': + resolution: {integrity: sha512-dIqnZnJSmHCMOUpUcWQOiV14o3DDPVx1DSsMaSzvdhNjC1tB1iEPZbdiMSCIEYbkgbsYznHXWqFdKL8WUB3F8g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.65.0': - resolution: {integrity: sha512-A7xfghw250m4a1sPV+q44Mow2G5bhiC9FBvhAuIhJS6QovWnqzuL5AFQPEuwOB+PM4DhABkqxVa3Iwe3Y/nFlQ==} + '@oxlint/binding-linux-x64-gnu@1.68.0': + resolution: {integrity: sha512-zc9lEnfV/HreDTY6gdMlZe+irkwHSxQ4/B1pS9GyK7RVaA5LxhoZY/w6/o2vIwLLEYiXQ5ujGxOM1ZazeFAAIA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.65.0': - resolution: {integrity: sha512-reqOun1+pWO3fW6cv7bsa8hHG0TN3t/82qPdaoJo90FwugXiMjKhZMChmH5Z01cFNRHmxN4+543Fy8478cM/iA==} + '@oxlint/binding-linux-x64-musl@1.68.0': + resolution: {integrity: sha512-Dl5QEX0TCo/40Cdh1o1JdPS//+YiWqjC+Hrrya5OQmStZZr4svAFtdlqcpCrU9yq2Mo3vRVyO9B3h0dzD8s36Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.65.0': - resolution: {integrity: sha512-KQpqOb/juDBO0xyloDkVDhOVxDUgAfZ2OAAVq99TJScJDzT319xry1QzB9LQohV9QGnA7p6m/XATZkMXc84lwA==} + '@oxlint/binding-openharmony-arm64@1.68.0': + resolution: {integrity: sha512-/qy6dOvi4S3/LeXq0l5BT5pRKPYA7oj3uKwJOAZOr5HRLL+HK6jdBynvWuXIA2wwfE01RzNYmbBdM7vwYx00sA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.65.0': - resolution: {integrity: sha512-xfqcOc3nJFeAd1kDY4T9d3XeJIhr00twaaW0kOAzGPyUHkruXtNJv6zz1Ra9fRtSek5VpW2Yoj5AcwPIlT0ZiQ==} + '@oxlint/binding-win32-arm64-msvc@1.68.0': + resolution: {integrity: sha512-fHNtVqPHSYE7UFDSLVFUjxQjnSVXxseNJmRW+XuP4pXXDwePdPda43NL7/BBCFTxHjycOc44JNDaOPtFDNui9A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.65.0': - resolution: {integrity: sha512-JV+pXm45p8sdgs3c7LOPAohW23optCNZETFOXUcjn6cS4PYZhEU/RI54Z5dHdMudab3nw7T48PZILthM+Q0COQ==} + '@oxlint/binding-win32-ia32-msvc@1.68.0': + resolution: {integrity: sha512-NnKXr4Wgo4nps3erhrE0f8shBvBPZMHg72nDsvX0JyrRvsNiP3f1JNvbCKh+A6VFvpF7ZoJxu904P3cKMhvZnA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.65.0': - resolution: {integrity: sha512-D7L/oBbskLss21bYrRbFuIs81AiSQV+wRzwck54dOkHIlq2qu1xjLz8u6jCqGH8Fltk8bB5DLBpVhE7v/fA8XQ==} + '@oxlint/binding-win32-x64-msvc@1.68.0': + resolution: {integrity: sha512-zg5pA+84AlU6XHJ3ruiRxziO71QTrz8nLsk6u01JGS5+tL9/bnlakFiklFrcy4R1/V7ktWtaNitN3JZWmKnf6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/binding-android-arm64@1.0.1': - resolution: {integrity: sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==} + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.1': - resolution: {integrity: sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==} + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.1': - resolution: {integrity: sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==} + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.1': - resolution: {integrity: sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==} + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': - resolution: {integrity: sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.1': - resolution: {integrity: sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==} + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.1': - resolution: {integrity: sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==} + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.1': - resolution: {integrity: sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==} + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.1': - resolution: {integrity: sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==} + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.1': - resolution: {integrity: sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==} + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.1': - resolution: {integrity: sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==} + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.1': - resolution: {integrity: sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==} + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.1': - resolution: {integrity: sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==} + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.1': - resolution: {integrity: sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==} + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.1': - resolution: {integrity: sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==} + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -981,218 +935,46 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@smithy/chunked-blob-reader-native@4.2.3': - resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==} + '@smithy/core@3.24.6': + resolution: {integrity: sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader@5.2.2': - resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==} + '@smithy/credential-provider-imds@4.3.8': + resolution: {integrity: sha512-5cAM+KZC02sTqDt6NaLXyu50M/GNMd1eTzDVR8Lb0BBsVtu7RWHo47VPPEEv1vt3Yub6uzr+M5FHC+GtoT0USg==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.4.17': - resolution: {integrity: sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==} - engines: {node: '>=18.0.0'} - - '@smithy/core@3.23.17': - resolution: {integrity: sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==} - engines: {node: '>=18.0.0'} - - '@smithy/credential-provider-imds@4.2.14': - resolution: {integrity: sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-codec@4.2.14': - resolution: {integrity: sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-browser@4.2.14': - resolution: {integrity: sha512-8IelTCtTctWRbb+0Dcy+C0aICh1qa0qWXqgjcXDmMuCvPJRnv26hiDZoAau2ILOniki65mCPKqOQs/BaWvO4CQ==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-config-resolver@4.3.14': - resolution: {integrity: sha512-sqHiHpYRYo3FJlaIxD1J8PhbcmJAm7IuM16mVnwSkCToD7g00IBZzKuiLNMGmftULmEUX6/UAz8/NN5uMP8bVA==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-node@4.2.14': - resolution: {integrity: sha512-Ht/8BuGlKfFTy0H3+8eEu0vdpwGztCnaLLXtpXNdQqiR7Hj4vFScU3T436vRAjATglOIPjJXronY+1WxxNLSiw==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-universal@4.2.14': - resolution: {integrity: sha512-lWyt4T2XQZUZgK3tQ3Wn0w3XBvZsK/vjTuJl6bXbnGZBHH0ZUSONTYiK9TgjTTzU54xQr3DRFwpjmhp0oLm3gg==} - engines: {node: '>=18.0.0'} - - '@smithy/fetch-http-handler@5.3.17': - resolution: {integrity: sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-blob-browser@4.2.15': - resolution: {integrity: sha512-0PJ4Al3fg2nM4qKrAIxyNcApgqHAXcBkN8FeizOz69z0rb26uZ6lMESYtxegaTlXB5Hj84JfwMPavMrwDMjucA==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-node@4.2.14': - resolution: {integrity: sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-stream-node@4.2.14': - resolution: {integrity: sha512-tw4GANWkZPb6+BdD4Fgucqzey2+r73Z/GRo9zklsCdwrnxxumUV83ZIaBDdudV4Ylazw3EPTiJZhpX42105ruQ==} - engines: {node: '>=18.0.0'} - - '@smithy/invalid-dependency@4.2.14': - resolution: {integrity: sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==} + '@smithy/fetch-http-handler@5.4.6': + resolution: {integrity: sha512-FEwEYJ1jlBKdhe9TPzfghEi1bP55ZeEImlDkEa62bBBYzUcnB6RUCyuiS2mqKt6ZVjUbBgcNhzfIctH+Hevx9g==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@4.2.2': - resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + '@smithy/node-http-handler@4.7.7': + resolution: {integrity: sha512-ZAFvHXrEk6K180EVhmZVg8GU5pUH5BSFqRs27JW3j1qEFx9YyYwWFx17x/MHcjALYimGAji7qEOlF1++be+G5A==} engines: {node: '>=18.0.0'} - '@smithy/md5-js@4.2.14': - resolution: {integrity: sha512-V2v0vx+h0iUSNG1Alt+GNBMSLGCrl9iVsdd+Ap67HPM9PN479x12V8LkuMoKImNZxn3MXeuyUjls+/7ZACZghA==} + '@smithy/protocol-http@5.4.6': + resolution: {integrity: sha512-H6S7NyaaL+7qO8kIL7VQ7KyrGnKXdllGzJqvtp3hvDen25UOydKV51qGDVK0UciW125jV3CoLJQy/ihc0OEC6A==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.2.14': - resolution: {integrity: sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==} + '@smithy/signature-v4@5.4.6': + resolution: {integrity: sha512-Ojg4B6oIDlIr1R86xCDJt1zJWnYa0VINmqdjfe9qxWjdRivHalZ3iSlQgVqYbW0MdpFOC5XfHEWsnbmdnpIILQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.32': - resolution: {integrity: sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-retry@4.5.7': - resolution: {integrity: sha512-bRt6ZImqVSeTk39Nm81K20ObIiAZ3WefY7G6+iz/0tZjs4dgRRjvRX2sgsH+zi6iDCRR/aQvQofLKxxz4rPBZg==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-serde@4.2.20': - resolution: {integrity: sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-stack@4.2.14': - resolution: {integrity: sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==} - engines: {node: '>=18.0.0'} - - '@smithy/node-config-provider@4.3.14': - resolution: {integrity: sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==} - engines: {node: '>=18.0.0'} - - '@smithy/node-http-handler@4.6.1': - resolution: {integrity: sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==} - engines: {node: '>=18.0.0'} - - '@smithy/property-provider@4.2.14': - resolution: {integrity: sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==} - engines: {node: '>=18.0.0'} - - '@smithy/protocol-http@5.3.14': - resolution: {integrity: sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-builder@4.2.14': - resolution: {integrity: sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-parser@4.2.14': - resolution: {integrity: sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==} - engines: {node: '>=18.0.0'} - - '@smithy/service-error-classification@4.3.1': - resolution: {integrity: sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==} - engines: {node: '>=18.0.0'} - - '@smithy/shared-ini-file-loader@4.4.9': - resolution: {integrity: sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==} - engines: {node: '>=18.0.0'} - - '@smithy/signature-v4@5.3.14': - resolution: {integrity: sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==} - engines: {node: '>=18.0.0'} - - '@smithy/smithy-client@4.12.13': - resolution: {integrity: sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==} - engines: {node: '>=18.0.0'} - - '@smithy/types@4.14.1': - resolution: {integrity: sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==} - engines: {node: '>=18.0.0'} - - '@smithy/url-parser@4.2.14': - resolution: {integrity: sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-base64@4.3.2': - resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-body-length-browser@4.2.2': - resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-body-length-node@4.2.3': - resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + '@smithy/types@4.14.3': + resolution: {integrity: sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@4.2.2': - resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} - engines: {node: '>=18.0.0'} - - '@smithy/util-config-provider@4.2.2': - resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-defaults-mode-browser@4.3.49': - resolution: {integrity: sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-defaults-mode-node@4.2.54': - resolution: {integrity: sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-endpoints@3.4.2': - resolution: {integrity: sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-hex-encoding@4.2.2': - resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-middleware@4.2.14': - resolution: {integrity: sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-retry@4.3.8': - resolution: {integrity: sha512-LUIxbTBi+OpvXpg91poGA6BdyoleMDLnfXjVDqyi2RvZmTveY5loE/FgYUBCR5LU2BThW2SoZRh8dTIIy38IPw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-stream@4.5.25': - resolution: {integrity: sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==} - engines: {node: '>=18.0.0'} - - '@smithy/util-uri-escape@4.2.2': - resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} - engines: {node: '>=18.0.0'} - '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@4.2.2': - resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-waiter@4.3.0': - resolution: {integrity: sha512-JyjYmLAfS+pdxF92o4yLgEoy0zhayKTw73FU1aofLWwLcJw7iSqIY2exGmMTrl/lmZugP5p/zxdFSippJDfKWA==} - engines: {node: '>=18.0.0'} - - '@smithy/uuid@1.1.2': - resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} - engines: {node: '>=18.0.0'} - '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -1293,8 +1075,8 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - '@tanstack/eslint-plugin-query@5.100.10': - resolution: {integrity: sha512-Ddou3agTWv5rvHSBby4yHlugUFHVh0nyo2fyoZ81qSxaTBIwNCoPgpiJhjo5QkThrH1wGC7k548BcMTszZCkBw==} + '@tanstack/eslint-plugin-query@5.101.0': + resolution: {integrity: sha512-wsfg821y4yw21J7nKI2oM5yyGSz3vASXqgWbmWCXZpnyY9ObLrBCcXivwZKj4YHF2fUWiqoOIRX2pbE79cf6gQ==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ^5.4.0 || ^6.0.0 @@ -1311,16 +1093,16 @@ packages: resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==} engines: {node: '>=20.19'} - '@tanstack/query-core@5.100.10': - resolution: {integrity: sha512-8UR0yJR+GiQ40m3lPhUr0xbfAupe6GSQiksSBSa9SM2NjezFyxXCIA69/lz8cSoNKZLrw1/PktIyQBJcVeMi3w==} + '@tanstack/query-core@5.101.0': + resolution: {integrity: sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==} - '@tanstack/react-query@5.100.10': - resolution: {integrity: sha512-FLaZf2RCrA/Zgp4aiu5tG3TyasTRO7aZ99skxQpr3Hg/zXOhu6yq5FZCYQ/tRaJtM9ylnoK8tFK7PolXQadv6Q==} + '@tanstack/react-query@5.101.0': + resolution: {integrity: sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==} peerDependencies: react: ^18 || ^19 - '@tanstack/react-router@1.170.1': - resolution: {integrity: sha512-s8Nn5c4UhbSyOefxOScvG3TIWlonWJfwb1txYXJvk9BCEqM0W7b0/0bw2Y063QTVofK/Ap4vmhLdmaBlwiT1Aw==} + '@tanstack/react-router@1.170.13': + resolution: {integrity: sha512-acCHtMDzTm+EB7S6OBjRIgbiTdN/+Wn56he+F4l+GhhFOpsopHmnhvd+fW6jTscGWd3kq6oeP/FUpLG+mCx8tQ==} engines: {node: '>=20.19'} peerDependencies: react: '>=18.0.0 || >=19.0.0' @@ -1332,20 +1114,20 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-core@1.170.1': - resolution: {integrity: sha512-uYxlIbdw2VY6xcEnBmLJ6lCuULV728vTQoK27BQSKPDMBB3TkV1+vkm8U24AT1iS2EbPVuHli1SNEKYd+WWGWw==} + '@tanstack/router-core@1.171.11': + resolution: {integrity: sha512-yIhaarrDBKuvjUaugZm7u28qztESMIaR5ZAZ8ZMQHynv1Jrgw+TrBQQXZvdfR7ggfyvi985dnvf754D9UigpDg==} engines: {node: '>=20.19'} - '@tanstack/router-generator@1.167.1': - resolution: {integrity: sha512-grpwEwuDht4M8P3nDUPt45B2BXxG0pIB5AdXSe8DHRP+GL++1UWxM5cAHK1WkXViGQhHNA2hhwKYhyP4z8RlcA==} + '@tanstack/router-generator@1.167.15': + resolution: {integrity: sha512-BxRzEkULCHP2nbzCn2g2D1j5fPDG/tF7lR938vgnAZKCApoMQepwNiXMXZFU50T0Rh/EkaLwkSYCbFg3AXhdrQ==} engines: {node: '>=20.19'} - '@tanstack/router-plugin@1.168.1': - resolution: {integrity: sha512-JPxBjrobxjyNqk4lgurHBGWNR7hE90wnlLCtANMBS6aY+Om71RKdxgiHIEqDiv8b6LLCiQFVHObJSa8O6LePNg==} + '@tanstack/router-plugin@1.168.16': + resolution: {integrity: sha512-C3RVFZpcsdRhKw0jQoEWB4Din9yB9U7bXM6cH/oyjkI7cjJ3ugtuKJKjZtXmWFugDrR98z4vu1P7LTekp4NIKw==} engines: {node: '>=20.19'} peerDependencies: '@rsbuild/core': '>=1.0.2 || ^2.0.0' - '@tanstack/react-router': ^1.170.1 + '@tanstack/react-router': ^1.170.13 vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' vite-plugin-solid: ^2.11.10 || ^3.0.0-0 webpack: '>=5.92.0' @@ -1361,8 +1143,8 @@ packages: webpack: optional: true - '@tanstack/router-utils@1.162.0': - resolution: {integrity: sha512-c3GhqhBRCP636B41nf3TKvVz8EWzC5PTZ3I4J4LDH2tVjpxbyFNYsQKRtbNWiMFl+GTtgK4nCha346Wv7j4hcQ==} + '@tanstack/router-utils@1.162.2': + resolution: {integrity: sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==} engines: {node: '>=20.19'} '@tanstack/store@0.9.3': @@ -1419,16 +1201,19 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@24.12.3': - resolution: {integrity: sha512-8oljBDGun9cIsZRJR6fkihn0TSXJI0UDOOhncYaERq6M0JMDoPLxyscwruJcb4GKS6dvK/d8xebYBg27h/duaQ==} + '@types/node@24.13.1': + resolution: {integrity: sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==} + + '@types/node@25.9.2': + resolution: {integrity: sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==} '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} '@types/whatwg-mimetype@3.0.2': resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} @@ -1442,26 +1227,52 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/project-service@8.60.1': + resolution: {integrity: sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/scope-manager@8.59.3': resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.60.1': + resolution: {integrity: sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.59.3': resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/tsconfig-utils@8.60.1': + resolution: {integrity: sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/types@8.59.3': resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.60.1': + resolution: {integrity: sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.59.3': resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/typescript-estree@8.60.1': + resolution: {integrity: sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.59.3': resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1469,54 +1280,65 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.60.1': + resolution: {integrity: sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/visitor-keys@8.59.3': resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260515.1': - resolution: {integrity: sha512-CSvyLkNV0k4Ro0B6nzNdDXFrRD8aa6sDvXSGla2FTmBio+JLKqNuJXq1ppyj42j9pAwdUhDZV/PNdjeHRCBjWQ==} + '@typescript-eslint/visitor-keys@8.60.1': + resolution: {integrity: sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260605.1': + resolution: {integrity: sha512-FR2ToauEC9dIAMV8OeTD7cXjgnpR09p1YZYrQzIpIOfzkg5FPccil+ca75BMbF0jT9h/0x+6+oy7JxzPO8sT+Q==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [darwin] - '@typescript/native-preview-darwin-x64@7.0.0-dev.20260515.1': - resolution: {integrity: sha512-QTmm0dpF6CC3hrfudAyVcIvbr1tlBTZ7aGQHIs4PYmi+tnTi2R8jy6gDBCJWlDBSXqjPWlQZrdiOxRBCSlShSA==} + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260605.1': + resolution: {integrity: sha512-vkqJpLeVN5RTtl/ibzasCubv4bQn45KDZRt8hHhneeibw9mZzm+vuvMvH/z/F5Sg/t/nz5kXKdDfhT+CV7aHAA==} engines: {node: '>=16.20.0'} cpu: [x64] os: [darwin] - '@typescript/native-preview-linux-arm64@7.0.0-dev.20260515.1': - resolution: {integrity: sha512-bH/CYrtXURXr3YwqJmlKblQ858wuJ0Qw2VSMvlsWwYZpb8eOd07T2bl1Ba6QxGBuL6EotcDQFd2OYJIj0uvM7A==} + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260605.1': + resolution: {integrity: sha512-YYzUP7HlKNUzfHFND+CgrYpPPcQyrcYmV0GhjxxMVWvlhQe6SvmWQLcZdp8ZW0t1BDfvs+jMtL/lI85TiUpY2w==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] - '@typescript/native-preview-linux-arm@7.0.0-dev.20260515.1': - resolution: {integrity: sha512-0b+Sxh8JUNMqvw06wfIoh45+G68/ikCW+aRqHGipU2uc9M9dWUKvI3zDyzP1rF6FOJ7xMKMbTbi5b2IvcRqh8g==} + '@typescript/native-preview-linux-arm@7.0.0-dev.20260605.1': + resolution: {integrity: sha512-gBsGhto81phtQHoQczJenb2aY2y4JVrgZDb7LPHQIEiFSEYe0uT6E1DR/ZT0XXdkLXn6wFBwcmoDLlDjo/uW2g==} engines: {node: '>=16.20.0'} cpu: [arm] os: [linux] - '@typescript/native-preview-linux-x64@7.0.0-dev.20260515.1': - resolution: {integrity: sha512-eXgYE6pmkCiEEX7T43TYeF24uiY8+h9mbi9YSvnCoRp4BLgn9b7uJagh4ctSGCNxUy3QSDeoznkWfSVlrvJ5pA==} + '@typescript/native-preview-linux-x64@7.0.0-dev.20260605.1': + resolution: {integrity: sha512-VRtSycQWdN2hMniqVXAzjWG5zgVwT9Yy+yk3O4qGJ+3ncFJ3nYu4VerupNNUNrmP4FFPViS8hjhQJbOUSuINXg==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] - '@typescript/native-preview-win32-arm64@7.0.0-dev.20260515.1': - resolution: {integrity: sha512-K1YJimcOr3/lFTJyOChT1GnRdfvoHtIvG/qcwHhHpeCBzpGfG7u2aH9MkBgsXqNJHAXHLnQRuON2uYgU8wWDzw==} + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260605.1': + resolution: {integrity: sha512-fBvmDKqL10ST+FnryPLTD1lY8XbEiOYGSa6hi17jYJwCMZA6mLbYPre06lysOB1eO9oRM7N0zUsEQu57YiFWTw==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [win32] - '@typescript/native-preview-win32-x64@7.0.0-dev.20260515.1': - resolution: {integrity: sha512-1cGEWLao/d2+Q3yMvtnDES85sM9HuI6J6PBmrcjwXzsQxnjXJkujStdp+LB6JZfY+VSDi+cpaCZmrKU6sYUfkg==} + '@typescript/native-preview-win32-x64@7.0.0-dev.20260605.1': + resolution: {integrity: sha512-JDfR4uPr3ZJ7kfkEkPw2y4LvEVcnjLCRTCT9r3MxtYpKg0Oeg88E8TmDPbatVQ48GC9LlhLZ+4WSFSZRGUkIRA==} engines: {node: '>=16.20.0'} cpu: [x64] os: [win32] - '@typescript/native-preview@7.0.0-dev.20260515.1': - resolution: {integrity: sha512-dWhzJGb9iJfCDsjz/LzPkbMj5XDbFWyZmDyTixcmQPgX5zFOPZ+sAdlCpTZFGEJhA7OqXG+lZyajbJfjYvQorA==} + '@typescript/native-preview@7.0.0-dev.20260605.1': + resolution: {integrity: sha512-JWqt9yAOq2BTh6u6nXiEAfdJVO7akc2bD23OuEwdCcc5sANhp5T3Hgci85NVs9rIQwDh59lqU23phGIUvkc0CA==} engines: {node: '>=16.20.0'} hasBin: true @@ -1539,20 +1361,20 @@ packages: babel-plugin-react-compiler: optional: true - '@vitest/coverage-v8@4.1.6': - resolution: {integrity: sha512-36l628fQ/9a/8ihy97eOtEnvWQEdqULQOJtcaxtoNq0G1w3Mxd4szSahOaMM9/NGyZ+hyKcMtIW/WIxq0XQViQ==} + '@vitest/coverage-v8@4.1.8': + resolution: {integrity: sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==} peerDependencies: - '@vitest/browser': 4.1.6 - vitest: 4.1.6 + '@vitest/browser': 4.1.8 + vitest: 4.1.8 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.1.6': - resolution: {integrity: sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==} + '@vitest/expect@4.1.8': + resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} - '@vitest/mocker@4.1.6': - resolution: {integrity: sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==} + '@vitest/mocker@4.1.8': + resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1562,20 +1384,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.6': - resolution: {integrity: sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==} + '@vitest/pretty-format@4.1.8': + resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} - '@vitest/runner@4.1.6': - resolution: {integrity: sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==} + '@vitest/runner@4.1.8': + resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} - '@vitest/snapshot@4.1.6': - resolution: {integrity: sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==} + '@vitest/snapshot@4.1.8': + resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} - '@vitest/spy@4.1.6': - resolution: {integrity: sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==} + '@vitest/spy@4.1.8': + resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} - '@vitest/utils@4.1.6': - resolution: {integrity: sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==} + '@vitest/utils@4.1.8': + resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1602,14 +1424,10 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansis@4.3.0: - resolution: {integrity: sha512-44mvgtPvohuU/70DdY5Oz2AIrLJ9k6/5x4KmoSvPwO+5Moijo0+N9D0fKbbYZQWP1hNm5CpOf+E01jhxG/r8xg==} + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} engines: {node: '>=14'} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1624,8 +1442,8 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-v8-to-istanbul@1.0.0: - resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} + ast-v8-to-istanbul@1.0.3: + resolution: {integrity: sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==} babel-dead-code-elimination@1.0.12: resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} @@ -1643,34 +1461,30 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.29: - resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} + baseline-browser-mapping@2.10.34: + resolution: {integrity: sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==} engines: {node: '>=6.0.0'} hasBin: true - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - browserslist@4.28.2: resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-image-size@0.6.4: + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + engines: {node: '>=4.0'} + buffer@5.6.0: resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} @@ -1678,8 +1492,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001792: - resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -1689,9 +1503,9 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} citty@0.2.2: resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} @@ -1767,8 +1581,8 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - electron-to-chromium@1.5.356: - resolution: {integrity: sha512-9NgFd7m5t5MCJ5rUSjJITUXAH9mEGlrlofnMf4YEr+pz6JlP7cWmTAH+JFmbPnaSW8koVTkuW7pacORWAnA5Yw==} + electron-to-chromium@1.5.368: + resolution: {integrity: sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==} enhanced-resolve@5.21.2: resolution: {integrity: sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==} @@ -1861,14 +1675,14 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-wrap-ansi@0.2.0: - resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} fast-xml-builder@1.2.0: resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} - fast-xml-parser@5.7.2: - resolution: {integrity: sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==} + fast-xml-parser@5.7.3: + resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==} hasBin: true fdir@6.5.0: @@ -1884,10 +1698,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1908,10 +1718,6 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -1927,8 +1733,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - happy-dom@20.9.0: - resolution: {integrity: sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ==} + happy-dom@20.10.1: + resolution: {integrity: sha512-awPoqPjx8CgjapJllyDlgzgVHjBExcitKK5ZJkxwhQJyQpHFkyS2bEcqCm7IeW20cQvuCI0cz2Ifq79CJKqtiw==} engines: {node: '>=20.0.0'} has-flag@4.0.0: @@ -1960,10 +1766,6 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1972,12 +1774,8 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - isbot@5.1.40: - resolution: {integrity: sha512-yNeeynhhtIVRBk12tBV4eHNxwB42HzR4Q3Ea7vCOiJhImGaAIdIMrbJtacQlBizGLjUPw+akkFI5Dn9T70XoVQ==} + isbot@5.1.41: + resolution: {integrity: sha512-9WFV/Vhh0FEj6CQ7MoHweEL9/vLKPjeoD2I2htbAjX7kbW7VJs3OCpWOVyd+JraNTWVU6/DRx2MZy2KaUNXHcg==} engines: {node: '>=18'} isexe@2.0.0: @@ -2005,8 +1803,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true jsesc@3.1.0: @@ -2119,15 +1917,15 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lru-cache@11.3.6: - resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@1.16.0: - resolution: {integrity: sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ==} + lucide-react@1.17.0: + resolution: {integrity: sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -2171,48 +1969,52 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-releases@2.0.44: - resolution: {integrity: sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} + node-releases@2.0.47: + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + engines: {node: '>=18'} nypm@0.6.6: resolution: {integrity: sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q==} engines: {node: '>=18'} hasBin: true - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.1.2: + resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} + engines: {node: '>=12.20.0'} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - oxfmt@0.50.0: - resolution: {integrity: sha512-owwjTnhfM5aCOJhYeqDvk7iM504OeYFZpdRU7cxx7xtZMo4uVpjlryTUon+Cf76CugsvnqA32e6rC73pr1hXaw==} + oxfmt@0.53.0: + resolution: {integrity: sha512-9cB5glS3Ip6NMuZ+6NYTao9FCWkDhRtPYCtR3QBu/NxHoFbgzzTvi41N4jxz/GqGfuLKspui1qb/LlSu2IbMcw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: svelte: ^5.0.0 + vite-plus: '*' peerDependenciesMeta: svelte: optional: true + vite-plus: + optional: true - oxlint-tsgolint@0.22.1: - resolution: {integrity: sha512-YUSGSLUnoolsu8gxISEDio3q1rtsCozwfOzASUn3DT2mR2EeQ93uEEnen7s+6LpF+lyTQFln1pQfqwBh/fsVEg==} + oxlint-tsgolint@0.23.0: + resolution: {integrity: sha512-3mBv3CoPbh8dFbzfDGIWa2ytZjn2v+3EX4aKRXjIhsoGFzG8GCjfRirz3rwZf1wYbZzsNLTSgpw8VjQuWdp/jA==} hasBin: true - oxlint@1.65.0: - resolution: {integrity: sha512-ChUuE3Q7XnAbscvT4XLMsH7HFJmLgLVv9lu+RRgFL5wSXnDqUOzTp5IS8qWDBGd/ZDSzQ2tbX8fjAmijlGLC7A==} + oxlint@1.68.0: + resolution: {integrity: sha512-dXcbq+xsmLrMy6T8d0euf3IYUfLmjHIE11pOxiUSi5LHkFZaYPv568R6sEjcavVpUxoaQe66UBuK4HEi74NxpA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: oxlint-tsgolint: '>=0.22.1' + vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: optional: true + vite-plus: + optional: true p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} @@ -2248,16 +2050,12 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} - postcss@8.5.14: - resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -2277,13 +2075,13 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - react-dom@19.2.6: - resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: - react: ^19.2.6 + react: ^19.2.7 - react-hook-form@7.76.0: - resolution: {integrity: sha512-eKtLGgFeSgkHqQD8J59AMZ9a4uD1D83iSIzt4YlTGD7liDen5rrjcUO1rVIGd9yC1gofryjtHbv+4ny4hkLWlw==} + react-hook-form@7.77.0: + resolution: {integrity: sha512-Sslh9YDYc0GDlWT/lxasnIduNo4v3yyvqRGvmGKUre5AFjDs/HV9/OafHGD8d+sB2yoL4UIL9L8X9i0WlZZebg==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -2291,31 +2089,31 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - react@19.2.6: - resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + reselect@5.2.0: + resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - rolldown@1.0.1: - resolution: {integrity: sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==} + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2334,6 +2132,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.2: + resolution: {integrity: sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==} + engines: {node: '>=10'} + hasBin: true + seroval-plugins@1.5.4: resolution: {integrity: sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw==} engines: {node: '>=10'} @@ -2402,12 +2205,12 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.1.2: - resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tinypool@2.1.0: @@ -2418,10 +2221,6 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -2440,8 +2239,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - ultracite@7.7.0: - resolution: {integrity: sha512-ygdKJwOloPuBXccmpgAOzyNBf+XPqFxDeIP59GF4idZ+YS7Lr1lp5favgtkPtenKUV8bc+nkoOJBy0bb/+7IxA==} + ultracite@7.8.1: + resolution: {integrity: sha512-yVkUHR9gMvqt7uEzC45tkSgJNUfAYztJPqZLSK6Z/PkWksoQOWjSVAcff7pEQarQKUJF/KR2S41UJ7wVAlo44Q==} hasBin: true peerDependencies: oxfmt: '>=0.1.0' @@ -2452,8 +2251,11 @@ packages: oxlint: optional: true - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} unplugin@3.0.0: resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==} @@ -2476,8 +2278,8 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@8.0.13: - resolution: {integrity: sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==} + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -2519,20 +2321,20 @@ packages: yaml: optional: true - vitest@4.1.6: - resolution: {integrity: sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==} + vitest@4.1.8: + resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.6 - '@vitest/browser-preview': 4.1.6 - '@vitest/browser-webdriverio': 4.1.6 - '@vitest/coverage-istanbul': 4.1.6 - '@vitest/coverage-v8': 4.1.6 - '@vitest/ui': 4.1.6 + '@vitest/browser-playwright': 4.1.8 + '@vitest/browser-preview': 4.1.8 + '@vitest/browser-webdriverio': 4.1.8 + '@vitest/coverage-istanbul': 4.1.8 + '@vitest/coverage-v8': 4.1.8 + '@vitest/ui': 4.1.8 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2581,8 +2383,8 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - ws@8.20.0: - resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2609,9 +2411,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -2622,20 +2421,20 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.8 + '@aws-sdk/types': 3.973.11 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.8 + '@aws-sdk/types': 3.973.11 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.8 + '@aws-sdk/types': 3.973.11 '@aws-sdk/util-locate-window': 3.965.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -2653,7 +2452,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.8 + '@aws-sdk/types': 3.973.11 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -2662,600 +2461,302 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.973.6 + '@aws-sdk/types': 3.973.11 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-ec2@3.1045.0': + '@aws-sdk/checksums@3.1000.2': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 + tslib: 2.8.1 + + '@aws-sdk/client-acm@3.1063.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/credential-provider-node': 3.972.39 - '@aws-sdk/middleware-host-header': 3.972.10 - '@aws-sdk/middleware-logger': 3.972.10 - '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-sdk-ec2': 3.972.22 - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/region-config-resolver': 3.972.13 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.24 - '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.17 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/hash-node': 4.2.14 - '@smithy/invalid-dependency': 4.2.14 - '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-retry': 4.5.7 - '@smithy/middleware-serde': 4.2.20 - '@smithy/middleware-stack': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.1 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.49 - '@smithy/util-defaults-mode-node': 4.2.54 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-utf8': 4.2.2 - '@smithy/util-waiter': 4.3.0 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/credential-provider-node': 3.972.52 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/fetch-http-handler': 5.4.6 + '@smithy/node-http-handler': 4.7.7 + '@smithy/types': 4.14.3 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-elastic-load-balancing-v2@3.1045.0': + '@aws-sdk/client-ec2@3.1063.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/credential-provider-node': 3.972.39 - '@aws-sdk/middleware-host-header': 3.972.10 - '@aws-sdk/middleware-logger': 3.972.10 - '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/region-config-resolver': 3.972.13 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.24 - '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.17 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/hash-node': 4.2.14 - '@smithy/invalid-dependency': 4.2.14 - '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-retry': 4.5.7 - '@smithy/middleware-serde': 4.2.20 - '@smithy/middleware-stack': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.1 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.49 - '@smithy/util-defaults-mode-node': 4.2.54 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-utf8': 4.2.2 - '@smithy/util-waiter': 4.3.0 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/credential-provider-node': 3.972.52 + '@aws-sdk/middleware-sdk-ec2': 3.972.32 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/fetch-http-handler': 5.4.6 + '@smithy/node-http-handler': 4.7.7 + '@smithy/types': 4.14.3 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-iam@3.1045.0': + '@aws-sdk/client-eks@3.1063.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/credential-provider-node': 3.972.39 - '@aws-sdk/middleware-host-header': 3.972.10 - '@aws-sdk/middleware-logger': 3.972.10 - '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/region-config-resolver': 3.972.13 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.24 - '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.17 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/hash-node': 4.2.14 - '@smithy/invalid-dependency': 4.2.14 - '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-retry': 4.5.7 - '@smithy/middleware-serde': 4.2.20 - '@smithy/middleware-stack': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.1 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.49 - '@smithy/util-defaults-mode-node': 4.2.54 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-utf8': 4.2.2 - '@smithy/util-waiter': 4.3.0 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/credential-provider-node': 3.972.52 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/fetch-http-handler': 5.4.6 + '@smithy/node-http-handler': 4.7.7 + '@smithy/types': 4.14.3 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-s3@3.1045.0': + '@aws-sdk/client-elastic-load-balancing-v2@3.1063.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/credential-provider-node': 3.972.52 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/fetch-http-handler': 5.4.6 + '@smithy/node-http-handler': 4.7.7 + '@smithy/types': 4.14.3 + tslib: 2.8.1 + + '@aws-sdk/client-iam@3.1063.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/credential-provider-node': 3.972.52 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/fetch-http-handler': 5.4.6 + '@smithy/node-http-handler': 4.7.7 + '@smithy/types': 4.14.3 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1063.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/credential-provider-node': 3.972.39 - '@aws-sdk/middleware-bucket-endpoint': 3.972.10 - '@aws-sdk/middleware-expect-continue': 3.972.10 - '@aws-sdk/middleware-flexible-checksums': 3.974.16 - '@aws-sdk/middleware-host-header': 3.972.10 - '@aws-sdk/middleware-location-constraint': 3.972.10 - '@aws-sdk/middleware-logger': 3.972.10 - '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-sdk-s3': 3.972.37 - '@aws-sdk/middleware-ssec': 3.972.10 - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/region-config-resolver': 3.972.13 - '@aws-sdk/signature-v4-multi-region': 3.996.25 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.24 - '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.17 - '@smithy/eventstream-serde-browser': 4.2.14 - '@smithy/eventstream-serde-config-resolver': 4.3.14 - '@smithy/eventstream-serde-node': 4.2.14 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/hash-blob-browser': 4.2.15 - '@smithy/hash-node': 4.2.14 - '@smithy/hash-stream-node': 4.2.14 - '@smithy/invalid-dependency': 4.2.14 - '@smithy/md5-js': 4.2.14 - '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-retry': 4.5.7 - '@smithy/middleware-serde': 4.2.20 - '@smithy/middleware-stack': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.1 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.49 - '@smithy/util-defaults-mode-node': 4.2.54 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-stream': 4.5.25 - '@smithy/util-utf8': 4.2.2 - '@smithy/util-waiter': 4.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/core@3.974.8': - dependencies: - '@aws-sdk/types': 3.973.8 - '@aws-sdk/xml-builder': 3.972.22 - '@smithy/core': 3.23.17 - '@smithy/node-config-provider': 4.3.14 - '@smithy/property-provider': 4.2.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/signature-v4': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/util-base64': 4.3.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-utf8': 4.2.2 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/credential-provider-node': 3.972.52 + '@aws-sdk/middleware-flexible-checksums': 3.974.27 + '@aws-sdk/middleware-sdk-s3': 3.972.48 + '@aws-sdk/signature-v4-multi-region': 3.996.32 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/fetch-http-handler': 5.4.6 + '@smithy/node-http-handler': 4.7.7 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/crc64-nvme@3.972.7': + '@aws-sdk/core@3.974.18': dependencies: - '@smithy/types': 4.14.1 + '@aws-sdk/types': 3.973.11 + '@aws-sdk/xml-builder': 3.972.28 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/core': 3.24.6 + '@smithy/signature-v4': 5.4.6 + '@smithy/types': 4.14.3 + bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.34': + '@aws-sdk/credential-provider-env@3.972.44': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/types': 4.14.1 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.36': + '@aws-sdk/credential-provider-http@3.972.46': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/types': 3.973.8 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/node-http-handler': 4.6.1 - '@smithy/property-provider': 4.2.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/util-stream': 4.5.25 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/fetch-http-handler': 5.4.6 + '@smithy/node-http-handler': 4.7.7 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.38': + '@aws-sdk/credential-provider-ini@3.972.50': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/credential-provider-env': 3.972.34 - '@aws-sdk/credential-provider-http': 3.972.36 - '@aws-sdk/credential-provider-login': 3.972.38 - '@aws-sdk/credential-provider-process': 3.972.34 - '@aws-sdk/credential-provider-sso': 3.972.38 - '@aws-sdk/credential-provider-web-identity': 3.972.38 - '@aws-sdk/nested-clients': 3.997.6 - '@aws-sdk/types': 3.973.8 - '@smithy/credential-provider-imds': 4.2.14 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-login@3.972.38': - dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/nested-clients': 3.997.6 - '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-node@3.972.39': - dependencies: - '@aws-sdk/credential-provider-env': 3.972.34 - '@aws-sdk/credential-provider-http': 3.972.36 - '@aws-sdk/credential-provider-ini': 3.972.38 - '@aws-sdk/credential-provider-process': 3.972.34 - '@aws-sdk/credential-provider-sso': 3.972.38 - '@aws-sdk/credential-provider-web-identity': 3.972.38 - '@aws-sdk/types': 3.973.8 - '@smithy/credential-provider-imds': 4.2.14 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-process@3.972.34': - dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/credential-provider-env': 3.972.44 + '@aws-sdk/credential-provider-http': 3.972.46 + '@aws-sdk/credential-provider-login': 3.972.49 + '@aws-sdk/credential-provider-process': 3.972.44 + '@aws-sdk/credential-provider-sso': 3.972.49 + '@aws-sdk/credential-provider-web-identity': 3.972.49 + '@aws-sdk/nested-clients': 3.997.17 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/credential-provider-imds': 4.3.8 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.38': + '@aws-sdk/credential-provider-login@3.972.49': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/nested-clients': 3.997.6 - '@aws-sdk/token-providers': 3.1041.0 - '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/nested-clients': 3.997.17 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.38': + '@aws-sdk/credential-provider-node@3.972.52': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/nested-clients': 3.997.6 - '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@aws-sdk/credential-provider-env': 3.972.44 + '@aws-sdk/credential-provider-http': 3.972.46 + '@aws-sdk/credential-provider-ini': 3.972.50 + '@aws-sdk/credential-provider-process': 3.972.44 + '@aws-sdk/credential-provider-sso': 3.972.49 + '@aws-sdk/credential-provider-web-identity': 3.972.49 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/credential-provider-imds': 4.3.8 + '@smithy/types': 4.14.3 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/lib-storage@3.1045.0(@aws-sdk/client-s3@3.1045.0)': + '@aws-sdk/credential-provider-process@3.972.44': dependencies: - '@aws-sdk/client-s3': 3.1045.0 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.49': + dependencies: + '@aws-sdk/core': 3.974.18 + '@aws-sdk/nested-clients': 3.997.17 + '@aws-sdk/token-providers': 3.1063.0 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.49': + dependencies: + '@aws-sdk/core': 3.974.18 + '@aws-sdk/nested-clients': 3.997.17 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 + tslib: 2.8.1 + + '@aws-sdk/lib-storage@3.1063.0(@aws-sdk/client-s3@3.1063.0)': + dependencies: + '@aws-sdk/client-s3': 3.1063.0 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.972.10': + '@aws-sdk/middleware-flexible-checksums@3.974.27': dependencies: - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-arn-parser': 3.972.3 - '@smithy/node-config-provider': 4.3.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-config-provider': 4.2.2 + '@aws-sdk/checksums': 3.1000.2 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.972.10': + '@aws-sdk/middleware-sdk-ec2@3.972.32': dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/signature-v4': 5.4.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.974.16': + '@aws-sdk/middleware-sdk-s3@3.972.48': dependencies: - '@aws-crypto/crc32': 5.2.0 - '@aws-crypto/crc32c': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/crc64-nvme': 3.972.7 - '@aws-sdk/types': 3.973.8 - '@smithy/is-array-buffer': 4.2.2 - '@smithy/node-config-provider': 4.3.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.25 - '@smithy/util-utf8': 4.2.2 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/signature-v4-multi-region': 3.996.32 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.972.10': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-location-constraint@3.972.10': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-logger@3.972.10': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-recursion-detection@3.972.11': - dependencies: - '@aws-sdk/types': 3.973.8 - '@aws/lambda-invoke-store': 0.2.4 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-sdk-ec2@3.972.22': - dependencies: - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-format-url': 3.972.10 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/protocol-http': 5.3.14 - '@smithy/signature-v4': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-sdk-s3@3.972.37': - dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-arn-parser': 3.972.3 - '@smithy/core': 3.23.17 - '@smithy/node-config-provider': 4.3.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/signature-v4': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/util-config-provider': 4.2.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.25 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@aws-sdk/middleware-ssec@3.972.10': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-user-agent@3.972.38': - dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@smithy/core': 3.23.17 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-retry': 4.3.8 - tslib: 2.8.1 - - '@aws-sdk/nested-clients@3.997.6': + '@aws-sdk/nested-clients@3.997.17': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/middleware-host-header': 3.972.10 - '@aws-sdk/middleware-logger': 3.972.10 - '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/region-config-resolver': 3.972.13 - '@aws-sdk/signature-v4-multi-region': 3.996.25 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.24 - '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.17 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/hash-node': 4.2.14 - '@smithy/invalid-dependency': 4.2.14 - '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-retry': 4.5.7 - '@smithy/middleware-serde': 4.2.20 - '@smithy/middleware-stack': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.1 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.49 - '@smithy/util-defaults-mode-node': 4.2.54 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/region-config-resolver@3.972.13': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/config-resolver': 4.4.17 - '@smithy/node-config-provider': 4.3.14 - '@smithy/types': 4.14.1 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/signature-v4-multi-region': 3.996.32 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/fetch-http-handler': 5.4.6 + '@smithy/node-http-handler': 4.7.7 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.25': + '@aws-sdk/signature-v4-multi-region@3.996.32': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.37 - '@aws-sdk/types': 3.973.8 - '@smithy/protocol-http': 5.3.14 - '@smithy/signature-v4': 5.3.14 - '@smithy/types': 4.14.1 + '@aws-sdk/types': 3.973.11 + '@smithy/signature-v4': 5.4.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1041.0': + '@aws-sdk/token-providers@3.1063.0': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/nested-clients': 3.997.6 - '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@aws-sdk/core': 3.974.18 + '@aws-sdk/nested-clients': 3.997.17 + '@aws-sdk/types': 3.973.11 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 + tslib: 2.8.1 + + '@aws-sdk/types@3.973.11': + dependencies: + '@smithy/types': 4.14.3 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt '@aws-sdk/types@3.973.6': dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/types@3.973.8': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/util-arn-parser@3.972.3': - dependencies: - tslib: 2.8.1 - - '@aws-sdk/util-endpoints@3.996.8': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-endpoints': 3.4.2 - tslib: 2.8.1 - - '@aws-sdk/util-format-url@3.972.10': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/querystring-builder': 4.2.14 - '@smithy/types': 4.14.1 + '@smithy/types': 4.14.3 tslib: 2.8.1 '@aws-sdk/util-locate-window@3.965.5': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.972.10': + '@aws-sdk/xml-builder@3.972.28': dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 - bowser: 2.14.1 - tslib: 2.8.1 - - '@aws-sdk/util-user-agent-node@3.973.24': - dependencies: - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/types': 3.973.8 - '@smithy/node-config-provider': 4.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-config-provider': 4.2.2 - tslib: 2.8.1 - - '@aws-sdk/xml-builder@3.972.22': - dependencies: - '@nodable/entities': 2.1.0 - '@smithy/types': 4.14.1 - fast-xml-parser: 5.7.2 + '@smithy/types': 4.14.3 + fast-xml-parser: 5.7.3 tslib: 2.8.1 '@aws/lambda-invoke-store@0.2.4': {} - '@babel/code-frame@7.29.0': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.3': {} + '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.0': + '@babel/core@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.29.2 - '@babel/parser': 7.29.3 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -3265,83 +2766,77 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.29.1': + '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.28.6': + '@babel/helper-compilation-targets@7.29.7': dependencies: - '@babel/compat-data': 7.29.3 - '@babel/helper-validator-option': 7.27.1 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.28.6': {} - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helpers@7.29.2': - dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/helper-validator-option@7.29.7': {} - '@babel/parser@7.29.3': + '@babel/helpers@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/parser@7.29.7': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.29.7 '@babel/runtime@7.29.2': {} - '@babel/template@7.28.6': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@babel/runtime@7.29.7': {} - '@babel/traverse@7.29.0': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.3 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -3351,41 +2846,46 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@base-ui/react@1.4.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@babel/types@7.29.7': dependencies: - '@babel/runtime': 7.29.2 - '@base-ui/utils': 0.2.8(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@floating-ui/utils': 0.2.11 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - use-sync-external-store: 1.6.0(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.14 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 - '@base-ui/utils@0.2.8(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@base-ui/react@1.5.0(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 + '@base-ui/utils': 0.2.9(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@floating-ui/utils': 0.2.11 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - reselect: 5.1.1 - use-sync-external-store: 1.6.0(react@19.2.6) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 + + '@base-ui/utils@0.2.9(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@babel/runtime': 7.29.7 + '@floating-ui/utils': 0.2.11 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + reselect: 5.2.0 + use-sync-external-store: 1.6.0(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 '@bcoe/v8-coverage@1.0.2': {} - '@clack/core@1.3.1': + '@clack/core@1.4.1': dependencies: - fast-wrap-ansi: 0.2.0 + fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@clack/prompts@1.4.0': + '@clack/prompts@1.5.1': dependencies: - '@clack/core': 1.3.1 + '@clack/core': 1.4.1 fast-string-width: 3.0.2 - fast-wrap-ansi: 0.2.0 + fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 '@emnapi/core@1.10.0': @@ -3435,7 +2935,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 4.2.0 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -3459,22 +2959,22 @@ snapshots: '@floating-ui/core': 1.7.5 '@floating-ui/utils': 0.2.11 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@floating-ui/dom': 1.7.6 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) '@floating-ui/utils@0.2.11': {} - '@fontsource-variable/geist@5.2.8': {} + '@fontsource-variable/geist@5.2.9': {} '@fontsource-variable/jetbrains-mono@5.2.8': {} - '@hookform/resolvers@5.2.2(react-hook-form@7.76.0(react@19.2.6))': + '@hookform/resolvers@5.4.0(react-hook-form@7.77.0(react@19.2.7))': dependencies: '@standard-schema/utils': 0.3.0 - react-hook-form: 7.76.0(react@19.2.6) + react-hook-form: 7.77.0(react@19.2.7) '@humanfs/core@0.19.2': dependencies: @@ -3520,444 +3020,240 @@ snapshots: '@nodable/entities@2.1.0': {} - '@oxc-project/types@0.130.0': {} + '@oxc-project/types@0.133.0': {} - '@oxfmt/binding-android-arm-eabi@0.50.0': + '@oxfmt/binding-android-arm-eabi@0.53.0': optional: true - '@oxfmt/binding-android-arm64@0.50.0': + '@oxfmt/binding-android-arm64@0.53.0': optional: true - '@oxfmt/binding-darwin-arm64@0.50.0': + '@oxfmt/binding-darwin-arm64@0.53.0': optional: true - '@oxfmt/binding-darwin-x64@0.50.0': + '@oxfmt/binding-darwin-x64@0.53.0': optional: true - '@oxfmt/binding-freebsd-x64@0.50.0': + '@oxfmt/binding-freebsd-x64@0.53.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.50.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.53.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.50.0': + '@oxfmt/binding-linux-arm-musleabihf@0.53.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.50.0': + '@oxfmt/binding-linux-arm64-gnu@0.53.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.50.0': + '@oxfmt/binding-linux-arm64-musl@0.53.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.50.0': + '@oxfmt/binding-linux-ppc64-gnu@0.53.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.50.0': + '@oxfmt/binding-linux-riscv64-gnu@0.53.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.50.0': + '@oxfmt/binding-linux-riscv64-musl@0.53.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.50.0': + '@oxfmt/binding-linux-s390x-gnu@0.53.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.50.0': + '@oxfmt/binding-linux-x64-gnu@0.53.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.50.0': + '@oxfmt/binding-linux-x64-musl@0.53.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.50.0': + '@oxfmt/binding-openharmony-arm64@0.53.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.50.0': + '@oxfmt/binding-win32-arm64-msvc@0.53.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.50.0': + '@oxfmt/binding-win32-ia32-msvc@0.53.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.50.0': + '@oxfmt/binding-win32-x64-msvc@0.53.0': optional: true - '@oxlint-tsgolint/darwin-arm64@0.22.1': + '@oxlint-tsgolint/darwin-arm64@0.23.0': optional: true - '@oxlint-tsgolint/darwin-x64@0.22.1': + '@oxlint-tsgolint/darwin-x64@0.23.0': optional: true - '@oxlint-tsgolint/linux-arm64@0.22.1': + '@oxlint-tsgolint/linux-arm64@0.23.0': optional: true - '@oxlint-tsgolint/linux-x64@0.22.1': + '@oxlint-tsgolint/linux-x64@0.23.0': optional: true - '@oxlint-tsgolint/win32-arm64@0.22.1': + '@oxlint-tsgolint/win32-arm64@0.23.0': optional: true - '@oxlint-tsgolint/win32-x64@0.22.1': + '@oxlint-tsgolint/win32-x64@0.23.0': optional: true - '@oxlint/binding-android-arm-eabi@1.65.0': + '@oxlint/binding-android-arm-eabi@1.68.0': optional: true - '@oxlint/binding-android-arm64@1.65.0': + '@oxlint/binding-android-arm64@1.68.0': optional: true - '@oxlint/binding-darwin-arm64@1.65.0': + '@oxlint/binding-darwin-arm64@1.68.0': optional: true - '@oxlint/binding-darwin-x64@1.65.0': + '@oxlint/binding-darwin-x64@1.68.0': optional: true - '@oxlint/binding-freebsd-x64@1.65.0': + '@oxlint/binding-freebsd-x64@1.68.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.65.0': + '@oxlint/binding-linux-arm-gnueabihf@1.68.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.65.0': + '@oxlint/binding-linux-arm-musleabihf@1.68.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.65.0': + '@oxlint/binding-linux-arm64-gnu@1.68.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.65.0': + '@oxlint/binding-linux-arm64-musl@1.68.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.65.0': + '@oxlint/binding-linux-ppc64-gnu@1.68.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.65.0': + '@oxlint/binding-linux-riscv64-gnu@1.68.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.65.0': + '@oxlint/binding-linux-riscv64-musl@1.68.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.65.0': + '@oxlint/binding-linux-s390x-gnu@1.68.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.65.0': + '@oxlint/binding-linux-x64-gnu@1.68.0': optional: true - '@oxlint/binding-linux-x64-musl@1.65.0': + '@oxlint/binding-linux-x64-musl@1.68.0': optional: true - '@oxlint/binding-openharmony-arm64@1.65.0': + '@oxlint/binding-openharmony-arm64@1.68.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.65.0': + '@oxlint/binding-win32-arm64-msvc@1.68.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.65.0': + '@oxlint/binding-win32-ia32-msvc@1.68.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.65.0': + '@oxlint/binding-win32-x64-msvc@1.68.0': optional: true - '@rolldown/binding-android-arm64@1.0.1': + '@rolldown/binding-android-arm64@1.0.3': optional: true - '@rolldown/binding-darwin-arm64@1.0.1': + '@rolldown/binding-darwin-arm64@1.0.3': optional: true - '@rolldown/binding-darwin-x64@1.0.1': + '@rolldown/binding-darwin-x64@1.0.3': optional: true - '@rolldown/binding-freebsd-x64@1.0.1': + '@rolldown/binding-freebsd-x64@1.0.3': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.1': + '@rolldown/binding-linux-arm64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.1': + '@rolldown/binding-linux-arm64-musl@1.0.3': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.1': + '@rolldown/binding-linux-ppc64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.1': + '@rolldown/binding-linux-s390x-gnu@1.0.3': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.1': + '@rolldown/binding-linux-x64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-x64-musl@1.0.1': + '@rolldown/binding-linux-x64-musl@1.0.3': optional: true - '@rolldown/binding-openharmony-arm64@1.0.1': + '@rolldown/binding-openharmony-arm64@1.0.3': optional: true - '@rolldown/binding-wasm32-wasi@1.0.1': + '@rolldown/binding-wasm32-wasi@1.0.3': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.1': + '@rolldown/binding-win32-arm64-msvc@1.0.3': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.1': + '@rolldown/binding-win32-x64-msvc@1.0.3': optional: true - '@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.1)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0))': + '@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7 picomatch: 4.0.4 - rolldown: 1.0.1 + rolldown: 1.0.3 optionalDependencies: - '@babel/runtime': 7.29.2 - vite: 8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0) + '@babel/runtime': 7.29.7 + vite: 8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0) '@rolldown/pluginutils@1.0.1': {} - '@smithy/chunked-blob-reader-native@4.2.3': - dependencies: - '@smithy/util-base64': 4.3.2 - tslib: 2.8.1 - - '@smithy/chunked-blob-reader@5.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/config-resolver@4.4.17': - dependencies: - '@smithy/node-config-provider': 4.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-config-provider': 4.2.2 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - tslib: 2.8.1 - - '@smithy/core@3.23.17': - dependencies: - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.25 - '@smithy/util-utf8': 4.2.2 - '@smithy/uuid': 1.1.2 - tslib: 2.8.1 - - '@smithy/credential-provider-imds@4.2.14': - dependencies: - '@smithy/node-config-provider': 4.3.14 - '@smithy/property-provider': 4.2.14 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - tslib: 2.8.1 - - '@smithy/eventstream-codec@4.2.14': + '@smithy/core@3.24.6': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.14.1 - '@smithy/util-hex-encoding': 4.2.2 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@4.2.14': + '@smithy/credential-provider-imds@4.3.8': dependencies: - '@smithy/eventstream-serde-universal': 4.2.14 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@4.3.14': + '@smithy/fetch-http-handler@5.4.6': dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/eventstream-serde-node@4.2.14': - dependencies: - '@smithy/eventstream-serde-universal': 4.2.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/eventstream-serde-universal@4.2.14': - dependencies: - '@smithy/eventstream-codec': 4.2.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/fetch-http-handler@5.3.17': - dependencies: - '@smithy/protocol-http': 5.3.14 - '@smithy/querystring-builder': 4.2.14 - '@smithy/types': 4.14.1 - '@smithy/util-base64': 4.3.2 - tslib: 2.8.1 - - '@smithy/hash-blob-browser@4.2.15': - dependencies: - '@smithy/chunked-blob-reader': 5.2.2 - '@smithy/chunked-blob-reader-native': 4.2.3 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/hash-node@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/hash-stream-node@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/invalid-dependency@4.2.14': - dependencies: - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@4.2.2': + '@smithy/node-http-handler@4.7.7': dependencies: + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@smithy/md5-js@4.2.14': + '@smithy/protocol-http@5.4.6': dependencies: - '@smithy/types': 4.14.1 - '@smithy/util-utf8': 4.2.2 + '@smithy/core': 3.24.6 tslib: 2.8.1 - '@smithy/middleware-content-length@4.2.14': + '@smithy/signature-v4@5.4.6': dependencies: - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.6 + '@smithy/types': 4.14.3 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.32': - dependencies: - '@smithy/core': 3.23.17 - '@smithy/middleware-serde': 4.2.20 - '@smithy/node-config-provider': 4.3.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-middleware': 4.2.14 - tslib: 2.8.1 - - '@smithy/middleware-retry@4.5.7': - dependencies: - '@smithy/core': 3.23.17 - '@smithy/node-config-provider': 4.3.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/service-error-classification': 4.3.1 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/uuid': 1.1.2 - tslib: 2.8.1 - - '@smithy/middleware-serde@4.2.20': - dependencies: - '@smithy/core': 3.23.17 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/middleware-stack@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/node-config-provider@4.3.14': - dependencies: - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/node-http-handler@4.6.1': - dependencies: - '@smithy/protocol-http': 5.3.14 - '@smithy/querystring-builder': 4.2.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/property-provider@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/protocol-http@5.3.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/querystring-builder@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - '@smithy/util-uri-escape': 4.2.2 - tslib: 2.8.1 - - '@smithy/querystring-parser@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/service-error-classification@4.3.1': - dependencies: - '@smithy/types': 4.14.1 - - '@smithy/shared-ini-file-loader@4.4.9': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/signature-v4@5.3.14': - dependencies: - '@smithy/is-array-buffer': 4.2.2 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-hex-encoding': 4.2.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-uri-escape': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/smithy-client@4.12.13': - dependencies: - '@smithy/core': 3.23.17 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-stack': 4.2.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-stream': 4.5.25 - tslib: 2.8.1 - - '@smithy/types@4.14.1': - dependencies: - tslib: 2.8.1 - - '@smithy/url-parser@4.2.14': - dependencies: - '@smithy/querystring-parser': 4.2.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-base64@4.3.2': - dependencies: - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-body-length-browser@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-body-length-node@4.2.3': + '@smithy/types@4.14.3': dependencies: tslib: 2.8.1 @@ -3966,87 +3262,11 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.2.2': - dependencies: - '@smithy/is-array-buffer': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-config-provider@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-defaults-mode-browser@4.3.49': - dependencies: - '@smithy/property-provider': 4.2.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-defaults-mode-node@4.2.54': - dependencies: - '@smithy/config-resolver': 4.4.17 - '@smithy/credential-provider-imds': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/property-provider': 4.2.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-endpoints@3.4.2': - dependencies: - '@smithy/node-config-provider': 4.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-hex-encoding@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-middleware@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-retry@4.3.8': - dependencies: - '@smithy/service-error-classification': 4.3.1 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-stream@4.5.25': - dependencies: - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/node-http-handler': 4.6.1 - '@smithy/types': 4.14.1 - '@smithy/util-base64': 4.3.2 - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-hex-encoding': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-uri-escape@4.2.2': - dependencies: - tslib: 2.8.1 - '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.2.2': - dependencies: - '@smithy/util-buffer-from': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-waiter@4.3.0': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/uuid@1.1.2': - dependencies: - tslib: 2.8.1 - '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} @@ -4112,16 +3332,16 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0) - '@tanstack/eslint-plugin-query@5.100.10(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@tanstack/eslint-plugin-query@5.101.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) eslint: 9.39.4(jiti@2.7.0) optionalDependencies: typescript: 6.0.3 @@ -4138,81 +3358,76 @@ snapshots: '@tanstack/history@1.162.0': {} - '@tanstack/query-core@5.100.10': {} + '@tanstack/query-core@5.101.0': {} - '@tanstack/react-query@5.100.10(react@19.2.6)': + '@tanstack/react-query@5.101.0(react@19.2.7)': dependencies: - '@tanstack/query-core': 5.100.10 - react: 19.2.6 + '@tanstack/query-core': 5.101.0 + react: 19.2.7 - '@tanstack/react-router@1.170.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@tanstack/react-router@1.170.13(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@tanstack/history': 1.162.0 - '@tanstack/react-store': 0.9.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@tanstack/router-core': 1.170.1 - isbot: 5.1.40 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@tanstack/react-store': 0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.11 + isbot: 5.1.41 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - '@tanstack/react-store@0.9.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@tanstack/react-store@0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@tanstack/store': 0.9.3 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - use-sync-external-store: 1.6.0(react@19.2.6) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + use-sync-external-store: 1.6.0(react@19.2.7) - '@tanstack/router-core@1.170.1': + '@tanstack/router-core@1.171.11': dependencies: '@tanstack/history': 1.162.0 cookie-es: 3.1.1 seroval: 1.5.4 seroval-plugins: 1.5.4(seroval@1.5.4) - '@tanstack/router-generator@1.167.1': + '@tanstack/router-generator@1.167.15': dependencies: - '@babel/types': 7.29.0 - '@tanstack/router-core': 1.170.1 - '@tanstack/router-utils': 1.162.0 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.11 + '@tanstack/router-utils': 1.162.2 '@tanstack/virtual-file-routes': 1.162.0 jiti: 2.7.0 magic-string: 0.30.21 prettier: 3.8.3 - zod: 3.25.76 + zod: 4.4.3 transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.168.1(@tanstack/react-router@1.170.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0))': + '@tanstack/router-plugin@1.168.16(@tanstack/react-router@1.170.13(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@tanstack/router-core': 1.170.1 - '@tanstack/router-generator': 1.167.1 - '@tanstack/router-utils': 1.162.0 - '@tanstack/virtual-file-routes': 1.162.0 - chokidar: 3.6.0 + '@babel/core': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.11 + '@tanstack/router-generator': 1.167.15 + '@tanstack/router-utils': 1.162.2 + chokidar: 5.0.0 unplugin: 3.0.0 - zod: 3.25.76 + zod: 4.4.3 optionalDependencies: - '@tanstack/react-router': 1.170.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - vite: 8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0) + '@tanstack/react-router': 1.170.13(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + vite: 8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@tanstack/router-utils@1.162.0': + '@tanstack/router-utils@1.162.2': dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 - ansis: 4.3.0 + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + ansis: 4.3.1 babel-dead-code-elimination: 1.0.12 diff: 8.0.4 pathe: 2.0.3 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 transitivePeerDependencies: - supports-color @@ -4222,8 +3437,8 @@ snapshots: '@testing-library/dom@10.4.1': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.29.2 + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -4240,15 +3455,15 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@babel/runtime': 7.29.2 '@testing-library/dom': 10.4.1 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': dependencies: @@ -4272,15 +3487,19 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@24.12.3': + '@types/node@24.13.1': dependencies: - undici-types: 7.16.0 + undici-types: 7.18.2 - '@types/react-dom@19.2.3(@types/react@19.2.14)': + '@types/node@25.9.2': dependencies: - '@types/react': 19.2.14 + undici-types: 7.24.6 - '@types/react@19.2.14': + '@types/react-dom@19.2.3(@types/react@19.2.17)': + dependencies: + '@types/react': 19.2.17 + + '@types/react@19.2.17': dependencies: csstype: 3.2.3 @@ -4288,7 +3507,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 24.12.3 + '@types/node': 25.9.2 '@typescript-eslint/project-service@8.59.3(typescript@6.0.3)': dependencies: @@ -4299,17 +3518,37 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.60.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@6.0.3) + '@typescript-eslint/types': 8.60.1 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.59.3': dependencies: '@typescript-eslint/types': 8.59.3 '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/scope-manager@8.60.1': + dependencies: + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/visitor-keys': 8.60.1 + '@typescript-eslint/tsconfig-utils@8.59.3(typescript@6.0.3)': dependencies: typescript: 6.0.3 + '@typescript-eslint/tsconfig-utils@8.60.1(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + '@typescript-eslint/types@8.59.3': {} + '@typescript-eslint/types@8.60.1': {} + '@typescript-eslint/typescript-estree@8.59.3(typescript@6.0.3)': dependencies: '@typescript-eslint/project-service': 8.59.3(typescript@6.0.3) @@ -4319,7 +3558,22 @@ snapshots: debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.0 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.60.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.60.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@6.0.3) + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/visitor-keys': 8.60.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.2 + tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -4336,106 +3590,122 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.60.1 + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.59.3': dependencies: '@typescript-eslint/types': 8.59.3 eslint-visitor-keys: 5.0.1 - '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260515.1': - optional: true - - '@typescript/native-preview-darwin-x64@7.0.0-dev.20260515.1': - optional: true - - '@typescript/native-preview-linux-arm64@7.0.0-dev.20260515.1': - optional: true - - '@typescript/native-preview-linux-arm@7.0.0-dev.20260515.1': - optional: true - - '@typescript/native-preview-linux-x64@7.0.0-dev.20260515.1': - optional: true - - '@typescript/native-preview-win32-arm64@7.0.0-dev.20260515.1': - optional: true - - '@typescript/native-preview-win32-x64@7.0.0-dev.20260515.1': - optional: true - - '@typescript/native-preview@7.0.0-dev.20260515.1': - optionalDependencies: - '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260515.1 - '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260515.1 - '@typescript/native-preview-linux-arm': 7.0.0-dev.20260515.1 - '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260515.1 - '@typescript/native-preview-linux-x64': 7.0.0-dev.20260515.1 - '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260515.1 - '@typescript/native-preview-win32-x64': 7.0.0-dev.20260515.1 - - '@vitejs/plugin-basic-ssl@2.3.0(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0))': + '@typescript-eslint/visitor-keys@8.60.1': dependencies: - vite: 8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0) + '@typescript-eslint/types': 8.60.1 + eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.1)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0))': + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260605.1': + optional: true + + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260605.1': + optional: true + + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260605.1': + optional: true + + '@typescript/native-preview-linux-arm@7.0.0-dev.20260605.1': + optional: true + + '@typescript/native-preview-linux-x64@7.0.0-dev.20260605.1': + optional: true + + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260605.1': + optional: true + + '@typescript/native-preview-win32-x64@7.0.0-dev.20260605.1': + optional: true + + '@typescript/native-preview@7.0.0-dev.20260605.1': + optionalDependencies: + '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260605.1 + '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260605.1 + '@typescript/native-preview-linux-arm': 7.0.0-dev.20260605.1 + '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260605.1 + '@typescript/native-preview-linux-x64': 7.0.0-dev.20260605.1 + '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260605.1 + '@typescript/native-preview-win32-x64': 7.0.0-dev.20260605.1 + + '@vitejs/plugin-basic-ssl@2.3.0(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0))': + dependencies: + vite: 8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0) + + '@vitejs/plugin-react@6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0) optionalDependencies: - '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.1)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) + '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) babel-plugin-react-compiler: 1.0.0 - '@vitest/coverage-v8@4.1.6(vitest@4.1.6)': + '@vitest/coverage-v8@4.1.8(vitest@4.1.8)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.6 - ast-v8-to-istanbul: 1.0.0 + '@vitest/utils': 4.1.8 + ast-v8-to-istanbul: 1.0.3 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 magicast: 0.5.3 - obug: 2.1.1 + obug: 2.1.2 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.6(@types/node@24.12.3)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.10.1)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) - '@vitest/expect@4.1.6': + '@vitest/expect@4.1.8': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.6(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.6 + '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0) - '@vitest/pretty-format@4.1.6': + '@vitest/pretty-format@4.1.8': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.6': + '@vitest/runner@4.1.8': dependencies: - '@vitest/utils': 4.1.6 + '@vitest/utils': 4.1.8 pathe: 2.0.3 - '@vitest/snapshot@4.1.6': + '@vitest/snapshot@4.1.8': dependencies: - '@vitest/pretty-format': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/pretty-format': 4.1.8 + '@vitest/utils': 4.1.8 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.6': {} + '@vitest/spy@4.1.8': {} - '@vitest/utils@4.1.6': + '@vitest/utils@4.1.8': dependencies: - '@vitest/pretty-format': 4.1.6 + '@vitest/pretty-format': 4.1.8 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -4460,12 +3730,7 @@ snapshots: ansi-styles@5.2.0: {} - ansis@4.3.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.2 + ansis@4.3.1: {} argparse@2.0.1: {} @@ -4477,7 +3742,7 @@ snapshots: assertion-error@2.0.1: {} - ast-v8-to-istanbul@1.0.0: + ast-v8-to-istanbul@1.0.3: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -4485,10 +3750,10 @@ snapshots: babel-dead-code-elimination@1.0.12: dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.3 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color @@ -4502,13 +3767,11 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.29: {} - - binary-extensions@2.3.0: {} + baseline-browser-mapping@2.10.34: {} bowser@2.14.1: {} - brace-expansion@1.1.14: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 @@ -4517,18 +3780,18 @@ snapshots: dependencies: balanced-match: 4.0.4 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.29 - caniuse-lite: 1.0.30001792 - electron-to-chromium: 1.5.356 - node-releases: 2.0.44 + baseline-browser-mapping: 2.10.34 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.368 + node-releases: 2.0.47 update-browserslist-db: 1.2.3(browserslist@4.28.2) + buffer-image-size@0.6.4: + dependencies: + '@types/node': 25.9.2 + buffer@5.6.0: dependencies: base64-js: 1.5.1 @@ -4536,7 +3799,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001792: {} + caniuse-lite@1.0.30001793: {} chai@6.2.2: {} @@ -4545,17 +3808,9 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chokidar@3.6.0: + chokidar@5.0.0: dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + readdirp: 5.0.0 citty@0.2.2: {} @@ -4607,7 +3862,7 @@ snapshots: dom-accessibility-api@0.6.3: {} - electron-to-chromium@1.5.356: {} + electron-to-chromium@1.5.368: {} enhanced-resolve@5.21.2: dependencies: @@ -4712,7 +3967,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-wrap-ansi@0.2.0: + fast-wrap-ansi@0.2.2: dependencies: fast-string-width: 3.0.2 @@ -4721,7 +3976,7 @@ snapshots: path-expression-matcher: 1.5.0 xml-naming: 0.1.0 - fast-xml-parser@5.7.2: + fast-xml-parser@5.7.3: dependencies: '@nodable/entities': 2.1.0 fast-xml-builder: 1.2.0 @@ -4736,10 +3991,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -4757,10 +4008,6 @@ snapshots: gensync@1.0.0-beta.2: {} - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -4775,14 +4022,15 @@ snapshots: graceful-fs@4.2.11: {} - happy-dom@20.9.0: + happy-dom@20.10.1: dependencies: - '@types/node': 24.12.3 + '@types/node': 25.9.2 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 + buffer-image-size: 0.6.4 entities: 7.0.1 whatwg-mimetype: 3.0.0 - ws: 8.20.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -4806,19 +4054,13 @@ snapshots: inherits@2.0.4: {} - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-extglob@2.1.1: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-number@7.0.0: {} - - isbot@5.1.40: {} + isbot@5.1.41: {} isexe@2.0.0: {} @@ -4841,7 +4083,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.1.1: + js-yaml@4.2.0: dependencies: argparse: 2.0.1 @@ -4921,15 +4163,15 @@ snapshots: lodash.merge@4.6.2: {} - lru-cache@11.3.6: {} + lru-cache@11.5.1: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lucide-react@1.16.0(react@19.2.6): + lucide-react@1.17.0(react@19.2.7): dependencies: - react: 19.2.6 + react: 19.2.7 lz-string@1.5.0: {} @@ -4939,13 +4181,13 @@ snapshots: magicast@0.5.3: dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 source-map-js: 1.2.1 make-dir@4.0.0: dependencies: - semver: 7.8.0 + semver: 7.8.2 min-indent@1.0.1: {} @@ -4955,7 +4197,7 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 1.1.15 minipass@7.1.3: {} @@ -4965,17 +4207,15 @@ snapshots: natural-compare@1.4.0: {} - node-releases@2.0.44: {} - - normalize-path@3.0.0: {} + node-releases@2.0.47: {} nypm@0.6.6: dependencies: citty: 0.2.2 pathe: 2.0.3 - tinyexec: 1.1.2 + tinyexec: 1.2.4 - obug@2.1.1: {} + obug@2.1.2: {} optionator@0.9.4: dependencies: @@ -4986,61 +4226,61 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - oxfmt@0.50.0: + oxfmt@0.53.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.50.0 - '@oxfmt/binding-android-arm64': 0.50.0 - '@oxfmt/binding-darwin-arm64': 0.50.0 - '@oxfmt/binding-darwin-x64': 0.50.0 - '@oxfmt/binding-freebsd-x64': 0.50.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.50.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.50.0 - '@oxfmt/binding-linux-arm64-gnu': 0.50.0 - '@oxfmt/binding-linux-arm64-musl': 0.50.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.50.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.50.0 - '@oxfmt/binding-linux-riscv64-musl': 0.50.0 - '@oxfmt/binding-linux-s390x-gnu': 0.50.0 - '@oxfmt/binding-linux-x64-gnu': 0.50.0 - '@oxfmt/binding-linux-x64-musl': 0.50.0 - '@oxfmt/binding-openharmony-arm64': 0.50.0 - '@oxfmt/binding-win32-arm64-msvc': 0.50.0 - '@oxfmt/binding-win32-ia32-msvc': 0.50.0 - '@oxfmt/binding-win32-x64-msvc': 0.50.0 + '@oxfmt/binding-android-arm-eabi': 0.53.0 + '@oxfmt/binding-android-arm64': 0.53.0 + '@oxfmt/binding-darwin-arm64': 0.53.0 + '@oxfmt/binding-darwin-x64': 0.53.0 + '@oxfmt/binding-freebsd-x64': 0.53.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.53.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.53.0 + '@oxfmt/binding-linux-arm64-gnu': 0.53.0 + '@oxfmt/binding-linux-arm64-musl': 0.53.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.53.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.53.0 + '@oxfmt/binding-linux-riscv64-musl': 0.53.0 + '@oxfmt/binding-linux-s390x-gnu': 0.53.0 + '@oxfmt/binding-linux-x64-gnu': 0.53.0 + '@oxfmt/binding-linux-x64-musl': 0.53.0 + '@oxfmt/binding-openharmony-arm64': 0.53.0 + '@oxfmt/binding-win32-arm64-msvc': 0.53.0 + '@oxfmt/binding-win32-ia32-msvc': 0.53.0 + '@oxfmt/binding-win32-x64-msvc': 0.53.0 - oxlint-tsgolint@0.22.1: + oxlint-tsgolint@0.23.0: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 0.22.1 - '@oxlint-tsgolint/darwin-x64': 0.22.1 - '@oxlint-tsgolint/linux-arm64': 0.22.1 - '@oxlint-tsgolint/linux-x64': 0.22.1 - '@oxlint-tsgolint/win32-arm64': 0.22.1 - '@oxlint-tsgolint/win32-x64': 0.22.1 + '@oxlint-tsgolint/darwin-arm64': 0.23.0 + '@oxlint-tsgolint/darwin-x64': 0.23.0 + '@oxlint-tsgolint/linux-arm64': 0.23.0 + '@oxlint-tsgolint/linux-x64': 0.23.0 + '@oxlint-tsgolint/win32-arm64': 0.23.0 + '@oxlint-tsgolint/win32-x64': 0.23.0 - oxlint@1.65.0(oxlint-tsgolint@0.22.1): + oxlint@1.68.0(oxlint-tsgolint@0.23.0): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.65.0 - '@oxlint/binding-android-arm64': 1.65.0 - '@oxlint/binding-darwin-arm64': 1.65.0 - '@oxlint/binding-darwin-x64': 1.65.0 - '@oxlint/binding-freebsd-x64': 1.65.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.65.0 - '@oxlint/binding-linux-arm-musleabihf': 1.65.0 - '@oxlint/binding-linux-arm64-gnu': 1.65.0 - '@oxlint/binding-linux-arm64-musl': 1.65.0 - '@oxlint/binding-linux-ppc64-gnu': 1.65.0 - '@oxlint/binding-linux-riscv64-gnu': 1.65.0 - '@oxlint/binding-linux-riscv64-musl': 1.65.0 - '@oxlint/binding-linux-s390x-gnu': 1.65.0 - '@oxlint/binding-linux-x64-gnu': 1.65.0 - '@oxlint/binding-linux-x64-musl': 1.65.0 - '@oxlint/binding-openharmony-arm64': 1.65.0 - '@oxlint/binding-win32-arm64-msvc': 1.65.0 - '@oxlint/binding-win32-ia32-msvc': 1.65.0 - '@oxlint/binding-win32-x64-msvc': 1.65.0 - oxlint-tsgolint: 0.22.1 + '@oxlint/binding-android-arm-eabi': 1.68.0 + '@oxlint/binding-android-arm64': 1.68.0 + '@oxlint/binding-darwin-arm64': 1.68.0 + '@oxlint/binding-darwin-x64': 1.68.0 + '@oxlint/binding-freebsd-x64': 1.68.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.68.0 + '@oxlint/binding-linux-arm-musleabihf': 1.68.0 + '@oxlint/binding-linux-arm64-gnu': 1.68.0 + '@oxlint/binding-linux-arm64-musl': 1.68.0 + '@oxlint/binding-linux-ppc64-gnu': 1.68.0 + '@oxlint/binding-linux-riscv64-gnu': 1.68.0 + '@oxlint/binding-linux-riscv64-musl': 1.68.0 + '@oxlint/binding-linux-s390x-gnu': 1.68.0 + '@oxlint/binding-linux-x64-gnu': 1.68.0 + '@oxlint/binding-linux-x64-musl': 1.68.0 + '@oxlint/binding-openharmony-arm64': 1.68.0 + '@oxlint/binding-win32-arm64-msvc': 1.68.0 + '@oxlint/binding-win32-ia32-msvc': 1.68.0 + '@oxlint/binding-win32-x64-msvc': 1.68.0 + oxlint-tsgolint: 0.23.0 p-limit@3.1.0: dependencies: @@ -5062,18 +4302,16 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.3.6 + lru-cache: 11.5.1 minipass: 7.1.3 pathe@2.0.3: {} picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.4: {} - postcss@8.5.14: + postcss@8.5.15: dependencies: nanoid: 3.3.12 picocolors: 1.1.1 @@ -5091,18 +4329,18 @@ snapshots: punycode@2.3.1: {} - react-dom@19.2.6(react@19.2.6): + react-dom@19.2.7(react@19.2.7): dependencies: - react: 19.2.6 + react: 19.2.7 scheduler: 0.27.0 - react-hook-form@7.76.0(react@19.2.6): + react-hook-form@7.77.0(react@19.2.7): dependencies: - react: 19.2.6 + react: 19.2.7 react-is@17.0.2: {} - react@19.2.6: {} + react@19.2.7: {} readable-stream@3.6.2: dependencies: @@ -5110,39 +4348,37 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.2 + readdirp@5.0.0: {} redent@3.0.0: dependencies: indent-string: 4.0.0 strip-indent: 3.0.0 - reselect@5.1.1: {} + reselect@5.2.0: {} resolve-from@4.0.0: {} - rolldown@1.0.1: + rolldown@1.0.3: dependencies: - '@oxc-project/types': 0.130.0 + '@oxc-project/types': 0.133.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.1 - '@rolldown/binding-darwin-arm64': 1.0.1 - '@rolldown/binding-darwin-x64': 1.0.1 - '@rolldown/binding-freebsd-x64': 1.0.1 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.1 - '@rolldown/binding-linux-arm64-gnu': 1.0.1 - '@rolldown/binding-linux-arm64-musl': 1.0.1 - '@rolldown/binding-linux-ppc64-gnu': 1.0.1 - '@rolldown/binding-linux-s390x-gnu': 1.0.1 - '@rolldown/binding-linux-x64-gnu': 1.0.1 - '@rolldown/binding-linux-x64-musl': 1.0.1 - '@rolldown/binding-openharmony-arm64': 1.0.1 - '@rolldown/binding-wasm32-wasi': 1.0.1 - '@rolldown/binding-win32-arm64-msvc': 1.0.1 - '@rolldown/binding-win32-x64-msvc': 1.0.1 + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 safe-buffer@5.2.1: {} @@ -5152,6 +4388,8 @@ snapshots: semver@7.8.0: {} + semver@7.8.2: {} + seroval-plugins@1.5.4(seroval@1.5.4): dependencies: seroval: 1.5.4 @@ -5203,9 +4441,9 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.1.2: {} + tinyexec@1.2.4: {} - tinyglobby@0.2.16: + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 @@ -5214,10 +4452,6 @@ snapshots: tinyrainbow@3.1.0: {} - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - ts-api-utils@2.5.0(typescript@6.0.3): dependencies: typescript: 6.0.3 @@ -5230,9 +4464,9 @@ snapshots: typescript@6.0.3: {} - ultracite@7.7.0(oxfmt@0.50.0)(oxlint@1.65.0(oxlint-tsgolint@0.22.1)): + ultracite@7.8.1(oxfmt@0.53.0)(oxlint@1.68.0(oxlint-tsgolint@0.23.0)): dependencies: - '@clack/prompts': 1.4.0 + '@clack/prompts': 1.5.1 commander: 14.0.3 cross-spawn: 7.0.6 deepmerge: 4.3.1 @@ -5242,10 +4476,12 @@ snapshots: yaml: 2.9.0 zod: 4.4.3 optionalDependencies: - oxfmt: 0.50.0 - oxlint: 1.65.0(oxlint-tsgolint@0.22.1) + oxfmt: 0.53.0 + oxlint: 1.68.0(oxlint-tsgolint@0.23.0) - undici-types@7.16.0: {} + undici-types@7.18.2: {} + + undici-types@7.24.6: {} unplugin@3.0.0: dependencies: @@ -5263,51 +4499,51 @@ snapshots: dependencies: punycode: 2.3.1 - use-sync-external-store@1.6.0(react@19.2.6): + use-sync-external-store@1.6.0(react@19.2.7): dependencies: - react: 19.2.6 + react: 19.2.7 util-deprecate@1.0.2: {} - vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0): + vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 - postcss: 8.5.14 - rolldown: 1.0.1 - tinyglobby: 0.2.16 + postcss: 8.5.15 + rolldown: 1.0.3 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 24.12.3 + '@types/node': 24.13.1 fsevents: 2.3.3 jiti: 2.7.0 yaml: 2.9.0 - vitest@4.1.6(@types/node@24.12.3)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)): + vitest@4.1.8(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.10.1)(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)): dependencies: - '@vitest/expect': 4.1.6 - '@vitest/mocker': 4.1.6(vite@8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.6 - '@vitest/runner': 4.1.6 - '@vitest/snapshot': 4.1.6 - '@vitest/spy': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/expect': 4.1.8 + '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.8 + '@vitest/runner': 4.1.8 + '@vitest/snapshot': 4.1.8 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 - obug: 2.1.1 + obug: 2.1.2 pathe: 2.0.3 picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.1.2 - tinyglobby: 0.2.16 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.13(@types/node@24.12.3)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@24.13.1)(jiti@2.7.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.12.3 - '@vitest/coverage-v8': 4.1.6(vitest@4.1.6) - happy-dom: 20.9.0 + '@types/node': 24.13.1 + '@vitest/coverage-v8': 4.1.8(vitest@4.1.8) + happy-dom: 20.10.1 transitivePeerDependencies: - msw @@ -5326,7 +4562,7 @@ snapshots: word-wrap@1.2.5: {} - ws@8.20.0: {} + ws@8.21.0: {} xml-naming@0.1.0: {} @@ -5336,6 +4572,4 @@ snapshots: yocto-queue@0.1.0: {} - zod@3.25.76: {} - zod@4.4.3: {} diff --git a/spinifex/services/spinifexui/frontend/src/components/elbv2/certificate-import-dialog.tsx b/spinifex/services/spinifexui/frontend/src/components/elbv2/certificate-import-dialog.tsx new file mode 100644 index 000000000..1c5a65080 --- /dev/null +++ b/spinifex/services/spinifexui/frontend/src/components/elbv2/certificate-import-dialog.tsx @@ -0,0 +1,151 @@ +import { useState } from "react" + +import { ErrorBanner } from "@/components/error-banner" +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog" +import { Field, FieldTitle } from "@/components/ui/field" +import { Textarea } from "@/components/ui/textarea" +import { useImportCertificate } from "@/mutations/acm" + +interface CertificateImportDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + // Called with the new certificate ARN once the import succeeds. + onImported: (certificateArn: string) => void +} + +export function CertificateImportDialog({ + open, + onOpenChange, + onImported, +}: CertificateImportDialogProps) { + const [certificate, setCertificate] = useState("") + const [privateKey, setPrivateKey] = useState("") + const [certificateChain, setCertificateChain] = useState("") + const importMutation = useImportCertificate() + + const reset = () => { + setCertificate("") + setPrivateKey("") + setCertificateChain("") + importMutation.reset() + } + + const handleOpenChange = (next: boolean) => { + if (!next) { + reset() + } + onOpenChange(next) + } + + const canSubmit = + certificate.trim().length > 0 && privateKey.trim().length > 0 + + const handleImport = async () => { + if (!canSubmit) { + return + } + try { + const result = await importMutation.mutateAsync({ + certificate, + privateKey, + certificateChain, + }) + if (result.CertificateArn) { + onImported(result.CertificateArn) + } + reset() + onOpenChange(false) + } catch { + // surfaced via importMutation.error + } + } + + return ( + + + + Import certificate + + Paste a PEM-encoded certificate and its private key. The optional + chain is appended for intermediate CAs. + + + + {importMutation.error && ( + + )} + +
+ + + + +